What changed in pnpm from 11 to 12

9 releases numbered after v11.25.0 up to and including v12.3.4, stable releases only. v11.25.0 and v12.3.4 are the newest stable releases of 11 and 12 we track; this page follows them as new ones ship.

96 changes across 9 releases · 1 landed on more than one version

Added 14

v12.3.4

  • pnpm remove now accepts --unsafe-perm flag

v12.3.2

  • pnpm update now accepts --ignore-scripts and skips lifecycle scripts during the update

v12.3.0

  • Context-aware global commands (node, deno, bun, and shims created with pnpm shim add) are now native executables on every platform, allowing environment variables with non-shell-identifier names to reach these commands
  • pnpm remove and pnpm update now accept --trust-lockfile, --no-trust-lockfile, --trust-policy, --trust-policy-exclude, and --trust-policy-ignore-after flags
  • pnpm now honors --config.trust-lockfile=<value> and accepts bare --trust-lockfile / --no-trust-lockfile spelling on commands that previously took the setting from config file alone
  • pnpm config now accepts -g/--global, --location, and --json before its subcommand

v12.2.0

  • Catalogs can now resolve workspace dependencies through the `workspace:` protocol

v12.1.0

  • Persist completed recursive tasks so --resume-from skips exactly the work that passed during a matching interrupted or failed pnpm -r run or pnpm -r exec invocation
  • Added per-task concurrency limits to workspace task orchestration via tasks.<name>.concurrency in pnpm-workspace.yaml
  • A cache can now be declared write-only to populate one the run does not read
  • A dependency cycle among the tasks of a run is now an error naming the participating tasks instead of silently running in an arbitrary order
  • Setting ignoreWorkspaceCycles: true downgrades task cycle errors to warnings
  • pnpm -r run --dry-run <script> prints the task graph that would execute without running anything
  • Added macOS and Windows x64 and arm64 support to remote shared build artifacts
Changed 49

v12.3.3

  • Speed up writing the lockfile in large workspaces
  • Speed up dependency resolution in large workspacesalso inv12.3.4

v12.3.2

  • Sped up installs that have no lockfile by linking packages whose dependency subtree has no peer dependencies into the virtual store while resolution is still running
  • pnpm import now keeps the versions recorded in package-lock.json, npm-shrinkwrap.json, or yarn.lock when it generates pnpm-lock.yaml
  • pnpm import in a workspace now imports every workspace project into the shared lockfile
  • pnpm import now fails with ERR_PNPM_LOCKFILE_NOT_FOUND when none of the three source lockfiles is present and fails with ERR_PNPM_YARN_LOCKFILE_PARSE_FAILED when it cannot parse yarn.lock
  • pnpm import always resolves locally and warns when --pnpr-server or the pnpr-server setting is given
  • Sped up installs in large workspaces by optimizing workspace project discovery to no longer enumerate every matched directory
  • Sped up installs in large workspaces by reducing memory allocation in the resolver and peer pass for every dependency edge
  • Sped up installs in large workspaces by making lockfile saving faster and finishing without waiting for memory cleanup
  • Sped up dependency resolution when there is no lockfile and for dependencies a lockfile does not cover
  • Sped up installs in large workspaces by deriving workspace link: targets and importer ids from the paths' suffixes under the workspace root
  • Sped up installs that restore a deleted node_modules from a warm global virtual store by no longer re-linking packages that are already fully present in the global virtual store

v12.3.1

  • Global commands now launch normally, and their first launch migrates the global bin directory to native shims
  • Sped up installs in large workspaces by deriving the anchor for re-rendering workspace link: targets once per project instead of once per dependency edge
  • Sped up installs in large workspaces by hashing project ordering paths by their raw bytes
  • Sped up installs in large workspaces by running lockfile verification checks for each project in parallel

v12.3.0

  • On Windows, <name>.exe replaces the .cmd and .ps1 shims for context-aware global commands
  • pnpm remove verifies the lockfile against active policies the way pnpm install does
  • Sped up installs in large workspaces by reading pnpm-lock.yaml while workspace projects are being discovered
  • Sped up installs in large workspaces by improving the lockfile update check to no longer compare every project against every lockfile entry
  • Sped up dependency resolution in large workspaces that use link: dependencies
  • Sped up dependency resolution in large workspaces by having the resolver build fewer lookup keys for each dependency
  • Improved peer dependency resolution performance when many packages reuse the same peer ranges
  • pnpm outdated and pnpm update now follow local actions and reusable workflows referenced with GitHub's self-repository syntax (uses: $/.github/actions/setup) when looking for outdated GitHub Actions
  • On Linux, pnpm now resolves registry hostnames through the system resolver (getaddrinfo) instead of the bundled pure-Rust resolver

v12.2.0

  • Authenticate Node.js runtime downloads from `nodeDownloadMirrors` with URL-scoped npm registry credentials, including bearer tokens, basic auth, and `tokenHelper`
  • Sped up installs in large workspaces by resolving each named `workspace:` dependency once and reusing it across every project that declares it
  • Load pnpmfile `updateConfig` hooks before packing so hook-provided catalogs resolve in `pnpm pack`, `pnpm publish`, and `pnpm stage publish`
  • `pnpm deploy` no longer requires `injectWorkspacePackages` to be enabled and now reports `ERR_PNPM_DEPLOY_AMBIGUOUS_PEER` when a peer resolves to more than one version
  • `globalDir` and `globalBinDir` are honored wherever they are set and the global `config.yaml` is read again with environment variables and tilde expansion support
  • Speed up workspace discovery for literal directories and conventional trailing-star patterns with wildcard no longer matching dot-prefixed directories

v12.1.0

  • pnpm login and pnpm adduser now record the granted token in the global config.yaml under the _auth setting, with --scope's scope routed to that registry under registries
  • pnpm logout removes tokens from the global config.yaml and still removes them from auth.ini written by earlier versions
  • A scope set in a project's pnpm-workspace.yaml is now ignored with a warning
  • Verified remote build artifacts are persisted in the shared store with their signed origin metadata and reverified against current trust, policy, platform, and source before reuse
  • Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group
  • sideEffectsCache now declares the whole of how a package's build output is reused with read, write, and remote tier settings
  • sideEffectsCacheReadonly: true now blocks writing to the cache
  • Setting sideEffectsCacheReadonly alongside sideEffectsCache: false gives a read-only view rather than switching the cache off
  • pnpm -r run and pnpm -r exec now schedule per task instead of in topological chunks using a new tasks section in pnpm-workspace.yaml
  • --resume-from now skips exactly the transitive dependencies of the anchor package
  • Under --no-bail, tasks whose dependencies failed are reported as skipped instead of failed
  • Generalized the experimental shared-artifact protocol so candidates and signed payloads identify a discriminated subject

