Skip to content

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

Open
zkochan wants to merge 1 commit 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 1 commit 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

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 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

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 is moving to Yarn Switch

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

2 participants