pnpm 12.2-12.3
pnpm 12.2 and 12.3 are largely a catch-up pair: a long list of things pnpm 11
did that the Rust CLI did not, put back. Catalogs learned the workspace:
protocol, pnpm remove and pnpm update gained the supply-chain flags, every
project-aware global command became a real executable, and hostname resolution
went back to the system resolver on Linux and Windows. Large workspaces install
noticeably faster.
Minor Changes
Catalogs can hold a workspace: range
A catalog entry may now be a
workspace: range, so the version a workspace dependency is linked by is
written in one place like every other version:
catalog:
'@example/utils': workspace:^
A package.json referencing "@example/utils": "catalog:" links the workspace
project, and both protocols are replaced on publish.
Supply-chain flags on remove and update
pnpm remove and
pnpm update now take
--trust-lockfile, --no-trust-lockfile, --trust-policy,
--trust-policy-exclude, and --trust-policy-ignore-after, the same flags
install and add take, so a policy can be relaxed or tightened for one run.
Removing a package rewrites the lockfile, so pnpm remove verifies the whole
lockfile against the active policies rather than only the entries of the package
it is removing. pnpm also honors --config.trust-lockfile=<value> and accepts
the bare --trust-lockfile / --no-trust-lockfile spelling on the commands that
previously read the setting from the config file alone.
Project-aware global commands are native executables
Every context-aware global command, node, deno, bun, and the shims
pnpm shim add creates, is now a
native executable on every platform. On Windows, <name>.exe replaces the
.cmd and .ps1 pair.
The practical difference: no shell sits between you and the command, so an
environment variable whose name is not a valid shell identifier survives the
hop. Shims written by earlier pnpm 12 releases are migrated on the next global
install or pnpm self-update.
Installs in large workspaces
Most of the work in these two releases went here, all of it under #14352:
- Workspace patterns are probed concurrently and the discovered
package.jsonfiles are read in parallel. Literal directories and trailing-star patterns take a shortcut. pnpm-lock.yamlis parsed while the projects are being discovered, and the resolver no longer copies the whole lockfile before it starts.- The workspace dependency graph is built once per run instead of twice.
- The check that decides whether the lockfile needs updating stopped comparing every project against every lockfile entry.
- Each named
workspace:dependency is resolved once and reused by every project that declares it, andlink:dependencies re-anchor in lockfile-relative space. - Peer ranges are parsed once and cached, and the peer-dependency report at the end of a resolving install inspects only the projects resolution flagged (#14359).
- Sorting and rendering the big lockfile sections happens in parallel.
Workspace patterns also follow pnpm 11's dot-directory rule again: a wildcard
does not match a dot-prefixed directory, so packages/* and ** skip
packages/.cache and .git. A pattern that names one explicitly still matches.
Networking
On both Linux and Windows, pnpm now resolves registry hostnames through the system resolver instead of its own DNS client.
On Windows the built-in client bound a UDP socket per lookup, which made
Defender Firewall ask to allow pnpm.exe again after every pnpm self-update
(#14405). On Linux an
/etc/resolv.conf carrying an option the bundled 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).
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 cannot leak them into terminal scrollback or CI
logs.
pnpm deploy without injected dependencies
pnpm deploy no longer requires injectWorkspacePackages. 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.
Where 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. It now fails with
ERR_PNPM_DEPLOY_AMBIGUOUS_PEER, naming the package, the peer, and the
competing versions, and suggesting an overrides entry, instead of refusing
every non-injected workspace up front
(#9386).
Other changes
pnpm add <local directory>,pnpm add <local tarball>,pnpm add file:<path>andpnpm add <tarball URL>work again. A specifier given without a<name>@prefix is no longer read as a registry package name (#14437).pnpm update --interactiverenders its checklist the way pnpm 11 does. Group headings and column headers are separators the cursor skips, columns line up across groups,atoggles all andiinverts, and the confirmed selection is echoed (#14423).pnpm run,pnpm exec,pnpm rebuild, the script shortcuts, andpnpm link,pnpm outdated,pnpm import, and the packing commands all load the pnpmfile, soupdateConfigsettings such asextraEnvandextraBinPathsreach the processes they spawn (#14433, #14377).pnpm run "/pattern/"runs the matched scripts concurrently up toworkspaceConcurrency, with prefixed output. Filtered and recursiverunandexecno longer hang when a script reads from the terminal, so interactive prompts work again wherever pnpm never runs a second script alongside (#14397).- pnpm keeps the surrounding quotes out of
.npmrcvalues, restoring authentication with registries configured as:_authToken="${TOKEN}"(#14427). minimumReleaseAgeStrictdefaults totruewheneverminimumReleaseAgeis explicitly configured, wherever it was set. The built-in 1440-minute default stays non-strict (#14409).pnpm unpublishcompletes a registry's two-factor challenge instead of failing withERR_PNPM_UNAUTHORIZEDwhile logged in (#14464).catalogModeand--save-catalogno longer move a local path, tarball, orworkspace:<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 (#14437).pnpm configaccepts-g,--location, and--jsonbefore its subcommand, and a globalpnpm configskips project version switching, so registry authentication can be configured before pnpm downloads a project-pinned version (#14421, #14463).- Detached child processes survive on Windows, including when another program
launches
pnpmdirectly without a shell, asnrfrom@antfu/nidoes (#14447). - Transient Windows file-lock errors are retried while linking dependencies with the isolated linker and while replacing hoisted packages (#14407, #14349).
globalDirandglobalBinDirare honored wherever they are set, sopnpm add -gno longer fails afterpnpm config set -g global-bin-dir(#14336).- Resolution works against registries whose version manifests carry
_npmUser,dist.attestations,dist.unpackedSize,dist.fileCount, orpeerDependenciesMetain a shape npm does not use. Such a version was skipped as though it had never been published. pnpm audit --fixworks without a value and when another flag follows it, and--fix=overriderespectssaveExactandsavePrefix(#13261, #11523).
pnpr 0.1.0-alpha.10
A shared build artifact publication that cannot unregister itself no longer stops the registry reclaiming space or refusing further publications. A publication says at intervals that it is still working, and a registration that has gone quiet for an hour is written off. A publication whose bookkeeping write failed therefore stops holding back the collector that reclaims unreferenced blobs, returns the compatibility scopes it claimed, and stops counting toward the limit on publications in flight.
For the complete client changes, see the v12.2.0 and v12.3.0 release notes. The server changes are in the pnpr 0.1.0-alpha.10 release notes.
