Minor Changes
-
The Rust engine now reads four more settings from
pnpm-workspace.yamlandPNPM_CONFIG_*, instead of only accepting them as CLI flags:frozenLockfile—pnpm installgrows a--no-frozen-lockfileflag so the setting can be overridden in both directions. As in pnpm, it cannot be set in the globalconfig.yaml.savePrefix— the range operatorpnpm addsaves, still overridable with--save-prefix/--save-exact.savePeer—pnpm addalso records the new dependency inpeerDependencies.pnpm add --no-save-peeroverrides it back off.saveCatalogName— the catalogpnpm addsaves into.
-
The Rust engine now supports the
saveWorkspaceProtocolsetting, sopnpm add <pkg>@workspace:…writes back the same specifier pnpm does. Under the defaultrolling, a request likeworkspace:^1.2.3is saved asworkspace:^— a range with no version in it, so bumping the workspace package never has to touch its dependents' manifests.saveWorkspaceProtocol: truesaves the workspace package's resolved version instead (workspace:^2.5.0), andfalsekeeps theworkspace:form only when it was asked for explicitly. Previously the specifier was written back exactly as typed. -
pnpm update --workspaceis supported: dependencies that a workspace project publishes are re-pointed at the local copies through theworkspace:protocol. ThesaveWorkspaceProtocolsetting is honored — under itsrollingdefault an entry becomesworkspace:*,workspace:^, orworkspace:~(whichever matches the range it already declared), so a sibling's next release does not invalidate it. Naming a dependency that is not in the workspace fails withERR_PNPM_WORKSPACE_PACKAGE_NOT_FOUND, and combining the flag with--latestfails withERR_PNPM_BAD_OPTIONS.pnpm update --depth <number>is now applied per dependency instead of only distinguishing0from higher values: a dependency deeper than the given depth keeps its locked resolution, sopnpm update --depth 0updates direct dependencies only. -
pnpm run "/^build:(backend|frontend)$/"selects every script whose name matches the pattern, in single-project and recursive runs alike #13322. Flags on the selector are rejected withERR_PNPM_UNSUPPORTED_SCRIPT_COMMAND_FORMAT, as pnpm does. -
pnpm self-updateno longer takes any instruction from the project it is run in:- pnpm is fetched through the same trusted registry and auth configuration used when switching pnpm versions, so a project
.npmrcorpnpm-workspace.yamlcan no longer redirect the download or attach credentials to it, and the project's default.pnpmfile.(c|m)jsis no longer loaded. Pnpmfiles from trusted sources (thepnpmfilesetting, the global pnpmfile, config dependencies) still apply. - The
minimumReleaseAgesettings inpnpm-workspace.yamlno longer affectself-update. They still govern the project's own dependencies; forself-updatethe cooldown now comes from the built-in default, your global config, aPNPM_CONFIG_*environment variable, or a command-line flag. This fixesself-updatefailing inside a workspace that raises the cutoff while succeeding everywhere else, and stops a repository from either waiving the cooldown or keeping you on an outdated pnpm by raising it. - The same applies to the
trustPolicysettings and toci: a project can no longer weaken the trust check that guards the pnpm download, nor re-enable the confirmation prompt that a CI run suppresses.
When
self-updaterefuses a version that is younger than the cutoff, an interactive run now offers to update anyway; non-interactive runs still fail. CI never prompts, even on a runner that attaches a TTY. - pnpm is fetched through the same trusted registry and auth configuration used when switching pnpm versions, so a project
Patch Changes
-
An aliased dependency of a protocol that resolves under its own package name —
jsr:and the named registries — is recorded in the lockfile importer again."bar-from-jsr": "jsr:@pnpm-e2e/bar@1.0.0"resolved and installed, but the importer stayed empty, so nothing reading direct dependencies out of the lockfile (outdated,update,licenses, dedupe, frozen-install verification) could see it #13362. -
An
allowBuildsentry with theset this to true or falseplaceholder pnpm scaffolds no longer makes every command in that workspace fail with a config-parse error #13322. An undecided entry now leaves the package under the default-deny build policy, as pnpm does. -
Two
pnpm installresolution fixes that made large workspaces such as Astro produce a differentpnpm-lock.yamlthan pnpm 11 #13334:- A scoped workspace package referenced through the
file:protocol ("@test/pkg": "file:./pkg") is recorded as alink:again instead of being copied in as afile:snapshot. bundledDependencies/bundleDependenciesare no longer resolved as dependencies of their own. npm ships them inside the package's tarball, so installing them again added packages the lockfile should not contain (for examplenapi-wasmunder@parcel/watcher-wasm).
- A scoped workspace package referenced through the
-
Executables that a package ships inside its own tarball (
bundledDependencies) are linked again into that package'snode_modules/.bin, under both the isolated and the hoisted node linker. A package that declaresbundleDependencies: trueinstead of a list of names is now recorded inpnpm-lock.yamlthe way pnpm 11 records it, and such a lockfile can be read back. -
Aligned
pnpm licenses list --jsonpackage metadata and license-group ordering with the TypeScript CLI. -
Fixed
pnpm --filter <package> runto list the selected package's scripts and root workspace scripts when no script name is specified. -
Strip Unicode formatting characters from registry- and manifest-derived terminal output.
-
Prevented dependency verification before scripts from rewriting an up-to-date lockfile.
-
Concurrent commands in a repository that pins
packageManagerno longer race while installing the pinned pnpm version on a cold cache #13322. A task runner spawning severalpnpm runchildren at once could previously fail with "failed to remove existing directory … prior to swap", or leave a child looking for a binary another process had just unlinked. -
Config-load warnings, such as the warning about install settings left under the
pnpmfield ofpackage.json, are printed to stderr instead of stdout #13361. -
pnpm dedupe --checknow reports what deduplication would change: the importer and package snapshot diff, theERR_PNPM_DEDUPE_CHECK_ISSUESerror code, and the warning that points atpnpm peers checkwhen the install leaves peer-dependency issues behind.pnpm peers checkis also accepted again — the subcommand spelling used on pnpm.io and in pnpm's own dedupe output — instead of failing with "unexpected argument 'check' found" #13321. -
A deprecated package is reported once rather than once per workspace project that depends on it, and is no longer double-counted in the "deprecated subdependencies found" summary when it is also a direct dependency #13322. Ignored build scripts are also listed with their
(patch_hash=…)suffix, so two copies of a package that differ only by an applied patch are distinguishable. -
pnpm install --frozen-lockfileno longer re-imports a varying subset of packages on every repeat install of an unchanged project #13316. The global-virtual-store directory of a package that takes part in a dependency cycle was derived from an order that changed from run to run, so those packages landed on a fresh slot each time; it is now derived deterministically and matches the directory pnpm itself computes. -
When the pinned
packageManagerengine install cannot take its lock because the store cannot be written to, pnpm now reports that instead of quietly installing without the lock. A lock another process holds is unchanged — it is still waited for. -
Speed up installs after compatible catalog or direct dependency range changes by retaining the locked version without resolving the dependency graph again.
-
Speed up installs after safe override changes by reusing unambiguous compatible dependency resolutions, pruning obsolete dependencies, applying independent replacements and removals together, and handling parent-scoped
"-"overrides without full lockfile resolution. -
Fixed warm side-effects cache reuse for git dependencies.
-
Aligned
pnpm dedupe --checkprogress and error output with the TypeScript CLI. -
A frozen install now fails when
autoInstallPeers,dedupePeers, orexcludeLinksFromLockfilehas changed sincepnpm-lock.yamlwas written, instead of installing against a lockfile that no longer matches the settings. The error names the drifted setting, aspnpm install --frozen-lockfilehas always done. -
A repeat
pnpm install --frozen-lockfileis a no-op again when the project has a platform-incompatible optional dependency. The skipped package is kept innode_modules/.pnpm/lock.yaml(.modules.yamlis what records the skip), so the install can once more recognize an unchanged tree instead of re-running every lifecycle and dependency build script #13312. -
Reject frozen installs when the current pnpmfile does not match the lockfile's
pnpmfileChecksum. -
Fixed
pnpm licenses listto report dependencies from every workspace project, exclude unsupported platform packages, and mark development dependencies. -
Setting both
autoInstallPeers: falseanddedupePeerDependents: falsenow leaves missing peers alone, instead of still installing the ones a version elsewhere in the workspace could satisfy. -
Installing a local
file:directory dependency with the global virtual store enabled no longer fails withTypeError: Cannot read properties of undefined (reading 'split')#13335.Local directory dependencies —
file:directories and injected workspace packages — now get a global-virtual-store slot of their own per project. They used to share one slot across every project that depended on a directory of the same name, so a project could end up linked to another project's copy of the dependency. -
A missing required peer is no longer auto-installed as a prerelease that its declared range rejects. A package peer-depending on
^29.0.0 || ^30.0.0next to a30.0.0-alpha.6pulled in elsewhere in the graph now resolves a stable29.x/30.xfrom the registry instead of adopting the alpha #13341. -
A lockfile entry for a git-hosted archive that records no
integrityinstalls again instead of failing withERR_PNPM_MISSING_TARBALL_INTEGRITY. Older pnpm versions wrote that shape for dependencies like"ci-info": "watson/ci-info#f43f6a1c…", so any committed lockfile still carrying one could not be installed #13308. The archive URL pins a full commit SHA, and pnpm fetches it without an integrity check.Every other remote tarball still has to carry an
integrity, and the refusal now points at the repair:pnpm clean --lockfilefollowed bypnpm install.Error output no longer repeats the same message once per level of the internal error chain.
-
The
Workspacecolumn ofpnpm update --interactivenow falls back to the project's path when itsnameis only whitespace, as it already did for a missing or empty one — all three render an equally blank label otherwise. -
pnpm update --interactivenow groups the dependencies it offers by dependency type —dependencies,devDependencies,optionalDependencies,peerDependencies, and GitHub Actions each get their own heading — and lays each group out as a column-aligned table with aPackage/Current/Target/URLheader, instead of one flat list. -
pnpm update --interactivenow measures its table in terminal columns rather than in characters. A package name, workspace name, or version containing wide characters (CJK, most emoji) no longer knocks its row's columns out of line with the rest of the group, and a wide character in a version no longer aborts the command withSubject parameter value width cannot be greater than the container width#13357. -
pnpm update --interactiverun inside a workspace now shows aWorkspacecolumn naming the project each outdated dependency was found in, so the same package outdated in several projects can be told apart. -
Write single-value
libcpackage metadata in the same scalar form as pnpm. -
Fixed
pnpm installsilently skipping a localfile:*.tgzdependency: the package is now extracted into the virtual store, recorded underpackages:andsnapshots:, and linked intonode_modules#13379. -
A frozen install whose recorded settings no longer match the configuration —
overrides,catalogs,patchedDependencies, and the rest — now fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHnaming the one setting that changed, instead ofERR_PNPM_OUTDATED_LOCKFILEwith the whole map dumped #13322. -
Fixed
pnpm installdropping a package that ships nopackage.jsonof its own from the lockfile. Such a package is now named after its alias and recorded at version0.0.0underpackages:andsnapshots:, and its extraction gets the placeholderpackage.jsonpnpm writes #13410. -
Resolve optional peers from versions provided by local workspace packages, omit empty deprecation messages from generated lockfiles, and preserve valid lockfile pins in
pnpm dedupe --check. -
Aligned license reports,
dedupe --checkprogress and spacing, and dependency-verification output with pnpm. -
A
file:dependency declared by a package that was itself installed from a local directory is now resolved relative to that package's directory, not to the importer's #13323. Installing a project whose local dependency depends on a sibling directory (file:../child) no longer fails withCould not install from "…" as it does not exist, and the snapshot entry for such a dependency is now written asfile:<path>instead of<name>@file:<path>, matching the lockfile pnpm writes. -
npm_config_user_agentnow carries the configured user agent (pnpm/<version> …) in install lifecycle scripts,pnpm run,pnpm exec, andpnpm dlx#13322. It was previously unset for install scripts and the bare stringpnpmelsewhere, which madepreinstallguards that check for pnpm reject the install. -
An auto-installed optional peer is no longer hoisted at a version the workspace root's own dependency on that package excludes.
resolvePeersFromWorkspaceRootalready made the workspace root's specifier decide which version a missing required peer is installed at; the optional-peer picker ignored it and always took the highest version present anywhere in the graph. In a workspace whose root pinspostcss: 8.5.10, an importer that depends onwebpackand declares nopostcssof its own gotpostcss@8.5.22hoisted forterser-webpack-plugin's optionalpostcsspeer, leaving twopostcss@8.5.xinstances in the graph #13320. -
A missing optional peer dependency is no longer satisfied by a prerelease version that its declared range doesn't accept.
ts-jest, which declares@jest/transformandjest-utilas optional peers with^29.0.0 || ^30.0.0, was bound to30.0.0-alpha.6when ajest30 prerelease was elsewhere in the graph, whilejestitself stayed on 29. -
With
autoInstallPeers: false, a package's own optional peer dependencies are no longer added to its importer entry inpnpm-lock.yaml(and no longer linked into itsnode_modules) when another workspace project happens to resolve a matching version #13325. -
overridesnow also govern peers that pnpm auto-installs. Previously an override only rewrote dependencies declared in a manifest, so a peer nobody declares — installed becauseautoInstallPeersis on — resolved against its declared peer range and could bring in a second copy of the very package the override pinned. For example, withoverrides: { react: npm:react@19.2.0 }and a lonelucide-reactdependency, pnpm installedreact@18.3.1; it now installs the pinnedreact@19.2.0#13320. -
pnpm approve-builds -gis accepted again, reporting that the command is not supported with global packages rather than failing withunexpected argument '-g' found.approve-buildswas the only command that declared--globalwithout its-gshort form #13310. -
The Rust implementation of pnpm has moved from alpha to beta releases.
-
A catalog name containing a control character no longer corrupts
pnpm-workspace.yaml.pnpm add --save-catalog-name "$(printf 'a\nb')"(or the same value insaveCatalogName) now fails withERR_PNPM_WORKSPACE_MANIFEST_WRITER_INVALID_CONTROL_CHARACTERand leaves the file untouched, matching how the writer already treatsallowBuildsandoverridesentries. -
Preserve each direct dependency's locked optional peer context during
pnpm dedupe. -
Preserve optional peer providers recorded in peer suffixes when
pnpm deduperebuilds a workspace lockfile. -
With
nodeLinker: hoisted, a workspace project no longer gets its own copy of a dependency whose version already won the workspace-root slot. Only the versions that lost the root slot are nested, matching the pnpm CLI. Previously every project's direct dependency was materialized under that project as well, which gave lifecycle scripts a second copy to run in. -
The Rust engine now warns when
package.jsonstill declares install settings under thepnpmfield, which pnpm 10 moved topnpm-workspace.yaml. A project that hasn't migrated itspnpm.overrides/pnpm.packageExtensions/pnpm.patchedDependenciespreviously saw the settings silently ignored, and only met the downstream symptom. Keys thepnpmfield never owned are left alone. -
Fixed
pnpm licenses listandpnpm licenses lsparsing and license metadata discovery when using the global virtual store pnpm/pnpm#13332 and pnpm/pnpm#13333. -
A
pnpm-workspace.yamlthat declares a package pattern whose directory does not exist yet —packages/*before the first package is created, say — no longer fails every command withERR_PNPM_WORKSPACE_WALK_ERROR. The pattern now matches no projects, as it does in the JavaScript implementation #13296. -
Fixed
catalog:references failing to resolve when installing through a pnpr server, which errored with "No catalog entry '' was found for catalog 'default'." even though the catalog entry existed. The workspace the server reconstructs from the request has no catalog sections, so the client now sends its catalogs along with the request #13232. -
Validate the project's pinned package manager and runtimes before running a command, matching the pnpm CLI:
- A
packageManager/devEngines.packageManagerpin that the running pnpm does not satisfy now fails withERR_PNPM_BAD_PM_VERSION(orERR_PNPM_OTHER_PM_EXPECTEDwhen the project is pinned to another package manager), instead of being silently ignored. The check also runs under corepack, where pnpm cannot switch versions itself, and says so. devEngines.runtime/engines.runtimeentries withonFail: "error"oronFail: "warn"are validated against the Node.js, Deno, or Bun installed on the system, failing withERR_PNPM_BAD_RUNTIME_VERSION.pmOnFailandruntimeOnFailare honored as bypasses and can now be passed as--pm-on-fail=<value>/--runtime-on-fail=<value>, the form the error hints suggest.
Global commands (
--global) and commands that do not belong to the project (store,dlx,self-update, …) skip these checks, as does a project pin that only asked pnpm to switch versions whenmanage-package-manager-versionsis turned off. - A
-
Arguments after the script or command name now reach the script untouched for
pnpm run,pnpm exec,pnpm dlx, andpnpm with, matching the JavaScript implementation. Previouslypnpm run build --config.foo=barconsumed the argument as a pnpm setting instead of forwarding it, andpnpm run build --silenthanded the script--reporter=silent— a token the user never typed #13302. Put such flags before the script name (pnpm run --silent build) to apply them to pnpm. -
Fixed generated lockfiles to preserve packages' scalar
libcconstraints. -
Preserve a user-provided
TMPDIRwhen scripts run withunsafePermenabled; otherwise, continue using the package-local temporary directory. -
Added support for
publishConfig.name, which publishes a package under a different name than the one its manifest carries in the workspace. Only the published artifact is renamed — dependents,pnpm-lock.yaml, and release tooling keep addressing the project by its manifest name — and the new name reaches the packed manifest, the tarball filename, and everything that addresses the package at the registry: the already-published check ofpnpm publish -r, its registry selection, and the release-planning probes ofpnpm change statusandpnpm version -r. This also fixes the changelog of the Rust CLI itself, which is published aspnpmfrom a workspace project namedpacquet: its release notes were composed under the workspace name and so never made it into the published package #13345. -
pnpm run <script> <args>now forwards every argument after the script name to the script verbatim, matching the behavior of the JavaScript implementation. Previously the--separator was dropped, sopnpm run test -- --watchreached the underlying program as--watchand failed whenever that program claimed the option itself; arguments spelled likepnpm run's own flags (-s,--if-present) were also consumed by pnpm instead of reaching the script #13295. Pass those flags before the script name (pnpm run -s test) to apply them to pnpm. -
pnpm test,pnpm start, andpnpm stopnow forward their arguments to the script, matching the pnpm CLI.pnpm test --watchandpnpm start --port 3000previously failed with a usage error, andpnpm stopclaimed--if-presentand-sfor itself instead of passing them on. As withpnpm run, every token after the command name reaches the script verbatim, a--separator included. -
Added the
--workspace-root(-w) flag, which runs the command on the root workspace project.pnpm add -D typescript prettier -wfrom a workspace subdirectory now saves to the rootpackage.jsoninstead of failing with "unexpected argument '-w' found" #13031. Combined with--recursive, the flag narrows the run to the root project alone.-wmay not be used together with--global, and may only be used inside a workspace. -
patchedDependenciespatch files that pnpm applies no longer fail withERR_PNPM_PATCH_FAILED: a hunk whose last line is context in a file with no final newline, and an LF patch against a CRLF file, both apply again #13322. A hunk that has drifted from its recorded line numbers is also retried nearby, matching pnpm. -
A peer dependency is now recorded in the lockfile at the version and peer suffix the peer provider actually resolved to. Peers whose provider carried peer suffixes of its own could be recorded against a package instance that no importer installs, leaving an unreachable entry in
snapshots:and a peer bound to the wrong instance #13320. -
A peer dependency that the workspace root already provides is no longer installed a second time. With
resolvePeersFromWorkspaceRootenabled (the default), a missing peer is matched against the workspace root project's dependencies; it was matched against the dependencies of whichever project was being resolved, so a project that didn't declare the peer itself resolved its own copy from the registry. In vercel/next.js, whoseoverridespinreactto a single canary build, this pulled in a secondreactand paired it withreact-domfrom the canary — a combination the pin exists to prevent. -
Under
resolvePeersFromWorkspaceRoot, a workspace root dependency declared withlink:orfile:(or the path form ofworkspace:, such asworkspace:../pkg) now satisfies another project's missing peer dependency at the linked package's own version, instead of being hoisted as a path. Those specifiers are relative to the project that declares them, so the same specifier reached a different directory — or none — from the project the peer was hoisted into, leaving a broken link. The root now has the same authority over the peer as it has when it declares the package with a version range #13373. -
Two
pnpm installpeer-resolution fixes that made large workspaces such as Astro produce a differentpnpm-lock.yamlthan pnpm 11 #13334:- A package that declares the same name in both
dependenciesandpeerDependenciesno longer gets a nested copy of it when the parent already supplies that name, which is what pnpm does withautoInstallPeersdisabled. The nested copy hid the peer, so the package was recorded without the peer context it resolves in. - A duplicate peer-suffixed variant that collapses into a larger, compatible one now collapses everywhere it is referenced. A variant kept alive by a single consumer's edge no longer lingers in the lockfile.
- A package that declares the same name in both
-
Closed the remaining gaps in how unscoped per-registry
.npmrcsettings are pinned to the registry their own source file declared:- An inline
cert=/key=written with\nescapes now expands to a real multi-line PEM, matching the URL-scoped//host/:cert=spelling. pnpm config get/pnpm config listnow report a rescoped credential under the URL-scoped key it was pinned to, instead of the unscoped key it was written as.- The deprecation warning names the file it read and lists every setting it pinned, including
tokenHelper. - A credential with no registry of its own is no longer attached to the resolved default registry, which repository config can move. The same rule now covers the
@pnpm/napibindings: theauthHeaderByUrientry written with an empty ("") key is pinned to theregistry/registries.defaultthe host passed alongside it, never to a registry the project's.npmrcnames.
- An inline
-
The projects that run their own lifecycle scripts (
preinstall,install,postinstall,prepare, …) now match pnpm in every install-family command. A project runs them when the command installs it in full, and — in a workspace the command only partly covers — whenever the command mutates it at all; the workspace root runs them even when the command was pointed at another project, because it is installed in full alongside it. As a result,pnpm update <pkg>andpnpm add <pkg>in a workspace no longer skip the workspace root's scripts,pnpm updateat a workspace root no longer runs the other members' scripts, andpnpm update --latestno longer runs the project's own scripts (it rewrites named dependency specs, so it is a partial install likepnpm update <pkg>) #13358. -
Print the script command by default when running a filtered lifecycle script. The command remains hidden with
--silent. -
Run dependency verification consistently after regenerating a lockfile with
dedupePeersenabled. -
Aligned the
hoistedDependenciescontents and ordering innode_modules/.modules.yamlwith pnpm. -
Preserve whether package
libcmetadata uses a string or an array when writing the lockfile. -
A
package.jsonthat starts with a UTF-8 byte order mark is read again instead of failing withexpected value at line 1 column 1. Workspace discovery, dependency manifests (including bin linking), tarball extraction, andpnpm publishof a pre-built tarball all accept one, matching pnpm #13311. A manifest that really is malformed now reports its path in the error. -
Fixed
ERR_PNPM_BROKEN_LOCKFILEwhen installing with a pnpm 10 lockfile that has apatchedDependenciessection. See #13307. -
pnpm -r run "/pattern/" --no-bailno longer exits zero when one of a project's matched scripts fails and a later one passes. The run summary carries a single status per project, and the passing script overwrote the recorded failure. -
Resolution failures now report the error pnpm defines for them. A well-formed range that the registry publishes nothing for fails with
ERR_PNPM_NO_MATCHING_VERSION— naming the latest release, the other dist-tags, and thepnpm view <pkg> versionscommand that lists the rest — instead ofERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER. A package the registry doesn't have fails withERR_PNPM_FETCH_404and the "not in the npm registry, or you have no permission to fetch it" hint (plus which authorization header was sent, since a private registry often answers a permission failure with a 404) instead of a bare HTTP-client message. A wrapper that quotes its cause verbatim no longer prints the same sentence twice in the error report. -
Fixed
pnpm add <workspace-package>to resolve the local package whenlinkWorkspacePackagesis enabled. -
$dep-nameself-references inoverridesare now resolved against the root manifest's direct dependencies, so an override such asrolldown: $rolldownrecords the concrete specifier inpnpm-lock.yamland no longer fails a frozen install withERR_PNPM_OUTDATED_LOCKFILE#13314. A reference to a package that is not a direct dependency fails withERR_PNPM_CANNOT_RESOLVE_OVERRIDE_VERSION, and the deprecated syntax now warns, pointing at catalogs. -
The root project's
pnpm:devPreinstallscript now runs before resolution and linking, as it does in pnpm 11. It is skipped under--ignore-scripts,--lockfile-onlyand--dry-run, bypnpm fetchandpnpm rebuild, and by a repeat install that is already up to date. Workspaces that use the hook to prepare state the install depends on — such as next.js, which generates a placeholdernextbin with it — were left with dependents linked against files that were never created #13313. -
The lockfile-verification line now dates a cached verdict —
✓ Lockfile passes supply-chain policies (verified 253ms ago)— instead of the timeless(previously verified)#13315. -
pnpm install,run,test,update,remove,link,unlink,prune, andrebuildnow print the workspace scope they resolved —Scope: all 41 workspace projects, orScope: 5 of 41 workspace projectsunder a--filter. This is the confirmation that a filter selected what was intended #13315. -
An install that blocks a dependency's build scripts now appends a placeholder for it to
pnpm-workspace.yaml, so approving or denying the build is an edit rather than writing the block by hand:allowBuilds: es5-ext: set this to true or false
A placeholder is not a decision — the build stays blocked until it is replaced with
trueorfalse— and an existing entry is never overwritten #13315. -
Fixed
--parallelbeing treated as the script name when placed beforerunin a recursive command. -
scriptShellnow selects the shell for lifecycle scripts too — dependency build scripts and a project's ownpreinstall/install/postinstall/prepareandpnpm:devPreinstall— not only forpnpm runandpnpm exec. A workspace that configures a shell was still getting the platform default (sh/cmd) for everything the install itself spawns. -
Fixed
shamefullyHoist: trueto create public root dependency links. -
Prevented optional peers from being selected from an unrelated workspace package's shared dependency context.
-
The store index now keys URL, git-host, and
type: gitdependencies by their bare resolution id, matching the key pnpm 11 writes #13365. Previously these rows carried a<name>@prefix, so a store warmed by one pnpm major was cold for the other and every non-registry dependency was re-downloaded, re-extracted, and re-imported on a switch. A remote tarball also occupied two index rows instead of one, doubling its extraction work. -
A project that pins pnpm through
devEngines.packageManager(or a v12+packageManagerfield) now gets itspackageManagerDependenciesrecorded inpnpm-lock.yamlby every command, not just by the install-family ones #13348. Runningpnpm list(or any other command) in a freshly cloned project no longer leaves the lockfile without the pinned version. ThepmOnFailsetting now also decides whether the pin is recorded:--pm-on-fail=ignorekeeps it out of the lockfile even when the manifest asks for a stricter policy, and vice versa. -
Fixed
pnpm licenses listto detect licenses from license files and preserve the latest package version's development classification. -
pnpm update --latestnow rewritesjsr:dependencies. The manifest keeps the protocol and the range operator it declared, sojsr:1.0.0becomesjsr:2.0.0andjsr:@scope/name@^1.0.0becomesjsr:@scope/name@^2.0.0, instead of being left at the old version #13363. -
pnpm update --latestnow rewrites dependencies using a named registry alias. The manifest keeps the alias prefix and the range operator it declared, sogh:1.0.0becomesgh:2.0.0andgh:@acme/foo@^1.0.0becomesgh:@acme/foo@^2.0.0, instead of being left at the old version pnpm/pnpm#13393. -
Breaking change from pnpm v11. Under
engineStrict, an install fails when an incompatible package is reached through a regulardependenciesedge of an installable package, even when that whole subtree hangs off anoptionalDependenciesentry. 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. -
A lockfile entry whose tarball resolution records no
integrityis now reported by the lockfile-verification gate, before anything is downloaded: every offending entry is listed in oneERR_PNPM_MISSING_TARBALL_INTEGRITYerror instead of failing the install one fetch at a time after the gate had already passed the lockfile #13364. Anintegrity: ''that pins nothing is treated the same as a missing one, and the exemption for git-host archive URLs is now read from the URL rather than the lockfile's owngitHostedmarker. -
Fixed
pnpm licenses listto read licenses from legacy package manifest fields. -
Fixed
--workspace-root(-w) selecting the current workspace when--dirpointed at a nonexistent directory outside it (for examplepnpm --dir ../../elsewhere add -w foo). The command now fails withERR_PNPM_NOT_IN_WORKSPACE, matching pnpm. A nonexistent--dirinside the workspace still resolves to the workspace root as before.