TLDR: If you run into issues with the packageManager field in JS projects, you have to set COREPACK_ENABLE_AUTO_PIN to 0 in your bash profile and remove the existing packageManager field from your package.jsons.
In my experience, package.json was a stable, proven system that worked. Then came corepack's introduction of adding packageManager in each package.json, even when no one asked for it, for example when simply running “pnpm i”.
The idea is that to enforce reproducibility, you have to pin not just your packages, but also your package manager - and not just to a major version, but right down to the patch version.
In a kind of micro-management style, node/corepack now takes control from the package managers. Instead of trusting them not to break reproducibility, they introduce "phantom" package managers based on which directory you're in.
So for example, if I run:
$HOME/.nvm/versions/node/v20.17.0/bin/pnpm --version
it reports "9.10.0" when I'm in one folder and "9.15.2" when I'm in another directory. This totally breaks how "bin" files are supposed to work on UNIX/Linux/macOS. One "bin" file should equal one version.
So what’s in the file? It seems to be a wrapper calling corepack.cjs:
#!/usr/bin/env node
process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT??='1'
require('./lib/corepack.cjs').runMain(['pnpm', ...process.argv.slice(2)]);
Unfortunately corepack.cjs is a unreadable/bundled file, so it’s not easy to figure out what’s happening inside.
They added a possible way to turn this behaviour off (set COREPACK_ENABLE_AUTO_PIN to 0), but hid it in an ENV var, so it cannot be set in a repo - it has to be set on each contributor's machine. The first Google result for this COREPACK_ENABLE_AUTO_PIN env var is a GitHub issue, explaining why the default setting breaks open-source workflows, as each contributor is required to disable it in their local environment - it cannot be configured on repo level.
Moreover it's broken. Every time I run “pnpm i”, it annoys me to upgrade pnpm:
Except, when I run the displayed command:
corepack install -g pnpm@9.15.2
it does nothing. It definitely doesn't update the packageManager field in my package.json, so my project is always left on the old version. This means that tomorrow, it can tell me again that "Update is available!"
The confusing thing is that the update notice links to pnpm’s GitHub and even to their X page. There's not a single mention that its corepack which is artificially keeping back the package manager's version.
I opened a thread on pnpm where they recommended me to turn off this behavior of corepack. Which is basically the only choice they have, as corepack is breaking this system above their head and then users report thinking that pnpm is broken.
GitHub links
Open source maintainers explaining why it's not working (29 upvotes) https://github.com/nodejs/corepack/issues/485
My issue for pnpm trying to figure out what's happening: https://github.com/orgs/pnpm/discussions/8911
My issue for corepack: https://github.com/nodejs/corepack/issues/587
You have to explicitly pin and use the new version in the project (because the old version is still pinned):
1. Without doing `install -g` you can use `corepack up` to bump up (both installing and using) to the latest minor version. This doesn't work for major version upgrades.
2. Without doing `install -g` you can use `corepack use pnpm@latest` to bump up to the latest version. This works for both major and minor versions, and it also does the install.