v12.0.0

  • Git dependencies on known hosts are now treated as identities rather than transport choices, resolving through the host's canonical HTTPS URL and never recording SSH URLs in the lockfile
  • Unrecognized settings in pnpm-workspace.yaml are now reported as errors when the project pins a pnpm version, with warnings elsewhere
  • Dependency cycles are now broken canonically during peer resolution, producing byte-identical lockfiles regardless of importer or resolution order
  • packageImportMethod: auto now tries hardlinks before cloning on Linux for faster node_modules materialization
  • Under engineStrict, installs now fail when an incompatible package is reached through a regular dependencies edge of an installable package, even when that subtree hangs off optionalDependencies
Fixed 33

v12.3.3

  • Fix concurrent installs sharing a store occasionally failing with an ENOENT error while importing a package file
  • pnpm now runs through Node.js when it was installed by a tool that skips build scripts, such as Vercel's packageManager provisioning, Bun, Deno, or npm install --ignore-scripts

v12.3.4

  • Accept boolean settings as command-line flags on every command that takes them, such as --unsafe-perm, --offline, and --dangerously-allow-all-builds

v12.3.2

  • pnpm audit --fix update no longer aborts when a vulnerable package has no safe version inside its declared range and instead updates every package it can and lists the rest as remaining
  • pnpm install no longer reruns root lifecycle scripts when the global virtual store contains an unfinished-build marker in a package slot that the current lockfile does not use
  • pnpm run and pnpm exec now start without reinstalling on filesystems that keep sub-millisecond mtimes, such as NTFS
  • pnpm self-update, pnpm with, and automatic package-manager version switching no longer wait through registry retry delays when a configured registry has no signatures and registry.npmjs.org is unavailable
  • pnpm install now relinks workspace packages when publishConfig.linkDirectory changes
  • The pnpm npm wrapper keeps its placeholder shebang-less so pnpm 11 can install pnpm 12 through the version store
  • pnpm install now reports Already up to date when local tarball dependencies have not changed

v12.3.1

  • Fixed global commands such as node, npm, and yarn failing with unexpected argument '--shim' found after self-update from pnpm 12.2 to 12.3

v12.3.0

  • pnpm add <local directory>, pnpm add <local tarball>, pnpm add file:<path>, and pnpm add <tarball URL> work again
  • Fixed pnpm deploy --legacy ignoring allowUnusedPatches supplied through --config.allow-unused-patches or the PNPM_CONFIG_ALLOW_UNUSED_PATCHES environment variable
  • Fixed pnpm install --lockfile-only writing a lockfile that referenced a missing peer-suffixed snapshot when an npm-aliased dependency participated in a cyclic peer dependency graph
  • Fixed pnpm dedupe to converge in one pass when re-resolving a lockfile created by pnpm 11
  • Fixed detached child processes being terminated on Windows when another program launches pnpm directly without a shell
  • Fixed pnpm docs <package>@<version> ignoring the requested version
  • Fixed filtered and recursive pnpm run and pnpm exec hanging when a script reads from the terminal
  • Fixed false unmet peer errors for auto-installed peers in linked workspace packages

v12.2.1

  • Restore the pnpm executable target without a file extension so pnpm 12.1 and earlier can upgrade to newer pnpm 12 releases on POSIX systems

v12.2.0

  • Fixed `pnpm audit --fix` failing with `ERR_PNPM_INVALID_FIX_OPTION` when used without a value, including when another flag follows it
  • Fixed `pnpm audit --fix=override` ignoring the `saveExact` and `savePrefix` settings when writing vulnerability overrides
  • Fixed detached child processes being terminated after successful commands on Windows
  • Fixed `pnpm install --fix-lockfile` to derive its repair and filtered-merge views from one lockfile snapshot
  • Fixed global virtual store hashes for dependency cycles so every package that transitively depends on an allowed build includes the engine in its store path
  • Fixed `ERR_PNPM_CMD_SHIM_CHMOD` when several installs run at once against a shared global virtual store
  • Fixed the PowerShell shim generated by `npm install -g pnpm` on Windows so it invokes the native `pnpm.exe` binary
  • Fixed context-aware global shims on WSL2 so native Linux installations dispatch through the project runtime
  • `pnpm install` no longer writes global `minimumReleaseAgeExclude` entries to the project's `pnpm-workspace.yaml`
  • Fixed `catalog:` ranges in workspace package peer dependencies being reported as unmet
  • Fixed the install progress line reporting `added 0` under `nodeLinker: hoisted`, even when packages were linked into `node_modules`
  • An auto-installed optional peer is now resolved to a version its declared peer range accepts, even when the workspace root depends on that package at a version outside the range
  • Fixed `pnpm run "/pattern/"` running matching scripts one at a time and now runs them concurrently up to `workspaceConcurrency`

Original release notes, newest first

The list above is our reading of these notes; the originals from pnpm are here, one fold per release.

v12.3.3pnpm 12.3.3
Patch Changes
  • Fixed concurrent installs sharing a store occasionally failing with an ENOENT error while importing a package file #14353.

  • Sped up writing the lockfile in large workspaces #14352.

  • Sped up dependency resolution in large workspaces #14352.

  • pnpm now runs through Node.js when it was installed by a tool that skips build scripts, such as Vercel's packageManager provisioning, Bun, Deno, or npm install --ignore-scripts. Those installs previously failed with syntax error near unexpected token ')'. They still cannot run pnpm on Windows. On macOS only a shell can start it #14346.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.3.4pnpm 12.3.4
Patch Changes
  • Sped up dependency resolution in large workspaces #14352.

  • pnpm 12 now accepts the boolean settings as command-line flags on every command that takes them in pnpm 11, for example pnpm install --unsafe-perm, pnpm add foo --offline, and pnpm install --dangerously-allow-all-builds. pnpm 12 rejected them with unexpected argument, which failed every install on Vercel, whose build runs pnpm install --unsafe-perm #14346.

    pnpm remove now accepts --unsafe-perm, the same flag pnpm install, pnpm add, and pnpm update take.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.3.2pnpm 12.3.2
