NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #466 most downloaded on npm
Fast, disk space efficient package manager
Last release today
25 Sep 2026
Ships on a steady schedule
a new release about every 8 days
Nearly every release is documented
notes for 56 of the last 60 stable releases
376 versions withdrawn
withdrawn after publishing
14 years old
1328 releases · first in 2013
Restore the execute bit on the node-gyp shims packed inside @pnpm/exe (dist/node-gyp-bin/node-gyp, dist/node-gyp-bin/node-gyp.cmd, and dist/node_modul
Restore the execute bit on the node-gyp shims packed inside @pnpm/exe (dist/node-gyp-bin/node-gyp, dist/node-gyp-bin/node-gyp.cmd, and dist/node_modules/node-gyp/bin/node-gyp.js). Without this, pnpm/action-setup's standalone path (used on runners with Node.js < 22.13) failed any install whose lifecycle script invoked node-gyp rebuild with sh: 1: node-gyp: Permission denied #11483.
Fixed the pn, pnpx, and pnx aliases failing in Git Bash / MSYS2 on Windows when pnpm was installed via @pnpm/exe (or after pnpm self-update) #11486. Running pnpx (or pnx) printed the cmd.exe banner and dropped the user into an interactive command prompt instead of running pnpm dlx. The bin field rewrite on Windows was pointing those aliases at .cmd files; cmd-shim's Bash shim for a .cmd target wraps it in exec cmd /C ..., and MSYS2 mangles /C into a Windows path before cmd.exe sees it. The aliases are now .exe hardlinks of the SEA binary, which detects which name it was launched as via process.execPath and prepends dlx for pnpx / pnx.
Fix pnpm install recreating node_modules after pnpm fetch. pnpm fetch records empty hoistPattern and publicHoistPattern in .modules.yaml; since v11 removed the explicit-config gate, the follow-up install treated those as a hoist-pattern change and purged the modules directory. The fetch step now flags the modules manifest with virtualStoreOnly: true so the next install skips the hoist-pattern comparison and completes the missing post-import linking in place #11488.
Pin the integrity of git-hosted tarballs (codeload.github.com, gitlab.com, bitbucket.org) in the lockfile so that subsequent installs detect a tampered or substituted tarball and refuse to install it. Previously the lockfile only stored the tarball URL for git dependencies, so a compromised git host or a man-in-the-middle could serve arbitrary code on later installs without lockfile changes.
A new gitHosted: true field is recorded on git-hosted tarball resolutions in the lockfile, letting every reader/writer route them by a single typed check instead of pattern-matching the tarball URL in each call site. Lockfiles written by older pnpm versions are enriched on load (URL fallback) so the field can be relied on uniformly across the codebase.
Allow user-level preferences in the global config.yaml. The following settings can now be set in ~/.config/pnpm/config.yaml (or via pnpm config set --location global) instead of being restricted to pnpm-workspace.yaml: agent, globalVirtualStoreDir, initPackageManager, initType, registrySupportsTimeField, scriptShell, shellEmulator, sideEffectsCache, sideEffectsCacheReadonly, stateDir, strictDepBuilds, trustPolicy, trustPolicyExclude, trustPolicyIgnoreAfter, updateNotifier, useStderr, verifyDepsBeforeRun, verifyStoreIntegrity, virtualStoreDir, virtualStoreDirMaxLength #11474.
Make trusted publishing (OIDC) take precedence over a configured static _authToken in pnpm publish, mirroring the npm CLI's behavior. When OIDC succeeds, the OIDC-derived token overrides any pre-configured _authToken; when OIDC is not applicable (no CI environment, exchange fails, registry has no trusted publisher configured), the static token is used as a fallback. This applies on every package during recursive publish, so each workspace package independently attempts trusted publishing.
Additionally, the NPM_ID_TOKEN env var is now honored as a CI-agnostic injection point for an OIDC ID token. Previously OIDC was only attempted on GitHub Actions or GitLab; now any CI provider that exposes its own OIDC mechanism (e.g. CircleCI's CIRCLE_OIDC_TOKEN_V2, Buildkite, etc.) can forward its token via NPM_ID_TOKEN and trusted publishing will work without pnpm needing to recognize the provider explicitly.
--pm-on-fail=ignore (and other universal options like --loglevel, --reporter) is now honored when combined with --help or --version. Previously the CLI argument parser short-circuited those flags before universal options were preserved, so pnpm audit --pm-on-fail=ignore --help and pnpm --pm-on-fail=ignore --version reported the strict packageManager mismatch instead of running the requested action #11487.
Fix a regression where pnpm --recursive --filter '!<pkg>' run/exec/test/add would include the workspace root in the matched projects. The workspace root is now correctly excluded by default when only negative --filter arguments are provided, matching the documented behavior. To include the root, pass --include-workspace-root #11341.
Restore npm-CLI-compatible --json stdout output for pnpm publish (#11476). pnpm 11 reimplemented publish natively (#10591) and inadvertently dropped the per-package JSON object that pnpm 10 emitted transitively via the npm CLI, silently breaking downstream tooling — most notably nx release publish, which parses stdout JSON to confirm success (nrwl/nx#35575). On success, the output is now:
pnpm publish --json → single object { id, name, version, size, unpackedSize, shasum, integrity, filename, files, entryCount, bundled }, mirroring npm publish --json.pnpm publish -r --json → array of those objects, mirroring pnpm pack --json's shape choice.pnpm publish -r --report-summary → existing pnpm-publish-summary.json envelope { publishedPackages: [...] } is preserved, but each entry is upgraded to the same per-package shape (additive — name and version are still present).pnpm config get @<scope>:registry now reports the same URL that pnpm publish and the resolvers actually use. Previously, config get only consulted .npmrc, while publish/install used the merged map that includes pnpm-workspace.yaml's registries block — so the two could diverge silently and a publish could go to the wrong registry #11492.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
One column per quarter.
Fix pnpm_config_npmrc_auth_file and pnpm_config_userconfig env vars not actually loading the custom .npmrc. The env vars were parsed and assigned to t
pnpm_config_npmrc_auth_file and pnpm_config_userconfig env vars not actually loading the custom .npmrc. The env vars were parsed and assigned to the resolved config, but only after loadNpmrcConfig had already read the default ~/.npmrc — so the custom file path was set but never read. The relevant env vars are now consulted before the user-level .npmrc is loaded #11465.pnpm-workspace.yaml when updating it. Existing keys keep their position, and new keys are inserted in alphabetical position when the existing keys are already sorted (with a leading packages key allowed) or appended at the end otherwise.pnpm self-update on installations originally set up by pnpm v10. v10 added PNPM_HOME directly to PATH and wrote a pnpm bootstrap shim there. v11 setup writes shims under PNPM_HOME/bin instead, so when a v10 user upgrades to v11 the legacy shim at PNPM_HOME keeps pointing into the old .tools/<version> install — pnpm --version continues to report the pre-update version even though the new version was installed under global/v11. Self-update now detects this layout, refreshes the legacy shims so the upgrade actually takes effect, and prints a hint suggesting pnpm setup to migrate PATH to the v11 layout. #11464.nodeLinker, hoistPattern) are present in config.yaml and silently ignored. Previously these settings were dropped without any feedback, leaving users unsure why their global configuration had no effect. The warning suggests moving those settings to a project-level pnpm-workspace.yaml, or sharing them across projects via config dependencies.overrides has an invalid shape or contains a non-string value.readPackage dependency map fields, including devDependencies, and reject falsy non-object invalid values instead of silently accepting them.pnpm config, pnpm set, and pnpm get by tolerating configDependencies install failures. For these commands, a failure to install configDependencies (for example because the registry auth token has not been written yet) is now logged at debug level and the command proceeds. All other commands still surface the install error #10684.allowBuilds as an install-state input and clear previously ignored builds when they are explicitly disallowed.catalog: protocol with the pnpm dlx / pnpx command, resulting in a catalog entry not found error.PNPM_CONFIG_* (uppercase) environment variables in addition to pnpm_config_*. Previously, only the lowercase form was honored, so env vars renamed per the v11 migration guide (e.g. PNPM_CONFIG_USERCONFIG) silently had no effect on case-sensitive systems like macOS and Linux #11465.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Drop the darwin-x64 artifact from @pnpm/exe and from the GitHub release page. The Node.js SEA mechanism pnpm pack-app uses produces a binary that segf
Drop the darwin-x64 artifact from @pnpm/exe and from the GitHub release page. The Node.js SEA mechanism pnpm pack-app uses produces a binary that segfaults at startup on Intel Macs because of an upstream Node.js bug (nodejs/node#62893, tracked alongside #59553; the Node.js team has opted not to fix it on the grounds that x64 macOS is being phased out). Re-signing with codesign or ldid doesn't help — the corruption is in LIEF's Mach-O surgery, before signing.
Intel Mac users should install pnpm via npm install -g pnpm (uses the system Node.js, no SEA), or stay on pnpm 10.x. @pnpm/exe's preinstall on Intel Mac now exits with a clear error pointing at these alternatives.
Closes #11423.
pnpm dlx (and pnpx/pnx/pnpm create) now runs the same interactive approve-builds prompt as pnpm add -g when the package being launched depends on transitive packages with install scripts. Previously, the v11 strictDepBuilds default made dlx fail with ERR_PNPM_IGNORED_BUILDS and required users to re-run with --allow-build=<pkg> for every offending dependency. dlx also now removes the partially-populated cache directory when the install fails, so a subsequent run starts clean instead of reusing a broken install whose builds were silently skipped #11444.
72629fc: Fix pnpm -g ls --json and pnpm -g ls --parseable so they emit valid JSON and parseable output respectively, matching pnpm 10 behavior. Since the isolated global packages refactor in pnpm 11, the global list command had a custom path that always printed plain text and ignored --json/--parseable, which broke tools like npm-check-updates that parse the JSON output #11440.
pnpm -g ls --depth=<n> (with n > 0) now errors when more than one isolated global install would be involved, since each install has its own lockfile and merging their transitive trees would be incoherent. When the request can be narrowed to a single install group, the regular list flow is used and the full dependency tree is shown.
Fixed pnpm publish to honor publishConfig.registry from package.json when publishing a single package. The native publish flow introduced in v11 was reading the registry from .npmrc only, ignoring the per-package override #11419.
When strictPeerDependencies is true, the ERR_PNPM_PEER_DEP_ISSUES error once again renders the peer dependency issues inline using the same format as pnpm peers check, so users (and CI tools like Renovate) can see what failed without running pnpm peers check separately #11439.
The WARN and error code labels in pnpm's output now wrap in brackets ([WARN], [ERR_PNPM_FOO]). Previously the labels relied entirely on a colored background to stand out, which meant they blended into the surrounding text in terminals without color (e.g. when NO_COLOR is set or output is piped). The brackets are painted in the same color as the badge background, so they appear as ordinary padding in color-capable terminals — only the no-color rendering changes.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Fixed pnpm ci not reinstalling workspace package node_modules directories after the clean step #11427.
pnpm ci not reinstalling workspace package node_modules directories after the clean step #11427.pnpm ci performs a fresh install after the clean step.pnpm-lock.yaml during pnpm clean when lockfile: true is configured in pnpm-workspace.yaml. The lockfile is only removed when the --lockfile option is passed to pnpm clean.pnpm self-update (with no version argument) no longer downgrades pnpm when the registry's latest dist-tag points to an older release than the currently active version. Run pnpm self-update latest to force a downgrade #11418.minimumReleaseAgeStrict now defaults to true whenever the user explicitly sets minimumReleaseAge (via pnpm-workspace.yaml, the global config.yaml, the CLI, or pnpm_config_* env vars).<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Fix too many open files error sometimes happening on Windows, when creating command shims in node_modules/.bin #11412.
node_modules/.bin #11412.ERR_PNPM_FETCH_404 when installing a project whose lockfile depends on a file: tarball. The previous behavior dropped the tarball field from file: and git-hosted resolutions when lockfile-include-tarball-url=false (the default), even though those URLs cannot be reconstructed from the package name, version, and registry #11407.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Fix ENOENT symlink failure when pnpm add -g triggers the approve-builds prompt. The global add flow used to forward an absolute modulesDir ( /node_mod
ENOENT symlink failure when pnpm add -g triggers the approve-builds prompt. The global add flow used to forward an absolute modulesDir (<installDir>/node_modules) into the install run by approve-builds. The install layer treated modulesDir as a path relative to lockfileDir and joined it again, producing a doubled path on Windows because path.join does not collapse an embedded absolute path. The hoist step then tried to mkdir and symlink under <installDir>\<installDir>\node_modules\.pnpm\node_modules\... and failed with ENOENT #11403.packageManagerDependencies going stale when pnpm is invoked through corepack. The lockfile sync (and the devEngines.packageManager version check) previously ran only when pnpm was invoked directly; under corepack the entire block was skipped, so a stale entry would persist even after the running pnpm version changed. The lockfile sync now runs regardless of how pnpm was invoked, while the pnpm-managed version switch (onFail: 'download') remains skipped under corepack so it doesn't fight corepack's own version selection #11397.publishConfig.directory when packages publish from a generated directory #11239.os / cpu entries (e.g. ["!win32"]) being incorrectly rejected when supportedArchitectures expands to multiple platforms #11375.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Report unknown top-level options before falling back to implicit pnpm run scripts.
pnpm run scripts.null named catalogs in workspace manifests with InvalidWorkspaceManifestError instead of crashing with a raw TypeError.pnpm sbom emitted NOASSERTION (SPDX) and omitted the distribution reference (CycloneDX) for git dependencies. Now emits the git URL with commit hash, e.g. git+https://github.com/user/repo.git#commit.pnpm self-update now keeps package.json's packageManager and devEngines.packageManager in sync. When the legacy packageManager field pins pnpm, both fields are rewritten to the new exact pnpm version on update — packageManager to pnpm@<version> (without an integrity hash), and devEngines.packageManager.version to the same exact <version> (dropping any range operator). When only devEngines.packageManager is declared, the existing range-preserving behavior is unchanged #11388.pnpm audit --fix so that the log output order matches the order written to pnpm-workspace.yaml.packageManagerDependencies entry when devEngines.packageManager declares a pnpm version that the lockfile no longer satisfies. Previously, the stale entry was kept even though the running pnpm matched the declared version, silently breaking the integrity record #11387.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
…the legacy /security/audits endpoints are gone. CVE-based filtering has been replaced with GHSA-based filtering: migrate auditConfig.ignoreCves entrie…
Migration guide: Migrating from v10 to v11
minimumReleaseAge defaults to 1 day (newly published packages are not resolved for 24h) and blockExoticSubdeps defaults to true.allowBuilds replaces the old build-dependency settings — onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts have been removed.pnpm add -g gets its own directory with its own package.json, node_modules, and lockfile.pnpm publish, login, logout, view, deprecate, unpublish, dist-tag, and version no longer delegate to the npm CLI, and the remaining npm passthrough commands now throw "not implemented".pnpm audit uses npm's bulk advisories endpoint — the legacy /security/audits endpoints are gone. CVE-based filtering has been replaced with GHSA-based filtering: migrate auditConfig.ignoreCves entries to auditConfig.ignoreGhsas..npmrc is auth/registry only — all other settings must live in pnpm-workspace.yaml or the new global config.yaml, and environment variables use the pnpm_config_* prefix.node@runtime:<version> no longer extracts the bundled npm, npx, and corepack, roughly halving the files pnpm has to hash, write, and link.pnpm ci, pnpm sbom, pnpm clean, pnpm peers check, pnpm runtime set, pnpm docs/home, pnpm ping, pnpm search, pnpm star/unstar/stars, pnpm whoami, pnpm with, and pnpm pack-app, plus pn/pnx short aliases..pnpmfile.mjs, which takes priority over .pnpmfile.cjs when present.pnpm audit --fix=update fixes vulnerabilities by updating packages in the lockfile instead of adding overrides, and pnpm audit --fix --interactive lets you select which advisories to fix.pnpm pack-app packs a CommonJS entry into a standalone executable for one or more target platforms using Node.js Single Executable Applications.Changed default values: optimisticRepeatInstall is now true, verifyDepsBeforeRun is now install, minimumReleaseAge is now 1440 (1 day), and minimumReleaseAgeStrict is false. Newly published packages will not be resolved until they are at least 1 day old. This protects against supply chain attacks by giving the community time to detect and remove compromised versions. To opt out, set minimumReleaseAge: 0 in pnpm-workspace.yaml #11158.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Removed deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts #11220.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed allowNonAppliedPatches in favor of allowUnusedPatches.
Removed ignorePatchFailures; patch application failures now throw an error.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadGlobally installed binaries are now stored in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.pnpm no longer reads all settings from .npmrc. Only auth and registry settings are read from .npmrc files. All other settings (like hoistPattern, nodeLinker, shamefullyHoist, etc.) must be configured in pnpm-workspace.yaml or the global ~/.config/pnpm/config.yaml #11189.
Network settings (httpProxy, httpsProxy, noProxy, localAddress, strictSsl, gitShallowHosts) are now written to config.yaml (global) or pnpm-workspace.yaml (local) instead of .npmrc/auth.ini. They are still readable from .npmrc for easier migration from the npm CLI #11209.
pnpm no longer reads npm_config_* environment variables. Use pnpm_config_* environment variables instead (e.g., pnpm_config_registry instead of npm_config_registry).
pnpm no longer reads the npm global config at $PREFIX/etc/npmrc.
pnpm login writes auth tokens to ~/.config/pnpm/auth.ini.
New registries setting in pnpm-workspace.yaml:
registries:
default: https://registry.npmjs.org/
"@my-org": https://private.example.com/
"@internal": https://nexus.corp.com/
Auth tokens in ~/.npmrc still work — pnpm continues to read ~/.npmrc as a fallback for registry authentication. The new npmrcAuthFile setting can be used to point to a different file instead of ~/.npmrc.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs looks something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
pnpm config get (without --json) no longer prints INI formatted text. Instead, it prints JSON for objects and arrays, and raw strings for strings, numbers, booleans, and nulls. pnpm config get --json still prints all types of values as JSON, as before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase. Exception: keys that start with @ or // are preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non-camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer falls back to the npm CLI. Commands that were previously passed through to npm (access, bugs, docs, edit, find, home, issues, owner, ping, prefix, profile, pkg, repo, search, set-script, star, stars, team, token, unstar, whoami, xmas) and their aliases (s, se) now throw a "not implemented" error, with a suggestion to use the npm CLI directly #10642. Other previously passed-through commands — view (info, show, v), login (adduser), logout, deprecate, unpublish, dist-tag, and version — have been reimplemented natively in pnpm (see New Commands below).
pnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.$ command instead of > pkg@version stage path\n> command, and shows project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Added support for lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using the pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infoThe root workspace project is no longer excluded when it is explicitly selected via a filter #10465.
pnpm audit now calls npm's /-/npm/v1/security/advisories/bulk endpoint. The legacy /-/npm/v1/security/audits{,/quick} endpoints have been retired by the registry, so the legacy request/response contract is no longer supported.
The bulk endpoint does not return CVE identifiers. CVE-based filtering has been replaced with GitHub advisory ID (GHSA) filtering:
auditConfig.ignoreCves → auditConfig.ignoreGhsas (the previous key is no longer recognized)pnpm audit --ignore <id> / pnpm audit --ignore-unfixable now read and write GHSAs instead of CVEsurl (https://github.com/advisories/GHSA-xxxx-xxxx-xxxx)To migrate: replace each CVE-YYYY-NNNNN entry in your auditConfig.ignoreCves with the corresponding GHSA-xxxx-xxxx-xxxx value (visible in the More info column of pnpm audit output) and move it under auditConfig.ignoreGhsas.
Breaking: removed the managePackageManagerVersions, packageManagerStrict, and packageManagerStrictVersion settings. They existed only to derive the onFail behavior for the legacy packageManager field, and the pmOnFail setting introduced alongside pnpm with subsumes all three — it directly sets the onFail behavior of both packageManager and devEngines.packageManager. The COREPACK_ENABLE_STRICT environment variable is no longer honored (it only gated packageManagerStrict); use pmOnFail instead.
Migration:
| Removed setting | Replace with |
|---|---|
managePackageManagerVersions: true |
pmOnFail: download (default) |
managePackageManagerVersions: false |
pmOnFail: ignore |
packageManagerStrict: false |
pmOnFail: warn |
packageManagerStrictVersion: true |
pmOnFail: error |
COREPACK_ENABLE_STRICT=0 |
pmOnFail: warn |
node@runtime:<version> (including pnpm env use and pnpm runtime set node) no longer extracts the bundled npm, npx, and corepack from the Node.js archive. This cuts roughly half of the files pnpm has to hash, write to the CAS, and link during installation, making runtime installs noticeably faster. Users who still need npm can install it as a separate package.pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm logout command for logging out of npm registries. Revokes the authentication token on the registry and removes it from the local auth config file #11213.pnpm deprecate and pnpm undeprecate commands for setting and removing deprecation messages on package versions without delegating to the npm CLI #11120.pnpm unpublish command. Supports unpublishing specific versions, version ranges via semver, and entire packages with --force #11128.pnpm dist-tag command (ls, add, rm subcommands) #11218.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.pnpm audit --fix now supports a new interactive mode via --interactive/-i.pnpm docs command and its alias pnpm home. This command opens the package documentation or homepage in the browser. When the package has no valid homepage, it falls back to https://npmx.dev/package/<name>.pnpm ping command to test registry connectivity. Provides a simple way to verify connectivity to the configured registry without requiring external tools.search command and its aliases (s, se, find).star, unstar, stars, and whoami commands.pnpm with <version|current> <args...> command. Runs pnpm at a specific version (or the currently active one) for a single invocation, bypassing the project's packageManager and devEngines.packageManager pins.pnpm pack-app command that packs a CommonJS entry file into a standalone executable for one or more target platforms, using the Node.js Single Executable Applications API under the hood.Added support for a global YAML config file named config.yaml.
Configuration is now split into two categories:
rc file and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for loading environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not CLI arguments.
Added support for reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Added support for specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910. Config dependency and package manager integrity info is now stored in pnpm-lock.yaml instead of inlined in pnpm-workspace.yaml: the workspace manifest contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
Added nodeDownloadMirrors setting to configure custom Node.js download mirrors in pnpm-workspace.yaml. This replaces the node-mirror:<channel> .npmrc setting, which is no longer read #11194:
nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/
pnpm dlx and pnpm create now respect security and trust policy settings (minimumReleaseAge, minimumReleaseAgeExclude, minimumReleaseAgeStrict, trustPolicy, trustPolicyExclude, trustPolicyIgnoreAfter) from project-level configuration #11183.
pnpm init now writes a devEngines.packageManager field instead of the packageManager field when init-package-manager is enabled.
Added a new setting runtimeOnFail that overrides the onFail field of devEngines.runtime (and engines.runtime) in the root project's package.json. Accepted values: ignore, warn, error, download. For example, setting runtimeOnFail=download makes pnpm download the declared runtime version even when the manifest does not set onFail: "download".
Added a new setting minimumReleaseAgeIgnoreMissingTime, which is true by default. When enabled, pnpm skips the minimumReleaseAge maturity check if the registry metadata does not include the time field. Set to false to fail resolution instead.
.mjs extension. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded #9730.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds now accepts positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.pnpm audit --fix now adds the minimum patched version for each advisory to minimumReleaseAgeExclude in pnpm-workspace.yaml, so the security fix can be installed without waiting for minimumReleaseAge #11216.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules, avoiding the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.If-Modified-Since for conditional metadata fetches, avoiding re-downloading unchanged registry metadata #11161.minimumReleaseAge, reducing the amount of data fetched from the registry #11160.Switched to process.stderr.write instead of console.error for script logging #11140.
Respected the frozen-lockfile flag when migrating config dependencies #11067.
Removed the --workspace flag from the version command #11115.
Handled ENOTSUP error in the clone import path during parallel I/O #11117.
Fixed pnpm audit command.
Updated dependencies to fix vulnerabilities.
pnpm now checks whether a package is installable for non-npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
pnpm now explicitly passes the path of the global rc config file to npm.
Fixed YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is now written to allowBuilds.
Fixed a bug in which specifying filter in pnpm-workspace.yaml would cause pnpm to not detect any projects.
Deferred patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
pnpm now fails on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handled ENOTSUP error in linkOrCopy during parallel imports #11103.
Skipped linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fixed _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fixed a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevented catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolved patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagated error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
Fixed SQLite race condition during store initialization on Windows.
Removed rimrafSync in importIndexedDir fast-path error handler #11168.
Fixed pnpm dedupe --check unexpectedly failing due to non-deterministic resolution #11110.
Fixed empty files not being rejected in isEmptyDirOrNothing #11182.
Fixed .bat/.cmd token helpers not working on Windows due to missing shell: true option.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Fix the @pnpm/exe SEA executable crashing at startup on Node.js v25.7+. Two separate regressions in @pnpm/exe@11.0.0-rc.4 are addressed:
Fix the @pnpm/exe SEA executable crashing at startup on Node.js v25.7+. Two separate regressions in @pnpm/exe@11.0.0-rc.4 are addressed:
pnpm pack-app now pins the Node.js used to write the SEA blob to the exact embedded runtime version. The SEA blob format changed in Node.js v25.7 (ESM entry-point support added a ModuleFormat header byte), so a blob produced by a pre-25.7 builder cannot be deserialized by a 25.7+ runtime and vice versa. In rc.4 the CI host Node.js (v25.6.1) built blobs embedded in a v25.9.0 runtime, tripping SeaDeserializer::Read() ... format_value <= kModule on every invocation. pack-app now downloads a host-arch builder Node.js of the target version when the running Node.js doesn't already match.
The pnpm CJS SEA entry shim now loads dist/pnpm.mjs through Module.createRequire(process.execPath) instead of await import(pathToFileURL(...).href). In Node.js v25.7+, the ambient require and import() inside a CJS SEA entry are replaced with embedder hooks that only resolve built-in module names, causing external file:// loads to fail with ERR_UNKNOWN_BUILTIN_MODULE. An explicit createRequire() bypasses those hooks.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Installing a Node.js runtime via node@runtime: (including pnpm env use and pnpm runtime set node) no longer extracts the bundled npm, npx, and corepac
node@runtime:<version> (including pnpm env use and pnpm runtime set node) no longer extracts the bundled npm, npx, and corepack from the Node.js archive. This cuts roughly half of the files pnpm has to hash, write to the CAS, and link during installation, making runtime installs noticeably faster. Users who still need npm can install it as a separate package.pnpm pack-app: replaced the --node-version flag with --runtime, which takes a <name>@<version> spec (e.g. --runtime node@22.0.0). The corresponding pnpm.app.nodeVersion key in package.json was renamed to pnpm.app.runtime with the same syntax. Only node is supported today; the prefix leaves room for future runtimes (bun, deno).
The previous --node-version flag silently inherited from pnpm's global node-version rc setting (which controls which Node runs user scripts), causing the wrong Node build to be embedded in SEAs for users who had that rc key set.
@pnpm/{macos,win,linux,linuxstatic}-{x64,arm64} npm names for the platform-specific optional dependencies of @pnpm/exe, reverting the scope-nested @pnpm/exe.<platform>-<arch>[-musl] rename from #11316 on the published package names only — the workspace directory layout (pnpm/artifacts/<platform>-<arch>[-musl]/) and the GitHub release asset filenames stay on the new scheme. The rename broke pnpm self-update from v10, which looks up the platform child by its legacy name. linkExePlatformBinary now checks for both schemes so a later rename can ship without a v10-compatibility hazard.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Previously, pnpm audit --fix would fix all vulnerabilities regardless of the configured auditLevel, while pnpm audit (without --fix) correctly filtere…
Added a new pnpm pack-app command that packs a CommonJS entry file into a standalone executable for one or more target platforms, using the Node.js Single Executable Applications API under the hood. Targets are specified as <os>-<arch>[-<libc>] (e.g. linux-x64, linux-x64-musl, macos-arm64, win-x64) and each produces an executable under dist-app/<target>/ by default. Requires Node.js v25.5+ to perform the injection; an older host downloads Node.js v25 automatically.
pnpm audit --fix now respects the auditLevel setting and supports a new interactive mode via --interactive/-i. Previously, pnpm audit --fix would fix all vulnerabilities regardless of the configured auditLevel, while pnpm audit (without --fix) correctly filtered by severity. Now both commands consistently filter advisories by the auditLevel setting, and you can use pnpm audit --fix -i to review and select which vulnerabilities to fix interactively.
Overrides emitted by pnpm audit --fix now use a caret range (^X.Y.Z) instead of an open-ended >=X.Y.Z, so applying a security fix can no longer silently promote a dependency across a major version boundary.
Added a new setting minimumReleaseAgeIgnoreMissingTime, which is true by default. When enabled, pnpm skips the minimumReleaseAge maturity check if the registry metadata does not include the time field. Set to false to fail resolution instead.
Fixed and expanded pnpm version to match npm behavior:
pnpm version 1.2.3) in addition to bump types.--no-commit-hooks, --no-git-tag-version, --sign-git-tag, and --message.--no-git-checks which was previously parsed incorrectly.--no-git-tag-version is used). --message supports %s replacement with the new version, and --tag-version-prefix controls the tag prefix (defaults to v). Git commits and tags are always skipped in recursive mode since multiple packages may be bumped to different versions in a single run #11271.Renamed the platform-specific optional dependencies of @pnpm/exe to the new @pnpm/exe.<platform>-<arch>[-<libc>] scheme, using process.platform values (linux, darwin, win32) for the OS segment. The umbrella package @pnpm/exe itself is unchanged so existing npm i -g @pnpm/exe and pnpm self-update flows keep working.
| before | after |
|---|---|
@pnpm/linux-x64 |
@pnpm/exe.linux-x64 |
@pnpm/linux-arm64 |
@pnpm/exe.linux-arm64 |
@pnpm/linuxstatic-x64 |
@pnpm/exe.linux-x64-musl |
@pnpm/linuxstatic-arm64 |
@pnpm/exe.linux-arm64-musl |
@pnpm/macos-x64 |
@pnpm/exe.darwin-x64 |
@pnpm/macos-arm64 |
@pnpm/exe.darwin-arm64 |
@pnpm/win-x64 |
@pnpm/exe.win32-x64 |
@pnpm/win-arm64 |
@pnpm/exe.win32-arm64 |
GitHub release asset filenames follow the same scheme — pnpm-linuxstatic-x64.tar.gz becomes pnpm-linux-x64-musl.tar.gz, pnpm-macos-* becomes pnpm-darwin-*, pnpm-win-* becomes pnpm-win32-*. Anyone downloading releases directly needs to use the new filenames; get.pnpm.io/install.sh and install.ps1 will be updated in lockstep to accept both schemes based on the requested version.
Resolves #11314.
Cannot use both "packageManager" and "devEngines.packageManager" in package.json. "packageManager" will be ignored warning when the two fields specify the exact same package manager name and version string. This lets projects keep both fields during the migration from packageManager to devEngines.packageManager without a noisy warning #11301.file:<dir>) from an absolute path on a different drive on Windows. The directory fetcher was joining the stored directory onto lockfileDir, which on Windows concatenates an absolute cross-drive path literally (path.join('D:\\...', 'C:\\Users\\...') → 'D:\\...\\C:\\Users\\...'). Use path.resolve so absolute paths are respected. This surfaced as an ENOENT during pnpm setup in CI when PNPM_HOME and the OS temp directory were on different drives.pnpm sbom and pnpm licenses failing to resolve license information for git-sourced dependencies (git+https://, git+ssh://, github: shorthand). These commands now correctly read the package manifest from the content-addressable store for type: 'git' resolutions #11260.ERR_PNPM_OUTDATED_LOCKFILE when approving builds during a global install. The approve-builds flow called by pnpm add -g passed the global packages directory to the subsequent install as workspaceDir, which caused sibling install directories (such as those left behind by pnpm self-update) to be picked up as workspace projects and fail the frozen-lockfile check.filenamify upgrade changed how leading ./ and ../ segments were normalized, producing peer suffixes like (b@+packages+b) instead of (b@packages+b) for linked packages outside the workspace root, causing lockfile churn #11272.node@runtime:25.9.0 glibc vs. musl) no longer share a single global-virtual-store entry. The virtual store path now incorporates the selected variant's integrity, so installs with different --os/--cpu/--libc end up in separate directories and pnpm add --libc=musl node@runtime:<v> reliably fetches the musl binary even when the glibc variant is already cached.pnpm sbom now detects licenses declared via the deprecated licenses array in package.json (e.g. busboy, streamsearch, limiter) and falls back to scanning on-disk LICENSE files — mirroring the resolution logic of pnpm licenses. Previously these packages were reported as NOASSERTION. Shared license resolution (manifest parsing + LICENSE-file fallback) lives in the new @pnpm/deps.compliance.license-resolver package. When a manifest sets both license and licenses, the modern license field now takes precedence for both commands (previously pnpm licenses preferred licenses) #11248.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Breaking: removed the managePackageManagerVersions, packageManagerStrict, and packageManagerStrictVersion settings. They existed only to derive the on
Breaking: removed the managePackageManagerVersions, packageManagerStrict, and packageManagerStrictVersion settings. They existed only to derive the onFail behavior for the legacy packageManager field, and the pmOnFail setting introduced alongside pnpm with subsumes all three — it directly sets the onFail behavior of both packageManager and devEngines.packageManager. The COREPACK_ENABLE_STRICT environment variable is no longer honored (it only gated packageManagerStrict); use pmOnFail instead.
Migration:
| Removed setting | Replace with |
|---|---|
managePackageManagerVersions: true |
pmOnFail: download (default) |
managePackageManagerVersions: false |
pmOnFail: ignore |
packageManagerStrict: false |
pmOnFail: warn |
packageManagerStrictVersion: true |
pmOnFail: error |
COREPACK_ENABLE_STRICT=0 |
pmOnFail: warn |
pnpm dlx and pnpm create now respect security and trust policy settings (minimumReleaseAge, minimumReleaseAgeExclude, minimumReleaseAgeStrict, trustPolicy, trustPolicyExclude, trustPolicyIgnoreAfter) from project-level configuration #11183.
Implemented native star, unstar, stars, and whoami commands.
Add pnpm with <version|current> <args...> command. Runs pnpm at a specific version (or the currently active one) for a single invocation, bypassing the project's packageManager and devEngines.packageManager pins. Uses the same install mechanism as pnpm self-update, caching the downloaded pnpm in the global virtual store for reuse.
Examples:
pnpm with current install # ignore the pinned version, use the running pnpm
pnpm with 11.0.0-rc.1 install # install using pnpm 11.0.0-rc.1
pnpm with next install # install using the "next" dist-tag
Also adds a new pmOnFail setting that overrides the onFail behavior of packageManager and devEngines.packageManager. Accepted values: download, error, warn, ignore. Can be set via CLI flag, env var, pnpm-workspace.yaml, or .npmrc — useful when version management is handled by an external tool (asdf, mise, Volta, etc.) and the project wants pnpm itself to skip the check.
pnpm install --pm-on-fail=ignore # direct CLI flag
pnpm_config_pm_on_fail=ignore pnpm install # env var
# or in pnpm-workspace.yaml:
# pmOnFail: ignore
pnpm init now writes a devEngines.packageManager field instead of the packageManager field when init-package-manager is enabled.
When pnpm is declared via the packageManager field in package.json, its resolution info is no longer written to pnpm-lock.yaml — unless the pinned pnpm version is v12 or newer. The packageManagerDependencies section is still populated (and reused across runs) when pnpm is declared via devEngines.packageManager. This makes the transition from pnpm v10 to v11 quieter by avoiding unnecessary lockfile churn for projects that pin an older pnpm in the legacy packageManager field.
Added a new setting runtimeOnFail that overrides the onFail field of devEngines.runtime (and engines.runtime) in the root project's package.json. Accepted values: ignore, warn, error, download. For example, setting runtimeOnFail=download makes pnpm download the declared runtime version even when the manifest does not set onFail: "download".
pnpm init no longer adds the devEngines.packageManager field when run inside a workspace subpackage. The field is only added to the workspace root's package.json.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
The bulk endpoint does not return CVE identifiers. CVE-based filtering has been replaced with GitHub advisory ID (GHSA) filtering:
pnpm audit now calls npm's /-/npm/v1/security/advisories/bulk endpoint. The legacy /-/npm/v1/security/audits{,/quick} endpoints have been retired by the registry, so the legacy request/response contract is no longer supported.
The bulk endpoint does not return CVE identifiers. CVE-based filtering has been replaced with GitHub advisory ID (GHSA) filtering:
auditConfig.ignoreCves → auditConfig.ignoreGhsas (the previous key is no longer recognized)pnpm audit --ignore <id> / pnpm audit --ignore-unfixable now read and write GHSAs instead of CVEsurl (https://github.com/advisories/GHSA-xxxx-xxxx-xxxx)To migrate: replace each CVE-YYYY-NNNNN entry in your auditConfig.ignoreCves with the corresponding GHSA-xxxx-xxxx-xxxx value (visible in the More info column of pnpm audit output) and move it under auditConfig.ignoreGhsas.
pnpm docs command and its alias pnpm home. This command opens the package documentation or homepage in the browser. When the package has no valid homepage, it falls back to https://npmx.dev/package/<name>.pnpm ping command to test registry connectivity.
Provides a simple way to verify connectivity to the configured registry without requiring external tools.search command and its aliases (s, se, find).pnpm store prune removing packages used by the globally installed pnpm, breaking it.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
`pnpm audit --fix=update` fixes vulnerabilities by updating packages in the lockfile instead of adding overrides.
minimumReleaseAge defaults to 1 day (newly published packages are not resolved for 24h) and blockExoticSubdeps defaults to true.allowBuilds replaces the old build-dependency settings — onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts have been removed.pnpm add -g gets its own directory with its own package.json, node_modules, and lockfile.pnpm publish, login, logout, view, deprecate, unpublish, dist-tag, and version no longer delegate to the npm CLI, and the remaining npm passthrough commands now throw "not implemented"..npmrc is auth/registry only — all other settings must live in pnpm-workspace.yaml or the new global config.yaml, and environment variables use the pnpm_config_* prefix.pnpm ci, pnpm sbom, pnpm clean, pnpm peers check, and pnpm runtime set, plus pn/pnx short aliases..pnpmfile.mjs, which takes priority over .pnpmfile.cjs when present.pnpm audit --fix=update fixes vulnerabilities by updating packages in the lockfile instead of adding overrides.Changed default values: optimisticRepeatInstall is now true, verifyDepsBeforeRun is now install, minimumReleaseAge is now 1440 (1 day), and minimumReleaseAgeStrict is false. Newly published packages will not be resolved until they are at least 1 day old. This protects against supply chain attacks by giving the community time to detect and remove compromised versions. To opt out, set minimumReleaseAge: 0 in pnpm-workspace.yaml #11158.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Removed deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts #11220.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed allowNonAppliedPatches in favor of allowUnusedPatches.
Removed ignorePatchFailures; patch application failures now throw an error.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadGlobally installed binaries are now stored in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.pnpm no longer reads all settings from .npmrc. Only auth and registry settings are read from .npmrc files. All other settings (like hoistPattern, nodeLinker, shamefullyHoist, etc.) must be configured in pnpm-workspace.yaml or the global ~/.config/pnpm/config.yaml #11189.
Network settings (httpProxy, httpsProxy, noProxy, localAddress, strictSsl, gitShallowHosts) are now written to config.yaml (global) or pnpm-workspace.yaml (local) instead of .npmrc/auth.ini. They are still readable from .npmrc for easier migration from the npm CLI #11209.
pnpm no longer reads npm_config_* environment variables. Use pnpm_config_* environment variables instead (e.g., pnpm_config_registry instead of npm_config_registry).
pnpm no longer reads the npm global config at $PREFIX/etc/npmrc.
pnpm login writes auth tokens to ~/.config/pnpm/auth.ini.
New registries setting in pnpm-workspace.yaml:
registries:
default: https://registry.npmjs.org/
"@my-org": https://private.example.com/
"@internal": https://nexus.corp.com/
Auth tokens in ~/.npmrc still work — pnpm continues to read ~/.npmrc as a fallback for registry authentication. The new npmrcAuthFile setting can be used to point to a different file instead of ~/.npmrc.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs looks something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
pnpm config get (without --json) no longer prints INI formatted text. Instead, it prints JSON for objects and arrays, and raw strings for strings, numbers, booleans, and nulls. pnpm config get --json still prints all types of values as JSON, as before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase. Exception: keys that start with @ or // are preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non-camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer falls back to the npm CLI. Commands that were previously passed through to npm (access, bugs, docs, edit, find, home, issues, owner, ping, prefix, profile, pkg, repo, search, set-script, star, stars, team, token, unstar, whoami, xmas) and their aliases (s, se) now throw a "not implemented" error, with a suggestion to use the npm CLI directly #10642. Other previously passed-through commands — view (info, show, v), login (adduser), logout, deprecate, unpublish, dist-tag, and version — have been reimplemented natively in pnpm (see New Commands below).
pnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.$ command instead of > pkg@version stage path\n> command, and shows project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Added support for lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using the pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infoThe root workspace project is no longer excluded when it is explicitly selected via a filter #10465.
pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm logout command for logging out of npm registries. Revokes the authentication token on the registry and removes it from the local auth config file #11213.pnpm deprecate and pnpm undeprecate commands for setting and removing deprecation messages on package versions without delegating to the npm CLI #11120.pnpm unpublish command. Supports unpublishing specific versions, version ranges via semver, and entire packages with --force #11128.pnpm dist-tag command (ls, add, rm subcommands) #11218.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.Added support for a global YAML config file named config.yaml.
Configuration is now split into two categories:
rc file and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for loading environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not CLI arguments.
Added support for reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Added support for specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910. Config dependency and package manager integrity info is now stored in pnpm-lock.yaml instead of inlined in pnpm-workspace.yaml: the workspace manifest contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
Added nodeDownloadMirrors setting to configure custom Node.js download mirrors in pnpm-workspace.yaml. This replaces the node-mirror:<channel> .npmrc setting, which is no longer read #11194:
nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/
.mjs extension. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded #9730.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds now accepts positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.pnpm audit --fix now adds the minimum patched version for each advisory to minimumReleaseAgeExclude in pnpm-workspace.yaml, so the security fix can be installed without waiting for minimumReleaseAge #11216.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules, avoiding the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.If-Modified-Since for conditional metadata fetches, avoiding re-downloading unchanged registry metadata #11161.minimumReleaseAge, reducing the amount of data fetched from the registry #11160.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
$ command instead of > pkg@version stage path\n> command. Show project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true, verifyDepsBeforeRun is now install, and minimumReleaseAge is now 1440 (1 day). Newly published packages will not be resolved until they are at least 1 day old. This protects against supply chain attacks by giving the community time to detect and remove compromised versions. To opt out, set minimumReleaseAge: 0 in pnpm-workspace.yaml #11158.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer reads all settings from .npmrc. Only auth and registry settings are read from .npmrc files. All other settings (like hoistPattern, nodeLinker, shamefullyHoist, etc.) must be configured in pnpm-workspace.yaml or the global ~/.config/pnpm/config.yaml #11189.
Network settings (httpProxy, httpsProxy, noProxy, localAddress, strictSsl, gitShallowHosts) are now written to config.yaml (global) or pnpm-workspace.yaml (local) instead of .npmrc/auth.ini. They are still readable from .npmrc for easier migration from the npm CLI #11209.
pnpm no longer reads npm_config_* environment variables. Use pnpm_config_* environment variables instead (e.g., pnpm_config_registry instead of npm_config_registry).
pnpm no longer reads the npm global config at $PREFIX/etc/npmrc.
pnpm login writes auth tokens to ~/.config/pnpm/auth.ini.
New registries setting in pnpm-workspace.yaml:
registries:
default: https://registry.npmjs.org/
"@my-org": https://private.example.com/
"@internal": https://nexus.corp.com/
Auth tokens in ~/.npmrc still work — pnpm continues to read ~/.npmrc as a fallback for registry authentication. The new npmrcAuthFile setting can be used to point to a different file instead of ~/.npmrc.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts #11220.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm logout command for logging out of npm registries. Revokes the authentication token on the registry and removes it from the local auth config file #11213.pnpm deprecate and pnpm undeprecate commands for setting and removing deprecation messages on package versions without delegating to the npm CLI #11120.pnpm unpublish command. Supports unpublishing specific versions, version ranges via semver, and entire packages with --force #11128.pnpm dist-tag command (ls, add, rm subcommands) #11218.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
Added nodeDownloadMirrors setting to configure custom Node.js download mirrors in pnpm-workspace.yaml. This replaces the node-mirror:<channel> .npmrc setting, which is no longer read #11194:
nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.pnpm audit --fix now adds the minimum patched version for each advisory to minimumReleaseAgeExclude in pnpm-workspace.yaml, so the security fix can be installed without waiting for minimumReleaseAge #11216.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules. This avoids the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.If-Modified-Since for conditional metadata fetches, avoiding re-downloading unchanged registry metadata #11161.minimumReleaseAge, reducing the amount of data fetched from the registry #11160.Use process.stderr.write instead of console.error for script logging #11140.
Respect frozen-lockfile flag when migrating config dependencies #11067.
Removed --workspace flag from the version command #11115.
Handle ENOTSUP error in clone import path during parallel I/O #11117.
Fixed pnpm audit command.
Updated dependencies to fix vulnerabilities.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handle ENOTSUP error in linkOrCopy during parallel imports #11103.
Skip linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fix _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fix a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevent catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolve patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagate error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
Fix SQLite race condition during store initialization on Windows.
Remove rimrafSync in importIndexedDir fast-path error handler #11168.
Fixed pnpm dedupe --check unexpectedly failing due to non-deterministic resolution #11110.
Fixed empty files not being rejected in isEmptyDirOrNothing #11182.
Fixed .bat/.cmd token helpers not working on Windows due to missing shell: true option.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
$ command instead of > pkg@version stage path\n> command. Show project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true, verifyDepsBeforeRun is now install, and minimumReleaseAge is now 1440 (1 day). Newly published packages will not be resolved until they are at least 1 day old. This protects against supply chain attacks by giving the community time to detect and remove compromised versions. To opt out, set minimumReleaseAge: 0 in pnpm-workspace.yaml #11158.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer reads all settings from .npmrc. Only auth and registry settings are read from .npmrc files. All other settings (like hoistPattern, nodeLinker, shamefullyHoist, etc.) must be configured in pnpm-workspace.yaml or the global ~/.config/pnpm/config.yaml #11189.
pnpm no longer reads npm_config_* environment variables. Use pnpm_config_* environment variables instead (e.g., pnpm_config_registry instead of npm_config_registry).
pnpm no longer reads the npm global config at $PREFIX/etc/npmrc.
pnpm login writes auth tokens to ~/.config/pnpm/auth.ini.
New registries setting in pnpm-workspace.yaml:
registries:
default: https://registry.npmjs.org/
"@my-org": https://private.example.com/
"@internal": https://nexus.corp.com/
Auth tokens in ~/.npmrc still work — pnpm continues to read ~/.npmrc as a fallback for registry authentication. The new npmrcAuthFile setting can be used to point to a different file instead of ~/.npmrc.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
Added nodeDownloadMirrors setting to configure custom Node.js download mirrors in pnpm-workspace.yaml. This replaces the node-mirror:<channel> .npmrc setting, which is no longer read #11194:
nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules. This avoids the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.If-Modified-Since for conditional metadata fetches, avoiding re-downloading unchanged registry metadata #11161.minimumReleaseAge, reducing the amount of data fetched from the registry #11160.Use process.stderr.write instead of console.error for script logging #11140.
Respect frozen-lockfile flag when migrating config dependencies #11067.
Removed --workspace flag from the version command #11115.
Handle ENOTSUP error in clone import path during parallel I/O #11117.
Fixed pnpm audit command.
Updated dependencies to fix vulnerabilities.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handle ENOTSUP error in linkOrCopy during parallel imports #11103.
Skip linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fix _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fix a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevent catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolve patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagate error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
Fix SQLite race condition during store initialization on Windows.
Remove rimrafSync in importIndexedDir fast-path error handler #11168.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
$ command instead of > pkg@version stage path\n> command. Show project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules. This avoids the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.Use process.stderr.write instead of console.error for script logging #11140.
Respect frozen-lockfile flag when migrating config dependencies #11067.
Removed --workspace flag from the version command #11115.
Handle ENOTSUP error in clone import path during parallel I/O #11117.
Fixed pnpm audit command.
Updated dependencies to fix vulnerabilities.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handle ENOTSUP error in linkOrCopy during parallel imports #11103.
Skip linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fix _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fix a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevent catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolve patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagate error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
$ command instead of > pkg@version stage path\n> command. Show project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules. This avoids the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.Use process.stderr.write instead of console.error for script logging #11140.
Respect frozen-lockfile flag when migrating config dependencies #11067.
Removed --workspace flag from the version command #11115.
Handle ENOTSUP error in clone import path during parallel I/O #11117.
Fixed pnpm audit command.
Updated dependencies to fix vulnerabilities.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handle ENOTSUP error in linkOrCopy during parallel imports #11103.
Skip linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fix _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fix a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevent catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolve patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagate error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Nothing published for this version
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node_modules. This avoids the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handle ENOTSUP error in linkOrCopy during parallel imports #11103.
Skip linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fix _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fix a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevent catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolve patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagate error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Stop falling back to the npm CLI. Commands that were previously passed through to npm (access, adduser, bugs, deprecate, dist-tag, docs, edit, find, home, info, issues, login, logout, owner, ping, prefix, profile, pkg, repo, search, set-script, show, star, stars, team, token, unpublish, unstar, version, view, whoami, xmas) and their aliases (s, se, v) now throw a "not implemented" error with a suggestion to use the npm CLI directly #10642.
Store globally installed binaries in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds to receive positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix=update #10341.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadChanged default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
.mjs extension: .pnpmfile.mjs #9730..pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.-F as a short alias for the --filter option.Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix --fix-mode=update #10341…
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadpnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Added pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.
Allow loading certificates from cert, ca, and key for specific registry URLs. E.g., //registry.example.com/:ca=-----BEGIN CERTIFICATE-----.... Previously this was only working via certfile, cafile, and keyfile #10230.
Added --all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.
Added a new setting blockExoticSubdeps that prevents the resolution of exotic protocols in transitive dependencies #10265.
Added trustPolicyIgnoreAfter, which allows you to ignore trust policy checks for packages published more than a specified time ago #10352.
Added a new flag called --bare to pnpm init for creating a package.json with the bare minimum of required fields #10226.
Added support for allowBuilds, which is a new field that can be used instead of onlyBuiltDependencies and ignoredBuiltDependencies #10311.
Added support for object type in configDependencies when the tarball URL returned from package metadata differs from the computed URL #10431.
The pnpm dlx / pnpx command now supports the catalog: protocol. Example: pnpm dlx shx@catalog:.
Add support for a hook called beforePacking that can be used to customize the package.json contents at publish time #3816.
On systems using the musl C library (e.g. Alpine Linux), pnpm env use now automatically downloads the musl variant of Node.js. pnpm env add and pnpm env remove subcommands have been removed. pnpm env list now only lists remote Node.js versions.
Added support for --dry-run to the pack command #10301.
Added mark-and-sweep garbage collection for global virtual store. pnpm store prune now removes unused packages from the global virtual store's links/ directory.
Semi-breaking. Changed the location of unscoped packages in the virtual global store. They will now be stored under a directory named @ to maintain a uniform 4-level directory depth.
Support configuring auditLevel in the pnpm-workspace.yaml file #10540.
Support bare workspace: protocol without version specifier. It is now treated as workspace:* and resolves to the concrete version during publish #10436.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new command pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.
Added pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.
Increase the network concurrency on machines with many CPU cores #10068.
Added support for trustPolicyExclude #10164.
Compute integrity hash for HTTP tarball dependencies when fetching, storing it in the lockfile to prevent servers from serving altered content on subsequent installs #10287.
Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the Node.js version required by a dependency if that dependency declares a Node.js runtime in the engines field #10141.
Allow overriding the engines field on publish by the publishConfig.engines field.
Changed default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
Added a new setting: trustPolicy. When set to no-downgrade, pnpm will fail installation if a package's trust level has decreased compared to previous releases #8889.
Support .pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.
Added -F as a short alias for the --filter option.
When the global virtual store is enabled, packages that are not allowed to build (and don't transitively depend on packages that are) now get hashes that don't include the engine name (platform, architecture, Node.js major version). This means ~95% of packages in the GVS survive Node.js upgrades and architecture changes without re-import #10837.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix --fix-mode=update #10341.
Fixed global install output showing ??? instead of the linked package path when linking from the current directory #10899.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix --fix-mode=update #10341…
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadpnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in pnpm-lock.yaml instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Added pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.
Allow loading certificates from cert, ca, and key for specific registry URLs. E.g., //registry.example.com/:ca=-----BEGIN CERTIFICATE-----.... Previously this was only working via certfile, cafile, and keyfile #10230.
Added --all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.
Added a new setting blockExoticSubdeps that prevents the resolution of exotic protocols in transitive dependencies #10265.
Added trustPolicyIgnoreAfter, which allows you to ignore trust policy checks for packages published more than a specified time ago #10352.
Added a new flag called --bare to pnpm init for creating a package.json with the bare minimum of required fields #10226.
Added support for allowBuilds, which is a new field that can be used instead of onlyBuiltDependencies and ignoredBuiltDependencies #10311.
Added support for object type in configDependencies when the tarball URL returned from package metadata differs from the computed URL #10431.
The pnpm dlx / pnpx command now supports the catalog: protocol. Example: pnpm dlx shx@catalog:.
Add support for a hook called beforePacking that can be used to customize the package.json contents at publish time #3816.
On systems using the musl C library (e.g. Alpine Linux), pnpm env use now automatically downloads the musl variant of Node.js. pnpm env add and pnpm env remove subcommands have been removed. pnpm env list now only lists remote Node.js versions.
Added support for --dry-run to the pack command #10301.
Added mark-and-sweep garbage collection for global virtual store. pnpm store prune now removes unused packages from the global virtual store's links/ directory.
Semi-breaking. Changed the location of unscoped packages in the virtual global store. They will now be stored under a directory named @ to maintain a uniform 4-level directory depth.
Support configuring auditLevel in the pnpm-workspace.yaml file #10540.
Support bare workspace: protocol without version specifier. It is now treated as workspace:* and resolves to the concrete version during publish #10436.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new command pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.
Added pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.
Increase the network concurrency on machines with many CPU cores #10068.
Added support for trustPolicyExclude #10164.
Compute integrity hash for HTTP tarball dependencies when fetching, storing it in the lockfile to prevent servers from serving altered content on subsequent installs #10287.
Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the Node.js version required by a dependency if that dependency declares a Node.js runtime in the engines field #10141.
Allow overriding the engines field on publish by the publishConfig.engines field.
Changed default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
Added a new setting: trustPolicy. When set to no-downgrade, pnpm will fail installation if a package's trust level has decreased compared to previous releases #8889.
Support .pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.
Added -F as a short alias for the --filter option.
When the global virtual store is enabled, packages that are not allowed to build (and don't transitively depend on packages that are) now get hashes that don't include the engine name (platform, architecture, Node.js major version). This means ~95% of packages in the GVS survive Node.js upgrades and architecture changes without re-import #10837.
Support specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Add the ability to fix vulnerabilities by updating packages in the lockfile instead of adding overrides. Use pnpm audit --fix --fix-mode=update #10341.
Fixed global install output showing ??? instead of the linked package path when linking from the current directory #10899.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadpnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910.
Store config dependency and package manager integrity info in a separate pnpm-lock.env.yaml lockfile instead of inlining it in pnpm-workspace.yaml. The workspace manifest now contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the new env lockfile. The env lockfile also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Added pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.
Allow loading certificates from cert, ca, and key for specific registry URLs. E.g., //registry.example.com/:ca=-----BEGIN CERTIFICATE-----.... Previously this was only working via certfile, cafile, and keyfile #10230.
Added --all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.
Added a new setting blockExoticSubdeps that prevents the resolution of exotic protocols in transitive dependencies #10265.
Added trustPolicyIgnoreAfter, which allows you to ignore trust policy checks for packages published more than a specified time ago #10352.
Added a new flag called --bare to pnpm init for creating a package.json with the bare minimum of required fields #10226.
Added support for allowBuilds, which is a new field that can be used instead of onlyBuiltDependencies and ignoredBuiltDependencies #10311.
Added support for object type in configDependencies when the tarball URL returned from package metadata differs from the computed URL #10431.
The pnpm dlx / pnpx command now supports the catalog: protocol. Example: pnpm dlx shx@catalog:.
Add support for a hook called beforePacking that can be used to customize the package.json contents at publish time #3816.
On systems using the musl C library (e.g. Alpine Linux), pnpm env use now automatically downloads the musl variant of Node.js. pnpm env add and pnpm env remove subcommands have been removed. pnpm env list now only lists remote Node.js versions.
Added support for --dry-run to the pack command #10301.
Added mark-and-sweep garbage collection for global virtual store. pnpm store prune now removes unused packages from the global virtual store's links/ directory.
Semi-breaking. Changed the location of unscoped packages in the virtual global store. They will now be stored under a directory named @ to maintain a uniform 4-level directory depth.
Support configuring auditLevel in the pnpm-workspace.yaml file #10540.
Support bare workspace: protocol without version specifier. It is now treated as workspace:* and resolves to the concrete version during publish #10436.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new command pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.
Added pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.
Increase the network concurrency on machines with many CPU cores #10068.
Added support for trustPolicyExclude #10164.
Compute integrity hash for HTTP tarball dependencies when fetching, storing it in the lockfile to prevent servers from serving altered content on subsequent installs #10287.
Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the Node.js version required by a dependency if that dependency declares a Node.js runtime in the engines field #10141.
Allow overriding the engines field on publish by the publishConfig.engines field.
Changed default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
Added a new setting: trustPolicy. When set to no-downgrade, pnpm will fail installation if a package's trust level has decreased compared to previous releases #8889.
Support .pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.
Added -F as a short alias for the --filter option.
When the global virtual store is enabled, packages that are not allowed to build (and don't transitively depend on packages that are) now get hashes that don't include the engine name (platform, architecture, Node.js major version). This means ~95% of packages in the GVS survive Node.js upgrades and architecture changes without re-import #10837.
Fixed global install output showing ??? instead of the linked package path when linking from the current directory #10899.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is now stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadpnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Added pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.
Allow loading certificates from cert, ca, and key for specific registry URLs. E.g., //registry.example.com/:ca=-----BEGIN CERTIFICATE-----.... Previously this was only working via certfile, cafile, and keyfile #10230.
Added --all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.
Added a new setting blockExoticSubdeps that prevents the resolution of exotic protocols in transitive dependencies #10265.
Added trustPolicyIgnoreAfter, which allows you to ignore trust policy checks for packages published more than a specified time ago #10352.
Added a new flag called --bare to pnpm init for creating a package.json with the bare minimum of required fields #10226.
Added support for allowBuilds, which is a new field that can be used instead of onlyBuiltDependencies and ignoredBuiltDependencies #10311.
Added support for object type in configDependencies when the tarball URL returned from package metadata differs from the computed URL #10431.
The pnpm dlx / pnpx command now supports the catalog: protocol. Example: pnpm dlx shx@catalog:.
Add support for a hook called beforePacking that can be used to customize the package.json contents at publish time #3816.
On systems using the musl C library (e.g. Alpine Linux), pnpm env use now automatically downloads the musl variant of Node.js. pnpm env add and pnpm env remove subcommands have been removed. pnpm env list now only lists remote Node.js versions.
Added support for --dry-run to the pack command #10301.
Added mark-and-sweep garbage collection for global virtual store. pnpm store prune now removes unused packages from the global virtual store's links/ directory.
Semi-breaking. Changed the location of unscoped packages in the virtual global store. They will now be stored under a directory named @ to maintain a uniform 4-level directory depth.
Support configuring auditLevel in the pnpm-workspace.yaml file #10540.
Support bare workspace: protocol without version specifier. It is now treated as workspace:* and resolves to the concrete version during publish #10436.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new command pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.
Added pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.
Increase the network concurrency on machines with many CPU cores #10068.
Added support for trustPolicyExclude #10164.
Compute integrity hash for HTTP tarball dependencies when fetching, storing it in the lockfile to prevent servers from serving altered content on subsequent installs #10287.
Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the Node.js version required by a dependency if that dependency declares a Node.js runtime in the engines field #10141.
Allow overriding the engines field on publish by the publishConfig.engines field.
Changed default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
Added a new setting: trustPolicy. When set to no-downgrade, pnpm will fail installation if a package's trust level has decreased compared to previous releases #8889.
Support .pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.
Added -F as a short alias for the --filter option.
When the global virtual store is enabled, packages that are not allowed to build (and don't transitively depend on packages that are) now get hashes that don't include the engine name (platform, architecture, Node.js major version). This means ~95% of packages in the GVS survive Node.js upgrades and architecture changes without re-import #10837.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Runtime dependencies are always linked from the global virtual store #10233.
Optimized index file format to store the hash algorithm once per file instead of repeating it for every file entry. Each file entry now stores only the hex digest instead of the full integrity string (<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.
Store version bumped to v11.
Switched internal store and cache files from JSON to MessagePack format for improved performance.
This change migrates all internal index files and metadata cache files to use MessagePack serialization instead of JSON. MessagePack provides faster serialization/deserialization and more compact file sizes, resulting in improved installation performance.
Related PR: #10500
Store the bundled manifest (name, version, bin, engines, scripts, etc.) directly in the package index file, eliminating the need to read package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.
Global installs (pnpm add -g pkg) and pnpm dlx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadpnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Added pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.
Allow loading certificates from cert, ca, and key for specific registry URLs. E.g., //registry.example.com/:ca=-----BEGIN CERTIFICATE-----.... Previously this was only working via certfile, cafile, and keyfile #10230.
Added --all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.
Added a new setting blockExoticSubdeps that prevents the resolution of exotic protocols in transitive dependencies #10265.
Added trustPolicyIgnoreAfter, which allows you to ignore trust policy checks for packages published more than a specified time ago #10352.
Added a new flag called --bare to pnpm init for creating a package.json with the bare minimum of required fields #10226.
Added support for allowBuilds, which is a new field that can be used instead of onlyBuiltDependencies and ignoredBuiltDependencies #10311.
Added support for object type in configDependencies when the tarball URL returned from package metadata differs from the computed URL #10431.
The pnpm dlx / pnpx command now supports the catalog: protocol. Example: pnpm dlx shx@catalog:.
Add support for a hook called beforePacking that can be used to customize the package.json contents at publish time #3816.
On systems using the musl C library (e.g. Alpine Linux), pnpm env use now automatically downloads the musl variant of Node.js. pnpm env add and pnpm env remove subcommands have been removed. pnpm env list now only lists remote Node.js versions.
Added support for --dry-run to the pack command #10301.
Added mark-and-sweep garbage collection for global virtual store. pnpm store prune now removes unused packages from the global virtual store's links/ directory.
Semi-breaking. Changed the location of unscoped packages in the virtual global store. They will now be stored under a directory named @ to maintain a uniform 4-level directory depth.
Support configuring auditLevel in the pnpm-workspace.yaml file #10540.
Support bare workspace: protocol without version specifier. It is now treated as workspace:* and resolves to the concrete version during publish #10436.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new command pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.
Added pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.
Increase the network concurrency on machines with many CPU cores #10068.
Added support for trustPolicyExclude #10164.
Compute integrity hash for HTTP tarball dependencies when fetching, storing it in the lockfile to prevent servers from serving altered content on subsequent installs #10287.
Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the Node.js version required by a dependency if that dependency declares a Node.js runtime in the engines field #10141.
Allow overriding the engines field on publish by the publishConfig.engines field.
Changed default values: optimisticRepeatInstall is now true and verifyDepsBeforeRun is now install.
Added a new setting: trustPolicy. When set to no-downgrade, pnpm will fail installation if a package's trust level has decreased compared to previous releases #8889.
Support .pnpmfile.mjs as the default pnpmfile. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded.
Added -F as a short alias for the --filter option.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Runtime dependencies are always linked from the global virtual store #10233.
Optimized index file format to store the hash algorithm once per file instead of repeating it for every file entry. Each file entry now stores only the hex digest instead of the full integrity string (<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.
Store version bumped to v11.
Switched internal store and cache files from JSON to MessagePack format for improved performance.
This change migrates all internal index files and metadata cache files to use MessagePack serialization instead of JSON. MessagePack provides faster serialization/deserialization and more compact file sizes, resulting in improved installation performance.
Related PR: #10500
Store the bundled manifest (name, version, bin, engines, scripts, etc.) directly in the package index file, eliminating the need to read package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Runtime dependencies are always linked from the global virtual store #10233.
Optimized index file format to store the hash algorithm once per file instead of repeating it for every file entry. Each file entry now stores only the hex digest instead of the full integrity string (<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.
Store version bumped to v11.
Switched internal store and cache files from JSON to MessagePack format for improved performance.
This change migrates all internal index files and metadata cache files to use MessagePack serialization instead of JSON. MessagePack provides faster serialization/deserialization and more compact file sizes, resulting in improved installation performance.
Related PR: #10500
Store the bundled manifest (name, version, bin, engines, scripts, etc.) directly in the package index file, eliminating the need to read package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
7fab2a2: Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
cb367b9: Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
075aa99: Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.e146e98: Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
7d5ada0: pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
5bf7768: A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
2b14c74: When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Runtime dependencies are always linked from the global virtual store #10233.
Optimized index file format to store the hash algorithm once per file instead of repeating it for every file entry. Each file entry now stores only the hex digest instead of the full integrity string (<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.
Store version bumped to v11.
Switched internal store and cache files from JSON to MessagePack format for improved performance.
This change migrates all internal index files and metadata cache files to use MessagePack serialization instead of JSON. MessagePack provides faster serialization/deserialization and more compact file sizes, resulting in improved installation performance.
Related PR: #10500
Store the bundled manifest (name, version, bin, engines, scripts, etc.) directly in the package index file, eliminating the need to read package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Runtime dependencies are always linked from the global virtual store #10233.
Optimized index file format to store the hash algorithm once per file instead of repeating it for every file entry. Each file entry now stores only the hex digest instead of the full integrity string (<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.
Store version bumped to v11.
Switched internal store and cache files from JSON to MessagePack format for improved performance.
This change migrates all internal index files and metadata cache files to use MessagePack serialization instead of JSON. MessagePack provides faster serialization/deserialization and more compact file sizes, resulting in improved installation performance.
Related PR: #10500
Store the bundled manifest (name, version, bin, engines, scripts, etc.) directly in the package index file, eliminating the need to read package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
This package is now pure ESM.
Node.js v18, 19, 20, and 21 support discontinued.
The standalone exe version of pnpm requires at least glibc 2.27.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed the deprecated allowNonAppliedPatches completely in favor of allowUnusedPatches.
Remove ignorePatchFailures so all patch application failures should throw an error.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infopnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Support reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace roots. This replaces the previous forward-tree output which was noisy and hard to read for deeply nested dependencies.
A new --yes flag can be passed to pnpm to automatically confirm prompts. This is useful when running pnpm in non-interactive script.
When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
Check if a package is installable for non npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
Explicitly tell npm the path to the global rc config file.
Fix YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is written to allowBuilds.
Fix a bug in which specifying filter on pnpm-workspace.yaml would cause pnpm to not detect any projects.
Defer patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
Remove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs would look something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
strictDepBuilds is true by default.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
blockExoticSubdeps is true by default.
This package is now pure ESM.
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
The pnpm CLI now gets installed with a specific version of Node.js, which it uses for its runtime. This makes pnpm more stable and it doesn't rely on your globally install Node.js anymore.
Node.js v18 and 19 support discontinued.
pnpm config get <array> now prints a JSON array.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
Runtime dependencies are always linked from the global virtual store #10233.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infoRemove deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, and ignoredBuiltDependencies.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> </tbody> </table>
pnpm config get (without --json) no longer print INI formatted text. Instead, it would print JSON for both objects and arrays and raw string for strin
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
This package is now pure ESM.
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
The pnpm CLI now gets installed with a specific version of Node.js, which it uses for its runtime. This makes pnpm more stable and it doesn't rely on your globally install Node.js anymore.
Node.js v18 and 19 support discontinued.
pnpm config get <array> now prints a JSON array.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
The standalone exe version of pnpm requires at least glibc 2.27.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
JSON files in the store and cache are replaced with binary files generated with v8.serialize.
Reading and deserializing a binary file generated with v8.serialize appears to be about 23% faster than reading and parsing a JSON file. Other operations are similar in speed. Therefore, we have switched to V8 binary files for storing data in pnpm’s global store and cache. The disadvantage of this approach is that V8 files generated by newer versions of Node.js cannot be deserialized by older versions. In such cases, we ignore the cache.
Related PR: #9971.
Runtime dependencies are always linked from the global virtual store #10233.
Support lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infoLoad environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
npm the path to the global rc config file.<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> </tbody> </table>
Nothing published for this version
pnpm config get (without --json) no longer print INI formatted text. Instead, it would print JSON for both objects and arrays and raw string for strin
pnpm config get (without --json) no longer print INI formatted text.
Instead, it would print JSON for both objects and arrays and raw string for
strings, numbers, booleans, and nulls.
pnpm config get --json would still print all types of values as JSON like before.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
This package is now pure ESM.
pnpm no longer loads non-auth and non-registry settings from rc files. Other settings must be defined in pnpm-workspace.yaml.
The pnpm CLI now gets installed with a specific version of Node.js, which it uses for its runtime. This makes pnpm more stable and it doesn't rely on your globally install Node.js anymore.
Node.js v18 and 19 support discontinued.
pnpm config get <array> now prints a JSON array.
The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase.
Exception: Keys that start with @ or // would be preserved (their cases don't change).
The standalone exe version of pnpm requires at least glibc 2.27.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config get and pnpm config list no longer load non camelCase options from the workspace manifest (pnpm-workspace.yaml).
JSON files in the store and cache are replaced with binary files generated with v8.serialize.
Reading and deserializing a binary file generated with v8.serialize appears to be about 23% faster than reading and parsing a JSON file. Other operations are similar in speed. Therefore, we have switched to V8 binary files for storing data in pnpm’s global store and cache. The disadvantage of this approach is that V8 files generated by newer versions of Node.js cannot be deserialized by older versions. In such cases, we ignore the cache.
Related PR: #9971.
Load environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not the CLI arguments.
Add support for a global YAML config file named config.yaml.
Now configurations are divided into 2 categories:
rc and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for pnpmfiles written in ESM. They should have the .mjs extension: .pnpmfile.mjs #9730.
npm the path to the global rc config file.<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"> </a> </td> </tr> </tbody> </table>
78e29fe: Fixed a path traversal vulnerability where a dependency whose manifest name was a scoped path traversal (e.g. @x/../../../ ) could be written…
pnpm-lock.yaml from writing package content outside the virtual store. A dependency path key whose name reconstructs to a path-traversal sequence (e.g. ../../../tmp/x@1.0.0) is now rejected by the isolated (virtual-store) linker and the Plug'n'Play resolver map, matching the containment already applied to the hoisted linker. Under the global virtual store, a traversal in the version-derived path segment (e.g. a snapshot version: "../../x") is now rejected at iterateHashedGraphNodes, the single point every global-virtual-store slot path funnels through.name was a scoped path traversal (e.g. @x/../../../<path>) could be written outside node_modules to an attacker-controlled location during pnpm install, even with --ignore-scripts. The isolated linker now validates the package name before using it as a directory name, matching the existing protection in the hoisted linker.pnpm and @pnpm/exe npm packages whose bins are placeholders replaced at install time by the host's native binary from a @pnpm/exe.<platform>-<arch>[-musl] optional dependency. Because pnpm installs its own engine with --ignore-scripts, that relinking never ran, leaving a non-executable placeholder. pnpm now relinks the native binary itself for v12 (recognizing the new platform-package naming scheme and the native pnpm package), and verifies the native binary's npm registry signature before running it.${...} environment-variable placeholders in the httpProxy, httpsProxy, noProxy, proxy, and noproxy settings are no longer expanded when these settings come from a project's pnpm-workspace.yaml. They now receive the same protection already applied to registry.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
352ae48: Security: validate config dependency names and versions before using them to build filesystem paths. A pnpm-workspace.yaml with a traversal-s
352ae48: Security: validate config dependency names and versions before using them to build filesystem paths. A pnpm-workspace.yaml with a traversal-shaped configDependencies name (such as ../../PWNED) or version (such as ../../../PWNED) could previously cause pnpm install to create symlinks or write package files outside node_modules/.pnpm-config and the store. Names must now be valid npm package names and versions must be exact semver versions. See GHSA-qrv3-253h-g69c.
352ae48: Reject path-traversal and reserved dependency aliases (such as ../../../escape, .bin, .pnpm, or node_modules) that come from a lockfile rather than a freshly resolved manifest. A crafted lockfile alias could otherwise be joined directly under a hoisted node_modules directory, letting package files be written outside the intended install root or overwrite pnpm-owned layout.
The nodeLinker: hoisted graph builder now validates each alias at the directory sink (safeJoinModulesDir), matching the validation pnpm already performs when resolving aliases from manifests. See GHSA-fr4h-3cph-29xv.
352ae48: Prevent pnpm patch-remove from removing files outside the configured patches directory.
217fbe0: Hardened the warning printed when a project .npmrc uses environment variables in registry/auth settings: the suggested pnpm config set command is now only included for keys made up of shell-inert characters. Because the key comes from a repository-controlled .npmrc and a shell expands $(...), backticks, and $VAR even inside double quotes, a crafted key could otherwise have turned the suggested copy-paste command into command execution.
<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Following GHSA-3qhv-2rgh-x77r, pnpm no longer expands ${ENV_VAR} placeholders that come from a repository-controlled config file, because a malicious
.npmrc (action may be required)Following GHSA-3qhv-2rgh-x77r, pnpm no longer expands ${ENV_VAR} placeholders that come from a repository-controlled config file, because a malicious repository could otherwise use them to leak your environment secrets (npm tokens, CI job tokens, etc.) to an attacker-controlled registry during install. This applies to:
.npmrc — registry, @scope:registry, proxy URLs, URL-scoped keys (//host/…), and credential values (_authToken, _auth, _password, username, tokenHelper, cert, key);pnpm-workspace.yaml.This release also closes a bypass where a project .npmrc could set userconfig, globalconfig, or prefix to make pnpm load a repo-supplied file as trusted config (via @pnpm/npm-conf@3.0.3).
Environment variables are still expanded in trusted config: your user-level ~/.npmrc, the global config, CLI options, and environment config.
If your authentication broke after upgrading, move the token out of the committed .npmrc:
# Writes to your user/global config, not the repository:
pnpm config set "//registry.npmjs.org/:_authToken" "$NPM_TOKEN"
Or keep the ${NPM_TOKEN} line but put it in your user-level ~/.npmrc instead of the repo. In GitHub Actions, actions/setup-node with registry-url already writes a user-level .npmrc, so NODE_AUTH_TOKEN keeps working. For other CI where editing each pipeline is hard, set NPM_CONFIG_USERCONFIG=.npmrc in the CI environment to declare the project .npmrc trusted.
See https://pnpm.io/npmrc for full migration details.
.npmrc uses an environment variable in a registry/proxy URL or in registry credentials. The message now explains why the setting was ignored and how to migrate it to a trusted source — for example by running pnpm config set "<key>" <value> to store it in the global config, or by keeping the ${...} line in the user-level ~/.npmrc — with a link to https://pnpm.io/npmrc..npmrc can no longer redirect which files pnpm loads as its trusted user and global configuration. Previously such a file could set userconfig, globalconfig, or prefix to point at an attacker-supplied file shipped in the repository, and pnpm would load it as a trusted config source — bypassing the protection that prevents repository config from expanding environment variables into registry request destinations and credentials, and allowing it to set tokenHelper. The user/global config file locations are now resolved only from trusted sources (CLI options, environment config, the npm builtin config, and defaults) before the project and workspace .npmrc files are read. Fixed by upgrading @pnpm/npm-conf to 3.0.3.<!-- sponsors -->
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table>
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table>
<!-- sponsors end -->
Your coding agent can read these notes before it upgrades. Set up the MCP server →