Update pnpm to v11 - #446
Open
renovate[bot] wants to merge 1 commit into
Open
Conversation
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
4 times, most recently
from
May 14, 2026 12:47
2be8dcf to
8daae99
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
3 times, most recently
from
May 24, 2026 13:13
fb26744 to
2a2067c
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
4 times, most recently
from
June 2, 2026 16:10
64a56a1 to
90c7065
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
3 times, most recently
from
June 12, 2026 01:02
346f01b to
3a153d4
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
2 times, most recently
from
June 18, 2026 20:00
ecb7f46 to
99977d3
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
from
June 23, 2026 16:54
99977d3 to
bed16a0
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
2 times, most recently
from
July 10, 2026 02:25
de342a6 to
d7673ac
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
5 times, most recently
from
July 18, 2026 22:13
7895af0 to
f39968a
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
3 times, most recently
from
July 23, 2026 23:09
7c7ecad to
92755e4
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
2 times, most recently
from
August 3, 2026 14:44
afeb53b to
980d639
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
2 times, most recently
from
August 15, 2026 18:13
187431f to
db8dc06
Compare
renovate
Bot
force-pushed
the
renovate/pnpm-11.x
branch
from
August 23, 2026 17:12
db8dc06 to
c027314
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
10.33.2→11.23.0Release Notes
pnpm/pnpm (pnpm)
v11.23.0: pnpm 11.23Compare Source
Minor Changes
pnpm config getandpnpm config listnow show the settings pnpm acts on under their documented names:registriesshows the registries pnpm resolves from, merged across every source (.npmrc,pnpm-workspace.yaml, the global config, CLI flags), in the shape the setting is written in: keyed by registry URL, with the default registry declared as the bare@scope. Built-in routes are included — the@jsrscope and thenpmjsandghprefixes — unless pointed elsewhere. Previouslypnpm config get registriesprintedundefined.updateandauditshow the effective sections, whichever spelling set them. The deprecated internal spellings (updateConfig,auditConfig,auditLevel) are no longer listed.catalogsshows the complete resolved catalog set — the singularcatalogblock is itsdefaultentry — whichever spelling declared it.registryand@scope:registryentries show the merged routes rather than raw.npmrcvalues, so they always agree with theregistriesview.Settings that no supported pnpm version recognizes get their own warning. A key in the global config file that this version of pnpm does not read is no longer reported with advice to move it to a project-level
pnpm-workspace.yaml(where it would be ignored too); the warning now says the setting is not recognized by this version of pnpm, names the pnpm version that does read it when there is one (for example,globalShimsis a pnpm v12 setting), and suggests the closest real setting name when the key looks like a typo. Unrecognized and non-camelCase keys in a project'spnpm-workspace.yaml, previously ignored silently, are now reported the same way.pnpm config get <key>andpnpm get <key>no longer print config-load warnings, so a script capturing the value gets the value alone.The
importPackagepnpmfile hook is deprecated. pnpm now prints a warning when a pnpmfile defines it, and the hook will be removed in the next major version. It also opts the installation out of the parallel package importer, making installation slower. If you rely on this hook, comment on #14101.node_modules/.modules.yamlno longer records the registries an install resolved from, and the recorded copy is dropped from the file on the first install that rewrites it.It dated from the lockfile format that spelled a dependency's path relative to its registry, where reading an installed tree meant knowing the registries it was installed with. Dependency paths have not carried a registry for several major versions, and the recorded copy outlived its use:
pnpm list,pnpm why, and single-project installs preferred it over the project's own configuration, so a project whose registry had changed since its last install was still read through the old one.They now use the configured registries, like every other command already did.
When
enableGlobalVirtualStoreis on, every process pnpm spawns for the project (pnpm run,pnpm exec, lifecycle scripts) now receives aNODE_PATHpointing at the project's hoistednode_modules, plus aNODE_OPTIONS--importflag that registers a resolve hook restoringNODE_PATHlookups for ESM imports. Dependencies that import undeclared ("phantom") packages keep resolving under the global virtual store — for both CommonJS and ESM — without installing the@pnpm/plugin-esm-node-pathconfig dependency pnpm/pnpm#9618. Tools run bypnpm dlxresolve such dependencies too: the JS CLI passes them the same environment, while the Rust CLI's dlx cache is self-contained, so its layout already exposes them.A registry can now declare that its abbreviated metadata carries the
timefield, soresolutionMode: time-basedreads the full metadata document only from the registries that need it:registry.npmjs.orgomitstimefrom abbreviated metadata, so a time-based resolution has to fall back to the much larger full document. That fallback used to be all-or-nothing:registrySupportsTimeFieldanswered for every registry at once, so a project resolving from both the public registry and a Verdaccio instance either paid for full metadata everywhere or claimed atimefield npmjs does not serve. The answer is now per registry, andregistrySupportsTimeFieldremains the answer for every registry that does not declare one.The declaration is also sent to a pnpr server, which applies it to the resolution it runs on the client's behalf.
A pnpr resolve request now carries the client's registries the way the
registriessetting declares them — keyed by URL, with the scopes routed to each, the bare-specifier prefix each answers to, and each one'sserverType— in place of the prefix map it used to send.The server routes them through the same inversion the config reader runs, so a pnpr-served install resolves a scoped dependency from the registry that scope is routed to, which it previously could not: only the default registry and the prefix-addressed ones reached the server. A declared
serverTypereaches it too, so the tarball URLs pnpr omits from the lockfile match the ones the client reconstructs.Built-in scope routes the project has not pointed elsewhere are not declared, so a pnpr server's allowlist is not asked about
npm.jsr.ioon requests that resolve no JSR package.A registry a request only declares is no longer refused up front for being off the server's allowlist — a client describes its whole configuration, including scopes a given resolve never reaches, so a stray
@scope:registryin a developer's~/.npmrcno longer fails every install against a pnpr server that does not serve it. The boundary moves to the fetch itself: an origin the resolve does reach is refused before the request leaves the server, with the same message.This changes the resolve and verify-lockfile request bodies. A pnpr server and its clients have to be on matching versions; the protocol is still experimental and unversioned.
The
registriessetting now declares a registry once, keyed by its URL, with everything about that registry in the entry: how it lays out tarball URLs, the scopes routed to it, and the bare-specifier prefix it answers to.serverTypetells pnpm how the registry lays out its tarball URLs, which decides whether a URL can be omitted frompnpm-lock.yaml:npm— the registry behaves likeregistry.npmjs.org, which also serves a scoped package from its percent-encoded path. Declare this for a faithful mirror or caching proxy of the public registry so its tarball URLs can be omitted too.artifactory— JFrog Artifactory repeats the scope in a scoped package's tarball filename (@acme/widget/-/@acme/widget-1.0.0.tgz) where the npm registry strips it (@acme/widget/-/widget-1.0.0.tgz). Declaring it lets pnpm rebuild that URL, so it is omitted frompnpm-lock.yamlinstead of being written out for every scoped package pnpm/get-npm-tarball-url#16.scopeslists the@-prefixed scopes that resolve from this registry. A bare'@'is the scope-less default registry, the one theregistrysetting names.prefixis the alias a dependency addresses this registry by, as in"foo": "work:^1.0.0".The layout is never inferred from the registry URL, so nothing changes unless you declare it;
registry.npmjs.orgcontinues to behave asnpmwithout being declared. Because the lockfile depends onserverType, it is read frompnpm-workspace.yamlonly — aserverTypein the globalconfig.yamlis ignored, so one developer's machine cannot shape a lockfile their collaborators read back with a different layout. Credentials are rejected in this setting, in a key as well as in a field, and still belong in.npmrc. An entry that routes nothing to itself and matches no configured registry is reported as a warning rather than silently ignored.Migrating
The older
registriesshape, a map of<scope>: <url>strings, still works and needs no change:namedRegistriesis deprecated in favor of theprefixfield, and is still read for prefixesregistriesdoes not declare.toLockfileResolutionandisCanonicalRegistryTarballUrlnow take their registry and layout as an options object rather than positional arguments, so@pnpm/lockfile.utilsand@pnpm/resolving.tarball-urlget a major bump.An install that had to re-hash store files to verify them now reports it. If that cost more than a second, it says how long —
The integrity of N files was checked in 2.5s.— and if it was quick but covered more than a thousand files, it names the cause instead: their timestamps changed since the store recorded them, which a backup tool, an antivirus scan or a copied store can do.Added
virtualStoreType, which names where the virtual store lives — one store per machine, or one per project:It is the canonical spelling of
enableGlobalVirtualStore, which keeps working. When a project sets both,virtualStoreTypewins. It can also be set throughPNPM_CONFIG_VIRTUAL_STORE_TYPEand read back withpnpm config get virtualStoreType. The default is unchanged —project, so the shared store stays opt-in.The setting is independent of
nodeLinker.isolatedandpnpboth work with either store type, andhoistedwrites no virtual store at all, so it is unaffected.Patch Changes
pnpm add --allow-buildnow adds to theallowBuildsentries already inpnpm-workspace.yamlinstead of replacing them #13872.Kept pending build approvals available after removing an unrelated dependency.
pnpm approve-buildsnow removesonlyBuiltDependencies,onlyBuiltDependenciesFile,neverBuiltDependencies, andignoredBuiltDependenciesfrompnpm-workspace.yamlwhen it writesallowBuilds. Those settings were replaced byallowBuildsin pnpm 11 and silently ignored since, so a workspace migrated from pnpm 10 kept them around looking active.pnpm auditno longer reports a patched version that was never published or is deprecated. The inferred patched range (e.g.>=4.17.24from<=4.17.23) is now checked against the registry packument, and the report is corrected to the lowest non-deprecated published version that satisfies it (e.g.>=4.18.1when4.17.24does not exist and4.18.0is deprecated). When no published version satisfies the range, the report showsPatched versions: None. This also preventspnpm audit --fixfrom adding overrides orminimumReleaseAgeExcludeentries for patches that do not exist #13824.pnpm audit --fixandpnpm audit --fix updateno longer add aminimumReleaseAgeExcludeentry when the registry packument shows that the minimum patched version was never published. Previously such entries were written for versions that do not exist, which would have let a later publish of that version bypass theminimumReleaseAgegate #11563.The
--jsonoutput ofpnpm auditnow returnspatched_versions: nullfor advisories whose inferred patch is not available (never published, skipped, yanked, or deprecated), making it easier for tooling to distinguish "no fix available" from "fix available at version X".Fixed
pnpm patch-commitin project and edit paths containing non-ASCII characters.The package and bump pickers of
pnpm changenow size their page from the terminal height instead of always showing 7 rows. They fall back to 7 rows when the terminal height is unknownpnpm/pnpm#13815.Canceling a
pnpm changeprompt with Ctrl-c no longer prints a stack trace. It reportsChange canceledand exits with a success status, like the other interactive commands #13814.Re-fetch full registry metadata when
minimumReleaseAgeis enabled and an abbreviated packument'stimemap omits timestamps for some versions. This prevents mature versions from being filtered out and resolution from falling back to the lowest matching version pnpm/pnpm#13741.A config dependency carrying an inline integrity (the
<version>+<integrity>form, or the object form without atarball) now takes its tarball URL from the registry's packument instead of deriving it from the registry URL, so migrating one costs an extra metadata request. On a registry that serves tarballs from a path pnpm cannot derive, GitLab's group endpoint for one, installing such a config dependency failed with a 404 while the same package installed fine as a regular dependency #13765.Fixed
PNPM_CONFIG_NODE_VERSIONbeing ignored when setting the Node.js version used for compatibility checks.A custom fetcher can no longer replace the archive integrity that
pnpm-lock.yamlpins: the locked value is restored after acanFetchorfetchhook rewrites the resolution, and delegating a locked archive to a directory or git source now fails instead of installing unverified content.The Rust CLI now also loads the pnpmfiles named by the
pnpmfilesetting (a single path or an ordered list), and hands custom fetchers nativelocalTarballandremoteTarballcallbacks — including on a fresh install that has to compute a missing tarball integrity, which is then reused by later offline installs. File maps a fetcher returns are accepted only when they match what those native callbacks extracted.Fixed an issue where running
pnpm dedupe --checkin projects withnodeLinker: hoistedwould cause dependencies to be moved out ofnode_modulesintonode_modules/.ignored.pnpm deploy --prodandpnpm deploy --no-optionalno longer list the excluded dependency groups in the deployedpackage.jsonandpnpm-lock.yaml. The deployed lockfile referenced packages that the deploy left out of its graph, so installing in the deploy directory afterwards created dangling symlinks #13623.Don't treat files like
license16.jsonas a package license when deciding if the workspace LICENSE file should be included in the packed package.pnpm exec --recursive --no-reporter-hide-prefixno longer prints a blank prefixed line after each chunk of a command's output, and no longer splits a line in two when it straddles a chunk boundary.Fixed
404errors when installing from a registry that serves scoped packages only from a percent-encoded path, such as GitHub Enterprise Server. Outsideregistry.npmjs.org, a tarball URL that encodes the scope separator as%2for%2Fis no longer mistaken for one that pnpm can rebuild from the package name, version, and registry, so it is kept inpnpm-lock.yamland requested verbatim on the next install #13534.Fixed
trustPolicyExcludeandminimumReleaseAgeExcludebeing ignored when set to a single string instead of a list. The value was read one character at a time, so the exclusion never matched the package it named — and a*anywhere in it matched every package, silently switching the policy off.pnpm initnow pins the exact pnpm version instead of a^range, and records it in thepackageManagerfield alongsidedevEngines.packageManager. Corepack reads onlypackageManagerand accepts nothing but an exact version, so it rejected the generatedpackage.jsonwith "expected a semver version" pnpm/pnpm#13969. A package created inside an existing workspace is still left unpinned — it follows the pin at the workspace root — and--no-init-package-managerstill scaffolds a manifest without any pin. In pnpm 12,pnpm initalso honorsinitTypeand its--init-typeflag, so the manifest it writes is the same one pnpm 11 writes.Fixed an issue where package overrides were written into the metadata cache, causing removed overrides to keep applying on subsequent installs pnpm/pnpm#13918.
On Windows, upgrading pnpm no longer leaves a stale
pnpm.ps1behind. PowerShell resolvespnpm.ps1ahead ofpnpm.cmd, so a shim written by an older installation kept running the previous version. Linking the pnpm CLI's bins now deletes it #13919.Fixed an inconsistency where
minimumReleaseAgeExclude(andtrustPolicyExclude) wildcard/bare-name rules behaved differently in the evaluator and normalizer. A bare rule now consistently evaluates as matching every version, preventing unexpected behavior and silent widening of version policy exemptions when pnpm rewrites the workspace manifest pnpm/pnpm#13725.A frozen install no longer rewrites the
packageManagerDependenciesblock ofpnpm-lock.yaml. When the pnpm version pinned bydevEngines.packageManager(or bypackageManager) is missing from the lockfile or no longer matches it,--frozen-lockfilenow fails withERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILEinstead of resolving the version and saving it, so a manifest whose pin was bumped without regenerating the lockfile can no longer pass CI #14009.A git dependency installed over HTTPS from a hosted repository now keeps its branch, tag, or version range in the specifier recorded in
package.json. It was written back without one, so the nextpnpm updatemoved the dependency to the repository's default branch #13999.Fixed
pnpm update --global --latestfailing with a 404 error when a globally installed package was not added from the registry by name. Packages installed from a local path (link:/file:), a git repository, a tarball URL, annpm:alias, or a named registry now keep their spec during a global update instead of being looked up by name in the default registry. See #12854.Fix recursive
pnpm update <name>@<version>so an exact pinned update stays scoped to the requested version line: copies of the same package on another major line — or, for a0.xrequest, another minor line — keep their locked resolution instead of being re-resolved along with the target.Under
nodeLinker: hoisted, a dependency declared against a peer-resolution variant of a package version is no longer dropped from the installed layout. All variants of a version share one hoisted copy, and edges pointing at any of them now resolve to it, so the depending project keeps the package in its.package-map.jsonand the depending package keeps it in itsnode_modules/.bin.Fixed
pnpm install --merge-git-branch-lockfilesdeleting the per-branch lockfiles when thelockfilesetting isfalse. Such an install never reads them, so it has nothing to merge them into and now leaves them alone.Fixed
pnpm installsometimes not exiting after printingDone in Xs#12297.Fixed pnpm failing to read
.modules.yamlfiles containing long dependency paths #13875. The manifest is now parsed as JSON (the format pnpm writes it in), falling back to the YAML parser only for manifests written by old pnpm versions.With
preferSymlinkedExecutables,NODE_PATHagain points at the virtual store of the workspace root when pnpm is run from inside a workspace package, so scripts can resolve dependencies that live only in the hoisted store #13912.Reduced registry metadata requests during dependency resolution by reusing cached metadata when lockfile preferences prove that no uncached version can win pnpm/pnpm#13976.
pnpm pkg getandpnpm pkg setnow accept hyphens inside a dot-notation property path, sopnpm pkg get dependencies.some-package-namereads the key instead of failing withERR_PNPM_UNEXPECTED_TOKEN_IN_PROPERTY_PATH. The bracketed and quoted forms already worked and are unchanged.A resolve request now carries the client's
resolutionMode, so an install delegated to a pnpr server picks versions the way the client would.time-basedandlowest-directreached the server as nothing at all, leaving it on itshighestdefault: the returned lockfile pinned the highest satisfying version of every dependency, and the setting appeared to be ignored.This adds a field to the resolve request body. A server older than its client ignores it and keeps resolving
highest; the protocol is still experimental and unversioned.Fixed
pnpminstalls using pnpr to honor the client'sautoInstallPeers,dedupePeers, andexcludeLinksFromLockfilesettings pnpm/pnpm#13389.pnpm removenow prunes undecided entries ("set this to true or false") fromallowBuildsinpnpm-workspace.yamlwhensharedWorkspaceLockfile: trueand the corresponding packages are removed pnpm/pnpm#13892.Fixed workspace discovery for
pnpm-workspace.yamlfiles without apackagesfield so commands only consider the workspace root instead of recursively scanning nested projects #14047.A runtime installed through
devEngines.runtimenow matches the host whensupportedArchitectureslists several platforms. Listingos: [darwin, linux]andcpu: [x64, arm64]used to install the runtime built for the first entry of each list, so a machine running Linux on arm64 got a macOS x64 Node.js that could not execute #13898.pnpm sbomnow fails withERR_PNPM_SBOM_MISSING_IMPORTERSwhenpnpm-lock.yamlhas no entry for a selected project, instead of writing an SBOM that under-reports that project's dependencies. Previously this crashed withCannot read properties of undefined (reading 'devDependencies').pnpm self-updatenow rewrites a simpledevEngines.packageManager.versionrange (^/~) to the newly installed version, keeping the operator — matching howpnpm updateandpnpm runtime setrewrite ranges. Complex ranges such as>=8.0.0that the new version satisfies are still left unchanged #13935.pnpm self-update <tag>no longer downgrades when the dist-tag points at the pnpm version already running and that version is younger thanminimumReleaseAge. The maturity cutoff moved the tag back to the previous mature release, sopnpm self-update next-12on v12.0.0-rc.4 switched to v12.0.0-rc.3.pnpm set-scriptnow updatespackage.jsoninstead of failing withERR_PNPM_NOT_IMPLEMENTEDpnpm/pnpm#13956.pnpm updatenow preserves the existing range operator when updating a prerelease dependency. See #7002.Installs are faster in workspaces that declare inter-workspace dependencies with plain ranges (
"*","^1.2.3") rather than theworkspace:protocol. WithpreferWorkspacePackagesenabled, linking such a dependency no longer makes a registry request that cannot change the outcome — and workspace packages that were never published no longer cost a 404 on every install.Added
fetchWarnTimeoutMsandfetchMinSpeedKiBpsto the Rust pnpm CLI and its N-API bindings. Slow registry metadata requests and tarball downloads now emit pnpm-compatible warnings without exposing URL credentials, query parameters, fragments, or control characters pnpm/pnpm#12042.An override change is now absorbed by the fast lockfile update even when another, unchanged override uses the
catalog:protocol. Previously anycatalog:-valued override forced a full re-resolution whenever the override list changed, which could move unrelated packages in the lockfile (for example afterpnpm audit --fixadded an override).Packed workspace package manifests now preserve dependency order, making repeated
pnpm packoutput deterministic #10167.pnpm update <name>@<version>now fails withERR_PNPM_UPDATE_VERSION_ON_INDIRECT_DEPwhen the package is not a direct dependency of any selected project, instead of quietly updating it to whatever a fresh install would resolve. There is nowhere to record the version in that case, so the request cannot be honored, and the error points at theoverridesentry that does pin a transitive dependency. Ranges and tags are unaffected, and a package that any selected project declares directly still takes its version as before.trustPolicy: no-downgradeno longer aborts the install withERR_PNPM_MISSING_TIMEon registries that serve no per-versiontimefield whenminimumReleaseAgeIgnoreMissingTimeis set. The trust check reads the same publish dates theminimumReleaseAgecheck does, so it now honors the same opt-in and skips the affected package with a warning #12446.minimumReleaseAgeIgnoreMissingTimeno longer lets a lockfile entry the registry does not list pass theminimumReleaseAgecheck during lockfile verification. The opt-in covers a registry that cannot date its releases; a packument that does date every version it lists is saying it never published this one, which stays a hard failure.The missing-
timewarning now names the check it is reporting on, so a package whoseminimumReleaseAgeandtrustPolicychecks are both skipped warns about both instead of only the first.pnpm update <pkg>@<version>now updates only the selected packages and leaves unrelated dependencies unchanged. A selector that renames the package it installs —pnpm update <alias>@npm:<pkg>@<version>or thejsr:equivalent — now targets the package the alias installs rather than the alias.Fixed
verifyDepsBeforeRunbeing ignored when set toinstall,warn,error, orpromptthrough thePNPM_CONFIG_VERIFY_DEPS_BEFORE_RUNenvironment variable or the--config.verify-deps-before-runflag #13816. Only the boolean values were accepted before, so a string value was silently dropped.pnpm version <bump>with--dry-runno longer editspackage.jsonfiles. It now only reports the bumps it would make, and skips the working tree check, the version lifecycle scripts, the commit, and the tagpnpm/pnpm#13953.Platinum Sponsors
Gold Sponsors
v11.22.0Compare Source
v11.21.0: pnpm 11.21Compare Source
Minor Changes
Added interactive group selection to
pnpm update --global --interactive.Running
pnpm setup,pnpm self-update, or a command that modifies the global installation (such aspnpm add --global) throughsudonow prints a warning. pnpm keeps global packages and configuration in the invoking user's home directory, so running these commands as root silently operates on the root user's home directory instead of yours. They will fail withERR_PNPM_SUDO_NOT_SUPPORTEDin pnpm v12. Read-only global commands (such aspnpm bin --global) are unaffected.Patch Changes
Fixed pnpm failing to start under asynchronous Node.js module loaders when no
.pnpmfile.mjsexists pnpm/pnpm#11701.Fixed
minimumReleaseAgefallback for custom dist-tags so the selected version does not exceed the registry’s original tag target.Removing a dependency from
package.jsonand reinstalling no longer re-resolves the dependency graph. The importer's entry is dropped frompnpm-lock.yaml, anything it made unreachable is pruned, and a catalog entry that loses its last referent is removed — all without registry access. Installs still fall back to a full resolution when a package that stays resolves a peer dependency through the removed one, since that would change the surviving package's entry rather than only prune.Changing a catalog entry to a different exact version no longer re-resolves the dependency graph. The package is replaced in
pnpm-lock.yamldirectly, reusing the same check thepnpm.overridesfast path applies: every locked dependency of the package must still satisfy the new version's manifest. Installs fall back to a full resolution when anything other than the catalog reaches the package — an importer that depends on it directly, or another package that depends on it — since the graph would then need both versions.Fixed a CI regression where
github:owner/repodependencies (and other shorthand Git specifiers) would fail to install withPermission denied (publickey)on CI runners that lack SSH keys. The Git resolver no longer records an SSH URL unless the user explicitly wrote one (e.g.git+ssh://orgit@host:...):429 Too Many Requests, so host throttling of CI runners is no longer mistaken for a private repository.git ls-remoteaccess is now tried before SSH, so a public repository whose visibility probe fails still resolves to a portable HTTPS URL instead of an SSH URL that only works where SSH keys are configured.codeload.github.com, which would fail to download for a private repository); it stays a regulargitresolution so installs can use ambient Git credentials such as credential helpers and tokens.Note that a private repository that is reachable both over authenticated HTTPS and over SSH now resolves to its HTTPS URL, where previous versions recorded the SSH URL.
Fixes pnpm/pnpm#13276.
ng buildandnuxt buildnow work under the global virtual store: pnpm's built-in compatibility extensions add thetslibdependency that@angular/builduses without declaring and theunplugindependency that@nuxt/vite-builderv4 uses without declaring.Fixed
link:dependencies underenableGlobalVirtualStoreso linked children are materialized and slots remain isolated by their resolved link targets.An install that skips resolution because
pnpm-lock.yamlis already up to date now reacts fully to packages the lockfile removed — for example after pulling a lockfile in which a dependency was deleted. The hoist layer is recomputed, so a package that became hoistable when a direct dependency was removed is hoisted, andpendingBuildsentries for removed packages are dropped instead of staying pending forever.The held-back-update warning printed by
pnpm updateno longer fires whenminimumReleaseAgeis the actual reason a newer version was not picked. The warning's baseline now applies the same maturity cutoff as the pick itself, so it no longer wrongly attributes the hold-back to "your manifests and already installed dependencies" or recommends an override that would defeat the age gate. See #13071.Checking whether
ignoredOptionalDependenciesis up to date no longer reorders the configured patterns. The check sorted them in place, which could move an!exclusion ahead of the pattern it excludes from and flip which optional dependencies were ignored.Changing
autoInstallPeers,dedupePeers,peersSuffixMaxLength,excludeLinksFromLockfile, orinjectWorkspacePackagesno longer re-resolves the dependency graph when the lockfile proves the setting cannot affect it: no package or project declares a peer dependency for the peer settings, and no project depends on a directory or on another workspace project for the link and injection settings. The new setting is recorded inpnpm-lock.yamland the install proceeds from the existing resolution. Every other case still falls back to a full resolution.Adding, editing, or removing an entry in
patchedDependenciesno longer re-resolves the dependency graph. Resolution never reads a patch — it only records the patch file's hash against the package it matches — so the install now rewrites the affected entries inpnpm-lock.yamland materializes the patched package from the store instead. Installs still fall back to a full resolution when the patched package is reachable as a peer dependency, and when the new configuration would leave a patch unused whileallowUnusedPatchesis off, soERR_PNPM_UNUSED_PATCHis still reported.Resolving a private git repository no longer blocks on an interactive credential prompt:
git ls-remotenow fails fast with an authentication error when git has no credentials for the repository #13522.Lockfile verification now honors offline mode by using cached registry metadata instead of reaching the registry. When the required metadata is not available locally, verification reports the same
ERR_PNPM_NO_OFFLINE_METAcondition used by offline resolution.POSIX shell shims now follow symbolic links before computing
basedir, preventing execution failures when a shim is invoked via an external symlink onPATH#13405.The automatic
packageManagerversion switch works again on registries whose tarball URLs point at a different host than the registry itself (load-balanced feed proxies, Artifactory-style mirrors). Package-manager entries are now always recorded with integrity-only resolutions — the download URL is derived from the trusted bootstrap registry instead — and entries persisted in an invalid shape by an earlier pnpm are discarded and re-resolved instead of failing every command #13619.Registries that serve no npm signature metadata (private mirrors and feed proxies commonly strip
dist.signatures) no longer break the automaticpackageManagerversion switch andpnpm self-update#13147. When the configured registry cannot provide a verifiable signature, pnpm now fetches the signature fromregistry.npmjs.organd verifies it against the same embedded npm keys over the installed integrity — which proves exactly the same thing. If no signature can be obtained from either source (for example, both are unreachable, or the registry publishes only ashasum), pnpm proceeds with a warning instead of failing, but only when the packages resolve through a registry configured in the user's own (non-project) configuration; the download stays pinned by the lockfile integrity, and a signature that exists but does not validate still fails the switch.pnpm fetch, and any install run withvirtualStoreOnly, no longer writes a.pnp.cjsloader undernodeLinker: pnp. These installs populate the virtual store without linking the project, so the loader would have claimed the project resolves out of a store it was never linked into. The importer links andnode_modules/.package-map.jsonwere already skipped; the PnP loader now follows the same rule.Prevent pnpm from removing project files when
modulesDirresolves to the project root.Speed up installs after adding
ignoredOptionalDependenciespatterns by removing newly ignored optional dependencies and pruning packages that are no longer reachable without resolving the dependency graph again.When a failed install re-copies a bin script from the store, rerunning
pnpm installnow reapplies the executable bit to the bin instead of leaving it non-executable #12742.pnpm root -gandpnpm bin -gnow print warnings to stderr instead of stdout, so their stdout stays a clean, machine-readable path. Previously, running either command with--globalin a project that pins a package manager (e.g. via thepackageManagerfield) printed a warning like[WARN] Using --global skips the package manager check for this projectahead of the path, breaking programs that capture the output as a path #13672.In pnpm 12,
pnpm root -gandpnpm prefix -gare now supported (they previously failed withERR_PNPM_CLI_ROOT_GLOBAL_UNSUPPORTED/ERR_PNPM_CLI_PREFIX_GLOBAL_UNSUPPORTED), and the reporter output ofdlx,create,config,sbom,with,store,prefix,root, andbingoes to stderr, matching pnpm 11.pnpm setupno longer makes Node.js print aMODULE_TYPELESS_PACKAGE_JSONwarning aboutdist/worker.json every command. Thepackage.jsonit writes next to a standalone executable now declares"type": "module".pnpm updatewithout saving no longer records a version that the manifest's range excludes. The kept range stays authoritative: a requested version outside it is skipped with a warning, and a requested range, a dist tag, or--latestresolves within it instead of past it. Previously each of these could write a lockfile entry that contradicted its own specifier, which the nextpnpm install --frozen-lockfilerejected withERR_PNPM_OUTDATED_LOCKFILE#12764.pnpm version -r --jsonnow outputs[]instead of human-readable text when no pending changes existpnpm/pnpm#13217.Platinum Sponsors
Gold Sponsors
v11.20.0: pnpm 11.20Compare Source
Minor Changes
Security fix. Affects projects using
namedRegistrieson pnpm 11.1.0–11.19.x. It is semi-breaking for those projects — see "If you use named registries" below.The lockfile recorded no marker for which registry a package came from. Packages were keyed by
name@versionalone, and entry lookup went throughrefToRelative(ref, name), so a dependency you declared against one registry could be satisfied by an entry that was actually resolved from another. When two registries served the same name and version, both collapsed onto a singlepackages:entry and whichever resolved first decided the tarball every consumer got.That is a package-substitution risk: a package you expect from your private registry could be installed from a different registry that publishes the same name and version, and the lockfile recorded nothing that would let you tell.
Packages resolved from a named registry are now recorded under registry-qualified keys (
<name>@<registryName>:<version>, e.g.foo@work:1.0.0), so each registry gets its own entry and the lockfile pins which one a dependency came from.The lockfile format version is unchanged. Registry-qualified keys appear only for packages resolved from a named registry, so a project that does not use
namedRegistriessees no difference, and older pnpm versions keep reading the file.If you use named registries
Your next non-frozen install re-keys those entries, which shows up as a lockfile diff. Commit it — that diff is the fix being applied. Review it: an entry that moves to a registry you did not expect is worth investigating.
Everyone working on the project should be on this version or newer before you do. An older pnpm reads the re-keyed lockfile fine — frozen installs are unaffected — but it does not produce registry-qualified keys itself, so any install that updates the lockfile writes those entries back to the old shape, and the next install on a current pnpm re-qualifies them. The result is a lockfile that flips back and forth, and while it is in the old shape the project is exposed again. Because the lockfile format version is deliberately unchanged, pnpm cannot detect this and warn you about it.
There is no setting to keep the old behavior: the old shape is the vulnerability.
Tarball URLs that follow the standard registry layout are no longer written to the lockfile for named-registry packages; they are recomputed from the
namedRegistriessetting on demand.To use named registries, map your aliases in
pnpm-workspace.yaml:New built-in
npmjs:aliasnpmjs:now resolves tohttps://registry.npmjs.org/with no configuration, alongside the existinggh:alias for GitHub Packages. It pins a dependency to the public registry even whenregistrypoints elsewhere, such as an internal proxy:{ "dependencies": { "left-pad": "npmjs:^1.3.0" } }npm:cannot do this — it is the alias protocol (npm:<name>@<range>) and resolves through whateverregistrypoints at.If you mirror or proxy npmjs, point the alias at your mirror:
Built-in registry URLs are also the prefixes a lockfile's recorded tarball URL is matched against when pnpm verifies a package. Without the override, an entry whose tarball URL is on
registry.npmjs.orgis verified against the public registry rather than your mirror. This only affects lockfiles that record such URLs — a canonical URL for your configured registry is omitted from the lockfile and unaffected — and only when a tarball-URL,minimumReleaseAge, ortrustPolicycheck runs. Overriding the alias is the same escape hatch GHES users already have forgh.Every alias the lockfile references must stay in
namedRegistries: reading an entry whose alias is gone fails withERR_PNPM_MISSING_NAMED_REGISTRYrather than silently falling back to the default registry, since that would fetch a different package. Renaming an alias re-resolves the packages that used it.Named registry aliases that shadow a reserved dependency specifier prefix (
file,link,workspace,runtime,npm,jsr, ...) are now rejected withERR_PNPM_RESERVED_NAMED_REGISTRY_NAMEinstead of being silently shadowed by the corresponding resolver.pnpm licensesandpnpm sbomnow keep the two artifacts apart as well: license records carry the registry alias, and SBOM components carry the purlrepository_urlqualifier.Patch Changes
An empty
http-proxy,https-proxy,proxy, orno-proxyvalue — from the.npmrc,pnpm-workspace.yaml, the CLI, or theHTTP_PROXY/HTTPS_PROXY/PROXY/NO_PROXYenvironment variables — no longer fails the install withERR_PNPM_INVALID_PROXY. Empty settings read as unset, so a shell exportingHTTP_PROXY=disables the proxy, and an emptyproxy=in the.npmrcno longer suppressesHTTPS_PROXY#13533.proxy=falsein the.npmrcorproxy: falseinpnpm-workspace.yamlnow turns proxying off instead of being read as a proxy host namedfalse.falseandnullonhttps-proxy/http-proxy/no-proxyread as unset, and on the command line they are ordinary host names, since a flag carries its value verbatim.The env lockfile no longer pins
@pnpm/exealongsidepnpmwhen the wanted pnpm version is 12 or newer. From v12 the unscopedpnpmpackage is itself the native executable, so@pnpm/exeis not published for it and resolving it would fail. The engine identity check now verifies the native binary through whichever package ships it.lexCompareandnerfDartare now published as@pnpm/text.ordinal-comparatorand@pnpm/config.registry-auth-key. Use these instead of@pnpm/util.lex-comparatorand@pnpm/config.nerf-dart.Fixed the order in which pnpm matches a lockfile's recorded tarball URL against known registry URLs. Two registry URLs of equal length were previously ordered arbitrarily, so which one a tarball URL matched could differ between runs.
Dependency resolution is faster: package metadata is now filtered once per packument instead of once per dependency edge when `minimumRe
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.