Patch Changes
  • pnpm audit --fix update no longer aborts when a vulnerable package has no safe version inside its declared range #14508. The run updates every package it can and lists the rest as remaining.

  • pnpm install no longer reruns root lifecycle scripts when the global virtual store contains an unfinished-build marker in a package slot that the current lockfile does not use pnpm/pnpm#14485.

  • Sped up installs that have no lockfile. pnpm now links packages whose dependency subtree has no peer dependencies into the virtual store while resolution is still running.

  • pnpm run and pnpm exec now start without reinstalling on filesystems that keep sub-millisecond mtimes, such as NTFS. Previously, every run on those filesystems reinstalled first pnpm/pnpm#14486.

  • pnpm import now keeps the versions recorded in package-lock.json, npm-shrinkwrap.json, or yarn.lock when it generates pnpm-lock.yaml. A range in package.json, a catalog, or an override still decides which versions are eligible, and the recorded version is preferred among them. The generated lockfile previously could pin newer versions than the source lockfile #14476.

    pnpm import in a workspace now imports every workspace project into the shared lockfile. It previously imported only the project in the current directory.

    pnpm import now fails with ERR_PNPM_LOCKFILE_NOT_FOUND when none of the three source lockfiles is present. It also fails with ERR_PNPM_YARN_LOCKFILE_PARSE_FAILED when it cannot parse yarn.lock. It previously generated a lockfile from scratch in both cases.

    pnpm import always resolves locally. It warns when --pnpr-server or the pnpr-server setting is given and does not use the server.

  • Sped up installs in large workspaces. Discovering the workspace projects no longer enumerates every matched directory to learn which manifest files it holds #14352.

  • Sped up installs in large workspaces. The resolver and the peer pass allocate less for every dependency edge #14352.

  • pnpm self-update, pnpm with, and automatic package-manager version switching no longer wait through registry retry delays when a configured registry has no signatures and registry.npmjs.org is unavailable #14483.

  • Sped up installs in large workspaces. Saving the lockfile is faster, and the install finishes without waiting for memory cleanup #14352.

  • pnpm install now relinks workspace packages when publishConfig.linkDirectory changes. Frozen installs report an outdated lockfile until it is regenerated pnpm/pnpm#14488.

  • The pnpm npm wrapper keeps its placeholder shebang-less so pnpm 11 can install pnpm 12 through the version store. Wrapper installs must allow lifecycle scripts to install the native binary #14502.

  • Sped up dependency resolution when there is no lockfile, and for the dependencies a lockfile does not cover.

  • Sped up installs in large workspaces. Workspace link: targets and importer ids are now derived from the paths' suffixes under the workspace root #14352.

  • pnpm install now reports "Already up to date" when local tarball dependencies have not changed #14495.

  • pnpm update now accepts --ignore-scripts and skips lifecycle scripts during the update pnpm/pnpm#14512.

  • Sped up installs that restore a deleted node_modules from a warm global virtual store. pnpm no longer re-links packages that are already fully present in the global virtual store #14510.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.3.1pnpm 12.3.1
Patch Changes
  • Sped up installs in large workspaces: the anchor for re-rendering workspace link: targets is now derived once per project instead of once per dependency edge, and project ordering hashes paths by their raw bytes #14352.

  • After a self-update from pnpm 12.2 to 12.3, global commands such as node, npm, and yarn failed with unexpected argument '--shim' found. Global commands now launch normally, and their first launch migrates the global bin directory to native shims. When self-update downgrades to pnpm 12.2 or older, it keeps the newer native shims so those commands continue to work.

  • Sped up installs in large workspaces. The check that verifies each project against the lockfile now runs the projects in parallel #14352.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.3.0pnpm 12.3
Minor Changes
  • Every context-aware global command (node, deno, bun, and the shims created with pnpm shim add) is now a native executable on every platform, so environment variables whose names are not valid shell identifiers reach these commands. On Windows, <name>.exe replaces the .cmd and .ps1 shims for them. Shims written by earlier pnpm 12 releases are migrated on the next global install or self-update.

  • pnpm remove and pnpm update now accept --trust-lockfile, --no-trust-lockfile, --trust-policy, --trust-policy-exclude and --trust-policy-ignore-after, the same flags pnpm install and pnpm add take, so the supply-chain settings can be overridden for a single run. pnpm remove verifies the lockfile against the active policies the way pnpm install does, and --trust-lockfile skips that pass for every entry, not only the package being removed.

    pnpm now also honors --config.trust-lockfile=<value>, and accepts the bare --trust-lockfile / --no-trust-lockfile spelling on the commands that previously took the setting from the config file alone.

