I'm always amazed at how the Node ecosystem manages to overcomplicate simple things that used to work perfectly. package.json was one stable, proven system that worked, while Python had half a dozen semi-compatible approaches to package management (until uv solved it).
Enter node/corepack's introduction of adding packageManager
in each package.json. 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 totally breaks open-source projects, as each contributor is required to disable it in their local environment - it cannot be configured on repo level.
So the first step was taking control from package managers and having it centrally overridden with these fake phantom "bin" files.
Second, it's downright 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