no notes
no notes
Migration guide: Migrating from v10 to v11
minimumReleaseAge defaults to 1 day (newly published packages are not resolved for 24h) and blockExoticSubdeps defaults to true.allowBuilds replaces the old build-dependency settings — onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts have been removed.pnpm add -g gets its own directory with its own package.json, node_modules, and lockfile.pnpm publish, login, logout, view, deprecate, unpublish, dist-tag, and version no longer delegate to the npm CLI, and the remaining npm passthrough commands now throw "not implemented".pnpm audit uses npm's bulk advisories endpoint — the legacy /security/audits endpoints are gone. CVE-based filtering has been replaced with GHSA-based filtering: migrate auditConfig.ignoreCves entries to auditConfig.ignoreGhsas..npmrc is auth/registry only — all other settings must live in pnpm-workspace.yaml or the new global config.yaml, and environment variables use the pnpm_config_* prefix.node@runtime:<version> no longer extracts the bundled npm, npx, and corepack, roughly halving the files pnpm has to hash, write, and link.pnpm ci, pnpm sbom, pnpm clean, pnpm peers check, pnpm runtime set, pnpm docs/home, pnpm ping, pnpm search, pnpm star/unstar/stars, pnpm whoami, pnpm with, and pnpm pack-app, plus pn/pnx short aliases..pnpmfile.mjs, which takes priority over .pnpmfile.cjs when present.pnpm audit --fix=update fixes vulnerabilities by updating packages in the lockfile instead of adding overrides, and pnpm audit --fix --interactive lets you select which advisories to fix.pnpm pack-app packs a CommonJS entry into a standalone executable for one or more target platforms using Node.js Single Executable Applications.Changed default values: optimisticRepeatInstall is now true, verifyDepsBeforeRun is now install, minimumReleaseAge is now 1440 (1 day), and minimumReleaseAgeStrict is false. Newly published packages will not be resolved until they are at least 1 day old. This protects against supply chain attacks by giving the community time to detect and remove compromised versions. To opt out, set minimumReleaseAge: 0 in pnpm-workspace.yaml #11158.
strictDepBuilds is true by default.
blockExoticSubdeps is true by default.
Removed deprecated build dependency settings: onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts #11220.
Use the allowBuilds setting instead. It is a map where keys are package name patterns and values are booleans:
true means the package is allowed to run build scriptsfalse means the package is explicitly denied from running build scriptsSame as before, by default, none of the packages in the dependencies are allowed to run scripts. If a package has postinstall scripts and it isn't declared in allowBuilds, an error is printed.
Before:
onlyBuiltDependencies:
- electron
onlyBuiltDependenciesFile: "allowed-builds.json"
neverBuiltDependencies:
- core-js
ignoredBuiltDependencies:
- esbuild
After:
allowBuilds:
electron: true
core-js: false
esbuild: false
Removed allowNonAppliedPatches in favor of allowUnusedPatches.
Removed ignorePatchFailures; patch application failures now throw an error.
<algo>-<digest>). Using hex format improves performance since file paths in the content-addressable store use hex representation, eliminating base64-to-hex conversion during path lookups.package.json from the content-addressable store during resolution and installation. This reduces I/O and speeds up repeat installs #10473.$STORE/index/, package metadata is stored in a single SQLite database at $STORE/index.db with MessagePack-encoded values. This reduces filesystem syscall overhead, improves space efficiency for small metadata entries, and enables concurrent access via SQLite's WAL mode. Packages missing from the new index are re-fetched on demand #10500 #10826.Global installs (pnpm add -g pkg) and pnx now use the global virtual store by default. Packages are stored at {storeDir}/links instead of per-project .pnpm directories. This can be disabled by setting enableGlobalVirtualStore: false #10694.
Isolated global packages. Each globally installed package (or group of packages installed together) now gets its own isolated installation directory with its own package.json, node_modules/, and lockfile. This prevents global packages from interfering with each other through peer dependency conflicts, hoisting changes, or version resolution shifts.
Key changes:
pnpm add -g <pkg> creates an isolated installation in {pnpmHomeDir}/global/v11/{hash}/pnpm remove -g <pkg> removes the entire installation group containing the packagepnpm update -g [pkg] re-installs packages in new isolated directoriespnpm list -g scans isolated directories to show all installed global packagespnpm install -g (no args) is no longer supported; use pnpm add -g <pkg> insteadGlobally installed binaries are now stored in a bin subdirectory of PNPM_HOME instead of directly in PNPM_HOME. This prevents internal directories like global/ and store/ from polluting shell autocompletion when PNPM_HOME is on PATH #10986. After upgrading, run pnpm setup to update your shell configuration.
Breaking changes to pnpm link:
pnpm link <pkg-name> no longer resolves packages from the global store. Only relative or absolute paths are accepted. For example, use pnpm link ./foo instead of pnpm link foo.pnpm link --global is removed. Use pnpm add -g . to register a local package's bins globally.pnpm link (no arguments) is removed. Use pnpm link <dir> with an explicit path instead.pnpm no longer reads all settings from .npmrc. Only auth and registry settings are read from .npmrc files. All other settings (like hoistPattern, nodeLinker, shamefullyHoist, etc.) must be configured in pnpm-workspace.yaml or the global ~/.config/pnpm/config.yaml #11189.
Network settings (httpProxy, httpsProxy, noProxy, localAddress, strictSsl, gitShallowHosts) are now written to config.yaml (global) or pnpm-workspace.yaml (local) instead of .npmrc/auth.ini. They are still readable from .npmrc for easier migration from the npm CLI #11209.
pnpm no longer reads npm_config_* environment variables. Use pnpm_config_* environment variables instead (e.g., pnpm_config_registry instead of npm_config_registry).
pnpm no longer reads the npm global config at $PREFIX/etc/npmrc.
pnpm login writes auth tokens to ~/.config/pnpm/auth.ini.
New registries setting in pnpm-workspace.yaml:
registries:
default: https://registry.npmjs.org/
"@my-org": https://private.example.com/
"@internal": https://nexus.corp.com/
Auth tokens in ~/.npmrc still work — pnpm continues to read ~/.npmrc as a fallback for registry authentication. The new npmrcAuthFile setting can be used to point to a different file instead of ~/.npmrc.
Replace workspace project specific .npmrc with packageConfigs in pnpm-workspace.yaml.
A workspace manifest with packageConfigs looks something like this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
"project-1":
saveExact: true
"project-2":
savePrefix: "~"
Or this:
# File: pnpm-workspace.yaml
packages:
- "packages/project-1"
- "packages/project-2"
packageConfigs:
- match: ["project-1", "project-2"]
modulesDir: "node_modules"
saveExact: true
pnpm no longer reads settings from the pnpm field of package.json. Settings should be defined in pnpm-workspace.yaml #10086.
pnpm config get (without --json) no longer prints INI formatted text. Instead, it prints JSON for objects and arrays, and raw strings for strings, numbers, booleans, and nulls. pnpm config get --json still prints all types of values as JSON, as before.
pnpm config get <array> now prints a JSON array.
pnpm config list now prints a JSON object instead of INI formatted text.
pnpm config list and pnpm config get (without argument) now hide auth-related settings.
pnpm config list and pnpm config get (without argument) now show top-level keys as camelCase. Exception: keys that start with @ or // are preserved (their cases don't change).
pnpm config get and pnpm config list no longer load non-camelCase options from the workspace manifest (pnpm-workspace.yaml).
pnpm no longer falls back to the npm CLI. Commands that were previously passed through to npm (access, bugs, docs, edit, find, home, issues, owner, ping, prefix, profile, pkg, repo, search, set-script, star, stars, team, token, unstar, whoami, xmas) and their aliases (s, se) now throw a "not implemented" error, with a suggestion to use the npm CLI directly #10642. Other previously passed-through commands — view (info, show, v), login (adduser), logout, deprecate, unpublish, dist-tag, and version — have been reimplemented natively in pnpm (see New Commands below).
pnpm publish now works without the npm CLI.
The One-time Password feature now reads from PNPM_CONFIG_OTP instead of NPM_CONFIG_OTP:
export PNPM_CONFIG_OTP='<your OTP here>'
pnpm publish --no-git-checks
If the registry requests OTP and the user has not provided it via the PNPM_CONFIG_OTP environment variable or the --otp flag, pnpm will prompt the user directly for an OTP code.
If the registry requests web-based authentication, pnpm will print a scannable QR code along with the URL.
Since the new pnpm publish no longer calls npm publish, some undocumented features may have been unknowingly dropped. If you rely on a feature that is now gone, please open an issue at https://github.com/pnpm/pnpm/issues. In the meantime, you can use pnpm pack && npm publish *.tgz as a workaround.
Removed the pnpm server command #10463.
Removed support for the useNodeVersion and executionEnv.nodeVersion fields. devEngines.runtime and engines.runtime should be used instead #10373.
Removed support for hooks.fetchers. We now have a new API for custom fetchers and resolvers via the fetchers field of pnpmfile.
npm_config_* environment variables from the pnpm config during lifecycle scripts. Only well-known npm_* env vars are now set, matching Yarn's behavior #11116.$ command instead of > pkg@version stage path\n> command, and shows project name and path only when running in a different directory. The $ command line is printed to stderr to keep stdout clean for piping #11132.pnpm peers check to view the issues #11133.patchedDependencies lockfile format from Record<string, { path: string, hash: string }> to Record<string, string> (selector to hash). Existing lockfiles with the old format are automatically migrated #10911.The default value of the type field in the package.json file of the project initialized by pnpm init command has been changed to module.
Added support for lowercase options in pnpm add: -d, -p, -o, -e #9197.
When using the pnpm add command only:
-p is now an alias for --save-prod instead of --parseable-d is now an alias for --save-dev instead of --loglevel=infoThe root workspace project is no longer excluded when it is explicitly selected via a filter #10465.
pnpm audit now calls npm's /-/npm/v1/security/advisories/bulk endpoint. The legacy /-/npm/v1/security/audits{,/quick} endpoints have been retired by the registry, so the legacy request/response contract is no longer supported.
The bulk endpoint does not return CVE identifiers. CVE-based filtering has been replaced with GitHub advisory ID (GHSA) filtering:
auditConfig.ignoreCves → auditConfig.ignoreGhsas (the previous key is no longer recognized)pnpm audit --ignore <id> / pnpm audit --ignore-unfixable now read and write GHSAs instead of CVEsurl (https://github.com/advisories/GHSA-xxxx-xxxx-xxxx)To migrate: replace each CVE-YYYY-NNNNN entry in your auditConfig.ignoreCves with the corresponding GHSA-xxxx-xxxx-xxxx value (visible in the More info column of pnpm audit output) and move it under auditConfig.ignoreGhsas.
Breaking: removed the managePackageManagerVersions, packageManagerStrict, and packageManagerStrictVersion settings. They existed only to derive the onFail behavior for the legacy packageManager field, and the pmOnFail setting introduced alongside pnpm with subsumes all three — it directly sets the onFail behavior of both packageManager and devEngines.packageManager. The COREPACK_ENABLE_STRICT environment variable is no longer honored (it only gated packageManagerStrict); use pmOnFail instead.
Migration:
| Removed setting | Replace with |
| ------------------------------------- | ------------------------------ |
| managePackageManagerVersions: true | pmOnFail: download (default) |
| managePackageManagerVersions: false | pmOnFail: ignore |
| packageManagerStrict: false | pmOnFail: warn |
| packageManagerStrictVersion: true | pmOnFail: error |
| COREPACK_ENABLE_STRICT=0 | pmOnFail: warn |
node@runtime:<version> (including pnpm env use and pnpm runtime set node) no longer extracts the bundled npm, npx, and corepack from the Node.js archive. This cuts roughly half of the files pnpm has to hash, write to the CAS, and link during installation, making runtime installs noticeably faster. Users who still need npm can install it as a separate package.pnpm view (info, show, v) command for viewing package metadata from the registry #11064.pnpm login (and pnpm adduser alias) command for authenticating with npm registries. Supports web-based login with QR code as well as classic username/password login #11094.pnpm logout command for logging out of npm registries. Revokes the authentication token on the registry and removes it from the local auth config file #11213.pnpm deprecate and pnpm undeprecate commands for setting and removing deprecation messages on package versions without delegating to the npm CLI #11120.pnpm unpublish command. Supports unpublishing specific versions, version ranges via semver, and entire packages with --force #11128.pnpm dist-tag command (ls, add, rm subcommands) #11218.pnpm sbom command for generating Software Bill of Materials in CycloneDX 1.7 and SPDX 2.3 JSON formats #9088.pnpm clean command that safely removes node_modules directories from all workspace projects #10707. Use --lockfile to also remove pnpm-lock.yaml files.pnpm runtime set <runtime name> <runtime version spec> [-g] for installing runtimes. Deprecated pnpm env use in favor of the new command.pnpm audit --fix=update #10341.pnpm ci command for clean installs #6100. The command runs pnpm clean followed by pnpm install --frozen-lockfile. Designed for CI/CD environments where reproducible builds are critical. Aliases: pnpm clean-install, pnpm ic, pnpm install-clean #11003.pnpm peers check command that checks for unmet and missing peer dependency issues by reading the lockfile #7087.version command natively in pnpm to support workspaces and workspace: protocols correctly. The new command allows bumping package versions (major, minor, patch, etc.) with full workspace support and git integration #10879.pnpm audit --fix now supports a new interactive mode via --interactive/-i.pnpm docs command and its alias pnpm home. This command opens the package documentation or homepage in the browser. When the package has no valid homepage, it falls back to https://npmx.dev/package/<name>.pnpm ping command to test registry connectivity. Provides a simple way to verify connectivity to the configured registry without requiring external tools.search command and its aliases (s, se, find).star, unstar, stars, and whoami commands.pnpm with <version|current> <args...> command. Runs pnpm at a specific version (or the currently active one) for a single invocation, bypassing the project's packageManager and devEngines.packageManager pins.pnpm pack-app command that packs a CommonJS entry file into a standalone executable for one or more target platforms, using the Node.js Single Executable Applications API under the hood.Added support for a global YAML config file named config.yaml.
Configuration is now split into two categories:
rc file and local .npmrc.config.yaml and local pnpm-workspace.yaml.Added support for loading environment variables whose names start with pnpm_config_ into config. These environment variables override settings from pnpm-workspace.yaml but not CLI arguments.
Added support for reading allowBuilds from pnpm-workspace.yaml in the global package directory for global installs.
Added support for pnpm config get globalconfig to retrieve the global config file path #9977.
Added a new setting virtualStoreOnly that populates the virtual store without creating importer symlinks, hoisting, bin links, or running lifecycle scripts. This is useful for pre-populating a store (e.g., in Nix builds) without creating unnecessary project-level artifacts. pnpm fetch now uses this mode internally #10840.
Added support for specifying the pnpm version via devEngines.packageManager in package.json. Unlike the packageManager field, this supports version ranges. The resolved version is stored in pnpm-lock.yaml and reused if it still satisfies the range #10932.
Added a new dedupePeers setting that reduces peer dependency duplication. When enabled, peer dependency suffixes use version-only identifiers (name@version) instead of full dep paths, eliminating nested suffixes like (foo@1.0.0(bar@2.0.0)). This dramatically reduces the number of package instances in projects with many recursive peer dependencies #11070.
Config dependencies are now installed into the global virtual store ({storeDir}/links/) and symlinked into node_modules/.pnpm-config/. This allows config dependencies to be shared across projects that use the same store, avoiding redundant fetches and imports #10910. Config dependency and package manager integrity info is now stored in pnpm-lock.yaml instead of inlined in pnpm-workspace.yaml: the workspace manifest contains only clean version specifiers for configDependencies, while the resolved versions, integrity hashes, and tarball URLs are recorded in the lockfile as a separate YAML document. The env lockfile section also stores packageManagerDependencies resolved during version switching and self-update. Projects using the old inline-hash format are automatically migrated on install #10912 #10964.
Added nodeDownloadMirrors setting to configure custom Node.js download mirrors in pnpm-workspace.yaml. This replaces the node-mirror:<channel> .npmrc setting, which is no longer read #11194:
nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/
pnpm dlx and pnpm create now respect security and trust policy settings (minimumReleaseAge, minimumReleaseAgeExclude, minimumReleaseAgeStrict, trustPolicy, trustPolicyExclude, trustPolicyIgnoreAfter) from project-level configuration #11183.
pnpm init now writes a devEngines.packageManager field instead of the packageManager field when init-package-manager is enabled.
Added a new setting runtimeOnFail that overrides the onFail field of devEngines.runtime (and engines.runtime) in the root project's package.json. Accepted values: ignore, warn, error, download. For example, setting runtimeOnFail=download makes pnpm download the declared runtime version even when the manifest does not set onFail: "download".
Added a new setting minimumReleaseAgeIgnoreMissingTime, which is true by default. When enabled, pnpm skips the minimumReleaseAge maturity check if the registry metadata does not include the time field. Set to false to fail resolution instead.
.mjs extension. When .pnpmfile.mjs exists, it takes priority over .pnpmfile.cjs and only one is loaded #9730.clean, setup, deploy, and rebuild commands now prefer user scripts over built-in commands. When a project's package.json has a script with the same name, pnpm executes the script instead of the built-in command. Added purge as an alias for the built-in clean command, which always runs the built-in regardless of scripts #11118.-F as a short alias for the --filter option.. are hidden and cannot be run directly via pnpm run. They can only be called from other scripts. Hidden scripts are also omitted from the pnpm run listing #11041.pnpm approve-builds now accepts positional arguments for approving or denying packages without the interactive prompt. Prefix a package name with ! to deny it. Only mentioned packages are affected; the rest are left untouched #11030.allowBuilds are automatically added to pnpm-workspace.yaml with a placeholder value, so users can manually set them to true or false #11030.pn and pnx short aliases for pnpm and pnpx (pnpm dlx) #11052.pnpm store prune now displays the total size of removed files #11047.pnpm audit --fix now adds the minimum patched version for each advisory to minimumReleaseAgeExclude in pnpm-workspace.yaml, so the security fix can be installed without waiting for minimumReleaseAge #11216.optimisticRepeatInstall skips shouldRefreshResolution hooks #10995.node-fetch with native undici for HTTP requests throughout pnpm #10537.node_modules, avoiding the overhead of creating a temp dir and renaming per package #11088.gunzipSync chunk size for fewer buffer allocations during tarball decompression #11086.If-Modified-Since for conditional metadata fetches, avoiding re-downloading unchanged registry metadata #11161.minimumReleaseAge, reducing the amount of data fetched from the registry #11160.Switched to process.stderr.write instead of console.error for script logging #11140.
Respected the frozen-lockfile flag when migrating config dependencies #11067.
Removed the --workspace flag from the version command #11115.
Handled ENOTSUP error in the clone import path during parallel I/O #11117.
Fixed pnpm audit command.
Updated dependencies to fix vulnerabilities.
pnpm now checks whether a package is installable for non-npm-hosted packages (e.g., git or tarball dependencies) after the manifest has been fetched.
pnpm now explicitly passes the path of the global rc config file to npm.
Fixed YAML formatting preservation in pnpm-workspace.yaml when running commands like pnpm update. Previously, quotes and other formatting were lost even when catalog values didn't change.
Closes #10425
The parameter set by the --allow-build flag is now written to allowBuilds.
Fixed a bug in which specifying filter in pnpm-workspace.yaml would cause pnpm to not detect any projects.
Deferred patch errors until all patches in a group are applied, so that one failed patch does not prevent other patches from being attempted.
pnpm now fails on incompatible lockfiles in CI when frozen lockfile mode is enabled #10978.
Fixed strictDepBuilds and allowBuilds checks being bypassed when a package's build side-effects are cached in the store #11039.
In GVS mode, pnpm approve-builds now runs a full install instead of rebuild, ensuring that GVS hash directories and symlinks are updated correctly after changing allowBuilds #11043.
Fixed a crash in the lockfile merger when merging non-semver version strings (e.g. link:, file:, git URLs) #11102.
Handled ENOTSUP error in linkOrCopy during parallel imports #11103.
Skipped linking bins that already reference the correct target. This avoids redundant I/O during repeated installs and prevents permission errors when the store is read-only (e.g. Docker layer caching, CI prewarm, NFS) #11069.
Fixed _password handling for the default registry to decode from base64 before use, consistent with scoped registry behavior #11089.
Fixed a bug where the CAS locker cache was not updated when a file already existed with correct integrity #11085.
Prevented catalog entries from being removed by cleanupUnusedCatalogs when they are referenced only from workspace overrides #11075.
Resolved patch file paths during pnpm fetch #11054.
Fixed invalid specifiers for peers on all non-exact version selectors #11049.
Fixed false "Command not found" error on Windows when the command exists but exits with a non-zero exit code #11000.
Prepended Bearer to the authorization token generated by tokenHelper if it is missing, aligning with npm's behavior #11097.
Propagated error cause when throwing PnpmError in @pnpm/npm-resolver #10990.
Fixed SQLite race condition during store initialization on Windows.
Removed rimrafSync in importIndexedDir fast-path error handler #11168.
Fixed pnpm dedupe --check unexpectedly failing due to non-deterministic resolution #11110.
Fixed empty files not being rejected in isEmptyDirOrNothing #11182.
Fixed .bat/.cmd token helpers not working on Windows due to missing shell: true option.