Patch Changes
  • pnpm add <local directory>, pnpm add <local tarball>, pnpm add file:<path> and pnpm add <tarball URL> work again. A specifier given without a <name>@ prefix is no longer read as a registry package name and rejected with ERR_PNPM_PACKAGE_MANAGER_ADD_RESOLVE_LATEST #14437.

  • Fixed pnpm deploy --legacy ignoring allowUnusedPatches supplied through --config.allow-unused-patches or the PNPM_CONFIG_ALLOW_UNUSED_PATCHES environment variable pnpm/pnpm#14450.

  • Fixed pnpm install --lockfile-only writing a lockfile that referenced a missing peer-suffixed snapshot when an npm-aliased dependency took part in a cyclic peer dependency graph. The following pnpm install --frozen-lockfile failed with ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY #14449.

  • pnpm config now accepts -g/--global, --location, and --json before its subcommand pnpm/pnpm#14421.

  • pnpm dedupe now converges in one pass when it re-resolves a lockfile created by pnpm 11, so a second run no longer changes the lockfile #14455.

  • Fixed detached child processes being terminated on Windows when another program launches pnpm directly, without a shell, as nr from @antfu/ni does #14447.

  • Fixed pnpm docs <package>@<version> ignoring the requested version. It now opens the selected version's homepage and reports a missing version instead of opening the package-level homepage pnpm/pnpm#14428.

  • Sped up installs in large workspaces. pnpm-lock.yaml is now read while the workspace projects are being discovered #14352.

  • Fixed filtered and recursive pnpm run and pnpm exec hanging when a script reads from the terminal. Interactive prompts work again in a script that pnpm never runs alongside another one, such as a single --filtered project, --workspace-concurrency=1, a dependency chain, or a task declaring concurrency: 1 #14397.

  • Fixed false unmet peer errors for auto-installed peers in linked workspace packages.

  • Fixed npm global installs on Windows so the PowerShell shims invoke pnpm.exe.

  • Fixed pnpm with current <command> when global options precede it, such as pnpm --workspace-root with current --version pnpm/pnpm#14413.

    A short-option cluster that mixes a global flag with an option owned by the command, such as pnpm -ro dist pack-app, is now parsed like the same options written after the command.

    An option written before the command name is now reported as an unknown option unless that command accepts it, instead of being taken for the command to run. pnpm -P exec echo and pnpm -z exec echo fail the way pnpm --tag next exec echo does.

  • Apply pure insertions in zero-context patches at the correct line instead of one line early.

  • Improved peer dependency resolution performance when many packages reuse the same peer ranges.

  • pnpm outdated and pnpm update now follow local actions and reusable workflows referenced with GitHub's self-repository syntax (uses: $/.github/actions/setup) when looking for outdated GitHub Actions, the same way they follow ./ references.

  • The pnpm install --help descriptions of --prod and --dev no longer claim that the flags take precedence over NODE_ENV. pnpm does not read NODE_ENV when selecting which dependency groups to install #14445.

  • Sped up installs in large workspaces. The check that decides whether the lockfile needs updating no longer compares every project against every lockfile entry #14352.

  • Sped up dependency resolution in large workspaces that use link: dependencies #14352.

  • On Linux, pnpm now resolves registry hostnames through the system resolver (getaddrinfo), as it already does on macOS and Windows and as pnpm 11 did. Previously, an /etc/resolv.conf containing an option the bundled pure-Rust resolver did not recognize, such as options no_tld_query, made pnpm ignore the configured nameservers and silently query Google's public DNS instead #14469.

  • Sped up dependency resolution in large workspaces. The resolver builds fewer lookup keys for each dependency #14352.

  • catalogMode and --save-catalog no longer move a local path, tarball, or workspace:<path> specifier into a catalog. Such a specifier is resolved against the project that declares it, so one catalog entry cannot mean the same directory for every project that references it #14437.

  • Sped up installs in large workspaces. The workspace dependency graph is now built once per run instead of twice #14352.

  • Sped up writing pnpm-lock.yaml in large workspaces #14352.

  • Fixed non-frozen installs through a pnpr server failing instead of regenerating a conflicted lockfile.

  • pnpm update --interactive renders its checklist the way pnpm 11 does. Group headings and column headers are separators the cursor skips instead of checkboxes that select nothing. The columns of one group line up with the next. a toggles all and i inverts the selection. The confirmed selection is echoed as a list of package names #14423.

  • Fixed pnpm config commands targeting global configuration to skip project package manager version switching, allowing registry authentication to be configured before pnpm downloads a project-pinned version pnpm/pnpm#14463.

  • Fixed pnpm retaining the surrounding quotes in .npmrc values, including auth tokens expanded from environment variables. This restores authentication with registries configured using :_authToken="${TOKEN}" pnpm/pnpm#14427.

  • Fetch and tarball errors no longer print the secrets of the URL they name. Inline user:pass@ credentials and the query string or fragment of a signed URL are hidden, so a failed install or pnpm add <url> cannot leak them into terminal scrollback or CI logs.

  • When dist-tags.latest names a version whose manifest pnpm cannot read, the error now names that version and the field it could not decode, instead of reporting the tag as empty.

  • Retry transient Windows file-lock errors, including sharing violations, while linking dependencies with the default (isolated) nodeLinker. This fixes pnpm/pnpm#14407.

  • pnpm run, pnpm exec, pnpm rebuild, and the script shortcuts such as pnpm test now load the pnpmfile, so updateConfig hook settings such as extraEnv and extraBinPaths reach the scripts they spawn #14433.

  • The pnpm executable of the npm package now works when the package was installed without running its install scripts, as under --ignore-scripts or the default build-script block of pnpm and Bun #14346. In that case it runs through Node.js and, in a terminal, says how to switch to the native binary.

  • Sped up installs in large workspaces. The resolver no longer copies the whole lockfile before resolving #14352.

  • minimumReleaseAgeStrict now defaults to true when minimumReleaseAge is explicitly configured, whether in pnpm-workspace.yaml, the global config.yaml, a PNPM_CONFIG_* variable, or a CLI flag. The built-in 1440-minute default stays non-strict. Previously an explicit cutoff was treated as non-strict, so immature versions were silently added to minimumReleaseAgeExclude instead of being gated with a prompt #14409.

  • Preserve environment variables whose names are not valid shell identifiers when launching Node.js installed by pnpm runtime set node --global on Unix pnpm/pnpm#14417.

  • Fixed pnpm repo and pnpm docs failing to open the Windows browser from WSL pnpm/pnpm#14467.

  • pnpm link, pnpm outdated, and pnpm import now apply pnpmfile updateConfig hooks before resolving dependencies.

  • Fixed standalone installations to preserve the bundled node-gyp files used to build native dependencies.

  • Fixed resolution against registries whose version manifests carry _npmUser, dist.attestations, dist.unpackedSize, dist.fileCount, or peerDependenciesMeta in a shape npm does not use. Such a version was skipped as though it had never been published, so pnpm add could fail with "no version found for the latest tag" even though the registry served it.

  • pnpm unpublish now completes the two-factor authentication a registry asks for instead of failing with ERR_PNPM_UNAUTHORIZED while logged in. A 401 that is an OTP challenge starts the web-based authentication flow, or prompts for a classic one-time password. The obtained password is reused by every request of the run #14464.

  • On Windows, pnpm now resolves host names through the system resolver instead of its own DNS client. The built-in client bound a UDP socket for every lookup, which made Windows Defender Firewall ask to allow pnpm.exe again after every pnpm self-update #14405.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.2.1pnpm 12.2.1
Patch Changes
  • Restored the pnpm executable target without a file extension so pnpm 12.1 and earlier can upgrade to newer pnpm 12 releases on POSIX systems.
Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.2.0pnpm 12.2
Minor Changes
  • Catalogs can now resolve workspace dependencies through the workspace: protocol.
