Skip to content

feat: support pnpm v12, which is distributed as a native executable - #887

Open
zkochan wants to merge 3 commits into
nodejs:mainfrom
zkochan:feat/pnpm-v12-native-executable
Open

feat: support pnpm v12, which is distributed as a native executable#887
zkochan wants to merge 3 commits into
nodejs:mainfrom
zkochan:feat/pnpm-v12-native-executable

Conversation

@zkochan

@zkochan zkochan commented Aug 8, 2026

Copy link
Copy Markdown

Fixes: #873
Refs: #775
Refs: pnpm/pnpm#13018

Problem

Starting with v12, pnpm is a native executable (a Rust port). The pnpm package on npm only ships shebang-less placeholders for its binaries; the actual platform-specific executable lives in a companion @pnpm/exe.<platform> package (pinned in the optionalDependencies of the main package), and a preinstall lifecycle script copies it over the placeholders. Corepack neither runs lifecycle scripts nor installs optionalDependencies, so corepack use pnpm@next-12 currently fails with MODULE_NOT_FOUND (#873). pnpm won't be adding a backward-compatible JS shim, as it would defeat the point of the native port (see pnpm/pnpm#13018 (comment)).

Solution

This PR replicates the effect of pnpm's install script inside Corepack, driven by a new optional nativePackages field in config.json range definitions (mapping ${platform}-${arch}[-musl] keys to {package, bin}):

  • Install: after downloading the main package, Corepack reads the companion package version from the downloaded package.json's optionalDependencies, downloads the companion package for the current platform (with the same signature + integrity verification as the main tarball), and hardlinks its executable over each placeholder bin. The pnpm executable adapts its behavior to the name it was invoked under, which is what keeps pnpx (= pnpm dlx) working.
  • Run: a native executable can't be loaded into the current Node.js process with Module.runMain, so it is spawned as a child process, with signal forwarding and exit-code propagation.
  • Cache recovery: installs of pnpm >=12 performed by previous Corepack releases "succeeded" but recorded bin paths that don't exist (./bin/pnpm.mjs). Such installs are detected on reuse and silently redone, so users don't need to clear COREPACK_HOME by hand.

The libc detection (glibc vs musl via process.report) and the platform table mirror the logic of pnpm's own install.js.

Testing

  • New integration test against the mock registry covering install, the pnpx alias, signature verification of the companion package, and cache reuse (skipped on Windows, where the fake native executable — a shell script — cannot be spawned).
  • Manually verified on Linux against the real registry with pnpm@12.0.0-rc.1:
    • corepack pnpm --version, corepack pnpm install, and corepack pnpx --help (correctly maps to pnpm dlx) all work;
    • the pnpm/pnpx bins are hardlinks of the same 38 MB native executable;
    • a cache previously poisoned by Corepack 0.35.0 is detected and reinstalled transparently;
    • pnpm <12, npm, and yarn flows are unchanged (full test suite passes).

🤖 Generated with Claude Code

@zkochan

zkochan commented Aug 8, 2026

Copy link
Copy Markdown
Author

First I wanted to add support for yarn 6 as well but then I have found out they don't recommend corepack for yarn anymore.

@MikeMcC399

MikeMcC399 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Hi @zkochan

Thanks for the PR! I tested it locally and it looks good to me. 🚀 Hopefully a maintainer will be able to review soon!

$ corepack use pnpm@next-12
Installing pnpm@12.0.0-rc.1 in the project...

Already up to date
Done in 4.6s using pnpm v12.0.0-rc.1

First I wanted to add support for yarn 6 as well but then I have found out they don't recommend corepack for yarn anymore.

Yarn 6 (currently in beta status) is moving to Yarn Switch.
Yarn 4, the current latest version, still recommends Corepack - see https://yarnpkg.com/getting-started/install

@aduh95

aduh95 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

FWIW Yarn v6 ships the following script to keep backward compat with Corepack: https://repo.yarnpkg.com/6.0.0/packages/yarnpkg-cli/bin/yarn.js
TBH Corepack makes sense for CJS-based package managers, otherwise it's pure overhead, so it's probably a good idea to no longer list it as a recommended installation method 👍 That being said, as long as Node.js 24 is not EOL and Corepack is shipped alongside it, it makes sense to keep that option open for users, so thanks for the PR.

@MikeMcC399

This comment was marked as outdated.

@zkochan

zkochan commented Aug 12, 2026

Copy link
Copy Markdown
Author

Probably best to deprecate corepack altogether as it has lost relevance.

@aduh95

aduh95 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Probably best to deprecate corepack altogether as it has lost relevance.

I don't think that's accurate, there are many users of the CJS-based package managers (including pnpm) that rely on Corepack

Comment thread sources/corepackUtils.ts Outdated
// builds leave it unset. `process.report` may be unavailable, in which case
// we default to glibc.
try {
const report = process.report?.getReport() as any;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as any looks wrong here. We should have the types or at least cast the expected types

Comment thread sources/corepackUtils.ts Outdated
// we default to glibc.
try {
const report = process.report?.getReport() as any;
if (report == null)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets also disable network to speed this up. See this:

lovell/detect-libc#21

@MikeMcC399

Copy link
Copy Markdown
Contributor

@zkochan

Probably best to deprecate corepack altogether as it has lost relevance.

  • Release corepack@1.0.0 #687 (comment) is probably a better place to follow the discussion about the experimental status of Corepack. If you want to propose a change of status to deprecated, that would be a significant change, and best covered in an issue of its own. npm registry downloads have increased from 2.5 Mio / week, this time last year, to 4.8 Mio / week now, as a measurable indicator of relevance.

As far as pnpm is concerned, since you have alternatives, it is understandable for you to de-prioritize Corepack. I see that you removed it from the https://pnpm.io/installation page.

If you are able to continue with this PR, then your pnpm users who would like to migrate to pnpm 12 and (continue to) use Corepack would benefit. Otherwise I'm not sure if anybody else would pick up your PR material and bring it to completion. In that case the Corepack documentation should probably list the incompatibility with pnpm 12.

It looks like you put significant effort into this PR, so I do hope you will be able to continue - meaning rebasing, resolving conflicts and responding to review comments.

@karlhorky

karlhorky commented Aug 13, 2026

Copy link
Copy Markdown

If you are able to continue with this PR, then your pnpm users who would like to migrate to pnpm 12 and (continue to) use Corepack would benefit.

@MikeMcC399 Corepack doesn't work with pnpm anymore, so maybe this is moot:

It looks like there's a fundamental mismatch between:

  1. Corepack (arcanis: "I'm still not convinced [package manager dependency ranges] is a good idea")
  2. pnpm (dependency range in devEngines.packageManager.version)

So probably it's best to not use Corepack with pnpm anymore.

@aduh95

aduh95 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Corepack doesn't work with pnpm anymore

It does though, not everyone is using pnpm init – and even if you do, you can run corepack use pnpm@11.x to get to a working state

@karlhorky

Copy link
Copy Markdown

Corepack doesn't work with pnpm anymore

It does though, not everyone is using pnpm init

Hmm... pnpm init is a pretty big feature of pnpm.

I would call "Corepack supports pnpm" into question if big features and workflows are not supported.

Maybe better would be "Corepack sometimes may work with pnpm".

Anyway, too tenuous for me and my students - I'll also be removing Corepack from everything.

Starting with v12, the `pnpm` package on npm only ships placeholders for
its binaries: the actual platform-specific executable lives in a
companion `@pnpm/exe.<platform>` package (pinned in the
`optionalDependencies` of the main package), and a `preinstall` script
copies it over the placeholders. Since Corepack never runs lifecycle
scripts, it now replicates their effect for package manager versions
whose config defines `nativePackages`: it downloads the companion
package for the current platform, verifies its signature and integrity,
and hardlinks its executable over each placeholder. The executable
adapts its behavior to the name it was invoked under, which keeps the
`pnpx` alias working.

Native executables cannot be loaded into the current Node.js process
like the JavaScript-based package managers, so they are spawned as a
child process instead.

Installs of pnpm >=12 performed by previous Corepack releases recorded
binary paths that don't exist; such installs are detected and redone.

Fixes: nodejs#873
Refs: nodejs#775
Refs: pnpm/pnpm#13018

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@zkochan

zkochan commented Aug 13, 2026

Copy link
Copy Markdown
Author

I will finish this PR but overall I would ask the corepack team to remove pnpm from corepack altogether.

Replace the `as any` cast with an explicit shape for the bits of the
diagnostic report we read, and exclude the network interfaces from the
report since gathering them is by far the slowest part of generating it
and we only care about the header.

Ref: lovell/detect-libc#21

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zkochan
zkochan force-pushed the feat/pnpm-v12-native-executable branch from 0f599b8 to b44b7cd Compare August 13, 2026 17:27
Keep only the comments explaining something the code doesn't already say,
and hoist the single `COREPACK_ROOT` assignment out of the native branch
so it isn't duplicated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MODULE_NOT_FOUND with corepack use pnpm@next-12

5 participants