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
03 Oct 2026
Ships on a steady schedule
a new release about every 8 days
Nearly every release is documented
notes for 57 of the last 60 stable releases
376 versions withdrawn
withdrawn after publishing
14 years old
1338 releases · first in 2013
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 -->
One column per quarter.
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>
46bc7c9 : Updated adm-zip to v0.6.0, which fixes a memory-exhaustion vulnerability where a crafted ZIP file could make it allocate 4 GB of memory. adm…
e7888e5: pnpm self-update now resolves and verifies pnpm through registry, authentication, proxy, and TLS settings from trusted non-project configuration. Project configuration and the default project pnpmfile can no longer redirect the pnpm download or disable engine identity verification.
46bc7c9: pnpm no longer tells you to update itself with Corepack or with pnpm add -g:
pnpm self-update, or the standalone install script when pnpm is running under Corepack. It used to suggest corepack use pnpm@<version>, or pnpm add -g pnpm / pnpm add -g @pnpm/exe when pnpm was not installed by the standalone script — but pnpm add -g refuses to install pnpm and points at pnpm self-update anyway, and @pnpm/exe is not published for pnpm v12 or newer, where the unscoped pnpm package is itself the native executable.pnpm self-update under Corepack now points at the standalone install script too, instead of telling you to update pnpm with Corepack.46bc7c9: Updated adm-zip to v0.6.0, which fixes a memory-exhaustion vulnerability where a crafted ZIP file could make it allocate 4 GB of memory. adm-zip is used to extract the Node.js, Bun, and Deno archives that pnpm downloads on Windows.
Updated the embedded Node.js release keys to the current canonical nodejs/release-keys list.
Updated the embedded npm registry signing keys to the set currently advertised by npm.
702ad5f: Update the embedded Node.js release keys with the new key added to nodejs/release-keys (Stewart X Addison, 655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD).
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 -->
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.
packageManager field, the registry it fetches from (and the proxy/TLS settings used for that traffic) now come exclusively from trusted config sources — CLI options, env config, user and global .npmrc — defaulting to the public npm registry, instead of the repository's project/workspace settings.packageManager field (or pnpm self-update) makes pnpm download another pnpm version, the staged install is verified corepack-style: the integrity recorded in the staged lockfile must carry a valid npm registry signature for the exact name@version, validated against npm's public signing keys that ship embedded in the pnpm CLI. Verification fails closed — a tampered download, an unsigned package, or an unreachable registry refuses the version switch rather than running an unverified binary. It runs only when the wanted version is actually downloaded (a tools-directory cache miss), so repeated commands pay no extra network round trip..npmrc and pnpm-workspace.yaml) can no longer expand ${...} placeholders in registry/proxy request destinations, URL-scoped keys, or registry credential values, preventing repository-controlled configuration from exfiltrating environment secrets through request URLs. Trusted user/global/CLI/env config keeps full env expansion, so existing token and registry setup flows continue to work.bin names ("", ".", "..", and scoped forms such as @scope/..) when resolving a package's bins. These names previously passed the bin-name guard and, when joined to the global bin directory during global remove/update/add operations, could resolve to the global bin directory itself or its parent and have it recursively deleted.onlyBuiltDependencies (and allowBuilds) entries can approve lifecycle scripts for git, git-hosted tarball, direct tarball, and local directory artifacts. To approve one of those artifacts explicitly, use its peer-suffix-free lockfile depPath as the key. Lockfile entries are now rejected when a registry-style dependency path (name@semver) is backed by a git, directory, or git-hosted tarball resolution (ERR_PNPM_RESOLUTION_SHAPE_MISMATCH), so the dependency path is a reliable artifact identity by the time scripts can run.SHASUMS256.txt against the Node.js release team's public keys (embedded in the pnpm CLI) before trusting its hashes. The Node.js download mirror is repository-configurable (node-mirror:<channel> in .npmrc), and the integrity check previously trusted a SHASUMS256.txt fetched from that same mirror — a circular check that a malicious mirror could satisfy with a tampered binary and matching hashes. A mirror that proxies the real signed SHASUMS keeps working unchanged. Only the release channel publishes signed SHASUMS files, so pre-release channels (rc, nightly, …) remain unverified.<!-- 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 -->
Reject pnpm-lock.yaml entries whose remote tarball resolution: block is missing the integrity field. Previously the worker that extracts a downloaded
pnpm-lock.yaml entries whose remote tarball resolution: block is missing the integrity field. Previously the worker that extracts a downloaded tarball skipped hash verification when no integrity was supplied and minted a fresh one from the unverified bytes, so an attacker who could both alter the lockfile (e.g. via a pull request that strips integrity:) and serve modified content at the referenced tarball URL could install a tampered package without any error — including under --frozen-lockfile. pnpm now fails closed at lockfile-read time with ERR_PNPM_MISSING_TARBALL_INTEGRITY. Git-hosted tarballs (gitHosted: true or a URL on codeload.github.com / bitbucket.org / gitlab.com) and file: tarballs are exempt — the commit SHA in a git-host URL and the user-controlled local path already anchor the bytes.<!-- 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 -->
…certificate authored for a different host. A deprecation warning is emitted whenever an unscoped per-registry setting is encountered, naming the sourc…
Treat tarball-integrity mismatches against the lockfile as a hard failure by default. Previously, pnpm install (non-frozen) would log ERR_PNPM_TARBALL_INTEGRITY, silently re-resolve from the registry, and overwrite the locked integrity — which meant a compromised registry, proxy, or republished version could substitute attacker-controlled content on a clean machine even though the project shipped a committed lockfile.
pnpm install now exits with ERR_PNPM_TARBALL_INTEGRITY and a hint pointing at the new opt-in flag.
The only opt-in is pnpm install --update-checksums — narrowly scoped to refreshing the locked integrity values from what the registry currently serves. Mirrors yarn's flag of the same name. A warning still prints when the bypass takes effect so the operation is auditable.
--force and pnpm update deliberately do not bypass the integrity check. They are routine refresh operations; silently overwriting a locked integrity in those flows would erase the protection a committed lockfile is supposed to provide. --frozen-lockfile behavior is unchanged. --fix-lockfile keeps its documented purpose (filling in missing lockfile entries) and is also not a bypass.
_authToken, _auth, username/_password, tokenHelper, inline cert/key) to the registry declared in the same config source at load time, so a later layer overriding registry= (workspace .npmrc, pnpm-workspace.yaml, CLI --registry) cannot redirect a credential or client certificate authored for a different host. A deprecation warning is emitted whenever an unscoped per-registry setting is encountered, naming the source and the URL it was pinned to. Reported by JUNYI LIU.minimumReleaseAge handling when cached metadata is abbreviated. The npm registry returns abbreviated package metadata (without the per-version time field) by default, which made the maturity check throw ERR_PNPM_MISSING_TIME whenever cached abbreviated metadata was reused. pnpm now upgrades cached abbreviated metadata to the full document via a follow-up fetch when minimumReleaseAge is active, persists the upgrade to the on-disk cache so subsequent installs skip the extra fetch, and lets ERR_PNPM_MISSING_TIME from the cache fast-path fall through to the network fetch even under strict mode.commit field is not a 40-character hexadecimal SHA before invoking git. A malicious lockfile could otherwise smuggle a value such as --upload-pack=<command> through git fetch / git checkout, which on SSH or local-file transports executes the supplied command.diff --git headers reference paths outside the patched package directory. Previously a malicious .patch file added via a pull request could write, delete, or rename arbitrary files reachable by the user running pnpm install.--prefix=<dir> not being honored when locating the workspace root. The --prefix → dir rename was applied after workspace detection, so workspace settings declared in <dir>/pnpm-workspace.yaml were not loaded when pnpm was invoked from outside <dir> #11535.@x/../../../../../.git/hooks) when reading them from a package manifest or symlinking them into node_modules. A malicious registry package could otherwise use a transitive dependency key to make pnpm install create symlinks at attacker-chosen paths outside the intended node_modules directory.<!-- 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 -->
Pin the integrity of git-hosted tarballs (codeload.github.com, gitlab.com, bitbucket.org) in the lockfile so that subsequent installs detect a tampere
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.
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.
<!-- 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 -->
When self-updating from v10's @pnpm/exe to v11+ on Intel macOS (darwin-x64), pnpm self-update now transparently switches to the JS-only pnpm package o
@pnpm/exe to v11+ on Intel macOS (darwin-x64), pnpm self-update now transparently switches to the JS-only pnpm package on npm instead of installing @pnpm/exe@v11+ (which doesn't ship a working binary for Intel Macs because of an upstream Node.js SEA bug — see #11423 and nodejs/node#62893). Without this, the self-update would silently leave the user with no working pnpm binary. The new install requires Node.js to be available on PATH; a warning is printed when the swap happens. All other host/version combinations are unchanged.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.<!-- 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 -->
Globally-installed bins no longer fail with ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND when pnpm was installed via the standalone @pnpm/exe binary (e.g. curl
Globally-installed bins no longer fail with ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND when pnpm was installed via the standalone @pnpm/exe binary (e.g. curl -fsSL https://get.pnpm.io/install.sh | sh -) on a system without a separate Node.js installation. Previously, when which('node') failed during pnpm add --global, pnpm fell back to process.execPath, which in @pnpm/exe is the pnpm binary itself — and that path was baked into the generated bin shim, causing the shim to invoke pnpm instead of Node #11291, #4645.
Fix an infinite fork-bomb that could happen when pnpm was installed with one version (e.g. npm install -g pnpm@A) and run inside a project whose package.json selected a different pnpm version via the packageManager field (e.g. pnpm@B), while a pnpm-workspace.yaml also existed at the project root.
The child's environment is now forced to manage-package-manager-versions=false (v10) and pm-on-fail=ignore (v11+), which disables the package-manager-version handling in whichever pnpm runs as the child.
Fixes #11337.
<!-- 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 -->
…npm (version, login, logout, publish, unpublish, deprecate, dist-tag, docs, ping, search, star, stars, unstar, whoami, etc.) are now handed over to th…
packageManager field selects pnpm v11 or newer, commands that v10 would have passed through to npm (version, login, logout, publish, unpublish, deprecate, dist-tag, docs, ping, search, star, stars, unstar, whoami, etc.) are now handed over to the wanted pnpm, which implements them natively. Previously they silently shelled out to npm — making, for example, pnpm version --help print npm's help on a project with packageManager: pnpm@11.0.0-rc.3 #11328.<!-- 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 -->
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@ve
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.Fail on incompatible lockfiles in CI when frozen lockfile mode is enabled, while preserving non-frozen CI fallback behavior.
When package metadata is malformed or can't be fetched, the error thrown will now show the originating error.
Fixed intermittent failures when multiple pnpm dlx calls run concurrently for the same package. When the global virtual store is enabled, the importer now verifies file content before skipping a rename, avoiding destructive swap-renames that break concurrent processes. Also tolerates EPERM during bin creation on Windows and properly propagates enableGlobalVirtualStore through the install pipeline.
Fixed handling of non-string version selectors in hoistPeers, preventing invalid peer dependency specifiers.
Improve the non-interactive modules purge error hint to include the confirmModulesPurge=false workaround.
When pnpm needs to recreate node_modules but no TTY is available, the error now suggests either setting CI=true or disabling the purge confirmation prompt via confirmModulesPurge=false.
Adds a regression test for the non-TTY flow.
Fixed false "Command not found" errors on Windows when a command exists in PATH but exits with a non-zero code. Also fixed path resolution for --filter contexts where the command runs in a different package directory.
When a pnpm-lock.yaml contains two documents, ignore the first one. pnpm v11 will write two lockfile documents into pnpm-lock.yaml in order to store pnpm version integrities and config dependency resolutions.
Fixed a bug preventing the clearCache function returned by createNpmResolver from properly clearing metadata cache.
<!-- 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 a regression where pnpm-workspace.yaml without a packages field caused all directories to be treated as workspace projects. This broke projects th
pnpm-workspace.yaml without a packages field caused all directories to be treated as workspace projects. This broke projects that use pnpm-workspace.yaml only for settings (e.g. minimumReleaseAge) without defining workspace packages #10909.<!-- 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 -->
Added --all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.
--all flag to pnpm approve-builds that approves all pending builds without interactive prompts #10136.lockfile-include-tarball-url. Fixes #10915.<!-- 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 -->
When pnpm updates the pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.
pnpm-workspace.yaml, comments, string formatting, and whitespace will be preserved.Added -F as a short alias for the --filter option in the help output.
Handle undefined pkgSnapshot in pnpm why -r #10700.
Fix headless install not being used when a project has an injected self-referencing file: dependency that resolves to link: in the lockfile.
Fixed a race condition when multiple worker threads import the same package to the global virtual store concurrently. The rename operation now tolerates ENOTEMPTY/EEXIST errors if another thread already completed the import.
When lockfile-include-tarball-url is set to false, tarball URLs are now always excluded from the lockfile. Previously, tarball URLs could still appear for packages hosted under non-standard URLs, making the behavior flaky and inconsistent #6667.
Fixed optimisticRepeatInstall skipping install when overrides, packageExtensions, ignoredOptionalDependencies, patchedDependencies, or peersSuffixMaxLength changed.
Fixed pnpm patch-commit failing with "unable to access '/.config/git/attributes': Permission denied" error in environments where HOME is unset or non-standard (Docker containers, CI systems).
The issue occurred because pnpm was setting HOME and the Windows user profile env var to empty strings to suppress user git configuration when running git diff. This caused git to resolve the home directory (~) as root (/), leading to permission errors when attempting to access /.config/git/attributes.
Now uses GIT_CONFIG_GLOBAL: os.devNull instead, which is git's proper mechanism for bypassing user-level configuration without corrupting the home directory path resolution.
Fixes #6537
Fix pnpm why -r --parseable missing dependents when multiple workspace packages share the same dependency #8100.
Fix link-workspace-packages=true incorrectly linking workspace packages when the requested version doesn't match the workspace package's version. Previously, on fresh installs the version constraint is overridden to * in the fallback resolution paths, causing any workspace package with a matching name to be linked regardless of version #10173.
Fixed pnpm update --interactive table breaking with long version strings (e.g., prerelease versions like 7.0.0-dev.20251209.1) by dynamically calculating column widths instead of using hardcoded values #10316.
Explicitly tell npm the path to the global rc config file.
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.
Print help message on running pnpm dlx without arguments and exit.
<!-- 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 version switching via packageManager field failing when pnpm is installed as a standalone executable in environments without a system Node.js #1
packageManager field failing when pnpm is installed as a standalone executable in environments without a system Node.js #10687.<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>
Update @zkochan/js-yaml to fix moderate vulnerability.
<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>
Use the /-/npm/v1/security/audits/quick endpoint as the primary audit endpoint, falling back to /-/npm/v1/security/audits when it fails #10649.
/-/npm/v1/security/audits/quick endpoint as the primary audit endpoint, falling back to /-/npm/v1/security/audits when it fails #10649.<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>
pnpm why now shows a reverse dependency tree. The searched package appears at the root with its dependents as branches, walking back to workspace root
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.pnpm why dependency pruning to prefer correctness over memory consumption. Reverted PR: #7122.pnpm why and pnpm list performance in workspaces with many importers by sharing the dependency graph and materialization cache across all importers instead of rebuilding them independently for each one #10596.<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>
Fixed an out-of-memory error in pnpm list (and pnpm why) on large dependency graphs by replacing the recursive tree builder with a two-phase approach:
pnpm list (and pnpm why) on large dependency graphs by replacing the recursive tree builder with a two-phase approach: a BFS dependency graph followed by cached tree materialization. Duplicate subtrees are now deduplicated in the output, shown as "deduped (N deps hidden)" #10586.allowBuilds not working when set via .pnpmfile.cjs #10516.enableGlobalVirtualStore option is set, the pnpm deploy command would incorrectly create symlinks to the global virtual store. To keep the deploy directory self-contained, pnpm deploy now ignores this setting and always creates a localized virtual store within the deploy directory.minimumReleaseAgeExclude not being respected by pnpm dlx #10338.<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>
Reverted a fix shipped in v10.29.1, which caused another issue #10571. Reverted fix: Fixed pnpm run -r failing with "No projects matched the filters"
<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>
Updating the version of dependency tar to 7.5.7 because the previous one have a security vulnerability reported here: CVE-2026-24842
pnpm dlx / pnpx command now supports the catalog: protocol. Example: pnpm dlx shx@catalog:.auditLevel in the pnpm-workspace.yaml file #10540.workspace: protocol without version specifier. It is now treated as workspace:* and resolves to the concrete version during publish #10436.Fixed pnpm list --json returning incorrect paths when using global virtual store #10187.
Fix pnpm store path and pnpm store status using workspace root for path resolution when storeDir is relative #10290.
Fixed pnpm run -r failing with "No projects matched the filters" when an empty pnpm-workspace.yaml exists #10497.
Fixed a bug where catalogMode: strict would write the literal string "catalog:" to pnpm-workspace.yaml instead of the resolved version specifier when re-adding an existing catalog dependency #10176.
Fixed the documentation URL shown in pnpm completion --help to point to the correct page at https://pnpm.io/completion #10281.
Skip local file: protocol dependencies during pnpm fetch. This fixes an issue where pnpm fetch would fail in Docker builds when local directory dependencies were not available #10460.
Fixed pnpm audit --json to respect the --audit-level setting for both exit code and output filtering #10540.
update tar to version 7.5.7 to fix security issue
Updating the version of dependency tar to 7.5.7 because the previous one have a security vulnerability reported here: <a href="https://github.com/advisories/GHSA-34x7-hfp2-rc4v">CVE-2026-24842</a>
Fix pnpm audit --fix replacing reference overrides (e.g. $foo) with concrete versions #10325.
Fix shamefullyHoist set via updateConfig in .pnpmfile.cjs not being converted to publicHoistPattern #10271.
pnpm help should correctly report if the currently running pnpm CLI is bundled with Node.js #10561.
Add a warning when the current directory contains the PATH delimiter character. On macOS, folder names containing forward slashes (/) appear as colons (:) at the Unix layer. Since colons are PATH separators in POSIX systems, this breaks PATH injection for node_modules/.bin, causing binaries to not be found when running commands like pnpm exec #10457.
<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>
Security fix: prevent path traversal in directories.bin field.
Security fix: prevent path traversal in directories.bin field.
When pnpm installs a file: or git: dependency, it now validates that symlinks point within the package directory. Symlinks to paths outside the package root are skipped to prevent local data from being leaked into node_modules.
This fixes a security issue where a malicious package could create symlinks to sensitive files (e.g., /etc/passwd, ~/.ssh/id_rsa) and have their contents copied when the package is installed.
Note: This only affects file: and git: dependencies. Registry packages (npm) have symlinks stripped during publish and are not affected.
Fixed optional dependencies to request full metadata from the registry to get the libc field, which is required for proper platform compatibility checks #9950.
<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>
Fix path traversal vulnerability in binary fetcher ZIP extraction
Fixed installation of config dependencies from private registries.
Added support for object type in configDependencies when the tarball URL returned from package metadata differs from the computed URL #10431.
Fix path traversal vulnerability in binary fetcher ZIP extraction
ERR_PNPM_PATH_TRAVERSAL errorSupport plain http:// and https:// URLs ending with .git as git repository dependencies.
Previously, URLs like https://gitea.example.org/user/repo.git#commit were not recognized as git repositories because they lacked the git+ prefix (e.g., git+https://). This caused issues when installing dependencies from self-hosted git servers like Gitea or Forgejo that don't provide tarball downloads.
Changes:
http:// and https:// URLs ending in .git as git repositoriesisRepository check from the tarball resolver since it's no longer needed with the new resolver orderFixes #10468
pnpm run -r and pnpm run --filter now fail with a non-zero exit code when no packages have the specified script. Previously, this only failed when all packages were selected. Use --if-present to suppress this error #6844.
Fixed a path traversal vulnerability in tarball extraction on Windows. The path normalization was only checking for ./ but not .\. Since backslashes are directory separators on Windows, malicious packages could use paths like foo\..\..\.npmrc to write files outside the package directory.
When running "pnpm exec" from a subdirectory of a project, don't change the current working directory to the root of the project #5759.
Fixed a path traversal vulnerability in pnpm's bin linking. Bin names starting with @ bypassed validation, and after scope normalization, path traversal sequences like ../../ remained intact.
Revert Try to avoid making network calls with preferOffline #10334.
Fix --save-peer to write valid semver ranges to peerDependencies for protocol-based installs (e.g. jsr:) by deriving from resolved versions when available and falling back to * if none is available #10417.
Do not exclude the root workspace project, when it is explicitly selected via a filter #10465.
<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>
Add support for a hook called beforePacking that can be used to customize the package.json contents at publish time #3816.
beforePacking that can be used to customize the package.json contents at publish time #3816.pnpm install --filter ...) was slower than running pnpm install without any filter arguments. This performance regression is now fixed. Filtered installs should be as fast or faster than a full install #10408.requiredScripts setting in pnpm-workspace.yaml #10261.<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>
Adding trustPolicyIgnoreAfter allows you to ignore trust policy checks for packages published more than a specified time ago#10352.
Adding trustPolicyIgnoreAfter allows you to ignore trust policy checks for packages published more than a specified time ago#10352.
Added project registry for global virtual store prune support.
Projects using the store are now registered via symlinks in {storeDir}/v10/projects/. This enables pnpm store prune to track which packages are still in use by active projects and safely remove unused packages from the global virtual store.
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.
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. The algorithm:
This includes support for workspace monorepos - all node_modules directories within a project (including those in workspace packages) are scanned.
tokenHelper or <url>:tokenHelper setting contains an environment variable.dangerouslyAllowAllBuilds settings #10376.pnpm store prune should not fail if the dlx cache directory has files, not only directories #10384pnpm add would incorrectly modify a catalog entry in pnpm-workspace.yaml to its exact version.<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>
Improve error message when a package version exists but does not meet the minimumReleaseAge constraint. The error now clearly states that the version
Improve error message when a package version exists but does not meet the minimumReleaseAge constraint. The error now clearly states that the version exists and shows a human-readable time since release (e.g., "released 6 hours ago") #10307.
Fix installation of Git dependencies using annotated tags #10335.
Previously, pnpm would store the annotated tag object's SHA in the lockfile instead of the actual commit SHA. This caused ERR_PNPM_GIT_CHECKOUT_FAILED errors because the checked-out commit hash didn't match the stored tag object hash.
Binaries of runtime engines (Node.js, Deno, Bun) are written to node_modules/.bin before lifecycle scripts (install, postinstall, prepare) are executed #10244.
Try to avoid making network calls with preferOffline #10334.
<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>
Don't fail on pnpm add, when blockExoticSubdeps is set to true #10324.
pnpm add, when blockExoticSubdeps is set to true #10324.HEAD points to the commit after checkout #10310.<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>
…verification and scanning for malware and vulnerabilities.
Semi-breaking. Block git-hosted dependencies from running prepare scripts unless explicitly allowed in onlyBuiltDependencies #10288.
Semi-breaking. 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 a new setting blockExoticSubdeps that prevents the resolution of exotic protocols in transitive dependencies.
When set to true, direct dependencies (those listed in your root package.json) may still use exotic sources, but all transitive dependencies must be resolved from a trusted source. Trusted sources include the configured registry, local file paths, workspace links, trusted GitHub repositories (node, bun, deno), and custom resolvers.
This helps to secure the dependency supply chain. Packages from trusted sources are considered safer, as they are typically subject to more reliable verification and scanning for malware and vulnerabilities.
Exotic sources are dependency locations that bypass the usual trusted resolution process. These protocols are specifically targeted and blocked: Git repositories (git+ssh://...) and direct URL links to tarballs (https://.../package.tgz).
Related PR: #10265.
Added support for allowBuilds, which is a new field that can be used instead of onlyBuiltDependencies and ignoredBuiltDependencies. The new allowBuilds field in your pnpm-workspace.yaml uses a map of package matchers to explicitly allow (true) or disallow (false) script execution. This allows for a single, easy-to-manage source of truth for your build permissions.
Example Usage. To explicitly allow all versions of esbuild to run scripts and prevent core-js from running them:
allowBuilds:
esbuild: true
core-js: false
The example above achieves the same result as the previous configuration:
onlyBuiltDependencies:
- esbuild
ignoredBuiltDependencies:
- core-js
Related PR: #10311
Added support for --dry-run to the pack command #10301.
injectWorkspacePackages setting from the lockfile on the deploy command #10294.package.json #10197.<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>
Allow loading certificates from cert, ca, and key for specific registry URLs. E.g., //registry.example.com/:ca=-----BEGIN CERTIFICATE-----.... Previou
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.
These properties are supported in .npmrc, but were ignored by pnpm, this will make pnpm read and use them as well.
Related PR: #10230.
Added a new flag called --bare to pnpm init for creating a package.json with the bare minimum of required fields #10226.
pnpm install should build any dependencies that were added to onlyBuiltDependencies and were not built yet #10256.pnpm publish -r --force should allow to run publish over already existing versions in the registry #10272.ERR_PNPM_MISSING_TIME error if a package that is excluded from trust policy checks is missing the time field in the metadata.<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>
Increased network concurrency on machines with many CPU cores. pnpm now automatically selects a network concurrency between 16 and 64, based on the nu
trustPolicy should ignore the trust evidences of prerelease versions, when installing a non-prerelease version.fs.linkSync(), which can occur in containerized environments (OverlayFS) instead of EXDEV. The operation now gracefully falls back to fs.copyFileSync() in these cases #10217.pnpm self-update should download pnpm from the configured npm registry #10205.package.json file (like Node.js) should not be reimported from the store on every install. Another file from the package should be checked in order to verify its presence in node_modules.<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>
Added --lockfile-only option to pnpm list #10020.
--lockfile-only option to pnpm list #10020.pnpm self-update should download pnpm from the configured npm registry #10205.pnpm self-update should always install the non-executable pnpm package (pnpm in the registry) and never the @pnpm/exe package, when installing v11 or newer. We currently cannot ship @pnpm/exe as pkg doesn't work with ESM #10190.pnpm add, if there's a engines.runtime setting declared in package.json #10209.pnpm list and pnpm why now display npm: protocol for aliased packages (e.g., foo npm:is-odd@3.0.1) #8660.pnpm store prune should not fail if the store contains Node.js packages #10131.<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>
Added support for trustPolicyExclude #10164.
Added support for trustPolicyExclude #10164.
You can now list one or more specific packages or versions that pnpm should allow to install, even if those packages don't satisfy the trust policy requirement. For example:
trustPolicy: no-downgrade
trustPolicyExclude:
- chokidar@4.0.3
- webpack@4.47.0 || 5.102.1
Allow to override the engines field on publish by the publishConfig.engines field.
<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>
Node.js Runtime Installation for Dependencies. Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the No
Node.js Runtime Installation for Dependencies. 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. For example:
{
"engines": {
"runtime": {
"name": "node",
"version": "^24.11.0",
"onFail": "download"
}
}
}
If the package with the Node.js runtime dependency is a CLI app, pnpm will bind the CLI app to the required Node.js version. This ensures that, regardless of the globally installed Node.js instance, the CLI will use the compatible version of Node.js.
If the package has a postinstall script, that script will be executed using the specified Node.js version.
Related PR: #10141
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 — for example, if it was previously published by a trusted publisher but now only has provenance or no trust evidence.
This helps prevent installing potentially compromised versions of a package.
Related issue: #8889.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
pnpm update on a dependency that is not directly listed in package.json, none of the direct dependencies should be updated #10155.gitBranchLockfile and related settings via pnpm-workspace.yaml should work #9651.<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>
Support --all option in pnpm --help to list all commands #8628.
--all option in pnpm --help to list all commands #8628.latest version doesn't satisfy the maturity requirement configured by minimumReleaseAge, pick the highest version that is mature enough, even if it has a different major version #10100.create command should not verify patch info.managePackageManagerVersions to false, when switching to a different version of pnpm CLI, in order to avoid subsequent switches #10063.<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
Nothing published for this version
Nothing published for this version
You can now allow specific versions of dependencies to run postinstall scripts. onlyBuiltDependencies now accepts package names with lists of trusted
You can now allow specific versions of dependencies to run postinstall scripts. onlyBuiltDependencies now accepts package names with lists of trusted versions. For example:
onlyBuiltDependencies:
- nx@21.6.4 || 21.6.5
- esbuild@0.25.1
Related PR: #10104.
Added support for exact versions in minimumReleaseAgeExclude #9985.
You can now list one or more specific versions that pnpm should allow to install, even if those versions don’t satisfy the maturity requirement set by minimumReleaseAge. For example:
minimumReleaseAge: 1440
minimumReleaseAgeExclude:
- nx@21.6.5
- webpack@4.47.0 || 5.102.1
<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>
Your coding agent can read these notes before it upgrades. Set up the MCP server →