Patch Changes
  • Fixed pnpm audit --fix failing with ERR_PNPM_INVALID_FIX_OPTION when used without a value, including when another flag follows it, as in pnpm audit --fix --json #13261. Fixed pnpm audit --fix=override ignoring the saveExact and savePrefix settings when writing vulnerability overrides #11523.

  • Authenticate Node.js runtime downloads from nodeDownloadMirrors with URL-scoped npm registry credentials, including bearer tokens, basic auth, and tokenHelper pnpm/pnpm#14334.

  • Fixed detached child processes being terminated after successful commands on Windows.

  • Sped up installs in large workspaces by resolving each named workspace: dependency (workspace:*, workspace:^, workspace:1.2.3) once and reusing it across every project that declares it, instead of re-resolving it per project.

  • Fixed pnpm install --fix-lockfile to derive its repair and filtered-merge views from one lockfile snapshot.

  • Load pnpmfile updateConfig hooks before packing so hook-provided catalogs resolve in pnpm pack, pnpm publish, and pnpm stage publish pnpm/pnpm#14377.

  • pnpm deploy no longer requires injectWorkspacePackages to be enabled. A linked workspace dependency is rewritten to a file: dependency in the dedicated deploy lockfile, and the peer dependencies it declares are bound to the deployed graph's own resolution.

    When a peer resolves to more than one version in that graph the binding is ambiguous, and choosing between the candidates is exactly what injecting the package would have decided, so the deploy still fails — now with ERR_PNPM_DEPLOY_AMBIGUOUS_PEER, which names the package, the peer, and the competing versions, instead of refusing every non-injected workspace up front, and suggests pinning the peer to one version with an overrides entry as the way to keep deploying without injection #9386.

  • Fixed global virtual store hashes for dependency cycles. Every package that transitively depends on an allowed build now includes the engine in its store path, independent of traversal order pnpm/pnpm#14341.

  • Fixed ERR_PNPM_CMD_SHIM_CHMOD when several installs run at once against a shared global virtual store. One install could remove a command shim while another was making it executable (pnpm/pnpm#14353).

  • Fixed the PowerShell shim generated by npm install -g pnpm on Windows so it invokes the native pnpm.exe binary pnpm/pnpm#14362.

  • Fixed context-aware global shims on WSL2 so native Linux installations dispatch through the project runtime.

  • pnpm install no longer writes global minimumReleaseAgeExclude entries to the project's pnpm-workspace.yaml pnpm/pnpm#14347.

  • Fixed catalog: ranges in workspace package peer dependencies being reported as unmet pnpm/pnpm#14361.

  • globalDir and globalBinDir are honored wherever they are set, so pnpm add -g no longer fails with ERR_PNPM_GLOBAL_BIN_DIR_NOT_IN_PATH after pnpm config set -g global-bin-dir #14336. The global config.yaml is read again, PNPM_CONFIG_GLOBAL_DIR / PNPM_CONFIG_GLOBAL_BIN_DIR reach the directories derived from them, and a leading ~/ is expanded before that derivation. A project's pnpm-workspace.yaml still cannot set either key.

  • Fixed the install progress line reporting added 0 under nodeLinker: hoisted, even when packages were linked into node_modules #14348.

  • An auto-installed optional peer is now resolved to a version its declared peer range accepts, even when the workspace root depends on that package at a version outside the range. Previously the root's version was used and then reported as an unmet optional peer #13867.

  • Fixed pnpm run "/pattern/" running matching scripts one at a time in a single project. Matching scripts now run concurrently up to workspaceConcurrency, and their output is prefixed so concurrent lines remain distinguishable pnpm discussion 14357.

  • Fixed a slowdown at the end of a resolving install in a large workspace. The peer-dependency report now inspects only the projects the resolution flagged, rather than every project in the lockfile (pnpm/pnpm#14359).

  • Speed up workspace discovery for literal directories and conventional trailing-star patterns.

    Workspace patterns now follow the same dot-directory rule as pnpm 11: a wildcard no longer matches a dot-prefixed directory, so packages/* and ** skip packages/.cache and .git. A pattern that names a dot-prefixed directory still matches it, as packages/.cache and packages/.* do.

  • pnpm audit now ends its output with a trailing newline, including the --json, --fix, and --ignore output.

  • Retry transient Windows file-lock errors while replacing hoisted packages during installation. This fixes pnpm/pnpm#14349.

  • Fixed command-line --side-effects-cache overrides being ignored when pnpm-workspace.yaml uses the object form of sideEffectsCache pnpm/pnpm#14338.

  • Speed up workspace project discovery in large monorepos: workspace patterns are now probed concurrently and the discovered projects' package.json files are read in parallel #14352.

  • Fixed repeated pnpm dedupe runs alternating between peer resolutions when a peer is provided through an npm alias.

  • Fixed pnpm repo <package> and pnpm docs <package> resolving bare package names through the latest tag, and prevented malformed package ranges from crashing registry selection.

  • Fixed non-ASCII characters in configuration values being mangled during environment-variable substitution. Paths such as storeDir: ./café-store are now preserved #14383.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.1.0pnpm 12.1
Minor Changes
  • pnpm login and pnpm adduser now record the granted token in the global config.yaml, under the _auth setting, with --scope's scope routed to that registry under registries. pnpm logout removes it from there, and still from an auth.ini an earlier version wrote. Tokens already in auth.ini keep working.

  • A scope set in a project's pnpm-workspace.yaml is now ignored, with a warning naming where to set it instead. pnpm login records the scope as a @scope:registry route in the machine-global auth.ini, which outranks ~/.npmrc in every project — so a repository-committed file could redirect a scope such as @acme for all of a user's other projects after one routine login. Use --scope, the PNPM_CONFIG_SCOPE environment variable, or the global config file instead #13557.

  • Verified remote build artifacts are persisted in the shared store with their signed origin metadata. Later installs reverify the artifact against current trust, policy, platform, and source before reuse, while invalid remote variants are quarantined per channel (pnpm/pnpm#13771).

  • Persist completed recursive tasks so --resume-from skips exactly the work that passed during a matching interrupted or failed pnpm -r run / pnpm -r exec invocation. When no compatible state exists, pnpm retains its graph-based resume behavior.

  • Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group.

  • Added per-task concurrency limits to workspace task orchestration. Set tasks.<name>.concurrency in pnpm-workspace.yaml to limit how many instances of that task may run across workspace projects at once:

    tasks:
      build:
        concurrency: 2
    
  • sideEffectsCache now declares the whole of how a package's build output is reused — whether one is restored, whether one is saved, and the remote tier that shares it between machines:

    sideEffectsCache:
      read: true
      write: true
      remote:
        org: acme
        packages: ['native-addon']
    

    sideEffectsCache: true, sideEffectsCacheReadonly, remoteSideEffectsCache, and its organization field all keep working. Where a field is set under both spellings the one above wins; where it is set under only one, it is kept.

    Two behaviors change, both bringing this CLI in line with what the Rust one already did: sideEffectsCacheReadonly: true now blocks writing to the cache, and setting it alongside sideEffectsCache: false gives a read-only view rather than switching the cache off entirely. A cache can also be declared write-only now, to populate one the run does not read.

  • Workspace task orchestration (pnpm/rfcs#23). pnpm -r run and pnpm -r exec now schedule per task instead of in topological chunks: a task starts as soon as the tasks it depends on have finished, so a project no longer waits for unrelated projects that happen to share its chunk.

    A new tasks section in pnpm-workspace.yaml declares what a task depends on, using the ^ convention:

    tasks:
      build:
        dependsOn: ['^build']
      test:
        dependsOn: ['build']
      lint: {}
    

    ^name means the named task in each of the project's workspace dependencies; a bare name means the task in the same project; an entry with no dependsOn declares an empty dependency list. A task with no entry behaves as dependsOn: ['^<its own name>'], which is exactly what the previous chunked ordering implied — an unconfigured workspace gets the scheduler improvement and nothing else changes meaning. A project without the script is reported skipped and passes its edges through to its own dependencies, so a scriptless package does not sever a chain.

    Also part of this change:

    • A dependency cycle among the tasks of a run is now an error naming the participating tasks (ERR_PNPM_TASK_CYCLE) instead of silently running in an arbitrary order. Setting ignoreWorkspaceCycles: true downgrades the error to a warning: the cycle's tasks run in an arbitrary order relative to each other.
    • --resume-from now skips exactly the transitive dependencies of the anchor package; work unrelated to the anchor still runs.
    • Under --no-bail, tasks whose dependencies failed are reported as skipped, not failed, and do not add to the exit code.
    • With --bail (the default), the first failure still ends the run at once and nothing new is dispatched — including scripts already queued behind the concurrency limit.
    • pnpm -r run --dry-run <script> prints the task graph that would execute without running anything (including skipping the verifyDepsBeforeRun check); --json emits the tasks and their resolved dependency edges.
    • Output is inherited rather than piped only when at most one script can ever be in flight (--workspace-concurrency=1, or the graph forces the scripts to run one after another).
  • Added macOS and Windows x64 and arm64 support to remote shared build artifacts pnpm/pnpm#13771.

  • Generalized the experimental shared-artifact protocol so candidates and signed payloads identify a discriminated subject. Dependency side effects use package and source-integrity subjects, while workspace tasks use project and task subjects.

    This changes shared-artifact request bodies and signed payloads. A pnpr server and its clients have to be on matching versions.

Patch Changes
  • An _auth entry in the global config file no longer decides which registry packages come from when something else says. A registry or registries declared in pnpm-workspace.yaml or the global config now wins over the route inferred from a stored credential, which still applies where nothing else declares one. The pnpm_config__auth environment variable is unchanged: it stays the way to point a CI runner at a mandated proxy, and still overrides what a repository declares.

  • Fixed pnpm deploy --legacy to exclude dependencies that are only reachable from unselected workspace projects after pnpm fetch.

  • Fixed dependency-verification install logs corrupting pnpm exec output and ignoring --silent pnpm/pnpm#14197.

  • pnpm clean / pnpm purge run from a workspace subdirectory now remove each project's own node_modules instead of emptying the workspace root's for every project #14239. A custom modulesDir is resolved against each project directory too.

  • pnpm dlx <pkg>@catalog: now resolves the specifier through the calling workspace's catalogs instead of failing with ERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC #14294.

  • Fixed pnpm doctor reporting a version that does not match pnpm --version pnpm/pnpm#14225.

  • Pacquet now strips exactly one leading path component from ./-prefixed tarball entries, matching pnpm and npm's tar extraction semantics and keeping shared store keys consistent.

  • Installs whose lockfile carries platform or engine constraints are up to ~150 ms faster when resolution runs: the node --version probe behind the installability checks now starts before the lockfile is parsed and finishes while dependencies resolve, instead of running afterwards.

  • Treat empty scripts selected by a regular expression as missing before running dependent tasks.

  • Filter hidden scripts matched by a regular expression during recursive runs when a visible script also matches.

  • Fixed .mjs pnpmfile hooks failing to load on Windows, including hooks supplied by config dependencies pnpm/pnpm#14301.

  • Fixed automatically switched pnpm versions forcing all descendant pnpm processes to use the same version pnpm/pnpm#14309.

  • Fixed pnpm deploy --prod failing when an excluded dev dependency was also declared as an optional peer dependency pnpm/pnpm#14302.

  • Fixed pnpm pack to respect the files field when deciding whether to include root-level changelog, history, and notice files.

  • pnpm update -g no longer downgrades a global package. --latest resolves the latest dist-tag, which can point at an older release than the one installed — after pnpm add -g <pkg>@next, for instance #14270.

    pnpm update -g also no longer changes the pnpm version. pnpm's own global install belongs to pnpm self-update #14270.

  • When multiple versions of the same package expose the same binary, pnpm now links the binary from the highest version #14249.

  • pnpm update no longer replaces the specifier a project declares for a dependency that is also listed in overrides. A catalog: reference stays a catalog: reference, and a declared range stays as written, instead of being rewritten to the version the override resolved to #12115.

  • pnpm update no longer moves the range a project declares for a dependency that overrides also lists, even when the override repeats that range verbatim. Previously the updated package.json disagreed with the lockfile, so the next pnpm install --frozen-lockfile failed with a specifier mismatch #14224.

  • Allowed pnpm's shared-artifact client to connect to an artifact-only pnpr tier.

  • Rebuilding node_modules from an up-to-date lockfile is up to ~200 ms faster: the node --version probe that installability checks and store keying need now runs concurrently with the store's warm-cache reads instead of before them.

  • Remove the duplicate colon from the one-time password prompt.

  • Print errors as JSON on stdout when --json is passed to pnpm view or its aliases (info, show, and v).

  • Installs complete faster on workspaces with many projects: each project's node_modules is now linked concurrently.

  • Fixed patchedDependencies matching for git-hosted dependencies during fresh and frozen installs pnpm/pnpm#14273.

  • pnpm pm <command> works again: the pm prefix, which forces pnpm's built-in command over a package.json script of the same name, is recognized instead of failing with ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL / Command "pm" not found. pnpm pm clean and pnpm pm purge now remove node_modules even when the project (or the workspace root) declares a clean / purge script #14226.

  • The settings that pnpm accepts as command-line flags are recognized again: --package-import-method, --hoist-pattern, --public-hoist-pattern, --no-hoist, --global-dir, --virtual-store-dir, --modules-dir, --child-concurrency, --no-lockfile, --strict-peer-dependencies, --side-effects-cache, --side-effects-cache-readonly, --trust-policy, --trust-policy-exclude, --trust-policy-ignore-after, and --optimistic-repeat-install. Each is accepted anywhere on the command line, spelled either --setting=value or --setting value, and overrides the same setting read from pnpm-workspace.yaml or .npmrc #14281.

  • pnpm add, pnpm update, and pnpm remove now save package.json before failing with ERR_PNPM_IGNORED_BUILDS. The dependency they were asked to change is already materialized by that point, so the manifest has to record it — otherwise the next install removes the packages again.

  • The progress output no longer overwrites the lines above it once it grows taller than the terminal window #14270.

  • Restoring a dependency's build from the remote side-effects cache no longer downloads files the store already holds.

  • Recognize pnpm install --fix-lockfile, including filtered installs, and regenerate broken lockfile metadata while preserving compatible locked versions pnpm/pnpm#14250.

  • Fixed intermittent Access is denied failures when concurrent global commands hand off the global bin lock on Windows.

  • Fixed the --shamefully-hoist CLI option being rejected pnpm/pnpm#14235.

  • The environment variables for the remote side-effects cache are named for the setting they configure: PNPM_SIDE_EFFECTS_CACHE_REMOTE_KEY_ID, ..._BUILDER_ID, ..._IMAGE_DIGEST, ..._ARCHITECTURE_BASELINE, ..._PRIVATE_KEY, ..._BUILD_ENV, ..._TRUSTED_KEYS and ..._PUBLISH. The PNPM_REMOTE_SIDE_EFFECTS_CACHE_* names keep working, and the new one wins when both are set.

  • Installs that run no build scripts finish faster, especially in workspaces with many projects.

  • A devEngines.packageManager range pin on pnpm is now recorded in pnpm-lock.yaml's packageManagerDependencies when the running pnpm already satisfies it, using the running version and keeping the range as the recorded specifier. Previously only an exact pin — or a range resolved on the way through a version switch — reached the lockfile, so a range pin written by hand (or by any tool other than pnpm add / pnpm self-update) left the project without the shared resolution the pin exists to provide.

  • Workspace installs are substantially faster (~0.7 s on a 60-project workspace): after hoisting, pnpm now shims only the bins of publicly hoisted workspace packages instead of re-walking every project's node_modules to rediscover bins that were already linked.

  • Fixed a large install-time regression on macOS for installs that rebuild node_modules from a warm store #14231. APFS serializes file-cloning and hard-linking syscalls volume-wide, so importing packages one file at a time from many threads was bounded by a per-volume ceiling and got slower the more CPU cores the machine had. On macOS, pnpm install now materializes each package once into the store's links directory (the same canonical slots enableGlobalVirtualStore uses) and copies it into node_modules/.pnpm with a single copy-on-write directory clone per package, replacing tens of thousands of per-file syscalls with one per package. Applies with the default nodeLinker: isolated when enableGlobalVirtualStore is off and packageImportMethod is auto, clone, or clone-or-copy; hoisted, global-virtual-store, and explicit hardlink/copy installs are unchanged.

  • Stop in-flight recursive run and exec commands when bailing after the first failure.

  • Warm installs that rebuild node_modules on macOS are about 10% faster: creating each package's virtual-store directory now issues fewer filesystem calls.

  • An _auth credential in an .npmrc now authenticates even when its base64 is written without the trailing = padding (or with extra padding, or with whitespace inside it), instead of failing with a 401. An _auth that is not valid base64, or that carries no : between the username and the password, now fails with ERR_PNPM_AUTH_INVALID_BASE64 / ERR_PNPM_AUTH_MISSING_SEPARATOR #14257.

  • Colored output is no longer printed as raw escape sequences in the Windows Command Prompt #14292. Commands such as pnpm list now style their output there.

Platinum Sponsors
Gold Sponsors

View originalPermalink

v12.0.0pnpm 12
Major Changes
  • Git dependencies on known hosts (GitHub, GitLab, Bitbucket) are now treated as identities rather than transport choices. Every representation of the same repository — github:owner/repo, owner/repo, git+https://…, git+ssh://git@… — resolves through the host's canonical HTTPS URL, and the lockfile never records an SSH URL for them. Repositories whose archive endpoint is anonymously reachable resolve to the host's archive (fast tarball download); all others resolve to a git clone of the canonical HTTPS URL, which every machine with access to the repository can fetch.

    To reach a private hosted repository over SSH, configure the machine (not the project) with git's own URL rewriting, for example:

    git config --global url."git@github.com:".insteadOf https://github.com/
    

    pnpm shells out to git, so the rewrite applies to all of pnpm's git operations automatically. URLs of unknown hosts (self-hosted servers) are unaffected and keep their exact URL, including SSH. URLs with embedded credentials are also kept verbatim and never resolve to a host archive.

    This removes the network probing that previously decided between HTTPS and SSH at resolution time, which could record a transport that only worked on the machine that happened to run the resolution (e.g. an SSH URL that broke CI runners without SSH keys).

  • A project's pnpm-workspace.yaml may no longer carry a setting pnpm does not recognize. Such a setting used to be ignored in silence — a misspelled minimumReleaseAge dropped the policy it was meant to set, and nothing said so. Now it is reported, suggesting the closest real setting name when the key looks like a typo, and it fails the command with ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS when the project pins a pnpm version the running pnpm satisfies: with the pin honored, the setting cannot be meant for a different pnpm version, so it is a mistake to fix rather than a key to ignore. Everywhere else it is a warning, so a project that has yet to be cleaned up keeps working.

    The pnpm config subcommands never fail on such a setting, so a broken file can still be inspected and repaired, and pnpm config get <key> prints the value with no warnings at all. Keys the global config file cannot set are likewise split between workspace-only settings (still directed to pnpm-workspace.yaml) and settings unknown to this version.

  • Dependency cycles are now broken canonically during peer resolution: the members of each cycle are ordered by package id, and the edges that close a cycle are always cut at the same place, no matter where the installation walks into the cycle from. Previously the cut depended on the walk path, so installing the same dependencies could produce different lockfiles depending on importer order or resolution order #13846, and a peer-resolution verdict computed for one occurrence of a cyclic package could be wrongly reused at another #13865.

    With canonical cycle breaking the lockfile is a pure function of the dependency graph: repeated installs, reordered importers, and reordered dependencies all produce byte-identical lockfiles. Peer dependencies of packages inside a cycle keep nearest-wins resolution along the canonical order, and a dependency edge that closes a cycle references an occurrence of its target resolved at the importer level. On large cycle-heavy workspaces peer resolution is 2–3× faster, uses about 25% less memory, and produces a substantially smaller lockfile (fewer redundant peer variants).

    Existing lockfiles keep working: headless (--frozen-lockfile) installs consume them unchanged, and installs that skip resolution leave them untouched. The first install that actually re-resolves (for example after a dependency change) re-keys walk-order-dependent peer variants of cyclic packages once.

  • packageImportMethod: auto now tries hardlinks before cloning on Linux. A reflink materializes a new inode and copies extent bookkeeping inside the filesystem's metadata trees, where a hardlink is one directory entry — on btrfs this roughly halves the time an install spends materializing node_modules from a warm store. ext4 installs are unchanged (cloning was never supported there, so auto already hardlinked), and macOS keeps clone-first, where APFS clonefile is the platform's cheap primitive. Cloning remains the fallback when the store refuses hardlinks, and remains available explicitly via packageImportMethod: clone.

  • Under engineStrict, an install fails when an incompatible package is reached through a regular dependencies edge of an installable package, even when that whole subtree hangs off an optionalDependencies entry. pnpm v11 installs the package and emits an install-check warning instead. Packages reachable only through optional edges, or through a package that was itself skipped, are still skipped in both versions #13286.

Minor Changes
  • Globally installed bins can now follow the project you run them in. The new globalShims setting is a record of package names to policies that selects which globally installed packages get project-aware shims; it defaults to { node: true, deno: true, bun: true } and merges key-wise, so globalShims: { bun: false } switches one default off and globalShims: { typescript: true } adds another package. With the default, a project that pins Node.js through devEngines.runtime or engines.runtime gets the pinned stable release — authenticated against the Node.js release-team signatures — downloaded on first use and run whenever you type node inside the project, with no shell hooks. Candidates that are not signature-verified (Deno, Bun, Node.js prereleases, and ordinary package bins you enable) ask "Do you trust this project?" once per candidate and remember the answer machine-locally; the record values name the policy per package: "auto" (or its shorthand true) defers to artifact authentication, "always" switches without ever asking (useful in CI), and "prompt" always asks, even for authenticated candidates. Set globalShims: false to disable the feature, or PNPM_SHIM_BYPASS=1 to bypass it for one invocation. On Windows, programs can keep spawning the global node.exe directly, without a shell.

  • pnpm installs the other package managers now, not just itself: npm, Yarn Classic, Yarn Berry, Yarn 6 (yarnpkg/zpm), and Bun. Each is resolved and fetched through the trusted package-manager registries, and an npm-published one is verified against npm's signature for its exact version before it is executed.

    Three things use it:

    • A git-hosted dependency is prepared with the package manager it asks for. Its packageManager / devEngines.packageManager pin is honored, and a yarn.lock written by Yarn Classic no longer gets installed by Yarn Berry. pnpm provides that package manager when the dependency pinned a version, or when the host cannot satisfy what the dependency needs — so a repository built with Yarn now installs on a machine that has only pnpm, while a host that already has a suitable one keeps using its own.
    • pnpm dlx (pnx) runs one of them for a single command: pnx yarn@4 install, pnx npm@11 ci, pnx bun@1.3.0 install. Naming a package manager, or a runtime (node, deno, bun), there now provisions the real thing instead of installing the npm package that shares its name — unless the specifier locates a package rather than asking for a released version (pnx yarn@npm:yarn@1.22.22, pnx yarn@yarnpkg/berry), which installs what it names — pnx yarn@4 was previously a missing version, since Yarn 4 is published as @yarnpkg/cli-dist, and pnx node@22 now runs that Node.js release rather than a wrapper that downloads one. --package naming a package manager picks which of its commands to run, so pnx --package npm@11 npx create-something runs that npm's npx.
    • pnpm shim add yarn links a yarn command that runs whatever version the current project pins, and pnpm shim rm / pnpm shim ls manage those shims. It works for any package, not only package managers. Shims are never created as a side effect of pnpm setup or an install — a shim shadows the rest of your PATH, so pnpm only writes one when asked.

    Installing a package manager globally (pnpm add -g yarn) now makes it follow a project's pin too, the way a globally installed Node.js already follows devEngines.runtime: the pinned version runs where a project pins one, and the globally installed copy is the fallback everywhere else. An explicit globalShims entry, including false, is left as you set it.

    pnpm add follows the same rule about what a name means. pnpm add -g yarn@4 installs Yarn Berry — it used to fail, because npm's yarn package stops at Classic — and pnpm add -g node@22 / pnpm add -g deno@2 install that Node.js or Deno release rather than a wrapper package that downloads one. In a project, naming a package manager records which one the project uses instead of installing it as a dependency, and naming a runtime records it under engines.runtime as node@runtime:22 already did.

    The declaration goes where the package manager reads it. Yarn is started from a project pin by corepack, which reads only packageManager and only accepts an exact version there, so pnpm add yarn@4 resolves the line and writes "packageManager": "yarn@4.18.0" — the same thing corepack use yarn@4 writes, down to the +sha512.… integrity for the Yarn Classic line that corepack pins its tarball with. Every other package manager is recorded in devEngines.packageManager, which holds a range. Only one of the two fields is ever left behind: they declare the same thing, and corepack refuses to run a project whose declarations disagree.

    A JavaScript package manager on a machine without Node.js gets a managed LTS runtime to run on.

    What changes for a project coming from v11: pnpm add yarn records the project's package manager instead of installing the npm package that shares the name (that package is still reachable as pnpm add yarn@npm:yarn@1.22.22), pnpm add -g yarn installs the current Yarn line rather than Classic, pnpm add -g node / pnpm add -g deno and pnx node / pnx deno install a Node.js or Deno release rather than a wrapper package, and a globally installed package manager defers to a project's pin where there is one.

  • Added an opt-in proof of concept that lets installs reuse a dependency's build output across machines, by publishing and restoring signed, organization-scoped artifacts through pnpr instead of running the lifecycle scripts locally.

    Configure it with the new remoteSideEffectsCache setting. A workspace names the eligible organization and packages; everything describing the act of signing — publish, keyId, builderId, trustedKeys, privateKey and the provenance fields — is refused in pnpm-workspace.yaml and read from the global config file or the environment instead.

  • Added the audit.ignorePrune setting. When set to true, pnpm audit --fix removes ignored GHSA entries that no longer appear in the audit report.

  • pnpm init now pins the latest pnpm version, instead of the version of pnpm that ran the command. A project scaffolded by an outdated pnpm therefore no longer inherits that staleness through its own devEngines.packageManager / packageManager pin #7490.

    The version is read from the latest tag on the package-manager registries. When that lookup cannot answer — no network, an unreachable or slow registry, offline, or a latest that the minimumReleaseAge / trustPolicy settings reject — pnpm init pins the running version as before, and never fails or hangs on the lookup. A latest that is older than the running pnpm is never pinned either.

  • Allowed pnpm update --patches to refresh registry revisions through a configured pnpr server while retaining locked package versions.

  • Added explicit registry revision selection with <version>+rN and pnpm update --patches for refreshing revision artifacts without changing package versions. Registry-backed lockfile policy checks recognize historical revisions, and pnpr now preserves safe revision histories from upstream registries.

  • Added support for registry replacement tarballs using standard integrity values, explicit revision fields, registry routing from the registries setting, non-redirecting integrity-addressed URLs, canonical safe-integer revision numbers, and pnpr proxying for immutable upstream revision artifacts.

  • Running pnpm setup, pnpm self-update, or a command that modifies the global installation (such as pnpm add --global) through sudo now fails with ERR_PNPM_SUDO_NOT_SUPPORTED instead of silently operating on the root user's home directory. pnpm keeps global packages and configuration in the invoking user's home directory, so these commands never need root permissions. Read-only global commands (such as pnpm bin --global) still work under sudo.

  • pnpm stage approve now approves several staged packages at once. Run it without a stage id to pick from the staged versions interactively, or pass a list of stage ids. The whole batch is approved with a single one-time password, and pnpm asks for a new one only once the registry stops accepting it. Inside a workspace, the selected packages are approved in dependency order, and a package whose workspace dependency could not be approved is skipped instead of being published against a dependency that never reached the registry.

Patch Changes
  • Deprecated the pnpmfile filterLog hook in pnpm v12. The Rust CLI ignores it and emits a warning.

  • The built-in compatibility database no longer adds dependencies that were detected by static analysis of published packages. Those entries named packages that are only imported for their types, so installing them was at best unnecessary and at worst broke the dependent: @typescript-eslint/types gained a typescript dependency resolved to the newest release, which put TypeScript 7 under older @typescript-eslint versions and made ESLint fail with "Cannot read properties of undefined (reading 'Intrinsic')". The database keeps its @yarnpkg/extensions entries and pnpm's own curated ones.

  • When no directory above the project accepts a hard link — inside an AI agent sandbox that only grants write access to the project, or a container with just the project mounted writable — the default store is now created at <project>/node_modules/.pnpm-store instead of in the pnpm home directory. In those environments the home store is either read-only or on another volume, which forces every package to be copied instead of hard linked #13525.

Platinum Sponsors
Gold Sponsors

View originalPermalink