| Index | index by Group | index by Distribution | index by Vendor | index by creation date | index by Name | Mirrors | Help | Search |
| Name: pnpm-fish-completion | Distribution: openSUSE Tumbleweed |
| Version: 11.28.11 | Vendor: openSUSE |
| Release: 1.1 | Build date: Fri Nov 6 12:26:12 2026 |
| Group: System/Shells | Build host: reproducible |
| Size: 688 | Source RPM: pnpm-11.28.11-1.1.src.rpm |
| Packager: https://bugs.opensuse.org | |
| Url: https://pnpm.io/ | |
| Summary: Fish completion for pnpm | |
Fish command line completion support for pnpm.
MIT
* Fri Nov 06 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.28.11:
pnpm 11.28.11 fixes frozen installs with
excludeLinksFromLockfile, pnpm link in workspaces, pnpm deploy
with dangling symlinks, and verifyDepsBeforeRun starting needless
installs.
* Patch Changes
- Installing packages
- pnpm deploy now keeps a symlink in the project whose target
is missing from the source, such as a link into a
node_modules directory that was removed after the build.
Deploy used to fail with ENOENT on such a link #17571.
- A package with bundledDependencies and a build script now
keeps its bundled dependencies when it is restored from the
side-effects cache #17523.
- pnpm install now links the runtime that an earlier pnpm
install --no-runtime skipped #17542.
- Commands of a package whose bin paths contain excess parent
directory segments, such as ../dist/cli.js, are no longer
missing. Such paths now resolve inside the package #17586.
- Generated POSIX shell shims now quote their paths, so shell
special characters in a path stay literal.
- A repeat pnpm install now rewrites the bin shims after
extendNodePath changes. Before, it reported "Already up to
date" and the shims kept the NODE_PATH of the previous
setting.
- Lockfile and resolution
- pnpm install --frozen-lockfile now accepts the lockfile it
wrote with excludeLinksFromLockfile and
linkWorkspacePackages on, when a project depends on a
workspace package through a plain version range or a
catalog: entry. It used to fail with
ERR_PNPM_OUTDATED_LOCKFILE.
- pnpm install --frozen-lockfile, pnpm approve-builds, and
pnpm rebuild now accept a lockfile that resolves a *
dependency to a prerelease latest version. pnpm wrote such
a lockfile and then reported it as outdated #17594.
- Packages from the default registry keep plain lockfile
keys, even when a prefix such as npmjs points at the
registry set as registry. A dependency written with such a
prefix shares the plain entry of the same package. The next
install rewrites keys that earlier versions qualified this
way #17575.
- Relative local paths in root overrides now resolve from the
workspace root when each project has its own lockfile
[#17691].
- Installing a local directory dependency no longer fails
when another project's install is writing a lockfile in
that directory.
- Peer deduplication no longer gives a project different
copies of a dependency across direct and transitive paths
when an optional peer has no provider #17666.
- A Node.js version range such as >=26.13, for example in
devEngines.runtime, now resolves to the newest stable
release that satisfies it. pnpm picks a prerelease only
when no stable release matches #17516.
- verifyDepsBeforeRun
- With verifyDepsBeforeRun, pnpm run and pnpm exec now report
dependencies as out of date after a dependency is added to
the package.json of a local directory dependency, such as
"lib": "file:../lib". Before, the command ran without the
new dependency installed.
- verifyDepsBeforeRun no longer reports dependencies as out
of sync when only the version field of a single-project
repository changed #17560. The same holds for a workspace
project that no dependency can resolve to. A script run
after such a change no longer starts an install first.
- pnpm link
- In a workspace, pnpm link <dir> now adds the link:
dependency to the package.json of the project where the
command runs, not to the workspace root's package.json. The
link is created in that project's node_modules, as
documented. The override is still written to the root
pnpm-workspace.yaml.
- pnpm link with several paths now records every linked
package in the overrides of pnpm-workspace.yaml. Some of
them could be missing before.
- pnpm link now fails without changing package.json or
pnpm-workspace.yaml when the linked package's name is not a
valid npm package name. It used to write that name to both
files, which made every later install fail #17475.
pnpm - update
- pnpm update with only negated selectors, such as pnpm
update '!foo', now leaves the dependencies listed in
update.ignoreDeps unchanged pnpm/tasks#243.
- pnpm update --depth 0 without selectors now does nothing
when every direct dependency is listed in
update.ignoreDeps. Before, it failed with
ERR_PNPM_NO_PACKAGE_IN_DEPENDENCIES pnpm/tasks#251.
- Store and self-update
- pnpm store prune now keeps packages linked from a
configured modules directory pnpm/tasks#230.
- pnpm store status no longer reports untouched packages as
modified when the global virtual store is enabled. If the
installed global-store layout cannot be verified, the
command reports ERR_PNPM_STORE_STATUS_UNVERIFIABLE and asks
for a reinstall #17631.
- pnpm store add now fails right away with
ERR_PNPM_FROZEN_STORE_WRITE when frozenStore is enabled. It
used to retry each download for minutes before failing, or
fail with a SQLite error on an empty store.
- pnpm self-update now reinstalls a cached engine if its
executable cannot run. This repairs shared store entries
damaged by an earlier install #17582.
- update to 11.28.10:
pnpm 11.28.10 stops pnpm store prune from deleting other stores
when cacheDir points to their parent directory. It also fixes
globally installed commands that load hoisted dependencies, and
repeat installs that missed changes to local dependencies.
* Patch Changes
- Installing packages
- pnpm store prune preserves other stores and global package
data when cacheDir points to their parent directory.
- Commands installed with pnpm add --global now find the
dependencies that pnpm hoists for their package. A package
that loads a dependency it does not declare, such as a
native addon's platform-specific package, failed with
"Cannot find module". Reinstall such a package with pnpm
add --global to update its existing command #17503.
- pnpm prune --prod no longer fails with ERR_PNPM_EXDEV when
node_modules was copied from an earlier Docker build stage
[#17424].
- pnpm install now retries a Node.js runtime zip download
when the connection drops partway through. A download that
still fails names its URL. Before, a single dropped
connection ended the install with terminated #17411.
- With the global virtual store, a change to a dependency of
one package in a dependency cycle now gives every package
in that cycle a new directory. Before, the other packages
kept their directories and still linked the old copy of the
changed package, so the project could load two copies of it
[#17466].
Packages in a dependency cycle, and packages that depend on
one, move to new global virtual store directories once
after this update. Those that run a build miss the
side-effects cache once.
- pnpm install and pnpm rebuild now refresh privately hoisted
.bin shims in node_modules/.pnpm/node_modules/.bin after a
dependency build replaces a script target with a native
executable #17402.
- The Node.js latest selector now chooses the newest stable
release #17490.
- The standalone @pnpm/exe builds now embed Node.js 26.13.0.
- Repeat installs and lockfiles
- pnpm install now resolves a local directory again after its
package.json changes when an override or a package
extension adds it to the dependencies of a registry
package. pnpm install --frozen-lockfile fails on a lockfile
that still records the old dependencies of such a directory
[#17486].
- With injectWorkspacePackages and per-project lockfiles,
pnpm install now updates a project's lockfile when a local
tarball that an injected workspace project depends on is
replaced. The project's lockfile kept the old version and
integrity before.
- pnpm install now detects a change to a local tarball or
directory that only a pnpmfile readPackage hook depends on.
A repeat install reported "Already up to date" and kept the
old contents before pnpm/tasks#227.
- pnpm install now relinks a workspace: or link: dependency
whose link was deleted from node_modules. It used to report
"Already up to date" and leave the link missing #17485.
- Merging git branch lockfiles now keeps a workspace project
whose directory is named __proto__. It was dropped from the
merged lockfile before pnpm/tasks#59.
- Adding and publishing packages
- pnpm add no longer rewrites a catalog entry set to the
dist-tag that the dependency resolves through. For example,
an entry beta stays beta after pnpm add foo@beta or pnpm
add foo. Before, the entry was replaced with the version
the tag pointed to pnpm/tasks#225.
- pnpm pack and pnpm publish now always include the binary
file declared by a string bin field in package.json, even
when files or .npmignore would otherwise exclude it.
- When the version or range given to pnpm unpublish matches
every published version, the error now says so and shows
the same command with --force. It suggested running pnpm
unpublish <pkg>@<version>, the command that had just
failed.
- Running commands
- Every command, including pnpm --version, pnpm self-update,
and invocations with unknown flags, now applies the same
packageManager and devEngines.packageManager settings when
switching to the pinned pnpm version or recording it in
pnpm-lock.yaml. --config.<key> flags and lockfileDir now
reach the version switch and lockfile sync on every entry
point. Unreadable settings in pnpm-workspace.yaml or the
global config.yaml that do not affect the pin decision no
longer block the version switch pnpm/tasks#222.
- pnx and pnpm create now run the command in the directory
selected by --dir or --workspace-root. Without either flag,
the command still runs in the directory pnpm was started
in.
- The error for a dependency with an invalid name in a
project's manifest now names the project's directory. It
used to say only "The current package", which did not tell
which workspace project to fix #17475.
- update to 11.28.9:
This release fixes a crash on registry responses with too many
content encodings (GHSA-xjj6-f33q-929c) and sizes the install
worker pool by the container's CPU quota. A repeat pnpm install
now acts on more setting changes.
* Patch Changes
- Crashes and security
- A registry response with too many content encodings can no
longer crash pnpm. pnpm now uses undici 7.30.1, which fixes
GHSA-xjj6-f33q-929c.
- pnpm install now sizes its worker pool by the CPU quota of
the container it runs in, such as a Kubernetes CPU limit,
when that quota is smaller than the number of CPUs the
container may run on. The default workspaceConcurrency
respects the quota the same way. On a CI runner with a
4-CPU quota and hundreds of host CPUs, pnpm started
hundreds of workers and ran out of memory #17382.
- Repeat installs and changed settings
- A repeat pnpm install now acts on a change to these
settings. Before, it reported "Already up to date".
- storeDir or virtualStoreDir: it recreates node_modules.
- virtualStoreDirMaxLength: it renames the virtual store directories.
- dangerouslyAllowAllBuilds turned on: it runs the builds it skipped.
- trustLockfile turned off: it verifies the lockfile.
- pnpm add, pnpm remove, and pnpm update now fail when the
node-linker setting differs from the one recorded in
node_modules. A pnpm install of the whole workspace
recreates node_modules with the new node-linker, and an
install filtered to some projects fails pnpm/tasks#203.
- An install filtered to some workspace projects now fails if
node_modules was installed with a different nodeLinker,
hoistPattern, publicHoistPattern, store, or virtual store.
Before, it recreated node_modules and left the projects it
skipped without their dependencies. A pnpm install of the
whole workspace still recreates node_modules
pnpm/tasks#217.
- pnpm install now takes its fast path in a workspace with
patchedDependencies when the previous install ran with
another major version of pnpm.
- pnpm install now relinks the bins of a workspace package
when its bin field changes, including with
- -frozen-lockfile and an unchanged lockfile. A filtered
install relinks them in the selected projects, and the next
install that covers the remaining projects relinks them
there #17324.
- pnpm install now removes command shims from
node_modules/.bin when a workspace dependency renames or
removes an entry from its bin field pnpm/tasks#211.
- With nodeLinker: hoisted, an install that stops partway no
longer leaves the next install trusting the packages it was
replacing. Before, reverting the change that started the
stopped install, such as a patch edit, kept the
half-replaced package in node_modules.
- pnpm run and pnpm exec no longer report the dependencies as
out of date after a lockfile change when dedupePeers is
true #17322.
- Resolving dependencies and the lockfile
- A package that a plain range and a <prefix>: specifier both
resolve from one registry is now recorded once in
pnpm-lock.yaml and installed once. The lockfile key names
the registry a package resolved from. When that registry
declares a prefix, through prefix in registries or through
namedRegistries, the package is keyed
<name>@<prefix>:<version> however its dependency was
written: with the prefix, as a plain range its scope routes
to that registry, or through the default registry. Before,
the plain range was keyed <name>@<version>, so one tarball
was installed twice and instanceof checks and module
singletons broke between the two copies. The same name and
version served by two registries still get separate
entries. When several prefixes declare one registry, its
packages are keyed by the lexicographically smallest
prefix.
If a scope you route reaches a registry that also declares
a prefix, your next pnpm install re-keys those entries,
which shows up as a lockfile diff. Commit it. pnpm install
- -frozen-lockfile installs the lockfile as recorded.
- pnpm install now resolves a scope's packages again when the
scope is routed to another registry in registries. Before,
the lockfile kept the versions and integrity resolved from
the old registry, so the install failed when the new
registry did not serve a locked version or served different
content. pnpm records in the workspace state which registry
each scope routed to, so a fresh clone without that record
installs the lockfile as recorded. Changing the default
registry resolves nothing again, and pnpm install
- -frozen-lockfile is unchanged.
- An optional dependency written with a named-registry
prefix, such as work:^1.0.0, now fails the install when it
cannot be resolved but the lockfile already has a matching
entry from that registry. Such a dependency was skipped,
which removed its entries from the lockfile.
- With injectWorkspacePackages: true, a workspace: dependency
written as a relative path, such as workspace:../lib-a, in
a workspace package now resolves from that package's
directory #17284.
- Fixed pnpm install failing with
ERR_PNPM_UNSUPPORTED_PROTOCOL when a workspace package
injected through a relative path, such as workspace:../lib,
has catalog: dependencies. Their catalog entries now
resolve the way they do for a package injected by name
[#17280].
- pnpm install now applies the serverType registry setting
when it updates the lockfile for an override or catalog
change without a full resolution. pnpm wrote the tarball
URLs of a declared registry into the lockfile in that case.
- Added the pnpr value to the serverType registry setting.
pnpm leaves the tarball URLs of a pnpr registry out of
pnpm-lock.yaml.
- A registries entry whose prefix is __proto__ is no longer
ignored.
- Installs delegated to a pnpr server now resolve every
prefix declared for a registry URL. A dependency or
lockfile entry that used the second of two prefixes for the
same registry, such as zeta: next to work: in
namedRegistries, failed on the server pnpm/tasks#204.
- Registries, proxies, and downloading pnpm
- The proxy settings httpProxy, httpsProxy, noProxy, proxy,
and noproxy set in the global config.yaml, through
PNPM_CONFIG_HTTPS_PROXY and the matching variables, or as
- -config.httpsProxy on the command line now apply to pnpm
self-update and to the download of the pnpm version pinned
in packageManager. Previously, these requests read the
proxy only from .npmrc and the HTTPS_PROXY family of
variables pnpm/tasks#215.
- When https-proxy is false, pnpm now downloads the pnpm
version pinned by packageManager through the legacy proxy
setting, as installs already do. Setting proxy to false now
also stops that download from using the HTTP_PROXY
environment variable. The signature check of the pinned
pnpm now uses the same proxy and TLS settings as its
download pnpm/tasks#193.
- strictSsl and localAddress set in the global config.yaml,
through PNPM_CONFIG_STRICT_SSL and
PNPM_CONFIG_LOCAL_ADDRESS, or on the command line now apply
to pnpm self-update and to the download of the pnpm version
pinned in packageManager. These requests read them from
.npmrc only before. --config.localAddress now reaches them
in the camelCase spelling too pnpm/tasks#201.
- Commands
- pnpm audit --ignore and pnpm audit --ignore-unfixable now
exit with a non-zero code when vulnerabilities at or above
the audit level remain after ignoring. Before, these
commands always exited with code 0.
- pnpm audit --fix and pnpm audit --interactive now fail with
ERR_PNPM_AUDIT_FIX_WITH_IGNORE when combined with --ignore
or --ignore-unfixable. Before, the fix ran and the ignore
flags had no effect.
- pnpm ci --lockfile and pnpm ci --config.lockfile=true no
longer delete pnpm-lock.yaml. pnpm ci now always keeps the
lockfile and runs a frozen install from it.
- pnpm deploy now keeps a deployed symlink to a directory
inside the project even if the directory is not among the
deployed files, such as a link into node_modules #17288.
- pnpm pack and pnpm publish again include the files of a
publishConfig.directory that is reached through a symlink,
together with its bundled dependencies #17390.
- pnpm -r publish --batch now sends PUT /-/pnpr/v0/publish,
the batch publish endpoint that pnpr serves.
- pnpm store prune now keeps the packages that a project
still uses when pnpm copied or cloned them into
node_modules. These include patched packages, built
packages, and every package installed with
packageImportMethod: copy. Before, the prune removed them,
and the next install downloaded them again #7192.
- pnpm store prune now keeps the packages of a project that
was installed with a different symlinked spelling of the
store path. Previously, prune dropped such a project from
its list and removed the packages only that project used
from the global virtual store.
- Speed and output
- Sped up installs of projects whose local directory
dependencies share transitive local directory dependencies.
pnpm no longer checks a shared directory again for each
direct dependency that reaches it #17374.
- Output that fills the terminal, such as pnpm run --stream
script output, is now written once. Before, every new line
also rewrote all the lines already on screen #17387.
- The warning about unrecognized settings now marks
symlinkedLockfileTargetDirs as a setting of a newer pnpm
version.
- update to 11.28.8:
* Patch Changes
- The standalone @pnpm/exe builds now embed Node.js 26.12.1.
The previously embedded Node.js 26.0.0 still trusted the
certificate authority intermediates that Node.js distrusted
in October 2026, so a standalone pnpm accepted TLS
certificates issued under them.
- update to 11.28.7:
This release rejects registry metadata that names a different
package than the one requested, applies trustPolicy to the pnpm
version a project pins, and caps unpacked package size with
maxTarballUnpackedSize. It also removes support for registry
revisions, fixes verifyDepsBeforeRun after filtered installs, and
finishes commands with their own exit code when a pipe reader
exits early.
* Patch Changes
- Security
- pnpm now fails with ERR_PNPM_REGISTRY_NAME_MISMATCH if a
registry returns metadata for a package other than the one
requested. Before, pnpm installed the package under the
name in the metadata, so allowBuilds rules for the
requested package did not apply to it.
- pnpm now applies trustPolicy to the pnpm version a project
pins in packageManager or devEngines.packageManager. A
pinned version whose trust evidence was downgraded fails
the command before pnpm switches to it or records it in the
lockfile #16319.
- minimumReleaseAgeExcludePrune and trustPolicyExcludePrune
now keep exclusions for resolved configuration dependencies
and their transitive dependencies #17142.
- Added the maxTarballUnpackedSize setting. pnpm install
fails with ERR_PNPM_TARBALL_TOO_LARGE when the files of one
package unpack to more than this many bytes, 4 GiB by
default pnpm/tasks#81.
- Resolving dependencies
- Registry revisions are no longer supported. pnpm now fails
with ERR_PNPM_REGISTRY_REVISIONS_UNSUPPORTED on registry
metadata that selects a revision, on a +rN version
specifier, and on a lockfile entry with a revision field.
pnpm update --patches is removed.
- When pnpm install resolves a dependency again, it now
reuses a version that the install already resolved for the
same package, as a fresh install does. Before, it kept the
version the lockfile pinned and added a second copy of the
package. A locked version still stays when nothing else in
the install prefers another version. To pin a transitive
dependency on purpose, use overrides #17084.
- When another workspace project adds a different version of
a package, a project's auto-installed peer on that package
now moves to the version a fresh install picks. It used to
stay on its locked version while any other project still
accepted that version #17058.
- An optional peer that pnpm adds to a project now keeps the
version the lockfile recorded for that project only while
the dependency graph still holds it. Otherwise it follows
its provider to a version the graph holds. Before, pnpm
kept a higher version that only the lockfile held, which
left two copies of the peer #16443.
- A package that lists the same dependency in dependencies
and as an optional peer now gets the peer bound when a
project adds a provider for it later. The lockfile now
records every peer a package declares, including one its
own dependency supplies #17056.
- pnpm install --prod no longer drops a dependency that a
package declares both as an optional peer and as its own
dependency, when the package resolved it as its own
dependency and a dev dependency provides the same package
under another alias.
- pnpm now measures a pnpm.overrides entry written as a
workspace: path, such as workspace:./packages/foo, from the
directory holding pnpm-workspace.yaml. It used to be
measured from each package the override rewrote. The
install then linked a directory that does not exist, or
failed if workspace packages were injected #17010.
- pnpm install now removes config dependencies that are no
longer declared, including when the entire
configDependencies setting is removed. Frozen installs
reject a lockfile that still records removed config
dependencies.
- pnpm add now reads the package name correctly from a
tarball URL under a registry URL that has a path, such as
https://example.com/npm/. Before, the registry path became
part of the name, so a scoped package failed with
ERR_PNPM_INVALID_PACKAGE_NAME.
- pnpm add no longer warns that a gist dependency was
replaced from a different source when the new specifier
names the same gist in another form, such as gist:<id> and
https://gist.github.com/<user>/<id>.
- With minimumReleaseAge set, pnpm now revalidates a
package's cached full metadata with a single ETag request
so an unchanged registry answers with 304, and keeps using
the cached copy once the registry confirms it is current,
even when the metadata does not list a publish time for
every version. Previously, pnpm downloaded that metadata
again whenever the cached copy was older than
minimumReleaseAge. Packages listed in
minimumReleaseAgeExclude also resolve from cached metadata
without a registry request when the lockfile already has
the version their range picks.
- Linking node_modules and workspaces
- Concurrent installs that import the same package into one
project now all complete. One of them used to fail with
ENOTEMPTY when its copy of the package collided with the
copy another install had already placed pnpm/tasks#48.
- pnpm install no longer fails on macOS with EISDIR when it
creates store directories in a group-writable store under a
umask that removes owner read, such as 0o477.
- pnpm install now succeeds when node_modules is a symlink to
an existing directory. Dependency hoisting previously
rejected a symlinked node_modules as an invalid destination
[#17157].
- A filtered pnpm install --no-lockfile with
hoistWorkspacePackages now hoists every workspace project
into node_modules/.pnpm/node_modules. It used to hoist only
the selected projects pnpm/tasks#168.
- Filtered installs remove obsolete dependencies from the
installed lockfile while preserving dependencies still
needed by unselected projects.
- With sharedWorkspaceLockfile: false, pnpm install now runs
the preResolution hooks and custom resolvers and fetchers
of the workspace root's pnpmfile for every project, next to
the project's own. A global pnpmfile now runs once per
project, and each project's lockfile records the checksum
of the root pnpmfile when the project has none of its own.
Before, a project's own pnpmfile replaced these root hooks,
the global hooks ran twice, and edits to the root pnpmfile
could go unnoticed.
- pnpm prune now removes extraneous direct packages from
node_modules when package.json and pnpm-lock.yaml are
already in sync. pnpm prune also accepts --filter and
- -recursive to prune selected workspace projects without
removing dependencies of unselected projects.
- Injected workspace packages
- pnpm install --frozen-lockfile now fails with
ERR_PNPM_OUTDATED_LOCKFILE when the dependencies of an
injected workspace package or local directory dependency no
longer match the lockfile. This covers
sharedWorkspaceLockfile: false #17008, a dependency changed
to a different tarball URL or git commit #17129, and a
change in a transitive injected or local directory
dependency #17128.
- pnpm install --frozen-lockfile no longer fails with
ERR_PNPM_OUTDATED_LOCKFILE when an injected workspace
package depends on a local directory under another name,
such as "renamed": "file:../b" #17130.
- An injected workspace dependency now becomes a link when
its dependencies match the workspace package's own, also on
an install that leaves the package's installed dependencies
as they are. Before, such an install kept it as a copy
[#17094].
- An injected workspace package that was linked now becomes a
copy when the project that depends on it starts providing
one of its peers at a version the package's own workspace
context lacks. The link used to stay until that dependency
was updated, so the package could not find the peer the
project provides #17057.
- An injected workspace package now keeps only the files its
manifest lists after a lifecycle script or a script named
in syncInjectedDepsAfterScripts rebuilds it. Before, these
refreshes copied every file of the package directory into
the injected copy, including files that files or .npmignore
leave out. With deployAllFiles: true the copy still gets
every file.
- A script listed in syncInjectedDepsAfterScripts now keeps a
package's links to its own files as links in the injected
copies, the way pnpm install writes them. It no longer
fails when the package links to one directory twice or
links back to a directory above the link. pnpm install with
deployAllFiles no longer fails on such a link back either.
- A script listed in syncInjectedDepsAfterScripts now updates
the injected copies of the package it ran in when
sharedWorkspaceLockfile is false or the package publishes
from publishConfig.directory. Before, those copies kept the
files from the last install.
- Running scripts and checking dependencies
- When the reader of pnpm's output exits early, as in pnpm
outdated | head, pnpm now finishes the command and exits
with the command's own exit code pnpm/tasks#133. It used to
exit with code 0 at the first write to the closed pipe.
That hid the result of commands such as pnpm outdated and
stopped pnpm clean before it removed anything.
- With enableGlobalVirtualStore, a Node.js resolve hook that
a script registers now sees the importer as
context.parentURL after pnpm's NODE_PATH fallback fails to
find a bare specifier. Hooks that resolve tsconfig paths
aliases from the importer failed with ERR_MODULE_NOT_FOUND
[#16979].
- pnpm run, pnpm exec, and pnpm restart now pass the install
flags they were given, such as --lockfile-dir,
- -modules-dir, --store-dir, --offline, and
- -ignore-scripts, to the pnpm install that
verifyDepsBeforeRun: install starts. When that install
leaves node_modules out of date, pnpm now prints a warning.
- After pnpm install --filter, pnpm run and pnpm exec now
notice a changed manifest or lockfile in a project that the
filtered install left out, including the workspace root
after a filtered install with pruneDirectDependencies: true
pnpm/tasks#181. A filtered install also preserves the
recorded ignored build scripts of unselected projects, and
pnpm dedupe --check and pnpm install --dry-run no longer
record outdated dependencies as installed.
- The verifyDepsBeforeRun check now holds the workspace
projects that the selected project depends on, including
link: workspace dependencies. --filter=<project>... now
selects link: dependencies as well. With
sharedWorkspaceLockfile: false, verifyDepsBeforeRun also
reports dependencies as outdated when a selected project's
pnpm-lock.yaml is missing pnpm/tasks#138.
- verifyDepsBeforeRun and repeat pnpm install runs now apply
overrides before comparing a modified package.json with
pnpm-lock.yaml #17127. verifyDepsBeforeRun also checks
whether each importer's resolved catalog versions match the
catalog snapshot in pnpm-lock.yaml, and respects
lockfileDir in single-project repositories.
- verifyDepsBeforeRun now compares manifests, lockfiles, and
patch files by content hash. It notices an edit that kept
the file's modification time, as cp -p, rsync -a, or an
archive extraction leave it, and a patch file that was
copied or touched without changing its content no longer
marks dependencies as outdated #16988.
- Deploying and other commands
- pnpm deploy now records the peer dependencies of the
workspace packages it copies into the deployed lockfile.
Before, pnpm rejected that lockfile with --frozen-lockfile
when a deployed workspace package declared a peer and
injectWorkspacePackages was off #17093.
- pnpm install --frozen-lockfile and verifyDepsBeforeRun now
succeed inside a directory created by pnpm deploy. pnpm
deploy writes self-contained workspace and lockfile
settings, prunes unused patchedDependencies, and preserves
workspace-level dependency resolutions on deployed
directory dependencies.
- In workspaces with sharedWorkspaceLockfile: false, pnpm
patch, pnpm patch-commit, and pnpm patch-remove now resolve
patch files and patchedDependencies entries at the
workspace root. pnpm install checks configured patches
across all workspace projects before reporting unused
patches.
- Trusted publishing with OIDC now works with registries
whose URL has a path, such as
https://registry.example/npm/private/. pnpm previously sent
the token exchange request to the root of the registry
host.
- pnpm owner ls now lists the owners of a package on the npm
registry. It reported every package as not found.
- pnpm sbom now reads project lockfiles in workspaces with
sharedWorkspaceLockfile: false, including filtered and
split output, and reports the repository of a package whose
repository field is a gist: shorthand.
- pnpm licenses list reports no licenses for workspace
projects not recorded in the lockfile. It errors when
pnpm-lock.yaml is missing. Empty results now use the
requested text or JSON format pnpm/tasks#174.
- pnpm why now reads each selected workspace project's
lockfile and modules directory when sharedWorkspaceLockfile
is false pnpm/tasks#169.
- pnpm pkg fix now converts a repository string such as
github:pnpm/example to the object form, as npm pkg fix
does. It also normalizes a GitHub, GitLab, or Bitbucket
repository URL the way npm pkg fix does #16980.
- Configuration and setting up pnpm
- pnpm now recognizes externalDependencies and
autoInstallPeersFromHighestMatch in pnpm-workspace.yaml
without warning and forwards both settings to pnpm install.
Previously, setting externalDependencies in
pnpm-workspace.yaml with nodeLinker: hoisted crashed pnpm
install with externalDependencies.has is not a function
pnpm/tasks#166.
- pnpm now ignores a PNPM_CONFIG_* environment variable for a
URL setting, such as PNPM_CONFIG_REGISTRY, when its value
is not a URL. Such a value used to make every command fail
with Invalid URL.
- The warning about unrecognized settings in
pnpm-workspace.yaml and the global config.yaml now names
addMissingPeerTypes, autoDedupe, bootConfig, macosBackup,
nodePackageMap, permissions, pipelineBase,
publishWaitTimeout, python, remoteCache, saveTypes, skills,
storeIndexFormat, and tools as pnpm v12 settings
pnpm/tasks#167.
- When a config file has a setting pnpm cannot read, pnpm
- -version now prints the version, and pmOnFail and
lockfileDir in pnpm-workspace.yaml stay in force.
Previously, pnpm switched to the pinned version despite
pmOnFail: ignore. pnpm also no longer writes the pinned
version to pnpm-lock.yaml when it cannot read every setting
and the running pnpm already matches the pin.
- pnpm now records the pinned pnpm version in the
pnpm-lock.yaml under the lockfileDir set in
pnpm-workspace.yaml, and resolves a relative lockfileDir
from the workspace root. pnpm used to write a second
pnpm-lock.yaml at the workspace root.
- pnpm self-update now updates the project's pnpm pin when
Homebrew installed pnpm or Corepack runs it. The global
pnpm is left alone #17001.
- pnpm setup now completes when Windows registry environment
values cannot be read or written. It prints PowerShell
commands to configure PNPM_HOME and PATH without registry
access #17167.
- pnpm setup now succeeds when it cannot tell which shell
configuration file to update, for example in a container
where SHELL is not set. It installs pnpm and prints the
PNPM_HOME and PATH lines to add yourself. Previously it
failed with ERR_PNPM_UNKNOWN_SHELL, and so did the
installer script at get.pnpm.io #16973.
* Sat Oct 17 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.28.6:
This release stops pnpm publish from sending a CI identity token
to a registry it was not issued for. It also fixes installs that
dropped a dependency also declared as an optional peer, and
accepts empty httpProxy and httpsProxy settings again.
* Patch Changes
- Security
- pnpm publish now uses an NPM_ID_TOKEN only if its aud claim
matches the selected registry, such as
npm:registry.npmjs.org. A token for another registry is
skipped with a warning, and pnpm falls back to the
configured credentials.
- The warning about renamed package files with invalid names
no longer prints control characters from those file names.
- Configuration
- pnpm again accepts httpProxy and httpsProxy set to null or
written with no value in pnpm-workspace.yaml and the global
config file, and treats them as unset. Since 11.28.5 these
values failed with ERR_PNPM_INVALID_SETTING.
- pnpm now switches to the pnpm version a project pins even
when it cannot read its configuration. Previously, a
setting it rejected in the global config file, an .npmrc
file, '_auth', or pnpm-workspace.yaml stopped every
command, pnpm --version included.
- A key under a registry URL in the '_auth' setting that does
not start with @ is now skipped with a warning, so a field
a later pnpm version adds there no longer breaks this one.
A key that holds an authToken object but lacks its @, such
as org for @org, is still an error.
- Resolving dependencies
- pnpm no longer drops a dependency that a package also
declares as an optional peer dependency, for example ajv of
ajv-formats. Since 11.28.0, in some dependency graphs the
package got the dependency neither as a dependency nor as a
peer, and failed to import it #16901.
- pnpm install no longer fails with
ERR_PNPM_NO_MATCHING_VERSION after a change to overrides
when the lockfile resolves an optional peer dependency to
an npm alias of another package #16654.
- pnpm install and pnpm dedupe now link an optional peer to
the workspace package that the workspace root depends on
when the picked version matches it. Previously they
installed the registry package with the same name and
version #16706.
- Removal overrides such as "debug>supports-color": "-" now
also apply to an optional peer that a package declares only
in peerDependenciesMeta #16681.
- pnpm dedupe now preserves a hoisted workspace package when
another package uses a separately installed peer with the
same name #16935.
- pnpm install and pnpm add --config now apply
minimumReleaseAge when they resolve a config dependency. A
config dependency range resolves to the newest version that
is old enough, so a later clean pnpm install
- -frozen-lockfile accepts the lockfile #16660.
- pnpm install --fix-lockfile no longer removes the
deprecated and hasBin fields from lockfile entries #6600.
- Workspaces and catalogs
- A filtered install, such as pnpm install --filter <pkg>,
now keeps the pnpm-lock.yaml entries of workspace projects
that are missing from disk, as in a sparse checkout. Their
importers, packages, and catalog entries stay even when the
filter matches every project on disk #16931.
- catalogPrune no longer removes catalog entries that
pnpm-lock.yaml still records for workspace projects missing
from disk. This affected pnpm remove and installs that
leave the lockfile unchanged, such as frozen installs.
Before, a following frozen install failed with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH #16679.
- Sped up workspace discovery with subtree exclusions such as
!generated/**.
- Linking node_modules
- pnpm install no longer fails on Windows with EPERM on rmdir
node_modules/@scope when several workspace packages under
one scope are hoisted, for example with nodeLinker: hoisted
[#16690].
- pnpm install hardlinks files from a group-writable or
world-writable store again. Before this fix, pnpm copied
every file from such a store into node_modules #16677.
- With enableGlobalVirtualStore, an install that updates
node_modules now restores the dependency links of a package
in the global virtual store that an interrupted install
left without them. Before, such an install kept the
incomplete package if the project's node_modules already
recorded it #16642.
- Removing a link: dependency now also removes its symlink
from node_modules when nodeLinker is hoisted #16884.
- An install that adds nothing to the store no longer changes
the modification time of the store directory. CI caches
that check modification times, such as GitLab's, no longer
re-upload an unchanged store #16929.
- Injected workspace packages
- With sharedWorkspaceLockfile: false, an injected workspace
package installed in the same run as its dependent now
holds only the files its files field selects. The copy used
to also hold other files of the project, such as
tsconfig.json #16683.
- A script listed in syncInjectedDepsAfterScripts no longer
fails when it rewrites package.json while pnpm is copying
its edits into the injected copies. The sync after the
script now replaces a half-copied manifest.
- Sped up frozen installs of unchanged injected workspace
packages that use hardlinks.
- Deploying
- pnpm deploy without injectWorkspacePackages now binds a
peer dependency of a linked workspace package to the
version its parent provides, as an injected install does.
Previously the deploy failed with
ERR_PNPM_DEPLOY_AMBIGUOUS_PEER whenever other packages in
the deployed graph depended on different versions of that
peer #16807.
- pnpm deploy in a workspace with injectWorkspacePackages no
longer crashes when a linked workspace package declares a
peer dependency named after an Object.prototype member,
such as constructor.
- Setting up pnpm
- pnpm now relinks the bins of a pnpm version it installed
earlier for packageManager or pnpm with. A pnpm 12 version
installed by a pnpm release older than 11.28.4 could keep a
launcher that ran node on the native binary, so every
command failed with SyntaxError: Invalid or unexpected
token #16646.
- pnpm setup no longer tries to install pnpm globally from
npm's bin directory when pnpm was installed with npm. It
printed Failed to create bin at <PNPM_HOME>/bin/pnpm
[#16855].
- Output
- Escape codes in script output that moved the cursor or
cleared the screen garbled pnpm's output. pnpm now drops
them and keeps only color codes. Color codes are dropped
too when pnpm's own output is not in color.
- When the registry rejects pnpm stage publish, the error
message now starts with "Failed to stage package".
* Sun Oct 11 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.28.5:
This release reads cached registry metadata faster and makes pnpm
config get --global ignore project settings. It also carries
several security fixes for package archives, git dependencies,
and config dependencies.
* Patch Changes
- Security
- pnpm now verifies locked config dependencies against their
registry before installing them. Config dependencies must
come from an npm registry. The lockfile can no longer
replace the integrity of a config dependency pinned with
version+integrity.
- Lockfile verification now checks the tarballs inside a
variations resolution against the registry. A name@version
lockfile entry with an empty variations resolution is now
rejected.
- pnpm audit signatures now verifies signatures against the
integrity recorded in the lockfile. Packages without a
recorded integrity cannot pass signature verification.
- pnpm now rejects a git dependency whose lockfile repository
is empty, begins with -, or contains a null byte. Git can
no longer read such a value as a command-line option
pnpm/tasks#84.
- A git dependency with a #path: subpath can no longer reach
files outside the repository through a symlink in the
subpath.
- pnpm pack, pnpm publish, and installs of git and local
directory dependencies now leave out files that a directory
symlink or a bundleDependencies entry points to outside the
package directory pnpm/tasks#83 pnpm/tasks#93.
- pnpm deploy with deployAllFiles now rejects symlinks that
point outside the package directory. Local package installs
with this setting apply the same check.
- pnpm no longer hangs on a package archive with a negative
PAX record length or an entry of 4 GiB or more
pnpm/tasks#78 pnpm/tasks#79.
- Large package downloads and large files inside gzip and
bzip2 package archives now use bounded memory during
installation. Package manifests and archive metadata larger
than 64 MiB are rejected. pnpm publish also rejects
manifests and README files larger than 64 MiB in pre-built
tarballs before reading them into memory.
- Two URL or local path dependencies no longer share a
virtual store directory when one URL has +, #, :, or ?
where the other has /. Such dependencies, including git
dependencies pinned with #, now get a hash suffix on their
directory name.
- pnpm licenses now removes terminal control characters from
package metadata in table output.
- The warnings about ignored project .npmrc registry and auth
settings no longer print the username and password of a
URL-scoped key such as
//user:password@registry.example.com/:_authToken.
- Installing and resolving dependencies
- Dependency resolution reads cached registry metadata
faster. The metadata cache moved to <cache-dir>/v12/, so
the first install after upgrading downloads registry
metadata again. A damaged cache entry is downloaded again,
or reported as an error when --offline is set #13512.
- pnpm install now fails with ERR_PNPM_UNSUPPORTED_PROTOCOL
when a dependency uses a specifier with a protocol pnpm
does not support, such as Yarn's patch:. pnpm linked such a
dependency to a directory that does not exist #16590.
- When a dependency moves an exact dependency of its own to
an older version, a peer dependency that pnpm installed
automatically now moves with it. Before, pnpm install and
pnpm dedupe kept the newer locked version of the peer, so
the lockfile held two copies of it, for example two copies
of vue pnpm/tasks#61.
- pnpm add and pnpm install now keep the peer dependencies
that pnpm-lock.yaml records for a package they did not
update. A registry whose metadata disagrees with the
package's package.json, for example by omitting
peerDependenciesMeta, made pnpm add and pnpm dedupe write
different lockfiles, so pnpm dedupe --check failed after
pnpm add #16615.
- Configuration
- pnpm config get and pnpm config list with --global or
- -location=global now show only the global configuration.
Both flags included the project's .npmrc before.
- -location=global also included the project's
pnpm-workspace.yaml. pnpm config get --global failed when
the global bin directory was not in PATH #16598.
- pnpm now prints config warnings, such as an unset
environment variable in .npmrc, when loading the config
fails.
- pnpm now fails when httpProxy or httpsProxy in
pnpm-workspace.yaml or the global configuration is not a
string.
- pnpm dlx now uses the release entry of nodeDownloadMirrors
from the workspace configuration when downloading Node.js
runtimes #11281. Mirrors for other channels, such as rc and
nightly, still apply only from the global configuration.
- Commands
- pnpm run and pnpm exec now forward --config.* command-line
flags to the install started by verifyDepsBeforeRun
pnpm/tasks#60.
- pnpm dlx with --package but no command now fails with 'pnpm
dlx' requires a command to run. Before, it installed the
package and then crashed trying to run an empty command.
- pnpm unpublish <pkg>@<version> now deletes the tarball
under the registry's path when the registry is served under
one, such as Gitea's npm registry. It used to send the
delete to the host root and report success without removing
the version #16568. It also no longer mistakes a sibling
path such as /npm-mirror/ for the registry path /npm/
pnpm/tasks#94.
- The interactive pnpm audit --fix picker now shows each
patched version with the saveExact and savePrefix style
that the override is written with #13209.
- pnpm list now reports the correct package paths when
nodeLinker is hoisted #9593.
- pnpm setup now puts $PNPM_HOME/bin first on PATH in login
shells that inherited it further down, such as the VS Code
terminal on macOS. Before, another node took precedence
over the one installed by pnpm runtime set node -g. Run
pnpm setup again to update the block in your shell config
[#16635].
- pnpm setup now names the shell config file even if it is
already up to date #16608.
- Output and messages
- A warning about a project's devEngines or packageManager
pin is now printed to stderr. A command such as pnpm cache
path or pnpm list --json keeps only its own output on
stdout #16584.
- The warning for a non-root resolutions field now points at
the overrides field in pnpm-workspace.yaml #11757.
- update to 11.28.4:
pnpm 11.28.4 fixes two ways credentials could leak, makes pnpm
install --frozen-lockfile accept several lockfiles it rejected,
warns when an optional dependency cannot be fetched, and stops
pnpm self-update from installing a second pnpm next to a Homebrew
one.
* Patch Changes
- pnpm login no longer forwards credentials in its request body
to another origin during redirects.
- The error for a tarball that fails its integrity check no
longer prints credentials, query strings, or fragments from
the tarball URL.
- Installing packages
- pnpm install --frozen-lockfile now succeeds in a project
with no dependencies when pnpm-lock.yaml records only the
pinned pnpm version. Other commands write such a lockfile
when they run before the first install. A lockfile missing
the --- line after that section is accepted too #16477.
- pnpm install --frozen-lockfile again succeeds when a
workspace project recorded in pnpm-lock.yaml has no
directory, such as a project left out of a Docker build
context. It still fails if the project's directory exists
without a package.json #16453.
- pnpm install --frozen-lockfile no longer fails with
ERR_PNPM_OUTDATED_LOCKFILE for a workspace project that
declares dependenciesMeta and whose dependencies are all
workspace links. pnpm now records that project's
dependenciesMeta in pnpm-lock.yaml #16457.
- Fixed frozen installs replacing a hoisted dependency with a
workspace package of the same name. A later pnpm dedupe
then removed the hoisted link #16485.
- With enableGlobalVirtualStore on, scripts can run entry
points that a CommonJS require hook loads again, such as
ts-node index.ts. They failed with
ERR_UNKNOWN_FILE_EXTENSION on Node.js versions without
built-in TypeScript support #16436.
- Optional dependencies
- pnpm install now prints a warning with the error when an
optional dependency cannot be fetched and is skipped. The
skipped package is no longer linked into node_modules as a
broken symlink or listed among the added dependencies. The
pnpm:skipped-optional-dependency log reports the skip with
the fetch_failure reason #16514.
- When an optional dependency fails to build, pnpm now
removes its link from node_modules. A repeat pnpm install
then reports "Already up to date" and no longer reruns the
failing build #16468.
- Fixed frozen installs creating symlinks to the working
directory for skipped optional dependencies and unresolved
peer dependencies #16454.
- Hoisted node_modules
- With nodeLinker: hoisted, a filtered install now keeps the
packages of the workspace projects an earlier install put
in node_modules. This also covers the install that pnpm
- -filter <selector> run and pnpm --filter <selector> exec
start before the command. Before, these installs removed
every package that only the unselected projects needed
[#16483].
- A filtered install of a workspace project also no longer
fails with ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY after a
filtered install of another project.
- pnpm install with nodeLinker: hoisted now refreshes
directories supplied by custom fetchers when reinstalling.
- Speed and network
- pnpm now revalidates cached registry metadata with a
conditional request, so the registry can answer 304 Not
Modified. Before, pnpm downloaded the whole document again
for registries whose responses forbid caching, such as
Cache-Control: no-store #16528, and for packages published
within minimumReleaseAge #16506.
- A fetch timeout while other downloads from the same host
are still running now lowers concurrency for that host to
one connection. Retries of that request, and later
downloads from that host, use the lower concurrency. Other
hosts keep the configured concurrency #12791.
- Running scripts
- Scripts run without a terminal no longer start a second sh
each. One watchdog per pnpm command now ends every script's
process group if pnpm is killed, so pnpm -r run across many
projects starts half as many processes #16489.
- pnpm run and pnpm exec now warn and run the command when
the install that verifyDepsBeforeRun starts fails. This
lets scripts run in sandboxes where pnpm cannot install,
such as containers with a read-only store or no network
[#15173].
- A filtered pnpm run or pnpm exec now finds dependencies out
of date when a workspace dependency of a selected project
has no node_modules directory, as after a filtered install.
With verifyDepsBeforeRun: install, pnpm installs that
dependency before running the command pnpm/tasks#45.
- pnpm rebuild and pnpm approve-builds refresh command
launchers when a build changes a command's interpreter or
replaces it with a native executable. Dependent packages'
build scripts use the refreshed launchers.
- Updating pnpm
- pnpm self-update now fails for Homebrew-installed pnpm and
prints the brew upgrade command for the installed formula,
such as brew upgrade pnpm or brew upgrade pnpm@11. It used
to install a second copy of pnpm that the Homebrew one kept
shadowing #16547.
- pnpm can now switch to a packageManager version below 11 on
x64 musl Linux, such as Alpine #16467.
- A devEngines.packageManager range now records the running
pnpm in pnpm-lock.yaml only if it meets minimumReleaseAge.
Otherwise pnpm records the newest version in the range that
meets it. If no version in the range does, pnpm still
records the running pnpm #16431.
- Filtering, settings, and other commands
- The [<since>] filter selector works again with Git 2.24
through 2.27 #16561. With Git older than 2.24, the selector
now fails with an error that names the required Git
version.
- Package-name filters now support ? to match one character
[#2817].
- pnpm -r pkg get now reports every selected project when
several share a package name. Projects with the same name
are keyed by their directory relative to the workspace
root. Before, only one of them appeared in the output.
- pnpm now reports an INVALID_SETTING error when
allowUnusedPatches in pnpm-workspace.yaml is not a boolean,
or when ignoredOptionalDependencies or requiredScripts is
not an array of strings. A quoted allowUnusedPatches value
such as "false" was treated as true.
- pnpm store path, pnpm store status, and other commands that
look up the default store no longer fail when the current
directory is not writable. pnpm now uses the store in the
pnpm home directory in that case #16554.
* Sat Oct 03 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.28.3:
pnpm 11.28.3 updates undici to clear a security advisory, fixes
"database disk image is malformed" errors when several pnpm
processes share a store, and makes packages, catalogs, projects,
and commands named like constructor work.
* Patch Changes
- Installing packages
- pnpm now ships undici 7.29.1, so security scans of pnpm no
longer report GHSA-3wwx-pv8p-q78v.
- pnpm no longer fails with "database disk image is
malformed" or reads stale store entries while another pnpm
process writes to the same store.
- Names that match built-in JavaScript object properties,
such as constructor, toString, or __proto__, now work like
any other name. pnpm crashed, wrote a wrong lockfile, or
silently skipped such names in:
- pnpm add, pnpm install, and pnpm import, for
dependencies, peer dependencies, and file: dependencies
that point to a directory named constructor.
- Catalog entries and catalog names. Pruning unused entries
crashed, and a new catalog named toString was not
written.
- Workspace projects, project directories, and files inside
injected packages.
- Registry prefixes and override version references such as
$toString.
- Hoisting, pnpm list, and pnpm why.
- Command names. pnpm constructor runs the constructor
script like any other unknown command, and pnpm help
constructor no longer crashes.
- Resolving through a pnpr server when a project lives in a
directory named constructor.
- pnpm install no longer re-resolves an up-to-date lockfile
on every run when a patched package is a peer in a peer
cycle #16418.
- POSIX bin shims and the pnpm, pn, pnpx, and pnx launchers
now run inside a Nix build, where the system default path
holds none of the utilities they call. Installing again
replaces the shims already in node_modules #16377.
- In a project that pins another pnpm version, pnpm now
passes a command with an option it does not know to the
pinned version. Before, pnpm install --auto-dedupe failed
with "Unknown option" even though the pinned pnpm supports
it #16353.
- pnpm now fails with ERR_PNPM_INVALID_ALLOW_BUILDS when
allowBuilds is not an object or one of its values is not
true, false, or a string. Such values used to be ignored
silently.
- Removing a dependency whose bins are declared through
directories.bin no longer leaves broken shims in
node_modules/.bin.
- A custom resolver's shouldRefreshResolution hook that
rejects no longer crashes pnpm with an unhandled rejection
when another hook has already asked for a refresh.
- Updating dependencies
- When minimumReleaseAge hides the version that latest points
to, pnpm now falls back to a prerelease of the same major
before a stable version of an older major. A stable version
of the same major is still preferred. For example, while a
new 1.0.0 is too new, pnpm picks 1.0.0-beta.4 rather than
an old 0.0.1 #16388.
- pnpm --filter <project> update <pkg> now fails with
ERR_PNPM_NO_PACKAGE_IN_DEPENDENCIES when the selected
projects do not depend on <pkg>, also in a workspace with a
shared lockfile and a root project. It used to exit
successfully.
- pnpm audit --fix now updates vulnerable packages in a
single project that sets updateConfig.ignoreDependencies.
It used to leave them on the vulnerable version.
- pnpm update --global now removes hard-linked executables
from PNPM_HOME when migrating packages from the old global
layout #16420.
- Updating a pinned GitHub Action now rewrites the version in
its # vX.Y.Z comment even when the action name contains the
same version text. The action name used to change while the
comment kept the old version.
- Workspaces and deploy
- pnpm deploy no longer fails with
ERR_PNPM_DEPLOY_AMBIGUOUS_PEER in a workspace with
injectWorkspacePackages: true when a workspace package also
lists its peer dependency as a dev dependency #16375.
- pnpm deploy no longer copies the workspace root's
packageManager and devEngines.packageManager fields into
the deployed package.json #16403.
- --filter fixes:
- A ...pkg... selector combined with another dependents
selector, such as --filter ...a --filter ...b..., no
longer adds the dependencies of the other selector's
dependents.
- --filter "[<since>]" now detects changes in projects
whose directory names contain non-ASCII characters. The
change used to be credited to the parent project.
- Running scripts
- After relaying a signal to a script, pnpm keeps waiting for
a process in the script's process group whose main thread
has exited while its other threads still run. Linux reports
such a process as a zombie, so the wait used to end before
those threads finished pnpm/tasks#56.
- A lifecycle script run with unsafePerm: false now fails
with an error when pnpm cannot create node_modules/.tmp. It
used to hang.
- pnpm run with verifyDepsBeforeRun no longer crashes with an
unhandled rejection when a lockfile it did not need to
compare fails to load.
- pnpm run -r now closes the collapsible CI log section of a
project whose script fails, so the output of later projects
is no longer nested inside it.
- pnpm run --resume-from no longer crashes when a saved run
state file contains null.
- Store
- pnpm store prune now removes the packages that only expired
pnpm dlx cache entries used, as long as the store still has
another registered project. They used to stay until the
next pnpm store prune #16383.
- pnpm store prune now stops with an error when it cannot
read a project directory for a reason other than the
directory missing, such as a permission error. It used to
skip the directory.
- Publishing and registry output
- pnpm publish now includes bare README files and README
files with Markdown extensions such as readme.markdown in
registry metadata #12704.
- pnpm pack-app now accepts an entry file or output directory
inside the project whose name starts with two dots, such as
..build/entry.cjs. It used to fail with
ERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT.
- Registry error messages now always say "(response body
truncated)" when pnpm cut the response body short. The
marker was missing when the body was cut at exactly 64 KiB.
- update to 11.28.2:
pnpm 11.28.2 fixes pnpm install skipping every workspace project
whose common ancestor is the filesystem root, and stops pnpm run
from reinstalling or installing when nothing needs it.
* Patch Changes
- pnpm install reported success without installing anything
when the workspace projects' common ancestor was the
filesystem root, such as / or a drive root like C:\. It now
installs these projects #16328.
- verifyDepsBeforeRun no longer reports dependencies as
outdated after a filtered install just because pnpm-lock.yaml
has a newer modification time. It checks the lockfile against
the packages that install put in place. Before, pnpm run
reinstalled the whole workspace with lifecycle scripts on,
for example after a Docker COPY brought in a lockfile with a
newer mtime #16322.
- After a filtered install, verifyDepsBeforeRun now also checks
that the install put the selected projects' dependencies in
place. A node_modules directory alone no longer counts as
proof.
- pnpm run and pnpm exec no longer install a project that has
never been installed and has nothing to install. Such a
project declares no dependencies, no peer dependencies that
autoInstallPeers would fetch, and no install lifecycle
scripts. The command now runs without writing node_modules or
pnpm-lock.yaml #16313.
- update to 11.28.1:
pnpm 11.28.1 makes pnpm install work in StackBlitz WebContainers,
repairs lockfiles whose patch hashes disagree with
patchedDependencies, and fixes many bugs in Windows command
shims, injected workspace dependencies, and pnpm deploy.
* Patch Changes
- Installing packages
- pnpm install now works in StackBlitz WebContainers. On
projects without a lockfile, it used to fail with ENOENT
... pnpm-lock.yaml, because pnpm did not recognize the
errors that WebContainers return from asynchronous file
system calls. It also failed with "this.db.exec is not a
function", because node:sqlite there lacks
DatabaseSync.exec. When node:sqlite cannot prepare
statements either, pnpm stores the index in index.fallback
[#15649].
- pnpm install now completes after downloading a Node.js
runtime specified by devEngines.runtime when pnpm runs on
Node.js 24.4.x #14667.
- pnpm install no longer fails when a package from the
registry declares a file: dependency on a directory inside
itself, such as "@types/css-tree":
"file:./typings/css-tree". pnpm links that dependency to
the directory inside the package, as npm and Yarn do. The
lockfile records it as link:<root>/typings/css-tree #9141.
- Installing a git-hosted dependency that has to be built no
longer fails when that dependency's own dependencies have
build scripts nobody approved. pnpm skips those builds
while preparing the dependency, as it does without
strictDepBuilds #9764.
- pnpm now uses pnpm to prepare a git-hosted dependency that
is a pnpm workspace without a committed lockfile. It used
npm before, which could skip the dependency's build #14011.
- When installing a git dependency over SSH fails with
Permission denied (publickey), pnpm suggests checking the
loaded keys with ssh-add -l. Resolving an SSH URL that
refuses the key also shows a local HTTPS rewrite that
leaves the recorded URL alone #13743.
- pnpm install --dev and pnpm fetch --dev now install the
optional dependencies of devDependencies, such as the
platform binaries of Biome and oxlint. The project's own
optionalDependencies are still skipped #9678.
- pnpm install --frozen-lockfile now works on a detached HEAD
when gitBranchLockfile is enabled. The install now reads
the lockfiles of the local and remote-tracking branches
that contain the checked-out commit. It still writes the
shared pnpm-lock.yaml #7672.
- pnpm install on CI now fails on an outdated lockfile when
preferFrozenLockfile is explicitly set to true. Setting it
to true used to let CI update the lockfile #9072.
- pnpm install now fails with ERR_PNPM_IGNORED_BUILDS on a
repeat install when strictDepBuilds is on and a
dependency's build is still undecided. A repeat install
against an existing node_modules reported success where a
fresh install failed #10450.
- pnpm install now removes an optional dependency from
node_modules if its install script fails. Code that checks
whether the package is installed no longer finds a package
that cannot load #8756.
- pnpm install --offline and pnpm add --offline now resolve a
version range to the newest matching version whose tarball
is already in the store. They used to pick the newest
version in the cached metadata and fail with
ERR_PNPM_NO_OFFLINE_TARBALL when its tarball was missing
[#10715].
- If an offline install fails because the registry metadata
cache uses the layout from before pnpm 11.27 and 12.4, the
error now names the older mirror on disk and explains that
one online install repopulates the cache #15656.
- pnpm install now fails right away when writing package
files fails because the store is full. It no longer retries
the tarball download first #8581.
- With nodeLinker: hoisted, pnpm install now restores a
workspace project's node_modules after it was deleted.
Before, the install printed "Already up to date" and left
the project without the dependencies nested under it.
- Under nodeLinker: hoisted, pnpm install now clears orphaned
package directories that an interrupted or failed install
leaves in a project's node_modules. A directory recorded by
the previous install is removed, while an unrecorded
directory is moved to node_modules/.ignored. A copy already
in .ignored is never overwritten #13676.
- Packages in an external virtualStoreDir can resolve the
project's direct dependencies selected by hoistPattern. Run
pnpm install --force to repair an existing installation
[#5652].
- A repeat install now keeps the fast path when a declared
local file dependency is replaced by an override #12892.
- Store, build cache, and global virtual store
- Files imported from the store now follow the umask of the
install that writes them. Installing with a umask of 077 no
longer leaves imported files readable by the group and
others #3807.
- With the global virtual store, pnpm rebuild no longer
modifies packages shared with projects that have not
approved their build scripts #12302.
- The side-effects cache now restores the symlinks that a
build script creates inside a package. A warm install used
to replace each of them with a copy of its target #12859.
- After upgrading, every package with a build script is built
once more.
- pnpm install now restores cached build artifacts when
reinstalling a workspace that uses separate lockfiles
[#12942].
- The global virtual store and the side-effects cache now key
built packages by the Node.js version that the root
project's devEngines.runtime or engines.runtime pins. That
is the Node.js their build scripts run with. A dependency
that declares its own engines.runtime no longer changes the
key for every other package.
- Concurrent installs that share a global virtual store now
run a package's build in its shared slot one at a time. A
failed build leaves the slot in place and marks it for the
next install to rebuild #15568.
- Concurrent installs no longer fail when they replace the
same stale hoisted dependency link. Virtual store cleanup
now keeps the temporary lockfiles that concurrent installs
are still writing.
- pnpm install keeps the owner, group, and mode of files
already in a shared store, including index.db. New store
files and directories inherit the store directory's
group-write bit. When that directory is setgid, new files
inherit its group. pnpm does not change a file's owner or
group #12765.
- When pnpm install repairs a store file that was modified
through a hard link in node_modules, the repair now keeps
the file's inode on Linux and macOS. Hard-linked copies in
other projects are healed at the same time. Before, only
the project running the install received the restored
content. On Windows the repair still replaces the file, so
other projects are healed on their next install #3445.
- A tarball whose integrity pnpm computed during download is
now found in the store on the next install. Before, that
install downloaded the tarball again once the lockfile
recorded the integrity #12562.
- pnpm now warns when it cannot hard link packages from an
existing store in the pnpm home directory and uses a store
on the project's filesystem. This can happen when the
project is on another filesystem, such as a bind-mounted
workspace in a container. The warning names both stores and
suggests setting storeDir #14505.
- Resolving and linking dependencies
- A peer dependency no longer resolves to two different
versions for one package. This happened when the package
peer-depends on another package and on one of that
package's peers, and it is installed deeper than a direct
dependency of the package that provides them #12098.
- An optional peer dependency is no longer resolved from
another workspace project's package when the project
provides one of that package's own peers at a version it
rejects. This avoids bogus unmet peer errors #13989.
- pnpm no longer reports unmet peer dependency warnings for
aliased npm: peer ranges that a tarball dependency
satisfies #11126.
- pnpm install now links the executables of auto-installed
peer dependencies into the workspace root's
node_modules/.bin, including after a frozen-lockfile
reinstall #8511.
- With resolutionMode: time-based and minimumReleaseAge both
set, pnpm install no longer reports a subdependency as too
new when only the time-based cutoff excludes it. Such
subdependencies used to fail a strict install with
ERR_PNPM_NO_MATURE_MATCHING_VERSION, or were added to
minimumReleaseAgeExclude #13569.
- A transitive dependency that has no matching version
published before the time-based cutoff now resolves to the
lowest matching version allowed by minimumReleaseAge. pnpm
picks a version younger than minimumReleaseAge only if no
older version matches #16298.
- With minimumReleaseAge set, re-resolving the lockfile no
longer rewrites the peerDependencies recorded for a package
whose version did not change. This happened when the
registry metadata of a package differed from the
package.json in its tarball #13988.
- pnpm no longer revalidates cached registry metadata when
the registry sends Cache-Control: max-age=0, no-cache, or
no-store. It downloads the metadata again, so a version
newly published to such a registry is visible on the next
install #13487.
- pnpm's built-in package compatibility database no longer
applies to a project's own manifest. A project named like a
published package, such as vue-loader, no longer gains
dependencies on pnpm install or pnpm update.
User-configured packageExtensions still apply to project
manifests #11700.
- Relative local tarball paths in pnpm.overrides without an
explicit file: prefix are now rebased correctly for
workspace packages #11131.
- pnpm no longer reports pnpm-lock.yaml as broken when a
project depends on a package named constructor. A __proto__
key in the lockfile is now kept as a plain entry when pnpm
reads or writes the lockfile. It no longer replaces the
prototype of the objects pnpm builds from it #11028.
- With nodeLinker: pnp, a workspace package can now require
another workspace package it depends on #3567. On Windows,
workspace dependency paths in the generated .pnp.cjs now
use forward slashes.
- Performance
- pnpm now uses less memory when installing a package whose
archive is larger than 64 MiB unpacked, and when installing
a runtime from a zip archive, such as Node.js on Windows,
Deno, or Bun #14164.
- A fresh install reusing a warm global virtual store skips
reimporting packages whose target directory is already
complete #11112.
- A warm pnpm install reuses on-disk package metadata for
five minutes when the registry does not send an ETag.
Registries that send an ETag, including the public npm
registry, still revalidate with a conditional request. pnpm
update still fetches current metadata #13976.
- Patched dependencies
- pnpm install now repairs a pnpm-lock.yaml whose
(patch_hash=<hash>) dependency paths disagree with its
patchedDependencies map, including paths that lack the hash
their patch calls for. pnpm previously accepted such a
lockfile as up to date and kept the old patched files. pnpm
install --frozen-lockfile now fails on such a lockfile with
ERR_PNPM_INCONSISTENT_PATCH_HASH. It fails with
ERR_PNPM_UNCHECKABLE_PATCH_HASH when a patch hash in the
lockfile is malformed, or when the lockfile lacks the package
version or patch entry that the check needs #15336.
- pnpm install now fails with ERR_PNPM_PATCH_NOT_FOUND when a
patch file listed in patchedDependencies does not exist. It
used to fail with a raw ENOENT error and a stack trace #5268.
- pnpm now fails with ERR_PNPM_INVALID_PATCHED_DEPENDENCY when
patchedDependencies has an invalid shape or contains a
non-string value.
- engineStrict now checks the patched package.json when a
patchedDependencies entry changes engines. A patch that
relaxes engines.node no longer fails the install against the
published range #9603.
- pnpm patch now applies the existing patch file to the edit
directory of a git-hosted dependency, as it already does for
packages from the registry #9699.
- pnpm patch-commit now fails with an error when git cannot be
found in PATH. It previously reported that no changes were
found #8666.
- Workspaces and filtering
- pnpm install refreshes injected copies of workspace
packages when source projects are rebuilt. Injected copies
previously stayed stale until pnpm install --force #4407.
- Scripts listed in syncInjectedDepsAfterScripts now update
injected dependencies while they run. A watcher on the
injected package, such as a dev server, sees each change
before the script exits #4410.
- pnpm install no longer fails for an injected workspace
dependency whose package publishes from a
publishConfig.directory that its own prepare script builds.
The injected copy now picks up that directory once prepare
finishes building it. pnpm install --frozen-lockfile no
longer reports the dependency as outdated while the
directory has not been built yet #7811.
- With sharedWorkspaceLockfile: false, an injected workspace
package that has lifecycle scripts is now hard linked into
the projects that depend on it. Before, pnpm left a plain
copy, so later edits to the package did not reach those
projects #9828.
- injectWorkspacePackages now hard links a workspace
dependency declared with a relative path, such as
workspace:../foo, the same way it already does for
workspace:* #10446.
- pnpm install no longer creates a node_modules symlink
inside the publishConfig.directory of a workspace package
linked with linkDirectory. A build tool that cleaned its
output directory through that symlink deleted the files of
the package's dependencies. pnpm install also removes a
symlink that an earlier install left there #16226.
- When verifyDepsBeforeRun triggers an install before a
filtered pnpm run or pnpm exec, pnpm now installs only the
selected projects and their dependencies. A later filtered
command also installs a selected project that an earlier
filtered install skipped #11865.
- pnpm import in a workspace now keeps the versions pinned by
a yarn.lock inside a workspace project #4385.
- Installing with pnprServer set now records the pnpmfile
checksum in the lockfile, so a later pnpm install
- -frozen-lockfile accepts that lockfile. A frozen install
through the pnpr server now fails if the pnpmfile changed.
If the pnpmfile defines a readPackage, afterAllResolved or
preResolution hook or custom resolvers, pnpm resolves
dependencies locally and prints a warning that the pnpr
server was not used #14460.
- Installing through a pnpr server now links a workspace
project at the directory its publishConfig.directory names.
An install that resolves through a server which does not
forward the setting fails with
ERR_PNPM_PNPR_PUBLISH_DIRECTORY_MISMATCH. The server
rejects a publishConfig.directory that points outside its
project.
- Adding, updating, and removing dependencies
- With autoInstallPeers, pnpm add and pnpm remove in a
workspace project keep the locked version of a peer
dependency the project declares. In a workspace where
another project depended on a different version of that
package, the peer could switch to that version #11225.
- Adding a dependency now keeps unrelated transitive
dependencies on their locked versions #11456.
- pnpm update --recursive <pkg> no longer changes the version
of a peer dependency that another workspace project
installs automatically. Such a peer could move to a version
outside the range the project declares, for example to
React 19 in a project that declares react: ^18.3.1 #14928.
- pnpm update package@beta on a dependency declared as
catalog: now updates the catalog entry and keeps catalog:
in package.json. Before, pnpm wrote the resolved specifier
to package.json #13399.
- pnpm update now applies an override that references a
catalog with the catalog's new value when the update bumps
that catalog entry. Before, the packages the override
targets kept the old version in the lockfile #12159.
- pnpm add <dir> now warns when the added directory declares
peer dependencies, as pnpm link does. The directory is
saved as a link: dependency, and its peers are not resolved
from the project that adds it. Use the file: protocol to
have them resolved #5523.
- Running scripts and commands
- pnpm run and pnpm exec no longer install dependencies
automatically when the root package.json still keeps
overrides, packageExtensions, patchedDependencies, or
ignoredOptionalDependencies in its pnpm field. pnpm no
longer reads that field, so the install rewrote the
lockfile without those settings. The command now fails and
asks to move the settings to pnpm-workspace.yaml #16278.
- pnpm run no longer reinstalls dependencies when a
node_modules directory installed outside CI is used with
CI=true, or the other way around #12337.
- pnpm no longer hangs after a lifecycle script exits while a
process it started in the background keeps the script's
output open. pnpm stops reading that output one second
after the script exits #5730.
- A signal sent to pnpm, such as SIGTERM, now reaches the
pnpm that pnpm switches to because of packageManager or
devEngines.packageManager, and the one that pnpm with runs.
The signal used to be dropped, so scripts running under
that pnpm never got to shut down #9948.
- pnpm run exits with the code of a script that handles
Ctrl+C and shuts down. A script that finished cleanly is
not reported as a lifecycle failure. The commands after it
in the same script still run #9945.
- pnpm run and lifecycle scripts use the configured
scriptShell, including Git Bash on Windows, when
shellEmulator is also enabled. shellEmulator still runs
scripts when scriptShell is not set. Extra arguments passed
to pnpm run are quoted for the shell that runs the script,
so a Windows path stays intact #14719.
- pnpm -r run /regexp/ now honors the tasks dependsOn
declared for each script the selector matches, like running
the script by name does. Matched scripts that depend on
each other run in order. Each matched script runs once
[#15596].
- Commands run from a POSIX shell through a dependency's own
node_modules/.bin, such as
node_modules/vite/node_modules/.bin/esbuild, no longer fail
with MODULE_NOT_FOUND #10189.
- pnpm rebuild with nodeLinker: hoisted no longer puts one
package's parent node_modules/.bin directories on the PATH
of the packages it builds after it.
- Publishing, packing, and deploying
- pnpm publish now waits at least 5 minutes for the registry
to answer a publish request, like npm. This fixes "409
Conflict - Failed to save packument" errors when the
registry is slow to answer #11454.
- pnpm deploy with a shared lockfile now copies workspace
dependencies into the deploy directory, even when
packageImportMethod is set to hardlink. Previously, their
files were hard-linked to the workspace sources, so editing
a source file also changed the deployed copy #12176.
- pnpm deploy --prod no longer fails with
ERR_PNPM_OUTDATED_LOCKFILE when the deployed project
declares a devEngines.runtime with onFail: download. The
runtime stays out of the deployed node_modules with the
rest of the dev dependencies #15703.
- pnpm deploy --legacy no longer leaves broken links to
nested local dependencies of workspace packages #9575.
- pnpm deploy no longer creates extra directories inside the
deploy target and workspace projects when using a relative
deploy path #10981.
- Manifests and configuration files
- Settings given on the command line, such as --registry and
- -store-dir, now take precedence over the values a pnpmfile
updateConfig hook sets #14063.
- An updateConfig hook that returns registriesByScope without
the default or @jsr entry no longer crashes the install
with Invalid URL. A missing default keeps the configured
registry, and a missing @jsr falls back to the built-in JSR
registry #15619.
- The hook's registry and the default entry of its
registriesByScope now set one default registry, which
installs, pnpm publish, and pnpm login all use. If a hook
changes both, registry wins. A route that is not a string
fails with ERR_PNPM_INVALID_UPDATE_CONFIG_RESULT.
- An updateConfig hook in .pnpmfile.cjs has to use
config.registriesByScope, config.registriesByPrefix, and
config.registryOptionsByUrl. These registry lookups were
renamed in pnpm 11.23.0 from config.registries,
config.namedRegistries, and config.registryOptions #15620.
- An async updateConfig hook that resolves to undefined now
fails with ERR_PNPM_CONFIG_IS_UNDEFINED, as a synchronous
hook that returns undefined already did.
- pnpm config set --location=project and pnpm config delete
- -location=project, run from a package inside a workspace,
now write settings that belong in pnpm-workspace.yaml to
the workspace root's pnpm-workspace.yaml. Before, they
created a new pnpm-workspace.yaml in the current package,
which made that package the workspace root. Settings stored
in .npmrc are still written to the current directory
[#13757].
- pnpm now reads the workspace directory override from
PNPM_CONFIG_WORKSPACE_DIR, like other settings.
NPM_CONFIG_WORKSPACE_DIR still works as a fallback #16275.
- A ${VAR} placeholder in .npmrc or pnpm-workspace.yaml whose
name matches a built-in object property, such as
${toString}, is now treated as an unset variable. It used
to be replaced with the source text of a JavaScript
function.
- pnpm now rejects a falsy non-array packages field in
pnpm-workspace.yaml, such as packages: false, with an
error. It used to treat the field as omitted.
- Global packages, pnpm versions, and runtimes
- pnpm update --global now reinstalls the global packages
that pnpm 10 installed into the previous global directory,
<global-dir>/5, so their commands are linked into the pnpm
home bin directory again and pnpm list --global lists them.
Once every package is migrated, pnpm deletes the previous
directory and the commands pnpm 10 linked into the pnpm
home #11528.
- pnpm env remove --global deletes Node.js versions that pnpm
installed into its own store, including when another tool
installed pnpm #8357.
- pnpm self-update no longer suggests a downgrade when
minimumReleaseAge holds back the registry's latest release.
It now says that release is still within the cutoff #12006.
- @pnpm/exe no longer ships a binary for arm64 musl Linux,
such as Alpine on ARM. The published binary crashed with a
segmentation fault at startup. Installing @pnpm/exe on that
platform now fails with an error that suggests npm install
- g pnpm or pnpm 12 #10443.
- The macOS and Linux release archives no longer include
Windows-only files, such as node-gyp.cmd and the fastlist
executables #11352.
- Inspecting dependencies
- pnpm licenses list now reports the actual on-disk package
locations when using nodeLinker: hoisted or
shamefully-hoist: true #8589. pnpm licenses list --json now
includes every installed copy of a package in its paths
array, including hoisted copies and isolated installations
with different peer dependencies. Its paths also exist on
disk when the isolated linker uses a custom modulesDir.
- pnpm list now shows the correct path of a link: dependency
that points to a directory on another drive on Windows. The
path used to be appended to the project directory, such as
C:\project\D:\lib, and pnpm list --long could not show the
package's details #10362.
- pnpm root now prints the configured modulesDir. It used to
print node_modules regardless of the setting. A project's
own modulesDir from packageConfigs is printed too #9113.
- pnpm audit and pnpm audit signatures now fail with an error
when the lockfile contains unresolvable dependency
references #13638.
Outp - ut and messages
- With the default and append-only reporters, installs with
- -loglevel warn or --loglevel error now print the full
output of a failed install script. The output of successful
scripts, including the root project's own install hooks,
stays hidden. With --loglevel warn, pnpm also prints
ignored build script warnings.
- The lockfile verification error now suggests relaxing the
policy that flagged an entry only if a fresh resolution
still fails and you trust the affected packages. Errors
from checks that no policy controls, such as a missing
tarball integrity, no longer suggest relaxing a policy
[#14411].
- pnpm run --recursive now prints GitLab CI collapsible
sections that GitLab recognizes. The section markers used
to appear as raw text in the job log.
- Local tarball dependencies using the file protocol are no
longer counted as downloaded in the progress banner #1103.
* Sat Sep 26 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.11.28:
pnpm 11.28.0 adds the forceIgnoresPlatform setting and pnpm
update --peer, and fixes many bugs in pnpm deploy, --filter,
nodeLinker: hoisted, and custom modulesDir setups. This release
also carries security fixes for shell completion, bin shims on
Nix, lifecycle scripts inside a custom modulesDir, and userAgent
placeholders in pnpm-workspace.yaml.
* Minor Changes
- Added the forceIgnoresPlatform setting. When it is false,
pnpm install --force skips optional dependencies whose os,
cpu or libc do not match the host instead of installing all
of them. The default stays true #6133.
* Patch Changes
- Security
- pnpm no longer expands environment variables in a userAgent
set in a project's pnpm-workspace.yaml. A userAgent with a
placeholder in that file is now ignored. Before this fix,
pnpm sent the variable's value to the configured registry
[#15415].
- pnpm no longer treats packages inside a custom modulesDir
as workspace projects, including one that packageConfigs
sets for a project. Before, with a modulesDir such as
vendor and a packages pattern such as '**', a repeat
install ran the lifecycle scripts of dependencies that
allowBuilds had not approved #15412.
- On Nix, a dependency's bin named like a system utility such
as sed can no longer redirect a POSIX bin shim or the pnpm,
pn, pnpx, and pnx launchers. The shims and launchers now
ignore node_modules and relative PATH entries while they
locate their own files. Installing again replaces the shims
already in node_modules #14883.
- Shell completion now omits candidates containing control or
invisible formatting characters, and fish completion omits
names containing backslashes. Package and script names can
no longer inject extra completion records or terminal
escape sequences.
- Commands that run pnpm again, such as pnpm runtime set and
pnpm env use, no longer re-run a script that only looks
like pnpm. A script named pnpm or pn that another package
installed was run as though it were pnpm.
- pnpm store prune now leaves a dlx cache root that is a
symlink or Windows junction untouched. Cleanup no longer
removes directories through that link.
- Installing packages
- pnpm install now fails at once when a registry or tarball
server presents a TLS certificate that fails verification,
such as a self-signed or expired one. The error names the
certificate problem. Such requests were retried for more
than a minute #9134.
- pnpm install no longer appears to hang when a git
dependency is fetched over SSH and ssh asks for a key
passphrase or a host key confirmation. pnpm now runs ssh in
batch mode, so the install fails right away with the ssh
error, and a key that needs a passphrase has to be loaded
into an SSH agent first. An ssh command selected through
GIT_SSH_COMMAND, GIT_SSH, or the core.sshCommand git
setting is kept as is #2227.
- pnpm no longer crashes on startup when the temporary
directory set by TMPDIR, TEMP, or TMP does not exist #4960.
- pnpm install --silent no longer fails when the install is
delegated to pacquet. pnpm also stops passing -s,
- -loglevel and the other reporting flags to pacquet #11936.
- Fixed pnpm install failing with EEXIST when a concurrent
install cleared the file or directory that was occupying a
symlink path. On Windows, a symlink another process is
still holding is no longer moved aside and recreated.
- pnpm install no longer fails when writing the workspace
state file encounters an error. Failures to update the
state file now emit a warning instead of aborting the
install #14550.
- Installing or adding dependencies no longer fails when a
previously installed local tarball file was deleted from
disk #8367.
- pnpm install now reads the same local tarball it installs
when a dependency's absolute file: path contains ... Such a
path could install a different tarball than the one it
read, failing with ERR_PNPM_TARBALL_INTEGRITY, or fail to
resolve at all.
- pnpm install --frozen-lockfile now rejects changed local
tarballs, even when the previous archive contents are in
the store #1889.
- pnpm add and pnpm install now support installing bzip2
compressed tarballs #6761.
- pnpm install now fetches committed submodules of git
dependencies #1470.
- Interrupting pnpm install with Ctrl+C or SIGTERM no longer
leaves a temporary lockfile (.pnpm-lock.yaml.*.tmp) behind
in the project #1418.
- pnpm install and pnpm run now reinstall a single project
that was moved or renamed together with its node_modules.
Before, they reported "Already up to date" while links such
as Windows junctions still pointed at the old location
[#9512].
- pnpm install now relinks a direct dependency whose link in
node_modules points to a missing target. Before, it
reported "Already up to date" and left the broken link
[#9758].
- pnpm install and pnpm add no longer skip optional
dependencies that the Node.js version resolved for a
devEngines.runtime range supports, when the range uses
onFail: download. An explicitly set nodeVersion still takes
priority #14628.
- pnpm install now uses the running Node.js when
devEngines.runtime declares a range without onFail:
download. Optional dependencies supported by the active
Node.js are no longer skipped #15230.
- pnpm install --engine-strict now respects engines relaxed
by readPackage hooks in .pnpmfile.cjs #15482.
- pnpm install now applies changes to or removal of a global
readPackage hook when an existing lockfile is present
[#15136].
- The project's .pnpmfile.mjs or .pnpmfile.cjs now runs after
the pnpmfiles of config dependency plugins #9891.
- A readPackage hook that sets a dependency range to a value
other than a string, such as undefined, now fails the
install with an error that names the dependency, the
package, and the pnpmfile. Delete the property to remove a
dependency #5517.
- pnpm install --prod and other installs that skip
devDependencies no longer run the pnpm:devPreinstall script
[#7065]. They skip prepare lifecycle scripts too, as does
pnpm install given package arguments. pnpm deploy does not
run the prepare scripts of the deployed project #7282.
- The root project's preinstall script now runs before
dependencies are resolved and linked. A guard such as npx
only-allow yarn can stop the install before pnpm populates
node_modules #3760.
- pnpm install --prod, pnpm fetch --prod and pnpm deploy
- -prod no longer install a devDependency that is only there
to satisfy an optional peer dependency of a production
dependency. pnpm list, pnpm why, pnpm licenses, pnpm sbom
and pnpm audit leave it out of --prod results too. The same
applies to --dev. A peer that is not optional is still
installed and audited #15344.
- pnpm prune --prod and production installs now prune
excluded development dependencies even when lockfile
generation is disabled.
- pnpm fetch now also installs the pnpm version that
pnpm-lock.yaml pins, when it differs from the running pnpm.
A later pnpm install --offline that switches to the pinned
version no longer fails because that version is missing
from the store #11808.
- A dependency that ships a binding.gyp and sets gypfile:
false no longer gets the node-gyp rebuild install script
pnpm synthesizes for it. Such a dependency needs no
allowBuilds entry and is no longer listed under "Ignored
build scripts".
- pnpm install no longer adds allowBuilds placeholder entries
to pnpm-workspace.yaml when it runs in CI or without a
terminal. Interactive installs still add them #11574.
- Installing through a pnpr server now installs a project's
peer dependencies when autoInstallPeers is enabled. A
project that declared only peer dependencies failed with
ERR_PNPM_OUTDATED_LOCKFILE or skipped its peers #14833.
- Resolving and linking dependencies
- pnpm now installs a dependency that a package also declares
as an optional peer dependency, for example lightningcss in
some vite builds. The dependency was missing from
node_modules, so the package failed to import it #8912.
- Removal overrides such as "parent>peer": "-" now prevent
optional peers from being installed from another workspace
package #15008.
- Removing an entry from overrides now re-resolves the
packages it targeted. A version the override had locked is
no longer kept just because the declared range still
accepts it #4587.
- packageExtensions and overrides entries with a ranged
selector (such as @<X or @*) no longer match a dependency
that has no package.json, such as a local directory
dependency #15007.
- Trim leading and trailing whitespace from dependency
override selectors in pnpm.overrides #6356.
- With trustPolicy: no-downgrade, pnpm now resolves the
newest matching version that is not a trust downgrade.
Previously a dependency failed with
ERR_PNPM_TRUST_DOWNGRADE even when an older version
satisfied its range. pnpm self-update picks its target
version the same way. A request for an exact version still
fails #14176.
- pnpm install and pnpm update now resolve a dependency range
to the newest matching version that is not deprecated. A
version already recorded in the lockfile is still used
[#15128].
- pnpm add and pnpm remove no longer move unrelated
transitive dependencies to other versions. Adding a package
and then removing it now leaves pnpm-lock.yaml unchanged.
Before, the dependencies of auto-installed peers and npm:
aliased subdependencies could move to a newer version that
was already in the lockfile #11859.
- pnpm dedupe now moves transitive dependencies to the
version a catalog: dependency pins, as it already did for
versions written directly in package.json.
- pnpm install --ignore-pnpmfile no longer removes
pnpmfileChecksum from an up-to-date pnpm-lock.yaml. pnpm
install --frozen-lockfile --ignore-pnpmfile no longer fails
with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH when the lockfile
records a pnpmfileChecksum. A command that resolves
dependencies with the pnpmfile ignored still writes the
lockfile without it #10944.
- pnpm install --frozen-lockfile now succeeds when an
optional dependency was unresolvable and skipped by the
install that wrote the lockfile. Previously, frozen
installs failed with ERR_PNPM_OUTDATED_LOCKFILE. The notice
states that the dependency could not be resolved and names
the requested range #3960.
- pnpm no longer rewrites packageManagerDependencies in
pnpm-lock.yaml when that block pins @pnpm/exe beside pnpm.
The rewrite ran on every command, so pnpm list left a clean
working tree dirty, and pnpm version then refused to run
[#14926].
- pnpm import and fresh resolutions now record integrity for
git-hosted tarballs, such as codeload.github.com URLs, even
when the tarball is already in the store #13338.
- Merging lockfiles now preserves recorded configuration
fields such as overrides, neverBuiltDependencies,
patchedDependencies, packageExtensionsChecksum, settings,
and catalogs #8366.
- A lockfile entry whose resolution is unchanged now keeps
its recorded deprecated message #5772.
- pnpm no longer writes a package's legacy array-form
engines, such as ["node >= 0.8"], to the lockfile. It was
recorded as an object keyed by index, such as {'0': node >=
0.8} #4518.
- With nodeLinker: hoisted, hoistWorkspacePackages now links
each workspace project that hoistPattern or
publicHoistPattern selects into the root node_modules,
unless a hoisted package or a root dependency already uses
its name. The project's bins are linked into the root
node_modules/.bin #7553.
- Workspace projects that hoistPattern or publicHoistPattern
selects are now hoisted on every install. A project added
to the workspace was not hoisted until node_modules was
deleted and reinstalled. A workspace that installs nothing
from a registry hoisted none of its projects at all #3642.
- With nodeLinker: hoisted, pnpm install now removes the
commands of the packages it removes from node_modules/.bin,
such as a nested copy deduped into the root node_modules
[#7568].
- pnpm install now links a dependency's bin even when the
bin's file does not exist yet, such as a workspace
package's bin that a build script creates after install.
Previously pnpm printed a Failed to create bin warning. The
command then stayed missing until node_modules was removed
[#10007], #10216.
- Executable linking now makes a bin executable for every
user. A target with only some execute permission bits set
was left non-executable for other users #3699. Bin linking
leaves workspace and linked dependency files outside
node_modules unchanged.
- Dependencies and executable binaries are now correctly
linked and accessible for workspace packages using
publishConfig.directory and publishConfig.linkDirectory
[#8338].
- Workspaces and filtering
- Fixed pnpm install for workspace projects reached through a
symlink, such as a packages directory that links to a
folder outside the workspace. pnpm now installs their
dependencies, and the links in their node_modules resolve
[#1044].
- A dependency declared with catalog: now counts as a
workspace dependency when its catalog entry points at a
workspace project, for example workspace:* #15587. With
linkWorkspacePackages enabled, so does an npm: alias of a
workspace project, such as "math-alias": "npm:math@^1.0.0".
pnpm -r run runs that project first. --filter <pkg>...
selects it.
- A workspace: dependency now resolves to a workspace project
whose version is not valid semver, such as 1 or 1.0.
workspace:*, workspace:^, and workspace:~ match it. A range
identical to the version also matches it #4567.
- A workspace: dependency with an exact version now resolves
to a workspace project whose version carries SemVer build
metadata. For example, workspace:0.5.6-next.3 matches a
project at 0.5.6-next.3+f60facc #6483. A workspace project
with build metadata is no longer skipped when it matches
the requested range and has the same version precedence as
the registry package #2812.
- Secondary dependencies now prefer the version resolved by
the local project's direct dependencies over versions from
sibling workspace projects #7191.
- pnpm install now re-resolves a workspace project's
auto-installed peer dependency when another workspace
project changes its specifier for that package to one that
excludes the locked version but still overlaps the peer
range. The peer then resolves to the version a fresh
install would pick #11800.
- pnpm install --frozen-lockfile now fails with
ERR_PNPM_OUTDATED_LOCKFILE when pnpm-lock.yaml lists a
workspace project whose directory or manifest file is
missing. The install used to report success without
installing that project's dependencies #7667.
- pnpm install --frozen-lockfile now fails when a workspace
package's version no longer satisfies the range that a
dependent workspace project declares for it. This includes
injected workspace dependencies #7823.
- pnpm install -r now installs every workspace project when
recursiveInstall is set to false in pnpm-workspace.yaml
[#7504].
- pnpm install with --filter now installs only the
dependencies of the selected projects when using
nodeLinker: hoisted #8882.
- pnpm install now updates an injected workspace dependency
after that package's own dependencies change, when
shared-workspace-lockfile is false #7209.
- syncInjectedDepsAfterScripts now copies files into injected
dependencies when node_modules is on another filesystem
than the package sources. The sync previously failed with a
cross-device link error and made the script run exit with
an error #14703.
- Fixed injected workspace dependency synchronization failing
with EPERM on Windows when removing nested directories.
- Workspace discovery now returns one project per directory
when multiple manifest formats are present. It selects
package.json, then package.json5, then package.yaml #3027.
- Wildcards in negated packages patterns of
pnpm-workspace.yaml now match directories whose names start
with a dot. For example, !packages/** now also excludes
packages/.dev/tool when another pattern includes .dev
explicitly.
- pnpm now warns when a workspace install covers a project
that has its own pnpm-workspace.yaml. The nested file's
settings, such as patchedDependencies, do not apply when
the outer workspace installs that project. pnpm reads
settings only from the pnpm-workspace.yaml at the workspace
root #11724.
- pnpm now warns when shared-workspace-lockfile is passed on
the command line outside a workspace #1617.
- The [<since>] filter selector now compares against the
commit where the current branch forked from <since>.
Projects changed only by newer commits on <since> are no
longer selected. Uncommitted changes are still included. In
a shallow clone without that commit, pnpm compares against
<since> directly, as before #9907.
- --filter "[<since>]" now selects workspace packages when
dependency versions change in a catalog in
pnpm-workspace.yaml #8718. It also selects projects that
files were moved out of when git detects the move as a
rename #15481.
- --filter now evaluates selectors in order, so later
inclusion filters can re-include packages that an earlier
exclusion filter excluded #9354.
- Directory filters such as --filter=./packages/* now select
projects when the current directory was entered with a
lowercase drive letter on Windows, like c:\repo #5500.
- Custom modulesDir
- pnpm run, pnpm exec, pnpm version hooks, and the lifecycle
scripts a project runs during install now find commands in
a custom modulesDir, and bin shims resolve dependency paths
there #3604. Project .hooks scripts are read from the
configured modules directory. Installs resolved by pnpr now
preserve configured modules and executable directories.
pnpm bin now reports the configured executable directory.
In a workspace whose projects keep their own lockfiles, a
packageConfigs entry that gives one project its own
modulesDir is followed too.
- Tools installed in a custom modulesDir can load CommonJS
plugins installed there, the same way they would from
node_modules. When executables are symlinks, as with
preferSymlinkedExecutables or the hoisted linker, this
works for pnpm run, pnpm exec, pnpm version hooks, and the
lifecycle scripts a project runs during install. A
symlinked tool started directly from a shell does not get
it. Paths containing the platform path-list separator do
not receive this fallback. extendNodePath: false disables
this fallback #3604.
- A repeat pnpm install in a workspace with a custom
modulesDir now takes the up-to-date fast path. Before, pnpm
looked for each workspace project's dependencies in
node_modules and ran a full install every time.
- Adding, updating, and removing dependencies
- pnpm add now keeps the specifier a readPackage hook
provides when the hook rewrites the requested one. When the
hook removes the dependency instead, the add skips it and
reports why. Previously the add wrote the request either
way, and the hook undid it on the next read, so pnpm
install --frozen-lockfile failed #15156.
- pnpm add now saves changes to package.json before running
lifecycle scripts, so a postinstall script failure leaves
the added dependency in package.json #8627.
- pnpm add now saves the requested exact version when adding
a dependency, even when the manifest already contains a
version range #6040.
- pnpm add and pnpm install keep an empty peerDependencies,
dependencies, devDependencies, or optionalDependencies
field that was already in package.json. pnpm still drops
such a field when it removes the last entry itself, as pnpm
remove does #5096.
- Added a --peer flag to pnpm update to update ranges in
peerDependencies #8081.
- pnpm update now keeps a version range whose shape has no
save prefix, such as <= 3.0.0 or >=1.0.0 <2.0.0, when the
updated version still satisfies it. Before, <= 3.0.0 became
^3.0.0 #6714.
- pnpm update <name> now updates a peer dependency that pnpm
installed automatically #10486.
- pnpm update <pkg> now moves a package off a locked version
the registry no longer serves, such as an unpublished
release. The lockfile check for supply-chain policies such
as minimumReleaseAge used to reject that version before the
update could replace it #9953.
- pnpm update --prod no longer installs devDependencies when
run in a project installed with --prod #8038.
- pnpm update --interactive --workspace now allows external
dependencies to be updated.
- pnpm outdated and pnpm update now apply minimumReleaseAge
to GitHub Actions. minimumReleaseAgeExclude entries match
action names such as actions/checkout #13923.
- pnpm remove now runs the project's own preuninstall,
uninstall, and postuninstall scripts. preuninstall and
uninstall run before dependencies are unlinked. A failure
in either stage aborts the removal. postuninstall runs
after unlinking completes. The ignoreScripts setting and
- -lockfile-only skip all three stages #3276.
- pnpm remove -r now fails if any requested dependency is
absent from all selected workspace projects. Validation
respects --save-prod, --save-dev, and --save-optional and
completes before modifying project manifests #2319.
- pnpm remove now accepts --trust-lockfile and
- -no-trust-lockfile to control supply-chain policy checks
while removing a package #14406.
- pnpm unlink now removes the link: dependency that pnpm link
<dir> added to package.json. The linked package is removed
from node_modules and the lockfile. A link: dependency to
another directory is kept #4219.
- minimumReleaseAgeExcludePrune and trustPolicyExcludePrune
now work in workspaces with
shared-workspace-lockfile=false. Once every project has
been installed, pnpm drops an entry only if no project
lockfile records it. Undecided allowBuilds entries are
pruned the same way #14612.
- pnpm import now converts dependencies that use Yarn's
patch: protocol. The dependency keeps the version it
patches, and the patch file is added to patchedDependencies
in pnpm-workspace.yaml. If the patch file is missing, pnpm
prints a warning and imports the dependency without the
patch #10278.
- pnpm import in a workspace now keeps the versions pinned by
the root yarn.lock, package-lock.json, or
npm-shrinkwrap.json when another workspace project's range
allows a newer version. Before, the root project got the
newest version in its range #4385.
- pnpm patch-commit now resolves default patch directory
locations when passed a package name or package specifier
(such as pnpm patch-commit <pkg> or pnpm patch-commit
<pkg>@<version>).
- pnpm patch-commit now updates the lockfile snapshot and
prunes removed dependencies when the patch modifies
package.json #6866.
- pnpm patch-commit now falls back to copying package files
when hard linking fails.
- make-dedicated-lockfile no longer removes fields such as
main and types from the publishConfig of the project's
package.json. It now restores package.json when it cannot
move the original node_modules back to its place. The error
then names .tmp_node_modules, where the original
node_modules was left. The command refuses to run while
that directory exists, so a retry cannot overwrite it.
- Running scripts and commands
- A script that pnpm runs without a terminal now ends when
pnpm itself is killed. Killing pnpm's process group, as
Playwright's webServer does to stop the command it started,
used to leave the script running and holding the caller's
output pipes open #15555.
- pnpm --filter <project> <command> and pnpm -r <command> now
run a command installed in the selected projects'
dependencies when none of them has a script by that name.
This matches pnpm <command> in a single project. pnpm run
with --filter or -r still reports the missing script
[#10151].
- pnpm exec and pnpm dlx now set npm_execpath, INIT_CWD,
npm_node_execpath, and NODE for child processes #7037.
Scripts that pnpx and pnx run now get pnpm itself as
npm_execpath. A script that ran $npm_execpath install there
ran pnpm dlx install.
- pnpm exec now sets the PWD environment variable to the
directory the command runs in. Shells and tools that read
PWD now report the logical path of a workspace package
reached through a symlink #1550.
- A script that runs pnpm run no longer adds duplicate
node_modules/.bin and node-gyp-bin entries to PATH #5352.
- Concurrent pnpm run and pnpm exec commands now serialize
their dependency installs #14551.
- pnpm run --recursive no longer reports interrupted scripts
as lifecycle failures after Ctrl+C.
- pnpm restart now runs the "stop" and "start" scripts when
the package has no "restart" script. Previously it ran
"stop" and then failed with "Missing script: restart"
[#4750].
- pnpm install-test now accepts --no-bail when executed
directly and in recursive runs #3777.
- pnpm dlx now keeps a separate cache entry for each Node.js
major version. A package built under one Node.js major
version, such as a native addon, is no longer reused under
another #8611.
- A runtime: version range that contains || or a space, such
as a devEngines.runtime version of ^22.18.0 || ^24.0.0, now
installs the requested runtime. pnpm used to install the
npm package with the same name, such as node #14817.
- When the configured scriptShell does not exist, running a
script now fails with an error that names the shell.
Previously pnpm printed only an exit code or the package
directory #7562.
- If a script is killed by a signal that pnpm survives, such
as SIGPIPE, the error now names the signal: Command failed
with signal SIGPIPE. #9821.
- Publishing, packing, and deploying
- pnpm publish now resolves workspace: dependencies from
workspace manifests when node_modules is not installed.
Previously, publishing without node_modules failed with
ERR_PNPM_CANNOT_RESOLVE_WORKSPACE_PROTOCOL #6567.
- pnpm publish now honors publishConfig["@scope:registry"]
for a package in that scope. It takes precedence over the
registry set for the same scope in .npmrc and over
publishConfig.registry #12071.
- pnpm pack and pnpm publish now include bundled dependencies
when using the isolated linker. This covers workspace
packages and the dependencies of each bundled package.
Bundled dependencies are also included when
publishConfig.directory selects a build directory #1643.
- pnpm pack, pnpm deploy, and installs of local directory
dependencies now keep symlinks that point to files or
directories included in the package. pnpm pack leaves out
symlinks that point outside the package #8208.
- pnpm pack now honors the files field of package.yaml and
package.json5 manifests. Git-hosted and injected local
dependencies that use these manifests now honor it too
[#7906]. The archive includes exactly one package.json when
the project uses an alternative manifest format, even when
.npmignore or files excludes the source file.
- pnpm pack now preserves file executable permissions in the
packed tarball when source files are executable on disk.
- pnpm publish and pnpm pack now report a missing version or
name field on a workspace dependency. Previously, pnpm
reported that the dependency was not installed #4164.
- pnpm publish and pnpm pack now report an error when a bin
script has a shebang line ending with CRLF #7311.
- pnpm deploy now copies the packageManager and
devEngines.packageManager fields of the workspace root
package.json into the deployed package.json, unless the
deployed project pins a package manager itself #9079.
- pnpm deploy now puts the virtual store at virtualStoreDir,
resolved against the deploy directory. A shared-lockfile
deploy records virtualStoreDir in the deployed
pnpm-workspace.yaml. With the global virtual store enabled
or an absolute virtualStoreDir, the deploy still uses
node_modules/.pnpm #8787.
- pnpm deploy now respects --package-import-method passed on
the command line and reports the package import method
correctly #7593.
- pnpm deploy no longer triggers an install when running
scripts in a read-only deployed filesystem #11617.
- A legacy pnpm deploy with node-linker=hoisted now puts the
deployed project's direct dependencies at the top of the
deployed node_modules #9671.
- pnpm deploy --legacy no longer rewrites
node_modules/.pnpm-workspace-state-v1.json in the source
workspace. The next verifyDepsBeforeRun check there
reported the workspace as out of date #15352.
- Manifests and configuration files
- Fixed pnpm version failing on projects using a package.yaml
manifest.
- Fixed pnpm init creating an extra package.json when
package.yaml is already present.
- pnpm now preserves CRLF line endings when it modifies
project manifests.
- pnpm version now applies pending bumps to private workspace
packages. A private package's changelog is written to its
committed CHANGELOG.md, also when
versioning.changelog.storage is registry #13736, #13519.
- pnpm change check now validates the pending change intents
in .changeset/. It fails when an intent names a package
that is not in the workspace or cannot be released.
- .npmrc files now support npm's ${VAR?} placeholder. It
expands to the value of VAR, or to an empty string without
a warning when VAR is unset #14404.
- pnpm now expands environment variables in '_auth.authToken'
values loaded from global config.yaml and pnpm_config__auth
[#12828].
- pnpm now warns when an empty environment variable removes
an .npmrc authentication token. Authentication environment
warnings now name the affected key #4806.
- pnpm now keeps the configured default registry when '_auth'
holds credentials for several registries and some of those
registries serve package scopes.
- Lockfile verification checks a tarball hosted on a scoped
registry against that registry's metadata, unless the
package's own scope has a registry assigned #15530.
- pnpm now treats a missing global config.yaml, auth.ini, or
other optional config file as absent in Node.js-compatible
runtimes such as StackBlitz WebContainers. Commands such as
pnpm --version failed there with ENOENT #14030.
- pnpm login now logs back in to an existing user on
registries without web login, such as verdaccio. The
classic login request sends the username and password as
basic auth, as npm login does #12055.
- pnpm doctor now checks the configured default registry and
sends its credentials. It used to always ping
https://registry.npmjs.org/ #15618.
- Global packages, pnpm versions, and runtimes
- Global commands such as pnpm add --global, pnpm list
- -global, and pnpm bin --global now run with the pnpm you
invoked, even in a project that pins another pnpm version.
Previously, a pin with onFail: "download" switched them to
the pinned pnpm, and a pinned pnpm 10 or older failed
because its global bin directory was not in PATH #14531.
- pnpm add -g, pnpm update -g, and pnpm remove -g now recover
a global package group whose entire node_modules directory
was deleted. pnpm remove -g leaves such a group's command
shims in the global bin directory #15093. These commands no
longer fail with ERR_PNPM_PACKAGE_MANIFEST_IO_ERROR when
another global package's link into the store dangles, for
example after the store was pruned. The pnpm install script
failed the same way on such a machine.
- pnpm update --global now skips a global package installed
from a file: path that no longer exists, prints a warning,
and updates the remaining global packages. Previously the
whole update failed with ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND
[#12533].
- pnpm update -g no longer asks more than once for approval
of the same immature name@version when
minimumReleaseAgeStrict is enabled #15091.
- pnpm self-update run in a project that pins pnpm through
packageManager or devEngines.packageManager now also
updates the global pnpm, as it does outside a project
[#14747].
- pnpm self-update no longer leaves the previous pnpm in the
global packages when it was installed as @pnpm/exe. pnpm ls
- g now lists a single pnpm #14709.
- pnpm setup no longer deletes aliases and other lines that
sit between a # pnpm comment and the pnpm block in a shell
startup file #7067.
- pnpm env remove now cleans up dangling Node.js executables
and symlinks. Surviving global commands remain intact.
- Inspecting dependencies
- pnpm audit and pnpm audit signatures now check only the
dependencies of the projects selected by --filter,
- -filter-prod, or --workspace-root. The filter used to be
ignored, so a filtered audit reported the whole workspace
[#10982].
- pnpm audit now lists at least one dependency path from
every workspace project that depends on a vulnerable
package. Before, a project whose dependency was reached
through more than 100 paths filled the path list, and other
projects that depend on the same package were left out
[#12200].
- pnpm audit --fix=update now fixes vulnerabilities in
dependencies declared through an npm alias. A specifier
such as "foo": "npm:vulnerable-pkg@1.0.0" moves to the
patched version and keeps the alias. Versions pinned with a
leading = are fixed as well #15155.
- pnpm audit --fix now prunes redundant overrides when one
vulnerable range is a subset of another for the same
package #8577.
- Running pnpm list inside a workspace package without
- -recursive or a filter now lists only the current package
[#14494]. pnpm licenses list does the same. Use --recursive
or --filter to list the licenses of other workspace
projects #5689.
- pnpm list --only-projects now prints every project selected
with --filter or --recursive, including a project that has
no workspace dependencies #9770. It also lists the
workspace projects when sharedWorkspaceLockfile is false
[#7151], and a project that sets publishConfig.directory
[#10635]. It no longer reports packages in node_modules that
are missing from the lockfile #9528.
- pnpm licenses list failed or reported nothing in a
workspace with sharedWorkspaceLockfile: false. It now reads
the lockfile of each selected project #10140.
- With nodeLinker: hoisted, pnpm licenses list reported paths
under node_modules/.pnpm that do not exist. It now reports
the directory where the hoisted linker placed each package
[#8589].
- pnpm outdated and pnpm -r outdated now fail with
ERR_PNPM_NO_PACKAGE_IN_DEPENDENCIES when a requested
package selector does not match any dependency in the
inspected projects #2319.
- pnpm -r outdated --json now includes every outdated
workspace dependency when multiple projects depend on
different versions or dependency types of the same package.
Such a package is keyed by its current version and
dependency type, for example vue@2.7.14 (dev) #7693.
- pnpm sbom filtered to a single workspace project no longer
replaces the project's own license or bugs field with the
workspace root's value when the project's value is blank.
The same applies to an author, description, license,
repository, or bugs field set to null #14882.
- pnpm store status no longer reports packages with build or
postinstall scripts as modified in the store. When packages
were mutated, it now lists only those packages and no
longer suggests running pnpm install --force #919.
- pnpm peers check and the ERR_PNPM_PEER_DEP_ISSUES error now
group peer dependency issues under the workspace project
they were found in #15351.
- The pnpm:peer-dependency-issues log event, which --reporter
ndjson prints, no longer lists peers silenced by
peerDependencyRules.ignoreMissing under conflicts or
intersections #8295.
- Output and messages
- The error for an incompatible pnpm-lock.yaml now reports
the lockfileVersion the file was generated with and the
lockfileVersion the current pnpm supports. The error also
warns that recreating the lockfile with --force may break
the application and suggests installing the pnpm version
that generated the lockfile #848.
- When the registry stops sending data for longer than
fetchTimeout, pnpm now reports that the metadata or tarball
request timed out. Previously the error did not mention the
timeout #3646.
- Resolution no longer logs an error when a package metadata
request fails and resolution succeeds via cached metadata
[#2522].
- The ignored build scripts warning and the update notice are
printed as plain lines when output is not a terminal, in
CI, or with --reporter append-only. They were drawn inside
a box that broke apart in CI logs #9421.
- pnpm run with --loglevel set to warn, error, or silent (or
the same loglevel setting) no longer prints the $ <command>
line before a script, nor the summary of the install that
verifyDepsBeforeRun runs first. Both are info-level output
[#8944].
- pnpm add now warns when replacing an existing dependency
with a specifier pointing to a different source #14869.
- pnpm update no longer warns "Skip adding ... to the default
catalog" for a dependency that already uses catalog:
[#13715].
- pnpm remove --help no longer shows a [@<version>] suffix in
its usage line. The command accepts package names only
[#7751].
- Bash completion now completes script names that contain a
colon, such as pnpm run test:u to pnpm run test:unit #5482.
- update to 11.11.27:
* Minor Changes
- nodeDownloadMirrors can now be set in the global config file
(config.yaml) and through the
PNPM_CONFIG_NODE_DOWNLOAD_MIRRORS environment variable, so a
Node.js download mirror can be configured once for a machine
instead of in every workspace #12124, #13611.
PNPM_CONFIG_NODE_DOWNLOAD_MIRRORS='{"release":"https://npmmirror.com/mirrors/node/"}'
- Added a new setting trustPolicyExcludePrune (default: false).
When enabled, pnpm add, pnpm update, and pnpm remove prune
the entries of trustPolicyExclude in pnpm-workspace.yaml that
the freshly written lockfile no longer resolves: versions
that are gone are dropped (an entry is removed once none of
its versions remain), and entries for packages that are no
longer in the lockfile are removed too. Name patterns
(@scope/*) are always kept. The cleanup is skipped when the
install's lockfile does not cover the whole workspace
(sharedWorkspaceLockfile: false), since entries another
project still needs would look stale.
* Patch Changes
- pnpm now reads the packageManager, devEngines.packageManager
and runtime pins from the workspace root's package.json when
lockfileDir is set. A project that moved its lockfile lost
the pins it declared there #14633.
- Fixed pnpm add -g, pnpm update -g, and pnpm remove -g
mutating global bins or install directories after only
partially reading an installed package group. If any declared
package manifest is missing, malformed, or unreadable, pnpm
now fails before activation or removal and leaves the
existing global installation intact pnpm/pnpm#13796.
- fetch-timeout now limits how long a request may make no
progress. The timer restarts on every chunk that arrives. A
large download over a slow connection is no longer aborted
while data is still coming in. A connection that stops
delivering data still fails after fetch-timeout #14604.
- pnpm peers check no longer reports a peer dependency declared
as workspace:^, workspace:~, or a bare workspace: as unmet.
pnpm reported these as unmet whatever version the linked
workspace project supplied #14770.
- A readPackage hook that edits its argument in place no longer
changes what a later install in the same command resolves. A
deprecated notice read from the lockfile no longer carries
over to another install either #13988.
- pnpm install now auto-installs missing transitive peers when
workspace projects share a dependency at different depths.
This also removes incomplete duplicate peer contexts from the
lockfile. Fixes pnpm/pnpm#14840.
- GitHub Actions updates now stop if an action reference
changes while its versions are being resolved. Unrelated
workflow edits are preserved.
GitHub Actions homepage links no longer expose server
credentials. GitHub server URLs now require HTTPS, with HTTP
allowed only for loopback hosts.
- pnpm licenses list now reports the runtime downloaded through
devEngines.runtime with onFail: "download". The command
previously failed with ERR_PNPM_UNSUPPORTED_PACKAGE_TYPE
[#14172].
- pnpm no longer creates a project pnpm-lock.yaml when
devEngines.packageManager.onFail is download and lockfile
writing is turned off with lockfile: false or --no-lockfile.
pnpm still switches to the pinned version #14728.
- A registry or @scope:registry set in an .npmrc now wins over
the registry a pnpm login credential stored in the global
config.yaml points at. Previously, after logging in to one
registry, installs in a project whose .npmrc named a private
registry went to the logged-in registry instead. They now go
to the registry the .npmrc names #14614.
- A patch that gives a dependency a preinstall, install, or
postinstall script, or a binding.gyp, now runs that build.
pnpm asks for build approval first, so the package is listed
under "Ignored build scripts" until it is allowed to build.
pnpm 12 ran nothing, and pnpm 11 ran it without asking
[#14648].
- Registries that share a host but differ by URL path — one
JFrog Artifactory, Nexus, AWS CodeArtifact or GitLab Packages
instance serving several repositories — now get a metadata
cache directory each. Previously they shared one, so
resolving a package from one of them could answer with
another's versions, integrity hashes and tarball URLs and
fail with ERR_PNPM_TARBALL_URL_MISMATCH #13558.
The URL scheme is part of the cache directory name too, so an
http registry can no longer hand its metadata — which can be
rewritten in transit — to a resolution configured for https
at the same host.
The first install after upgrading refetches registry metadata
once. The package store is untouched.
pnpm cache view now labels each entry with the full registry
URL. It printed registry.npmjs.org before and prints
https://registry.npmjs.org/ now.
pnpm cache list-registries and pnpm cache list print the new
directory names. Scripts that parse either command need
updating.
- Updated the embedded Node.js release keys to the current
canonical nodejs/release-keys list.
- pnpm sbom now omits package author fields when the manifest
author name is empty or contains only whitespace
pnpm/pnpm#14685. In a filtered or split workspace run, only a
project with no author field inherits the workspace root's
author.
- pnpm sbom --sbom-format spdx now writes creationInfo.created
with whole seconds, such as 2026-09-08T10:38:21Z. The
timestamp carried fractional seconds, which strict SPDX
consumers rejected #14684.
- Windows filesystem operations now retry permission errors for
up to one second. Permanent permission errors previously
delayed failure by a minute. Sharing and lock violations
retain their one-minute retry budget pnpm/pnpm#14682.
- pnpm now writes node_modules/.package-map.json only when
nodeExperimentalPackageMap is enabled. Nothing reads the file
without that setting. An install that stops writing the map
removes the one a previous install left.
- pnpm now unpacks a downloaded runtime archive into a randomly
named directory inside the store. It previously used a
predictable path, where another user of a shared store could
plant a symlink and redirect the write outside the store
(GHSA-vwc7-r8mq-g2x9).
- update to 11.11.26:
* Minor Changes
- Catalogs can now resolve workspace dependencies through the
workspace: protocol.
- pnpm remove and pnpm update now accept --trust-lockfile,
- -no-trust-lockfile, --trust-policy, --trust-policy-exclude,
and --trust-policy-ignore-after. pnpm remove checks the whole
lockfile against the active policies unless --trust-lockfile
is set.
- Added pnpm change check for CI validation of package versions
against the versioning.epics bands and versioning.fixed
groups in pnpm-workspace.yaml.
* Patch Changes
- Fetch and tarball errors and retry logs now hide URL
credentials, query strings, and fragments that could expose
secrets.
- Fixed a race during config dependency updates that could
redirect a lockfile write through a symlink #14322.
- pnpm add --allow-build=!<pkg> now correctly denies builds,
including in global installs. pnpm approve-builds <pkg> and
pnpm approve-builds !<pkg> now save decisions even when the
package is not awaiting approval, with a warning #14067.
- Fixed pnpm audit --fix failing without a value or when
followed by another flag. pnpm audit --fix=override now
respects saveExact and savePrefix when writing overrides
[#13261], #11523.
- pnpm audit now excludes ignored advisories from vulnerability
totals and severity counts, and reports them separately
[#14535].
- pnpm deploy no longer requires injectWorkspacePackages. If a
workspace dependency's peer has multiple possible versions,
deployment reports ERR_PNPM_DEPLOY_AMBIGUOUS_PEER with the
conflicting versions. Pin the peer with overrides to deploy
without injection #9386.
- Fixed concurrent installs sharing a store occasionally
failing with an ENOENT error while importing a package file
[#14353].
- Fixed installation failures when a linked local dependency
provides a peer dependency also provided by an ancestor,
including with pnpm deploy --legacy.
- pnpm install --node-linker=hoisted no longer downloads
skipped optional dependencies when node_modules already
exists #14139.
- Fixed pnpm install rejecting a symlinked lockfile when config
dependencies are unchanged. Updates to config dependencies
also preserve lockfiles with a byte order mark. Writes
through symlinked lockfiles remain blocked #14372.
- pnpm install now relinks workspace packages when
publishConfig.linkDirectory changes. Frozen installs require
the lockfile to be regenerated #14488.
- Auto-installed optional peers now satisfy their declared
range even when the workspace root uses a version outside
that range #13867.
- Fixed global virtual store paths for dependency cycles to
consistently account for the runtime engine when dependencies
have allowed builds #14341.
- Standalone installations now preserve the bundled node-gyp
files needed to build native dependencies.
- Downloaded runtimes are now available to dependency lifecycle
scripts during installation.
- Node.js downloads from nodeDownloadMirrors now use URL-scoped
npm credentials, including bearer tokens, basic auth, and
tokenHelper #14334.
- Fixed globalDir and globalBinDir handling in global
configuration and environment variables, including ~/
expansion. This fixes pnpm add -g failing after pnpm config
set -g global-bin-dir #14336.
- The JavaScript pnpm can again switch to the project's pinned
pnpm version on hosts without a matching native binary. If
the requested version requires an unavailable native binary,
the error now identifies the unsupported host #13622.
- Global pnpm config commands now skip project package manager
version switching, allowing authentication to be configured
before downloading the pinned version #14463.
- pnpm self-update, pnpm with, and automatic version switching
no longer wait through registry retries when a configured
registry has no signatures and registry.npmjs.org is
unavailable #14483.
- Fixed argument forwarding on Windows with shellEmulator
enabled. Trailing backslashes, line breaks, and literal shell
expressions are preserved #14548.
- Relative scriptShell paths now resolve from the workspace
root. Bare command names such as bash still use PATH #14422.
- pnpm import now preserves the project-local lockfile when
lockfileDir points elsewhere and restores the destination
lockfile on failure. Branch lockfile imports leave the shared
lockfile unchanged #14563.
- catalogMode and --save-catalog no longer move local paths,
tarballs, or workspace:<path> specifiers into catalogs
[#14437].
- --side-effects-cache, --no-side-effects-cache, and
PNPM_CONFIG_SIDE_EFFECTS_CACHE now toggle only the local
cache, preserving any remote cache configured in
sideEffectsCache.
- pnpm unpublish now handles registry two-factor authentication
challenges through web authentication or a one-time password
prompt #14464.
- pnpm outdated and pnpm update now follow GitHub Actions
references using self-repository syntax, such as uses:
$/.github/actions/setup.
- pnpm remove now accepts --unsafe-perm.
- update to 11.11.25:
* Minor Changes
- 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 macOS and Windows x64 and arm64 support to remote
shared build artifacts pnpm/pnpm#13771.
- Added the audit.ignorePrune setting. When set to true, pnpm
audit --fix removes ignored GHSA entries that no longer
appear in the audit report.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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
- 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.
- 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).
* 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.
- Prevent installs through a symlinked node_modules directory
from rewriting the target checkout pnpm/pnpm#14286.
- Treat empty scripts selected by a regular expression as
missing before running dependent tasks.
- The options type of the fetch command now declares
allowBuilds, a setting its handler already forwarded to the
installer. Type-level only — what pnpm fetch does is
unchanged.
- Filter hidden scripts matched by a regular expression during
recursive runs when a visible script also matches.
- Fixed automatically switched pnpm versions forcing all
descendant pnpm processes to use the same version
pnpm/pnpm#14309.
- Fixed ERR_PNPM_UNUSED_PATCH validation during incremental
installs pnpm/pnpm#13692.
- Fixed pnpm deploy --prod failing when an excluded dev
dependency was also declared as an optional peer dependency
pnpm/pnpm#14302.
- 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.
- Copying a built package to its other hoisted locations no
longer replaces the destination directory. With nodeLinker:
hoisted, that replacement deleted the dependencies nested
inside the destination's node_modules, and made concurrent
copies of the same build chunk fail with ERR_PNPM_ENOENT: no
such file or directory, rename '.../node_modules/_tmp_...'
[#12880].
- 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].
- Make pnpm add --lockfile-only skip dependency linking
pnpm/pnpm#14286.
- --production is accepted again as an alias of --prod on
install, fetch, prune, update, list, why, and sbom, and the
install that verifyDepsBeforeRun reproduces is now spelled
with --prod. pnpm run no longer aborts with "unexpected
argument '--production' found" after a production-only
install #14147.
- 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.
- Forward patchedDependencies hashes and packageExtensions to
pnpr so server-side resolution preserves patches and package
extensions in the lockfile and installed packages.
- Published the workspace task graph and scheduler as
@pnpm/workspace.task-scheduler so other workspace commands
can use the same dependency-aware scheduling as recursive run
and exec.
- 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.
- 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.
- Fixed recursive run cleanup on Windows when a lifecycle
script fails while another script's process tree is still
running.
- The update notification now suggests pnpm self-update when
PNPM_HOME manages the pnpm in use, and the standalone install
script otherwise — under Corepack, or when another package
manager installed pnpm. pnpm self-update under Corepack names
the standalone install script too.
- Enforce allowBuilds when a prepared git dependency is reused
from the shared store, and use the lockfile's canonical git
resolution ID in approval suggestions.
- Topologically sorting workspace projects now runs in linear
time, fixing installs and lockfile updates that stalled for
seconds on workspaces with thousands of projects forming deep
dependency chains #14149, #14151.
- update to 11.11.24:
* Minor Changes
- Added global build approvals pnpm/pnpm#14101.
* Patch Changes
- Fixed pnpm v11 incorrectly reporting confirmModulesPurge as
unrecognized when set in pnpm-workspace.yaml. The Rust CLI
now identifies the unsupported option as a pnpm v11 setting
instead of suggesting an unrelated setting.
- pnpm install --frozen-lockfile no longer fails with
ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE when the
pinned pnpm version recorded in pnpm-lock.yaml has to be
re-resolved before it can be installed. It runs the pnpm
version the lockfile pins and leaves the lockfile unchanged
[#14124].
- Under nodeLinker: hoisted, peer-resolution variants of an
injected directory dependency (a file: snapshot) are
materialized as separate copies again instead of collapsing
onto the first-seen variant. Each copy keeps its own
peer-resolved dependency set, so a project pinning one peer
version no longer resolves another project's variant — Bit
root components with conflicting peers across injected copies
rely on this.
- Fixed pnpm install --merge-git-branch-lockfiles
- -frozen-lockfile failing with ERR_PNPM_OUTDATED_LOCKFILE
when a branch lockfile predates the removal of a dependency,
or its move to another dependency group #13966. A dependency
that no project declares anymore is no longer reinstated by
the merge, and the packages it was the only path to are
dropped with it.
- Batch workspace publishing accepts a shared scope-specific
credential, rejects mismatched credentials for a registry
before publishing, and runs the publish and postpublish
scripts after each completed registry group pnpm/pnpm#14101.
- The Rust CLI now honors five settings it recognized but
ignored: updateNotifier, legacyDirFiltering, initAuthorName /
initAuthorEmail / initAuthorUrl, initLicense, and
initVersion. pnpm install and pnpm add check once a day for a
newer pnpm and print how to get it (turn it off with
updateNotifier: false); a {<dir>} filter selector can go back
to matching the subtree below the directory with
legacyDirFiltering: true; and pnpm init writes the configured
author, license, and version into the package.json it
scaffolds. PNPM_CONFIG_INIT_VERSION is now read as well.
maxsockets, npm's spelling of maxSockets, is no longer
ignored: both spellings are read from pnpm-workspace.yaml,
the global config file, the environment, and the command
line, in that increasing order of precedence — a value passed
on the command line now wins even when the two sides spelled
the setting differently.
A lastUpdateCheck timestamp dated in the future — after a
clock change, a restored snapshot, or a hand-edited state
file — no longer silences the update check until that time
comes around.
legacyDirFiltering no longer reaches the workspace-root
selectors pnpm generates for itself: the !{<workspace-root>}
exclusion a recursive run / exec / add / test appends, and
the {<workspace-root>} inclusion --workspace-root appends.
Read as subtree matches they named every project below the
root, so a recursive command under the setting selected
nothing at all, and --workspace-root pulled in every project
below the root instead of the root alone #14101.
- pnpm install --frozen-lockfile no longer fails when
pnpm-lock.yaml records the pinned pnpm version alongside an
engine package the running pnpm does not install it from. An
entry pinning another version is still refused, and a plain
install rewrites the block #14124.
- update to 11.11.23:
* Minor Changes
- pnpm config get and pnpm config list now show the settings
pnpm acts on under their documented names:
- registries shows 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 @jsr scope and the npmjs and gh prefixes —
unless pointed elsewhere. Previously pnpm config get
registries printed undefined.
- update and audit show the effective sections, whichever
spelling set them. The deprecated internal spellings
(updateConfig, auditConfig, auditLevel) are no longer
listed.
- catalogs shows the complete resolved catalog set — the
singular catalog block is its default entry — whichever
spelling declared it.
- The registry and @scope:registry entries show the merged
routes rather than raw .npmrc values, so they always agree
with the registries view.
- 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, globalShims is 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's
pnpm-workspace.yaml, previously ignored silently, are now
reported the same way. pnpm config get <key> and pnpm get
<key> no longer print config-load warnings, so a script
capturing the value gets the value alone.
- The importPackage pnpmfile 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.yaml no 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 enableGlobalVirtualStore is on, every process pnpm
spawns for the project (pnpm run, pnpm exec, lifecycle
scripts) now receives a NODE_PATH pointing at the project's
hoisted node_modules, plus a NODE_OPTIONS --import flag that
registers a resolve hook restoring NODE_PATH lookups 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-path config dependency pnpm/pnpm#9618.
Tools run by pnpm dlx resolve 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 time field, so resolutionMode: time-based reads
the full metadata document only from the registries that need
it:
resolutionMode: time-based
registries:
https://npm.internal.example/:
supportsTimeField: true
registry.npmjs.org omits time from 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:
registrySupportsTimeField answered 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 a time field npmjs does not serve. The
answer is now per registry, and registrySupportsTimeField
remains 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 registries setting declares them — keyed by URL,
with the scopes routed to each, the bare-specifier prefix
each answers to, and each one's serverType — 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
serverType reaches 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.io on 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:registry in a
developer's ~/.npmrc no 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 registries setting 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.
registries:
https://artifactory.example.com/artifactory/api/npm/npm-virtual/:
serverType: artifactory
scopes: ['@acme', '@acme-internal']
prefix: work
- serverType tells pnpm how the registry lays out its tarball
URLs, which decides whether a URL can be omitted from
pnpm-lock.yaml:
- undeclared (the default) — strict. Only the exact
canonical URL is treated as reconstructible.
- npm — the registry behaves like registry.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
from pnpm-lock.yaml instead of being written out for
every scoped package pnpm/get-npm-tarball-url#16.
- scopes lists the @-prefixed scopes that resolve from this
registry. A bare '@' is the scope-less default registry,
the one the registry setting names.
- prefix is 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.org
continues to behave as npm without being declared. Because
the lockfile depends on serverType, it is read from
pnpm-workspace.yaml only — a serverType in the global
config.yaml is 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 registries shape, a map of <scope>: <url> strings,
still works and needs no change:
registries:
'@acme': https://npm.acme.example/
namedRegistries is deprecated in favor of the prefix field,
and is still read for prefixes registries does not declare.
toLockfileResolution and isCanonicalRegistryTarballUrl now
take their registry and layout as an options object rather
than positional arguments, so @pnpm/lockfile.utils and
@pnpm/resolving.tarball-url get 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:
virtualStoreType: global # or: project
It is the canonical spelling of enableGlobalVirtualStore,
which keeps working. When a project sets both,
virtualStoreType wins. It can also be set through
PNPM_CONFIG_VIRTUAL_STORE_TYPE and read back with pnpm config
get virtualStoreType. The default is unchanged — project, so
the shared store stays opt-in.
The setting is independent of nodeLinker. isolated and pnp
both work with either store type, and hoisted writes no
virtual store at all, so it is unaffected.
* Patch Changes
- pnpm add --allow-build now adds to the allowBuilds entries
already in pnpm-workspace.yaml instead of replacing them
[#13872].
- Kept pending build approvals available after removing an
unrelated dependency.
- pnpm approve-builds now removes onlyBuiltDependencies,
onlyBuiltDependenciesFile, neverBuiltDependencies, and
ignoredBuiltDependencies from pnpm-workspace.yaml when it
writes allowBuilds. Those settings were replaced by
allowBuilds in pnpm 11 and silently ignored since, so a
workspace migrated from pnpm 10 kept them around looking
active.
- pnpm audit no longer reports a patched version that was never
published or is deprecated. The inferred patched range (e.g.
>=4.17.24 from <=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.1 when 4.17.24 does not exist and 4.18.0 is
deprecated). When no published version satisfies the range,
the report shows Patched versions: None. This also prevents
pnpm audit --fix from adding overrides or
minimumReleaseAgeExclude entries for patches that do not
exist #13824.
pnpm audit --fix and pnpm audit --fix update no longer add a
minimumReleaseAgeExclude entry 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 the minimumReleaseAge gate #11563.
The --json output of pnpm audit now returns patched_versions:
null for 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-commit in project and edit paths containing
non-ASCII characters.
- The package and bump pickers of pnpm change now size their
page from the terminal height instead of always showing 7
rows. They fall back to 7 rows when the terminal height is
unknown pnpm/pnpm#13815.
- Canceling a pnpm change prompt with Ctrl-c no longer prints a
stack trace. It reports Change canceled and exits with a
success status, like the other interactive commands #13814.
- Re-fetch full registry metadata when minimumReleaseAge is
enabled and an abbreviated packument's time map 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 a
tarball) 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_VERSION being ignored when setting the
Node.js version used for compatibility checks.
- A custom fetcher can no longer replace the archive integrity
that pnpm-lock.yaml pins: the locked value is restored after
a canFetch or fetch hook 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
pnpmfile setting (a single path or an ordered list), and
hands custom fetchers native localTarball and remoteTarball
callbacks — 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 --check in projects
with nodeLinker: hoisted would cause dependencies to be moved
out of node_modules into node_modules/.ignored.
- pnpm deploy --prod and pnpm deploy --no-optional no longer
list the excluded dependency groups in the deployed
package.json and pnpm-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.json as a package license
when deciding if the workspace LICENSE file should be
included in the packed package.
- pnpm exec --recursive --no-reporter-hide-prefix no 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 404 errors when installing from a registry that serves
scoped packages only from a percent-encoded path, such as
GitHub Enterprise Server. Outside registry.npmjs.org, a
tarball URL that encodes the scope separator as %2f or %2F is
no longer mistaken for one that pnpm can rebuild from the
package name, version, and registry, so it is kept in
pnpm-lock.yaml and requested verbatim on the next install
[#13534].
- Fixed trustPolicyExclude and minimumReleaseAgeExclude being
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 init now pins the exact pnpm version instead of a ^
range, and records it in the packageManager field alongside
devEngines.packageManager. Corepack reads only packageManager
and accepts nothing but an exact version, so it rejected the
generated package.json with "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-manager still
scaffolds a manifest without any pin. In pnpm 12, pnpm init
also honors initType and its --init-type flag, 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.ps1
behind. PowerShell resolves pnpm.ps1 ahead of pnpm.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 (and
trustPolicyExclude) 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
packageManagerDependencies block of pnpm-lock.yaml. When the
pnpm version pinned by devEngines.packageManager (or by
packageManager) is missing from the lockfile or no longer
matches it, --frozen-lockfile now fails with
ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE instead 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 next pnpm update moved the dependency to
the repository's default branch #13999.
- Fixed pnpm update --global --latest failing 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, an npm:
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 a 0.x
request, 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.json and the depending package keeps it in
its node_modules/.bin.
- Fixed pnpm install --merge-git-branch-lockfiles deleting the
per-branch lockfiles when the lockfile setting is false. Such
an install never reads them, so it has nothing to merge them
into and now leaves them alone.
- Fixed pnpm install sometimes not exiting after printing Done
in Xs #12297.
- Fixed pnpm failing to read .modules.yaml files 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_PATH again 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 get and pnpm pkg set now accept hyphens inside a
dot-notation property path, so pnpm pkg get
dependencies.some-package-name reads the key instead of
failing with ERR_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-based and lowest-direct reached the
server as nothing at all, leaving it on its highest default:
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 pnpm installs using pnpr to honor the client's
autoInstallPeers, dedupePeers, and excludeLinksFromLockfile
settings pnpm/pnpm#13389.
- pnpm remove now prunes undecided entries ("set this to true
or false") from allowBuilds in pnpm-workspace.yaml when
sharedWorkspaceLockfile: true and the corresponding packages
are removed pnpm/pnpm#13892.
- Fixed workspace discovery for pnpm-workspace.yaml files
without a packages field so commands only consider the
workspace root instead of recursively scanning nested
projects #14047.
- A runtime installed through devEngines.runtime now matches
the host when supportedArchitectures lists several platforms.
Listing os: [darwin, linux] and cpu: [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 sbom now fails with ERR_PNPM_SBOM_MISSING_IMPORTERS when
pnpm-lock.yaml has no entry for a selected project, instead
of writing an SBOM that under-reports that project's
dependencies. Previously this crashed with Cannot read
properties of undefined (reading 'devDependencies').
- pnpm self-update now rewrites a simple
devEngines.packageManager.version range (^/~) to the newly
installed version, keeping the operator — matching how pnpm
update and pnpm runtime set rewrite ranges. Complex ranges
such as >=8.0.0 that 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 than minimumReleaseAge. The maturity cutoff moved
the tag back to the previous mature release, so pnpm
self-update next-12 on v12.0.0-rc.4 switched to v12.0.0-rc.3.
- pnpm set-script now updates package.json instead of failing
with ERR_PNPM_NOT_IMPLEMENTED pnpm/pnpm#13956.
- pnpm update now 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 the workspace: protocol. With
preferWorkspacePackages enabled, 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 fetchWarnTimeoutMs and fetchMinSpeedKiBps to 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 any catalog:-valued override
forced a full re-resolution whenever the override list
changed, which could move unrelated packages in the lockfile
(for example after pnpm audit --fix added an override).
- Packed workspace package manifests now preserve dependency
order, making repeated pnpm pack output deterministic #10167.
- pnpm update <name>@<version> now fails with
ERR_PNPM_UPDATE_VERSION_ON_INDIRECT_DEP when 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 the
overrides entry 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-downgrade no longer aborts the install with
ERR_PNPM_MISSING_TIME on registries that serve no per-version
time field when minimumReleaseAgeIgnoreMissingTime is set.
The trust check reads the same publish dates the
minimumReleaseAge check does, so it now honors the same
opt-in and skips the affected package with a warning #12446.
minimumReleaseAgeIgnoreMissingTime no longer lets a lockfile
entry the registry does not list pass the minimumReleaseAge
check 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-time warning now names the check it is reporting
on, so a package whose minimumReleaseAge and trustPolicy
checks 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 the jsr: equivalent — now
targets the package the alias installs rather than the alias.
- Fixed verifyDepsBeforeRun being ignored when set to install,
warn, error, or prompt through the
PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN environment variable or
the --config.verify-deps-before-run flag #13816. Only the
boolean values were accepted before, so a string value was
silently dropped.
- pnpm version <bump> with --dry-run no longer edits
package.json files. It now only reports the bumps it would
make, and skips the working tree check, the version lifecycle
scripts, the commit, and the tag pnpm/pnpm#13953.
- update to 11.11.22:
* Minor Changes
- Added pnpm cache path, which prints the directory pnpm uses
for its metadata cache. CI setups can use it to cache that
directory — including the lockfile verification log, which
lets a job skip re-checking an unchanged lockfile against the
configured supply-chain policies.
- --config.config-dir no longer reaches the config through a
project's pnpm-workspace.yaml, and neither do the --config.
spellings of the other settings a project manifest may no
longer contribute (--config.pnpm-home-dir,
- -config.workspace-dir, --config.global-pkg-dir,
- -config.root-project-manifest-dir). None of them was ever a
supported way to set those directories: pnpm resolves them
from the environment, and these flags took effect only
because the project-manifest merge re-applied the command
line afterwards. The dedicated flags, such as --dir and
- -global-dir, are unaffected #13629.
- pnpm config set refuses to write a setting to a project's
pnpm-workspace.yaml that pnpm does not read from there,
rather than leaving a key in the file that does nothing.
Those settings are configDir, pnpmHomeDir, stateDir and the
others that name machine-level state. The command fails with
ERR_PNPM_CONFIG_SET_NOT_A_PROJECT_SETTING, naming where the
setting does belong when it belongs somewhere. pnpm config
delete still clears one that a file already carries, in
whichever spelling it uses #13629.
- Added a new setting minimumReleaseAgeExcludePrune. When
enabled, pnpm add, pnpm update, and pnpm remove prune the
entries of minimumReleaseAgeExclude in pnpm-workspace.yaml
that the freshly written lockfile no longer resolves:
versions that are gone are dropped (an entry is removed once
none of its versions remain), and entries for packages that
are no longer in the lockfile are removed too. Name patterns
(@scope/*) are always kept. The cleanup is skipped when the
install's lockfile does not cover the whole workspace
(sharedWorkspaceLockfile: false), since entries another
project still needs would look stale.
Renamed cleanupUnusedCatalogs to catalogPrune, so that
catalog pruning and release-age exclude pruning use one
vocabulary. cleanupUnusedCatalogs continues to work; when
both are set, catalogPrune wins.
- A project's pnpm-workspace.yaml can no longer choose where
pnpm keeps its credentials, its own installation, or the
registry it downloads its next version from. One of those
settings is configDir, which decided where pnpm login writes
the granted token. bin, dir, globalBinDir, globalDir,
npmrcAuthFile, pnpmHomeDir, stateDir, userconfig and
workspaceDir are ignored there now too, and pnpm warns about
the ones it finds. cacheDir and storeDir are unaffected
[#13629].
- Resolving a Node.js runtime version (devEngines.runtime /
runtime: specifiers) is now much faster: the per-version
release metadata is cached in the pnpm cache directory after
its signature is verified, and an exact stable version such
as runtime:22.23.2 no longer downloads the Node.js release
index. A pinned runtime whose metadata was fetched once
resolves without any network access, which removes the
noticeable delay on the first node invocation in a project
pinning an already-downloaded runtime #13899.
* Patch Changes
- Fixed intermittent ERR_PNPM_ENOENT and ERR_PNPM_ENOTEMPTY
errors while renaming _tmp_* directories during installation
with nodeLinker: hoisted, in workspaces that also use
patchedDependencies.
- pnpm add no longer re-resolves the dependency graph when
pnpm-lock.yaml already holds a version satisfying the request
— promoting a transitive dependency to a direct one, or
adding to a second workspace package what a first one already
depends on, now only saves the dependency in package.json and
records its importer entry. A satisfying locked version is
necessary but not sufficient: the install still falls back to
a full resolution for a dist tag, an alias, a
workspace:/catalog:/git/tarball specifier, --save-peer, an
overridden package, a catalogMode other than manual, and —
under resolutionMode: time-based or lowest-direct, which
resolve a direct dependency to the low end of its range — a
range several locked versions satisfy.
- Global installs now switch over atomically. The command shims
in the global bin directory point at a stable per-package
link rather than at the directory a particular install
produced, so pnpm add -g and pnpm update -g activate a new
version by moving that one link instead of rewriting every
shim. A command can no longer be missing from PATH while an
install is in progress, and a failed install leaves the
previous version in place.
- pnpm audit --fix and pnpm audit --fix update no longer add
minimumReleaseAgeExclude entries for patched versions that
were published before the minimumReleaseAge cutoff. The
publish time of each minimum patched version is now checked
against the registry metadata, and only versions young enough
to be blocked by the age gate get an exclusion entry #11563.
- pnpm add <pkg>@<version> and pnpm update <pkg>@<version>
under a non-manual catalogMode now move the catalog entry's
resolution to the requested version. Previously, when the
catalog entry was a range that covered the requested version
but resolved to a different one, the request was dropped
silently: nothing was installed, nothing was written, and no
error was raised.
- A project that wasn't part of an install that moved a catalog
entry now follows the entry the next time it is installed. It
used to keep the version the entry resolved to before — a
version the entry no longer allowed — and no later install
corrected it, so one catalog entry ended up resolved to two
versions.
- pnpm add <pkg>@<version> and pnpm update <pkg>@<version>
under catalogMode: strict no longer fail with
ERR_PNPM_CATALOG_VERSION_MISMATCH when the catalog entry is a
range that the wanted version satisfies. The dependency keeps
using the catalog; only a version that really falls outside
the catalog's range is rejected #13715.
- A changed catalogs or pnpm.overrides block no longer has to
be the only change for pnpm install to update the lockfile in
place. Editing an override while also removing a dependency,
or changing a catalog entry in the same commit as a range
bump, is now absorbed in one pass instead of re-resolving the
whole dependency graph #13799.
Fixed the lockfile an in-place override update wrote when the
overridden package was also a catalog entry: the entry kept
the version it had before the override moved the package. The
same could happen in reverse, when a catalog entry moved a
package an override pins. Both cases now re-resolve instead.
- pnpm install now updates the lockfile in place even when
several kinds of changes happened since the last install —
for example a removed dependency together with a widened
ignoredOptionalDependencies list, or a dependency edit
alongside a patch or settings change. Previously any
combination of changes forced a full re-resolution #13763.
- pnpm deploy injects workspace dependencies again, so the
deploy directory is self-contained instead of symlinking back
into the source workspace #13754. Enabling
injectWorkspacePackages with dedupeInjectedDeps disabled now
also rewrites already-linked workspace dependencies to
injected copies.
- pnpm deploy --no-optional no longer writes a lockfile whose
snapshots reference optional dependencies that the deploy
excluded.
- Removing the last dependency that references a catalog entry
via the fast lockfile update no longer leaves the stale
catalog entry in pnpm-lock.yaml.
- A git dependency whose clone (or shallow fetch) fails now
reports which package it belongs to, under the
ERR_PNPM_GIT_FETCH_FAILED code, with credentials in the
repository URL redacted. When the lockfile records an SSH
remote, the error also explains that fetching it needs an SSH
key for that host, and that a lockfile entry written before
pnpm v11.21 can be re-recorded over HTTPS with pnpm update
<package> #13743.
- An integrity recorded on a git dependency's resolution
(resolution: {type: git, repo, commit, integrity: sha512-…})
is no longer treated as a checksum. pnpm never verifies a git
checkout against such a hash — the commit pins the content —
so it is now dropped when the lockfile is rewritten, and pnpm
sbom no longer republishes it as a CycloneDX/SPDX checksum.
Lockfiles carrying one also load again instead of failing
with ERR_PNPM_BROKEN_LOCKFILE #13042.
pnpm sbom now also publishes the checksum of a type: binary
runtime archive, which pnpm does verify.
- A git dependency whose git ls-remote fails now reports the
ERR_PNPM_GIT_RESOLVE_FAILED code, naming the dependency
instead of printing a bare git invocation, with credentials
in the repository URL redacted. A specifier that does not ask
for SSH resolves over HTTPS, because the URL recorded in the
lockfile has to work on every machine that installs it, so
the error explains how to substitute the transport on a
machine that can only reach the host over SSH (git config
- -global url."git@<host>:".insteadOf "https://<host>/")
[#13743].
A missing git executable is reported as one, instead of
surfacing the raw failure to start the process.
Credentials embedded in a git specifier are redacted from the
"Could not resolve <ref> to a commit of <repo>" errors too.
Resolving a public repository makes one git ls-remote
round-trip instead of two.
- pnpm install after moving a dependency between dependencies,
devDependencies, and optionalDependencies now updates the
lockfile in place instead of re-resolving the whole
dependency graph #13696.
- syncInjectedDepsAfterScripts no longer fails with
ERR_PNPM_UNSUPPORTED_INODE_TYPE when a workspace package
contains an inode that is neither a file nor a directory,
such as the FIFO 1Password's environments create for .env.
Such an inode cannot be hardlinked into the injected copy, so
it is skipped and the rest of the package still syncs #13550.
- syncInjectedDepsAfterScripts also no longer fails with EEXIST
when a workspace package replaced a file with a directory of
the same name since the injected copy was last synced.
- syncInjectedDepsAfterScripts no longer fails with ENOTDIR
when a workspace package replaced a directory with a file of
the same name and the injected copy still held that
directory's contents.
- syncInjectedDepsAfterScripts now removes the bin link of a
bin the script dropped. Previously only new bins were linked,
so a build step that stopped declaring one left its shim
behind, pointing at a command that was no longer there.
- syncInjectedDepsAfterScripts now identifies a file by its
device as well as its inode number. An inode number is only
unique within one filesystem, so on its own it could match an
unrelated file on another device and leave that path stale in
the injected copy.
- pnpm store prune no longer deletes the lockfile verification
log. The log records which lockfile passed which supply-chain
policies, so it stays valid across a prune of the store;
keeping it lets the next install skip re-verifying an
unchanged lockfile.
- Widening a dependency's range no longer leaves the project on
an older version. The lockfile update now points the project
at the highest version of that dependency already in the
lockfile that satisfies the new range — matching what a full
resolution records — instead of keeping the locked version
whenever it happened to satisfy, which could leave a
duplicate behind. A range change that only an already-locked
version satisfies is now also handled without re-resolving
[#13778].
- resolutionMode is no longer ignored when minimumReleaseAge is
in effect. lowest-direct and time-based pick the lowest
satisfying version of a direct dependency again; previously
any active release-age cutoff — including the built-in
default — silently forced the highest, so resolutionMode only
worked when minimumReleaseAge: 0 was set explicitly #13752.
- Adding a package to a workspace no longer forces a full
re-resolution when every dependency it declares is already
locked for a sibling. The lockfile update writes the new
project's importer entry from the versions the lockfile
already holds; a dependency no locked version satisfies still
reaches the resolver #13696.
- pnpm config delete <key> no longer fails with ENOENT when the
config file it would edit does not exist. Clearing a setting
that was never set is a no-op #13651.
- Changing a pnpm.overrides entry to a version range now
updates the lockfile in place when a version the lockfile
already holds satisfies the range, instead of re-resolving
the whole dependency graph. Only exact versions were handled
before #13696.
- Changing a parent-scoped pnpm.overrides entry
("parent>child": "2.0.0") now updates the lockfile in place
instead of re-resolving the whole dependency graph. Only the
named parent's dependency moves; every other package keeps
the version it had #13795.
- Removing a dependency, or moving one to another
already-locked version, no longer re-resolves the whole
dependency graph just because some package resolves a peer
with the same name. The lockfile update now compares the peer
suffixes against the exact name@version the removal severed,
so a suffix that names a different — still present — version
of that dependency is left alone #13781.
- Projects with a pnpmfile now use the fast lockfile update
paths: an unchanged pnpmfile (proven by the recorded
pnpmfileChecksum) no longer forces a full re-resolution for
removals, dependency group moves, compatible range changes,
and the other in-place lockfile rewrites #13696.
- A lockfile entry whose resolution is unchanged no longer
loses its recorded deprecated marker when a registry serves
the package's metadata inconsistently — re-resolving to the
same version keeps the deprecation instead of silently
dropping the line #13846.
- pnpm prune is now recursive by default inside a workspace,
just like pnpm install. This fixes pnpm prune --prod in a
workspace root emptying the node_modules directories of the
other workspace projects, dropping the links to the workspace
packages they depend on in production #13718.
- A setting written in kebab-case in the global config.yaml is
now reported instead of being silently ignored #13650.
- pnpm remove no longer re-resolves the dependency graph. The
removed dependency's entries are dropped from pnpm-lock.yaml
and anything they made unreachable is pruned, without
registry access. The install still falls back to a full
resolution when a surviving package resolves a peer
dependency through the removed one.
- Removing a package from a workspace no longer forces a full
re-resolution. The lockfile update drops the departed
project's importer entry and prunes whatever only it depended
on. A project that is still linked from a surviving project
continues to be reported as an error #13696.
- An install sharing a global virtual store no longer removes
an incomplete package directory that another importer is
still writing, which could fail with failed to remove
existing directory ... prior to swap: Directory not empty.
Such a directory is now repaired in place, and a package file
left damaged by an interrupted install is restored instead of
being kept.
- pnpm sbom no longer emits components for optional
platform-specific dependencies that cannot be installed on
the current platform (for example, the native
@rolldown/binding-* variants for other operating systems).
Such packages are present in the lockfile but are never
downloaded, so their license (and other metadata) could not
be resolved and they appeared in the SBOM without one. pnpm
sbom --lockfile-only still describes the whole lockfile
graph, which is platform-independent by design.
- An ssh:// git dependency pointing at a bracketed IPv6 host,
such as ssh://[::1]/repo.git, is resolved now. Its colons
were read as an SCP-style path separator, which turned the
address into [:/1] and left the specifier unresolvable.
Applies to both the TypeScript CLI and pacquet.
In the TypeScript CLI, an ssh:// git dependency written
without user info — ssh://git.example.com/team/repo.git,
git+ssh://git.example.com:2222/team/repo.git — no longer
fails with TypeError: Cannot read properties of undefined
(reading 'includes'). Only the user@host form worked before.
- packageExtensions is now validated when the configuration is
read, so a malformed entry (for instance a dependency range
set to null) fails with an actionable error instead of
crashing later during peer dependency resolution #13756.
- Projects using resolutionMode: time-based now benefit from
the fast lockfile update paths. A removal, a dependency group
move, or a compatible range change no longer forces a full
re-resolution just because the lockfile carries a time field
[#13696].
- An install that drops the last dependent of a patched package
no longer updates the lockfile in place and succeeds
silently. Removing a dependency, widening
ignoredOptionalDependencies, or adding a removal override
could each prune the package while the patch stayed
configured; such an install now falls back to a full
resolution, which reports the unused patch with
ERR_PNPM_UNUSED_PATCH. Under allowUnusedPatches, where the
lockfile update is kept, the same install now warns that the
patch went unused instead of saying nothing #13827.
- update to 11.11.21:
* 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 as pnpm add --global)
through sudo now 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 with
ERR_PNPM_SUDO_NOT_SUPPORTED in pnpm v12. Read-only global
commands (such as pnpm bin --global) are unaffected.
* Patch Changes
- Fixed pnpm failing to start under asynchronous Node.js module
loaders when no .pnpmfile.mjs exists pnpm/pnpm#11701.
- Fixed minimumReleaseAge fallback for custom dist-tags so the
selected version does not exceed the registry’s original tag
target.
- Removing a dependency from package.json and reinstalling no
longer re-resolves the dependency graph. The importer's entry
is dropped from pnpm-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.yaml directly, reusing the same check
the pnpm.overrides fast 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/repo dependencies
(and other shorthand Git specifiers) would fail to install
with Permission 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:// or
git@host:...):
- The repository visibility probe (an HTTP HEAD request) now
retries transient failures such as 429 Too Many Requests,
so host throttling of CI runners is no longer mistaken for
a private repository.
- For non-SSH specifiers, anonymous HTTPS git ls-remote
access 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.
- When every probe fails, the resolver falls back to HTTPS
for shorthand and HTTPS-style specifiers, and only guesses
SSH when the user explicitly provided an SSH URL.
- A repository that could not be confirmed public is no
longer resolved to the host's anonymous archive URL (e.g.
codeload.github.com, which would fail to download for a
private repository); it stays a regular git resolution 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 build and nuxt build now work under the global virtual
store: pnpm's built-in compatibility extensions add the tslib
dependency that @angular/build uses without declaring and the
unplugin dependency that @nuxt/vite-builder v4 uses without
declaring.
- Fixed link: dependencies under enableGlobalVirtualStore so
linked children are materialized and slots remain isolated by
their resolved link targets.
- An install that skips resolution because pnpm-lock.yaml is
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, and pendingBuilds entries for removed
packages are dropped instead of staying pending forever.
- The held-back-update warning printed by pnpm update no longer
fires when minimumReleaseAge is 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 ignoredOptionalDependencies is 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, or injectWorkspacePackages no
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 in pnpm-lock.yaml and the install proceeds from
the existing resolution. Every other case still falls back to
a full resolution.
- Adding, editing, or removing an entry in patchedDependencies
no 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 in pnpm-lock.yaml and 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 while
allowUnusedPatches is off, so ERR_PNPM_UNUSED_PATCH is still
reported.
- Resolving a private git repository no longer blocks on an
interactive credential prompt: git ls-remote now 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_META condition 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 on PATH #13405.
- The automatic packageManager version 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 automatic packageManager version switch and
pnpm self-update #13147. When the configured registry cannot
provide a verifiable signature, pnpm now fetches the
signature from registry.npmjs.org and 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 a shasum), 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 with virtualStoreOnly, no
longer writes a .pnp.cjs loader under nodeLinker: 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 and node_modules/.package-map.json were
already skipped; the PnP loader now follows the same rule.
- Prevent pnpm from removing project files when modulesDir
resolves to the project root.
- Speed up installs after adding ignoredOptionalDependencies
patterns 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 install now reapplies the executable bit to
the bin instead of leaving it non-executable #12742.
- pnpm root -g and pnpm bin -g now print warnings to stderr
instead of stdout, so their stdout stays a clean,
machine-readable path. Previously, running either command
with --global in a project that pins a package manager (e.g.
via the packageManager field) printed a warning like [WARN]
Using --global skips the package manager check for this
project ahead of the path, breaking programs that capture the
output as a path #13672.
In pnpm 12, pnpm root -g and pnpm prefix -g are now supported
(they previously failed with
ERR_PNPM_CLI_ROOT_GLOBAL_UNSUPPORTED /
ERR_PNPM_CLI_PREFIX_GLOBAL_UNSUPPORTED), and the reporter
output of dlx, create, config, sbom, with, store, prefix,
root, and bin goes to stderr, matching pnpm 11.
- pnpm setup no longer makes Node.js print a
MODULE_TYPELESS_PACKAGE_JSON warning about dist/worker.js on
every command. The package.json it writes next to a
standalone executable now declares "type": "module".
- pnpm update without 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 --latest
resolves within it instead of past it. Previously each of
these could write a lockfile entry that contradicted its own
specifier, which the next pnpm install --frozen-lockfile
rejected with ERR_PNPM_OUTDATED_LOCKFILE #12764.
- pnpm version -r --json now outputs [] instead of
human-readable text when no pending changes exist
pnpm/pnpm#13217.
- update to 11.11.20:
* Minor Changes
- Security fix. Affects projects using namedRegistries on 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@version alone, and
entry lookup went through refToRelative(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 single packages: 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 namedRegistries sees 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
namedRegistries setting on demand.
To use named registries, map your aliases in
pnpm-workspace.yaml:
namedRegistries:
work: https://npm.enterprise.example.com/
- New built-in npmjs: alias
npmjs: now resolves to https://registry.npmjs.org/ with no
configuration, alongside the existing gh: alias for GitHub
Packages. It pins a dependency to the public registry even
when registry points 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 whatever registry
points at.
- If you mirror or proxy npmjs, point the alias at your mirror:
namedRegistries:
npmjs: https://npm.internal.example.com/
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.org is 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, or trustPolicy
check runs. Overriding the alias is the same escape hatch
GHES users already have for gh.
Every alias the lockfile references must stay in
namedRegistries: reading an entry whose alias is gone fails
with ERR_PNPM_MISSING_NAMED_REGISTRY rather 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 with
ERR_PNPM_RESERVED_NAMED_REGISTRY_NAME instead of being
silently shadowed by the corresponding resolver.
pnpm licenses and pnpm sbom now keep the two artifacts apart
as well: license records carry the registry alias, and SBOM
components carry the purl repository_url qualifier.
* Patch Changes
- An empty http-proxy, https-proxy, proxy, or no-proxy value —
from the .npmrc, pnpm-workspace.yaml, the CLI, or the
HTTP_PROXY / HTTPS_PROXY / PROXY / NO_PROXY environment
variables — no longer fails the install with
ERR_PNPM_INVALID_PROXY. Empty settings read as unset, so a
shell exporting HTTP_PROXY= disables the proxy, and an empty
proxy= in the .npmrc no longer suppresses HTTPS_PROXY #13533.
proxy=false in the .npmrc or proxy: false in
pnpm-workspace.yaml now turns proxying off instead of being
read as a proxy host named false. false and null on
https-proxy / http-proxy / no-proxy read 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/exe alongside pnpm when
the wanted pnpm version is 12 or newer. From v12 the unscoped
pnpm package is itself the native executable, so @pnpm/exe is
not published for it and resolving it would fail. The engine
identity check now verifies the native binary through
whichever package ships it.
- lexCompare and nerfDart are now published as
@pnpm/text.ordinal-comparator and
@pnpm/config.registry-auth-key. Use these instead of
@pnpm/util.lex-comparator and @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 minimumReleaseAge is active, and parsed semver
versions and ranges are reused instead of re-parsed on every
comparison.
- Security: pnpm rebuild now refuses a lockfile whose packages
key carries a path traversal in the package name (e.g.
../../../escaped@1.0.0), instead of running that package's
lifecycle scripts and linking its bins in a directory outside
the virtual store. Such a name is rejected with
ERR_PNPM_INVALID_DEPENDENCY_NAME.
- update to 11.11.19:
* Minor Changes
- pnpm login no longer requires an interactive terminal when
the registry supports web-based login: without a TTY it
prints the authentication URL (skipping the QR code and the
"Press ENTER to open the URL in your browser" prompt) and
polls the registry until the browser approval completes. Only
the classic username/password login still fails with
ERR_PNPM_LOGIN_NON_INTERACTIVE in a non-interactive terminal.
- The save-prefix setting now accepts =: newly added
dependencies are saved with an explicit = operator (=1.2.3)
instead of the setting being silently treated as the default
^.
* Patch Changes
- allowBuilds entries can now approve git-hosted packages that
pnpm downloads as a tarball, such as github: dependencies
(which are fetched from codeload.github.com rather than
cloned), by their repository URL without the resolved commit
hash. This matches the hashless git+ matching already
supported for cloned git dependencies. For example:
allowBuilds:
"foo@git+https://github.com/org/foo.git": true
This approves the package whether pnpm clones it or downloads
a tarball, so the entry no longer has to be updated every
time the pinned commit changes. GitLab and Bitbucket tarball
downloads are matched the same way. Approving or denying a
specific resolved commit by its full tarball dep path
continues to work.
- pnpm outdated --include-github-actions no longer blocks on an
interactive git credential prompt when a workflow uses a
private action repo.
- Prevented minimumReleaseAge from replacing latest with a
SemVer-greater version than the registry tag target #13034.
- Fixed empty bundledDependencies and bundleDependencies arrays
causing nondeterministic lockfile changes. See #13123.
- The install summary no longer prints (X is available) when
the registry's dist-tags.latest is still held back by the
active minimumReleaseAge policy. The hint only ever names the
actual latest tag, so an immature latest suppresses the hint
instead of advertising the version pnpm just refused to
install #11698.
- pnpm update keeps the explicit = operator of an exact version
pin: a dependency saved as =3.5.1 now updates to =3.5.2
instead of the bare 3.5.2. See #13168.
- Preserve a workspace dependency's link: entry when a run does
not target it — e.g. pnpm update <other-pkg> (with or without
- -recursive), or a plain install after a root/catalog
dependency change — with injectWorkspacePackages, instead of
spuriously rewriting it to a peer-suffixed file: protocol.
See #10433.
- Workspace dependencies declared with a relative path (e.g.
"foo": "workspace:../foo") are no longer silently dropped
from the workspace projects graph, so --filter selection and
the topological order of recursive commands take them into
account.
* Thu Jul 30 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.18.0:
* Minor Changes
- Fixed an installed optional dependency being left without one
of its own required dependencies. When a package reached
through optionalDependencies is installable on the current
system but one of its regular dependencies is not, a
lockfile-based install skipped that dependency and installed
the parent anyway, so importing the parent failed with
MODULE_NOT_FOUND. The dependency is now installed, and an
install-check warning reports the incompatibility. A
dependency is still only skipped when every path to it is
optional, or when the package that pulls it in was itself
skipped #13286.
- pnpm setup now appends PNPM_HOME and the global bin directory
to the GitHub Actions environment files (GITHUB_ENV and
GITHUB_PATH), so later steps in the same job can run pnpm add
- -global and other global commands #9191.
- Added support for publishConfig.name, which publishes a
package under a different name than the one its manifest
carries in the workspace. It is for a project whose published
name is already taken by a sibling project, which otherwise
has to be renamed by a build step just before publishing.
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 of pnpm publish -r, its registry selection, and the
release-planning probes of pnpm change status and pnpm
version -r #13345.
- pnpm self-update no 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 .npmrc or pnpm-workspace.yaml can no longer
redirect the download or attach credentials to it, and the
project's default .pnpmfile.(c|m)js is no longer loaded.
Pnpmfiles from trusted sources (the pnpmfile setting, the
global pnpmfile, config dependencies) still apply.
- The minimumReleaseAge settings in pnpm-workspace.yaml no
longer affect self-update. They still govern the project's
own dependencies; for self-update the cooldown now comes
from the built-in default, your global config, a
PNPM_CONFIG_* environment variable, or a command-line flag.
This fixes self-update failing 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 trustPolicy settings and to ci: 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-update refuses 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.
* Patch Changes
- Fixed pnpm licenses list to report every version when the
same package is installed under multiple aliases
pnpm/pnpm#13438.
- Sort pnpm dedupe --check snapshot changes for stable output
across pnpm implementations.
- Strip Unicode formatting characters from registry- and
manifest-derived terminal output.
- 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.
- Installing a local file: directory dependency with the global
virtual store enabled no longer fails with TypeError: 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.
- The Workspace column of pnpm update --interactive now falls
back to the project's path when its name is only whitespace,
as it already did for a missing or empty one — all three
render an equally blank label otherwise.
- Checking GitHub Actions dependencies for updates is now
opt-in for every command. Neither pnpm outdated nor pnpm
update reads the workflow files unless
- -include-github-actions is passed or update.githubActions is
set to true in pnpm-workspace.yaml. Reading them runs git
ls-remote against every referenced repository, which fails in
environments where GitHub is not reachable the way pnpm
assumes (a GitHub Enterprise Server, a custom certificate
authority, or an offline network) #13254.
pnpm outdated accepts the --include-github-actions option
too.
- pnpm update --interactive now 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 with Subject parameter value width cannot
be greater than the container width #13357.
- The Workspace column of pnpm update --interactive is more
informative in two cases. A dependency outdated at the same
version in several workspace projects is offered as one
choice, since selecting it updates every project — that
choice now names all of them instead of only the first. And a
workspace project without a name is now labelled with its
path rather than left blank, so several unnamed projects can
be told apart.
- An auto-installed optional peer is no longer hoisted at a
version the workspace root's own dependency on that package
excludes. resolvePeersFromWorkspaceRoot already 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 pins
postcss: 8.5.10, an importer that depends on webpack and
declares no postcss of its own got postcss@8.5.22 hoisted for
terser-webpack-plugin's optional postcss peer, leaving two
postcss@8.5.x instances in the graph #13320.
- overrides now also govern peers that pnpm auto-installs.
Previously an override only rewrote dependencies declared in
a manifest, so a peer nobody declares — installed because
autoInstallPeers is on — resolved against its declared peer
range and could bring in a second copy of the very package
the override pinned. For example, with overrides: { react:
npm:react@19.2.0 } and a lone lucide-react dependency, pnpm
installed react@18.3.1; it now installs the pinned
react@19.2.0 #13320.
- Under resolvePeersFromWorkspaceRoot, a workspace root
dependency declared with link: or file: (or the path form of
workspace:, such as workspace:../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.
- Installs through a pnpr server now apply the project's whole
verification policy. minimumReleaseAgeExclude,
minimumReleaseAgeIgnoreMissingTime, trustPolicy,
trustPolicyExclude, trustPolicyIgnoreAfter, and trustLockfile
were ignored, so excluded packages were still held back and a
lockfile containing them could be rejected.
trustPolicy: no-downgrade no longer fails with
TRUST_POLICY_INCOMPATIBLE_WITH_PNPR when a pnpr server is
configured.
- -frozen-lockfile and --no-prefer-frozen-lockfile are now
honored on the pnpr path, instead of resolving and rewriting
the lockfile anyway. Since frozenLockfile defaults to true on
CI, a CI install through a pnpr server now fails on an
out-of-date lockfile rather than updating it.
- Workspace installs through a pnpr server no longer crash with
Cannot read properties of undefined (reading 'filter') after
linking, when minimumReleaseAge is active #13275.
- Fixed pnpm dedupe updating valid catalog resolutions when
another matching version exists in the lockfile.
- pnpm -r run "/pattern/" --no-bail no 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.
- Restored the store block a first install prints, naming how
packages were materialized and where the stores live #13315:
Packages are hard linked from the content-addressable store to the virtual store.
Content-addressable store is at: ~/.local/share/pnpm/store/v11
Virtual store is at: node_modules/.pnpm
- The root project's pnpm:devPreinstall script now runs before
resolution and linking, as it does in pnpm 11. It is skipped
under --ignore-scripts, --lockfile-only and --dry-run, by
pnpm fetch and pnpm 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 placeholder next bin with it — were left with
dependents linked against files that were never created
[#13313].
- Prevented pnpm dedupe --check from removing an incompatible
node_modules directory.
- pnpm update --workspace no longer links dependencies the user
never named:
- Running it with updateConfig.ignoreDependencies configured
no longer fails with ERR_PNPM_WORKSPACE_PACKAGE_NOT_FOUND
for a dependency that is only published to the registry.
Such dependencies keep their specifiers, as they already
did when no dependencies were ignored.
- Passing package selectors that match no direct dependency
no longer falls back to linking every workspace dependency.
- update to 11.17.0:
* Minor Changes
- Added a new setting, update.githubActionsServer, for
specifying the base URL of the GitHub server that hosts the
repositories of the GitHub Actions referenced by the workflow
files (for example, a GitHub Enterprise Server). When the
setting is not defined, the URL is read from the
GITHUB_SERVER_URL environment variable, falling back to
https://github.com. The URL must use the https:// or http://
protocol #13220.
pnpm outdated and pnpm update no longer fail when the refs of
a GitHub Action's repository cannot be read (for example,
when the action's repository is private or hosted on a
different GitHub server). Such actions are now skipped with a
warning.
Setting update.githubActions to false now makes pnpm outdated
and the interactive pnpm update skip GitHub Actions
dependencies.
* Patch Changes
- The token poll for web-based authentication no longer reads
the body of non-OK or still-pending (HTTP 202) responses, and
caps the token response body it does read at 64 KiB, so a
malicious or compromised registry cannot exhaust memory
through the poll pnpm/pnpm#12721.
- Fixed catalog: references in dependencies and overrides
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. Also fixed
a crash on Windows when installing a nested workspace member
(e.g. packages/foo) through a pnpr server #13232.
- Republished every package: the tarballs published by the
v11.13.1 through v11.16.0 releases were missing most of their
compiled files due to a packing bug #13164.
- Revert script ordering change for pnpm run --sequential
/regex/
- Support the from-git argument in the pnpm version command.
- When the authentication URL cannot be rendered as a QR code
(for example when it exceeds the maximum QR data capacity),
web-based login now displays the URL alone with a warning
instead of aborting authentication pnpm/pnpm#12721.
- update to 11.16.0:
* Minor Changes
- The first release of a package now publishes the version
written in its manifest verbatim, instead of bumping off it.
pnpm version -r and pnpm change status check the registry for
each release's current version; when that version is not yet
published, the package debuts at it and its pending
changesets apply only from the next release. A newly added
package seeded at 1100.0.0 with a minor changeset is
therefore published as 1100.0.0 rather than skipping straight
to 1100.1.0.
- Added a --changeset flag to pnpm update. Set update.changeset
to true in pnpm-workspace.yaml to enable this behavior by
default, and use --no-changeset to override the setting for
one update. After the update completes, pnpm writes a
.changeset/pnpm-update-<suffix>.md file declaring a patch
bump for every workspace package whose dependencies or
optionalDependencies were changed by the update and a major
bump when peerDependencies changed, including packages that
consume an updated catalog entry via the catalog: protocol.
Private packages, packages without a name, and packages
listed in the ignore array of .changeset/config.json are
skipped. If .changeset/config.json does not exist, a warning
is printed and no changeset is generated.
- Added GitHub Actions dependencies to pnpm outdated and
interactive pnpm update. Non-interactive updates can include
them with --include-github-actions or by setting
update.githubActions to true in pnpm-workspace.yaml. Updated
actions are pinned to exact commit hashes with their release
tags preserved in comments.
- Added update and audit settings sections to
pnpm-workspace.yaml, superseding the awkwardly named
updateConfig, auditConfig, and top-level auditLevel settings:
update:
ignoreDeps: # was updateConfig.ignoreDependencies
- webpack
- "@babel/*"
audit:
level: high # was auditLevel
ignore: # was auditConfig.ignoreGhsas
- GHSA-xxxx-yyyy-zzzz
- update.ignoreDeps lists dependency name patterns that pnpm
update and pnpm outdated should skip. audit.level and
audit.ignore tune pnpm audit.
- The deprecated updateConfig, auditConfig, and auditLevel
settings keep working until the next major version. When both
a new section value and its deprecated counterpart are set,
the new section takes precedence and a warning is printed.
Both the TypeScript CLI and the Rust config surface (pacquet)
recognize the new sections.
* Patch Changes
- Fixed pnpm add --save-exact/--save-prefix and pnpm update
writing a package's version with the peerDependencies range's
prefix (e.g. ^19.2.7 instead of the requested 19.2.7)
whenever the same package also appeared in peerDependencies.
A real dependencies/devDependencies/optionalDependencies
entry now takes precedence over a same-named peerDependencies
entry when computing the current specifiers #13108.
- update to 11.15.1:
* Patch Changes
- pnpm install now detects a supportedArchitectures change and
re-evaluates previously skipped platform-specific optional
dependencies, instead of reporting the project as up to date
and leaving the packages for the old architecture set in
place.
- pnpm setup now removes leftover v10-layout shims at the top
of PNPM_HOME, so pnpm self-update no longer warns about a v10
installation layout after PATH has been migrated to the v11
PNPM_HOME/bin layout. Applies to both the TypeScript CLI and
pacquet.
In the TypeScript CLI, self-update also no longer treats a
dangling legacy shim (one whose install target was
garbage-collected) as a real v10 layout, so the warning can
no longer fire on dead shim files.
Closes #12496.
- Completed pnpm runtime installation parity for Node.js, Deno,
and Bun, including runtime failure policy, target
architecture selection, and dependency runtime engines.
Runtime failure overrides now preserve explicit runtime
dependencies without matching engine entries.
- Fixed pnpm install running out of memory while resolving
large dependency graphs #8441. The resolver kept full
registry documents — per-version readmes, scripts,
descriptions, and other install-irrelevant bulk — in memory
for every package fetched with full metadata (optional
dependencies, and packages re-fetched for minimumReleaseAge's
publish timestamps). Every retained document is now condensed
down to the field set installation actually reads, which
reduces peak resolution memory by several times on workspaces
with more than a thousand packages.
- When a dependency's build script fails under
enableGlobalVirtualStore, the global virtual store directory
it was being built in is now removed for scoped packages too.
Previously the cleanup resolved one directory level short of
the hash directory for a scoped name, leaving a half-built
directory behind that later installs would reuse.
- Fixed pnpm login, pnpm adduser, and pnpm logout against a
registry hosted under a URL subpath (e.g.
https://example.com/npm/registry) when the configured URL has
no trailing slash. Such URLs were left unnormalized, so the
last path segment was dropped when building the login and
token endpoints and the auth token was stored under a
truncated key. Registry URLs with a path now always get a
trailing slash appended during normalization, matching how
root-level registry URLs are handled.
* Sun Jul 19 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.15.0:
* Minor Changes
- Optional peer dependencies declared only via
peerDependenciesMeta (for example debug's supports-color
peer) are now resolved from a satisfying version already
present in the dependency graph, the same way explicitly
declared optional peer dependencies are. Previously such
peers were only resolved this way when the package's metadata
was read back from the lockfile, so an unrelated dependency
change could rewrite peer resolutions across the whole
lockfile.
* Patch Changes
- Updated adm-zip to prevent crafted ZIP archives from causing
excessive memory allocation.
- pnpm version -r no longer writes a versioning-ledger entry
with no consumed intents as a bare intents: key, which the
next run failed to read with
ERR_PNPM_INVALID_VERSIONING_LEDGER. Empty intent lists are
now written as intents: [], and the ledger reader accepts the
bare form left by earlier releases.
- Fixed pnpr workspace resolution to preserve project names and
versions for workspace: dependencies.
- update to 11.14.0:
* Minor Changes
- peerDependencies now accept dependency specifiers that carry
a scheme — a named-registry spec (<registry>:<version>), an
npm: alias, or a file:/git/URL spec — instead of rejecting
them with ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION
[#13095]. Such a peer is matched against the semver range
carried by the specifier (work:5.x.x is checked as 5.x.x,
npm:bar@^5 as ^5), or against * when it carries no version,
while the original specifier still selects the package to
auto-install. Bare name@version values, which are almost
always a mistake, are still rejected.
- Added pnpm doctor, which diagnoses the pnpm installation and
the environment it runs in: the versions and install method,
whether the global bin directory is on PATH, whether the
store and cache are writable, which link strategies (reflink,
hardlink, symlink) the store's filesystem supports, registry
connectivity, and an offline file: install that exercises the
resolve/store/link path end to end. Each check reports how to
fix what it finds, and the command exits non-zero when any
check fails.
- Use --offline to skip the checks that need network access,
- -json for machine-readable output, and --benchmark to time
the filesystem and install checks.
- Added support for executing multiple scripts matching a
RegExp passed to pnpm run (e.g., pnpm run "/^build:.*/"),
running matched scripts in deterministic lexicographical
order. Restored the --sequential (-s) CLI option for pnpm
run, which forces workspaceConcurrency to 1 so that matched
scripts run sequentially one by one across and within
packages.
* Patch Changes
- Fixed pnpm install failing with ERR_PNPM_LOCKFILE_IS_SYMLINK
when pnpm-lock.yaml is a symlink, as build sandboxes such as
Bazel and Nix stage it #13073. Reading a lockfile through a
symlink is allowed again, and an install that leaves the
lockfile unchanged no longer rewrites it, so
- -frozen-lockfile no longer needs to write at all. Writing a
changed lockfile through a symlink is still refused, as that
would redirect the write onto the symlink's target.
- Fixed frozen installs incorrectly treating equivalent Git
dependency specifiers as a stale lockfile. See #13039.
- pnpm owner ls now reports authentication and authorization
failures (401/403) as dedicated errors that include the
registry's response body, matching pnpm owner add/rm, instead
of a generic Failed to fetch owners message.
- Recover from a metadata cache entry that disappears
(concurrent cache cleanup, antivirus) after the registry has
already answered the conditional request with 304 Not
Modified. The metadata is re-requested once without cache
validators instead of failing the install with
ERR_PNPM_CACHE_MISSING_AFTER_304.
- A project pinned to a broken pnpm release via packageManager
or devEngines.packageManager now reports which release is
broken and what to do about it, instead of failing inside the
installer. pnpm self-update already refused these releases;
the version switch does too.
- Prevent broken-lockfile errors from including snippets of the
lockfile's contents.
- pnpm self-update now checks that the version it installed can
run before making it the active pnpm. A release that installs
but cannot execute is discarded with an error instead of
replacing a working installation.
- Fixed an out-of-memory regression when workspace projects
concurrently resolve a package with large registry metadata
pnpm/pnpm#13077.
- Fixed pnpm update rewriting exact version pins that use the =
operator (for example =3.5.1) to a caret range (^3.5.1).
Exact pins are now preserved and written back as the bare
version. See #12745.
* Sun Jul 19 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.13.1:
* Patch Changes
- Fixed pnpm pack applying workspace-root ignore rules when a
workspace package has its own .npmignore file.
- Keep the interactive minimumReleaseAge approval prompt
visible during pnpm install. The progress reporter now pauses
its redraws while a prompt is waiting for input instead of
overwriting it, so the install no longer hangs on a question
the user cannot see #13019.
- Fixed pnpm self-update failing to link native platform
binaries stored in sibling global virtual store slots.
- update to 11.13.0:
* Minor Changes
- Added versioning.epics to pnpm-workspace.yaml. An epic ties a
group of member packages to a lead package, constraining
every member's major version to a band derived from the
lead's major: while the lead is on major M, members live in
M*100 … M*100+99. Members move independently inside the band
(patch, minor, and a major intent that stays in-band); a bump
that would carry a member past the band ceiling is rejected
until the lead advances its own major. When a release plan
takes the lead to a new stable major, every member re-bases
to the band floor in the same plan. Membership is matched
with pnpm's package selectors — name globs, ./-prefixed
directory globs, and !-prefixed negations.
- Added the team command for managing organization teams and
team memberships on the registry, with create, destroy, add,
rm, and ls subcommands and support for --otp, --parseable,
and --json flags.
- Added native workspace release management #12952: the new
pnpm change command records change intents as
changesets-compatible .changeset/*.md files (pnpm change
status shows the pending release plan), and the bare pnpm
version -r consumes them — bumping versions across the
workspace with dependent propagation through workspace:
ranges, fixed groups, a maxBump cap, --filter narrowing, and
- -dry-run — writing changelogs, and recording consumed
intents in a committed ledger that keeps cherry-picks and
merge-backs between release branches safe. Packages can be
moved onto per-package release lanes with the new pnpm lane
<name> --filter <pkg> command and back with pnpm lane main
- -filter <pkg> (pnpm lane shows the membership), releasing
X.Y.Z-lane.N prereleases from the same runs that release
stable versions of the packages on the main lane.
Configuration lives under the new versioning key of
pnpm-workspace.yaml (fixed, ignore, maxBump, lanes,
changelog). When two workspace projects publish the same
name, intent files, versioning.lanes, and
versioning.fixed/ignore may reference a project by its
workspace-relative directory path (e.g. "./pnpm/npm/pnpm") —
the one additive extension to the changesets format, applied
automatically by pnpm change.
- Release changelogs default to registry storage
(versioning.changelog.storage): no CHANGELOG.md is committed.
Each release's section is composed at publish time and packed
into the published tarball on top of the previously published
version's changelog, and the consumed change intents are
garbage-collected by a later pnpm version -r only once the
registry confirms the version is published with its section.
Set versioning.changelog.storage: repository to keep
committed CHANGELOG.md files instead.
- Added a new override selector form with an empty range —
"pkg@": "<version>" — called a convergence override. It
rewrites a dependency edge only when its exact version
satisfies the edge's declared range, so compatible consumers
converge on one version while incompatible consumers keep
their own resolution — now and for any dependent added in the
future #12794.
overrides:
"form-data@": 4.0.6
The value must be an exact version. When a full resolution
detects that every declared range also admits a newer
version, pnpm warns that the override is stale and names the
version to converge on. Previously an empty range in an
override selector was undocumented and behaved like a bare
(unscoped) override.
* Patch Changes
- A tokenHelper set in the global pnpm auth.ini is no longer
rejected as project-level configuration. The guard that
blocks tokenHelper from a project .npmrc only treated
~/.npmrc as a trusted source, so a helper written to auth.ini
(for example by pnpm config set) failed on every command and
could not even be removed with pnpm config delete. A
tokenHelper in a workspace or project .npmrc is still
rejected.
- pnpm cache delete now removes a package's metadata from every
metadata cache directory (metadata, metadata-full, and
metadata-full-filtered), instead of only the one the current
resolution mode reads. Previously a package cached under a
different mode (e.g. metadata-full-filtered) was left behind.
Closes #12753.
- Fixed an injected workspace dependency
(injectWorkspacePackages: true) incorrectly staying as file:
instead of deduping back to link: when an unrelated, ordinary
shared dependency resolved to a peer-suffixed variant for the
target project's own copy but not for the injected
occurrence. See #10433.
- pnpm deploy now supports workspaces that use catalogs.
- Fixed pnpm deploy with a shared lockfile so local file:
tarball dependencies keep their package name in the generated
deploy lockfile. This prevents warm-store deploys from
failing with ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE when
the tarball filename includes the version.
- Options that follow create, exec, or test appearing as a
subcommand of another command are now parsed instead of being
silently treated as positional parameters. For example, pnpm
team create @org:team --registry <url> previously ignored the
- -registry option and sent the request to the default
registry.
- pnpm add -g, pnpm update -g, pnpm setup, and the self-updater
no longer fail with ERR_PNPM_MISSING_TIME when trustPolicy:
no-downgrade or resolutionMode: time-based is set in the
global config #12883. The decision to fetch full registry
metadata now lives in one place, and the no-downgrade trust
policy always requests full metadata (matching the
self-updater), since the trust evidence it checks is missing
from abbreviated metadata even on registries that include the
time field.
- pnpm list and pnpm why no longer crash with EMFILE: too many
open files when a project has a large number of unsaved
dependencies (packages present in node_modules but not in the
lockfile). The reads of those packages are now
concurrency-limited.
- The published pnpm package no longer declares dependencies or
devDependencies. Because the CLI bundles its runtime
dependencies into dist/node_modules, those fields are dropped
when packing, so npm install of the tarball no longer tries
to resolve internal-only packages such as
@pnpm/test-ipc-server. Closes #12955.
- Fixed pnpm publish --otp and pnpm publish --batch --otp to
send the configured OTP to the registry.
- pnpm publish again sends the package's README to the registry
as metadata, so registries can render it on the package page.
The readme is always included in the published metadata
(matching the npm CLI), while the embed-readme setting
continues to control only whether the readme is written into
the package.json inside the tarball. This restores the
behavior that was lost when publishing became fully native.
Closes #12966.
- Fixed the dependency status check wrongly reporting "up to
date" when a package.json, .pnpmfile.cjs, or patch file was
edited in the same second as the previous install, on
filesystems that record mtimes at whole-second resolution
(for example ext4 with 128-byte inodes). The optimistic
repeat-install fast path and verify-deps-before-run compared
mtimes strictly, so a same-second edit whose mtime rounded
down looked unchanged and re-resolution was skipped. Such a
file's whole second is now treated as possibly-modified,
falling through to the content check; behavior on sub-second
filesystems is unchanged.
- Retry package metadata requests when a registry or proxy
returns 304 Not Modified to an unconditional request,
preventing false ERR_PNPM_CACHE_MISSING_AFTER_304 failures
pnpm/pnpm#12882.
If the retry also returns 304, report
ERR_PNPM_META_NOT_MODIFIED_WITHOUT_CACHE instead.
- Fixed pnpm update removing transitive lockfile entries when
dedupePeerDependents is disabled and the selected package is
absent pnpm/pnpm#12456.
- Limit modern deploy lockfiles and localized virtual stores to
dependencies reachable from the selected dependency groups.
- A tokenHelper command is now given a 60-second time limit. A
helper that hangs (deadlock, stuck I/O) is killed and
reported as an error instead of leaving the command waiting
forever.
- Fixed orphaned child processes on Windows when pnpm exits on
an error while commands spawned by pnpm exec or pnpm dlx are
still running (for example, when one project's command fails
during pnpm --recursive exec). The PIDs of these commands are
now recorded when they are spawned and their whole process
trees are terminated with taskkill on an error exit.
Previously the cleanup relied on enumerating the system
process list, which is so slow on Windows that the
enumeration hit its timeout and the cleanup was silently
skipped #12406.
- pnpm pack now respects workspace-root .npmignore and
.gitignore files when packing workspace packages.
- update to 11.12.0:
* Minor Changes
- a897ef7: Custom fetchers exported from a pnpmfile can now
delegate by returning a { delegate: <resolution> } envelope:
pnpm rewrites the package's resolution to the delegated shape
and runs the built-in fetcher on it. This is the portable
delegation form that also works in pacquet, where cafs and
fetchers cannot be passed to the hook. Related to
pnpm/pnpm#11685.
* Patch Changes
- 2b02764: The changed-packages filter (--filter
"...[<since>]") no longer allows an option-like <since> value
(such as --output=<path>) to be interpreted as a git option —
git now rejects it as a bad revision. The repository root is
also resolved to the nearest .git entry, so the filter works
in a git worktree checked out inside another repository's
tree.
- 43711ce: pnpm outdated no longer checks the registry for
dependencies that are resolved from local link:, file:, or
workspace: references in the lockfile #12827.
- 3c6718b: Fixed a deadlock in peer dependency resolution: pnpm
install hung forever when a peer dependency cycle spanned a
project's own dependencies and auto-installed peer providers,
for example when installing electron-builder@26.15.3 #12921.
- 252f15e: Fixed peer dependency auto-install picking a version
the peer range rejects. In a workspace with several projects,
a package declaring a peer dependency with a semver range
(for example ^1.0.0) could get the highest version found
anywhere in the workspace (for example a 2.0.0 resolved for
another project) instead of a version that satisfies the
range. Peers are now deduplicated onto the highest preferred
version that satisfies the declared range, and when none
does, the range is resolved from the registry.
- Also fixed re-resolving with an existing lockfile hoisting a
different peer version than a fresh install of the same
manifest: root dependencies reused from the lockfile were
invisible to peer hoisting, so a peer that a root dependency
provides could be bound to another version.
- a38adda: pnpm self-update <version> now installs the
requested pnpm version when it matches the currently running
version but is missing from the global self-update directory.
- 6a85968: pnpm stage list now stops paginating after a
fail-safe cap of 1000 pages, so a misbehaving registry cannot
keep the command looping forever.
- eee7c9a: verify-deps-before-run no longer spawns a pnpm
install when pnpm is executed in a directory that has no
package.json. A mistyped command run outside a project (for
example pnpm witch 10 login) used to crash with a confusing
error from the spawned install; now it fails with the regular
"no package.json found" error.
* Fri Jul 10 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.11.0:
* Minor Changes
- 508b8c2: Added the pnpm access command for managing package
access and visibility on the registry, supporting listing
packages and collaborators, getting and setting package
status and MFA requirements, and granting or revoking team
access.
* Patch Changes
- c70e33e: Allow allowBuilds entries for git-hosted packages to
match by repository URL without pinning the resolved commit
hash. This lets trusted git repositories keep running their
build scripts after branch updates without approving each new
commit, while package-name-only rules still do not approve
git-hosted artifacts.
- 3067e4f: Reduced peak memory usage during cold-cache
dependency resolution. The metadata fetch is memoized for the
whole resolution phase, and it was retaining each package's
raw registry response body (used only to mirror the response
to disk) for that entire time. The memoized cache now holds a
body-less copy, so the raw body only lives as long as the
call that writes the disk mirror. On large graphs that fetch
full metadata (e.g. with minimumReleaseAge or trustPolicy
enabled) this cuts peak RSS by roughly 30%, back in line with
pnpm 10. The resolved lockfile is unchanged.
- 51300fd: Prevent a crafted pnpm-lock.yaml from writing
package content outside the virtual store. A dependency path
key whose name reconstructs to a path-traversal sequence
(e.g. ../../../tmp/x@1.0.0) is now rejected by the isolated
(virtual-store) linker and the Plug'n'Play resolver map,
matching the containment already applied to the hoisted
linker. Under the global virtual store, a traversal in the
version-derived path segment (e.g. a snapshot version:
"../../x") is now rejected at formatGlobalVirtualStorePath,
the single point every global-virtual-store slot path funnels
through — closing the same escape in the isolated linker, the
resolver's dependency-graph builder, and the
config-dependency installer.
- f8058eb: Reject symlinked pnpm-lock.yaml files when reading
or writing the env lockfile document.
- 9318a11: Allow registries and namedRegistries to be
configured in the global config.yaml file.
- 51300fd: Fixed a path traversal vulnerability where a
dependency whose manifest name was a scoped path traversal
(e.g. @x/../../../<path>) could be written outside
node_modules to an attacker-controlled location during pnpm
install, even with --ignore-scripts. The isolated linker now
validates the package name before using it as a directory
name, matching the existing protection in the hoisted linker.
- 14332f0: Fail instead of silently removing an optional
dependency's locked entries from pnpm-lock.yaml when the
registry cannot resolve it. Previously, when registry
metadata lacked a version that the lockfile already pinned
(for example, a mirror that had not synced a recent release
yet), pnpm install and pnpm dedupe silently dropped the
optional dependency's entries — emptying maps such as the
platform binaries of @napi-rs/canvas — so the lockfile
differed between machines and frozen installs on other hosts
had nothing to link #12853.
- fecfe83: Fixed peer dependency resolution with
autoInstallPeers when a workspace package depends on a
version of a package that a transitive dependency's
self-contained closure also provides for itself. The peer
providers that are attached to the root project for reuse are
no longer peer-resolved a second time in the root context, so
packages inside such a closure no longer get their peers
bound to the root project's incompatible version #4993.
- 5a4daec: ${...} environment-variable placeholders in the
httpProxy, httpsProxy, noProxy, proxy, and noproxy settings
are no longer expanded when these settings come from a
project's pnpm-workspace.yaml. They now receive the same
protection already applied to registry, namedRegistries, and
pnprServer.
- d1da02e: pnpm publish no longer prints credentials when the
target registry is configured with inline user:pass@
credentials (e.g. registry=https://user:pass@example.com/).
They are now redacted both from the "publishing to registry"
line and from the OIDC (trusted publishing) failure messages.
- dcfc611: pnpm self-update now honors
trustPolicy=no-downgrade. It resolves the target pnpm version
against full registry metadata, so it refuses to switch to a
version whose supply-chain trust evidence is weaker than an
earlier-published one, the same way a regular install does.
- a8ad82d: Register the pn alias in generated shell completion
scripts.
- 25bd5c3: Fixed standalone installer downgrades from pnpm v12
to v11.
- 23996e9: pnpm runtime set <name> <version> now validates its
arguments: the name must be node, deno, or bun, and the
version must not contain a comma. Previously these were
interpolated straight into a pnpm add selector, where an
unsupported name or a comma (e.g. node 22,is-positive) could
be misread as a list of packages or a local directory and
install unintended packages or bins.
- update to 11.10.0:
* Minor Changes
- e2e3c81: Added the issues command as an alias of bugs, so
pnpm issues opens the package's bug tracker URL in the
browser.
- 8491f8e: Added the prefix command which prints the current
package prefix directory (or global prefix directory if -g /
- -global is used).
- 3425e80: Added an '_auth' setting for configuring registry
authentication as a single structured (URL-keyed) value. It
can be set in the global pnpm config (config.yaml) or, for
CI, via the pnpm_config__auth environment variable. The env
form sidesteps the GitHub Actions / bash / zsh limitation
that broke the existing pnpm_config_//host/:_authToken=… form
(env var names containing /, :, or . are silently dropped).
Closes #12314.
The value is keyed by registry URL so each secret is
explicitly bound to the host that may receive it. Registry
URL keys must use http or https and must not include
credentials, query strings, or fragments:
export pnpm_config__auth='{"https://registry.npmjs.org":{"@":{"authToken":"npm-token"},"@org":{"authToken":"org-token"}}}'
The equivalent in the global config.yaml:
_auth:
https://registry.npmjs.org:
"@":
authToken: npm-token
"@org":
authToken: org-token
Within each registry URL, @ means registry-wide/default
credentials and package scopes like @org bind credentials to
that scope on the same host. The only supported credential
field is authToken (maps to '_authToken' / bearer auth); the
deprecated basicAuth / username + password forms are
intentionally not accepted here.
Each entry also infers a trusted registry route: @ routes the
default registry (and pnpm add <pkg> resolves there), and
@org routes that scope. Because the credential and
destination host arrive in one trusted value, repo-controlled
pnpm-workspace.yaml or project .npmrc cannot redirect the
token to a different host. _auth is honored only from the env
var and the global config — it is ignored in a project
pnpm-workspace.yaml / .npmrc, so repo-controlled config can
never supply registry auth. Precedence: CLI flags
(--registry, --@scope:registry) > pnpm_config__auth > global
config.yaml _auth > pnpm-workspace.yaml.
Both pnpm_config__auth (lowercase, documented form) and
PNPM_CONFIG__AUTH (all-caps, the shell convention some CI
runners apply) are honored. If both are set, lowercase wins
unless it is empty, in which case uppercase is used. The env
var wins over the global config.yaml _auth on a conflicting
key. tokenHelper is not supported in _auth. Parsing is
strict: a malformed value (bad JSON, wrong shape, invalid
registry URL or scope, an unsupported credential field) fails
fast with an error rather than being silently dropped.
Pacquet parity note: the pacquet (Rust) port supports the
same single credential field as the TS CLI: authToken.
- a33eeec: pnpm self-update and packageManager
version-switching can now install and link pnpm v12 (the Rust
port), published with equal content under both the pnpm and
@pnpm/exe names on the next-12 dist-tag. Its native binaries
ship as @pnpm/exe.<platform>-<arch> packages, which pnpm's
built-in installer links directly — no Node.js launcher, so
the command pays no Node startup cost. v12 is initialized
exactly like @pnpm/exe, including per-platform
global-virtual-store hashing. From v12 onward the install
converges on the unscoped pnpm package (the Rust exe) — even
when updating from the SEA @pnpm/exe build.
- 1dd12bd: When resolving through a pnpr install-accelerator
server, pnpm no longer forwards its own upstream registry
credentials in the resolve request. Only the Authorization
header identifying the caller to pnpr is sent. The pnpr
server now selects upstream credentials from its own route
policy (operator-configured upstream credential aliases), so
private dependencies resolve through a pnpr-managed alias the
caller is authorized to use, rather than by sending the
client's registry tokens to the server.
- 1e81761: Expose web authentication authUrl and doneUrl in
JSON error output when OTP is required in a non-interactive
terminal #12724.
* Patch Changes
- 2f389d6: Added the Node.js release team's new signing key
(Stewart X Addison, 655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD)
to the embedded Node.js release keys, so runtimes whose
SHASUMS256.txt is signed by the new releaser verify
successfully.
- acbdb94: Fixed shell tab completion not suggesting workspaces
after the -F alias for --filter option.
- dcabb78: Fixed pnpm up -r <pkg> bumping unrelated packages
that have open semver ranges. Previously, any update mutation
nullified the lockfile-derived preferredVersions globally, so
packages with ^x.y.z ranges could re-resolve to newer
compatible versions even though the user only asked to update
a specific package. The install layer now always seeds
preferredVersions from the lockfile, and caller-supplied
preferred versions (such as the vulnerability penalties of
pnpm audit --fix) layer on top of the seed instead of
replacing it. The targeted package still bumps: the
per-resolve updateRequested flag makes the resolver ignore
the target's own lockfile pins.
Closes #10662.
- d539172: Fixed pnpm pack and pnpm publish failing when
prepack generates files that are included in the package and
postpack cleans them up.
- be6505a: Hardened global package management:
- On Windows, removing or updating a global package now also
cleans up the node.exe flavor of a bin, so a stale node.exe
no longer survives on PATH after uninstall, and a new
global install no longer silently overwrites an existing
node.exe.
- pnpm add -g pnpm@<version> (and @pnpm/exe@<version>) is now
rejected like the bare pnpm form, pointing to pnpm
self-update.
- Dependency aliases read from a global package's manifest
are validated before being joined onto node_modules paths,
preventing a tampered manifest from escaping the install
directory.
- Each global install group is created in its own
freshly-made directory (no longer reusing a colliding or
pre-existing path).
- Removing or updating a global package no longer unlinks a
bin that belongs to a different globally installed package.
- 25c7388: pnpm now rejects jsr: specifiers whose package name
is not a valid npm package name — an empty scope or name
(e.g. jsr:@scope/), path separators inside the name, or any
other shape validate-npm-package-name rejects — with
ERR_PNPM_INVALID_JSR_PACKAGE_NAME instead of silently
converting them into a malformed @jsr/... npm package name.
- 25c7388: pnpm now rejects named-registry specifiers (e.g.
gh:) whose package name is not a valid npm package name — an
empty scope (e.g. gh:@/bar), path separators inside the name
(e.g. gh:@scope/../name), or any other shape
validate-npm-package-name rejects — with
ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME instead of
passing the name through to registry URLs and metadata cache
file paths.
- 96da7c5: node-gyp's gyp_main.py and gyp entrypoints are now
packed with the executable bit in the pnpm and @pnpm/exe
tarballs. Without it, building native addons from source
could fail with a permission error.
- 99982b9: Sped up resolution and reduced memory use against
registries that ignore npm's abbreviated metadata format and
always return the full package document (for example, Azure
DevOps Artifacts). pnpm now strips such documents down to the
abbreviated field set before caching them. Resolution output
is unchanged, and registries that honor the abbreviated
format (such as the npm registry) pay no extra cost.
- 11a7fdd: Sped up offline and --prefer-offline resolution on
large workspaces (e.g. pnpm dedupe --offline, pnpm install
- -offline). Package metadata loaded from the local cache is
now kept in memory, so each package's metadata is parsed once
per command instead of once per dependent that references it.
- 2c7369d: pnpm pack-app now rejects --entry / pnpm.app.entry
and --output-dir / pnpm.app.outputDir values that are
absolute paths or escape the project directory via .. (or a
symlink that resolves outside it), and refuses to write the
produced executable when its target path already exists as a
symlink (or other non-regular file). This prevents a
repository-controlled package.json from embedding host files
(such as an SSH key) into the produced executable, writing
build artifacts outside the project, or overwriting an
arbitrary file through a committed symlink. The new error
codes are ERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT,
ERR_PNPM_PACK_APP_OUTPUT_DIR_OUTSIDE_PROJECT, and
ERR_PNPM_PACK_APP_OUTPUT_FILE_NOT_REGULAR.
- When ad-hoc signing macOS targets, pnpm pack-app now runs the
system codesign by absolute path and resolves ldid to a
location outside the project, so a repository-controlled
node_modules/.bin on PATH cannot hijack the signer.
- ce5d5a5: Relative paths in patchedDependencies are now
resolved against the lockfile directory when computing patch
file hashes, so running pnpm install from a subdirectory no
longer fails with ENOENT looking for the patch file in the
wrong location #12762.
- ebb4096: pnpm peers no longer reports a conflict for a
missing peer dependency that is ignored via
pnpm.peerDependencyRules.ignoreMissing.
- dcabb78: Fixed a prototype-pollution hazard when seeding
preferred versions: a dependency named __proto__ in a
manifest or in pnpm-lock.yaml could write through
Object.prototype (or crash the install) while the
preferred-versions map was being built. The maps are now
null-prototype objects, so crafted package names land as
plain keys.
- f38e696: Hardened pnpm deploy --force so it refuses unsafe
deploy targets such as workspace roots, parent directories,
out-of-workspace paths, and symlinked target parents.
- 806c3ec: pnpm no longer warns about ignored project-level
auth settings when PNPM_CONFIG_NPMRC_AUTH_FILE points at the
project .npmrc — setting it to that file is an explicit
opt-in to trusting it, so auth env variables in it are
expanded pnpm/pnpm#12480.
- 991405e: Restore differential rendering (ansi-diff) to fix
duplicated output lines introduced by #12351.
- c121235: Fixed the topological order of --filtered commands
(pnpm run, pnpm exec, pnpm publish, pnpm pack, pnpm rebuild)
when the selected projects depend on each other only
transitively through projects that were not selected.
Previously such selected projects could run concurrently or
in the wrong order; now a project always runs after the
selected projects it transitively depends on, while projects
without a real dependency relationship still run
concurrently. This now also holds for prod-only filters
(--filter-prod), which resolve order through the production
dependency graph so transitive production dependencies are
respected without pulling back the dev dependencies the
filter drops, and for selections that mix --filter with
- -filter-prod #8335.
- d539172: pnpm pack and pnpm publish no longer follow a
symlinked workspace LICENSE file when injecting it into a
package that has no license of its own. Following the symlink
could pack bytes from outside the workspace into the
published tarball.
- dcabb78: Fixed pnpm up <pkg> producing a different result
than a fresh install of the same manifests would. The
resolver now distinguishes updateRequested (true only for
packages that match the user's update target) from the
broader update flag, and for the targeted package ignores
only its own lockfile-derived preferred-version pins — so the
target re-resolves exactly as if its lockfile entries were
deleted and pnpm install ran. Preferred versions a fresh
install applies (manifest pins, versions propagated down the
dependency chain, and the vulnerability-avoidance penalties
of pnpm audit --fix) stay in effect, so an update never
installs duplicate versions that a reinstall from scratch
would not reproduce. When a preferred version holds the
update target below the newest version its range admits, pnpm
now prints a warning explaining that reaching the newer
version everywhere requires an override.
- dcabb78: pnpm update <dep>@<version> now prints a warning
when <dep> is only present as a transitive dependency: the
requested version cannot be applied there (updates resolve
the target the way a fresh install would), and the warning
recommends adding the version to pnpm.overrides instead,
which is the mechanism that does pin transitive dependencies.
Closes #12744.
- a6c4d5f: When a dependency cannot be found in the registry
(404) or the registry has no matching version, and a
workspace project with the same name exists only at
non-matching versions, the error now reports the available
workspace versions
(ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE) instead of
the raw registry failure pnpm/pnpm#1379. Other registry
failures (authorization, network, server errors) still
propagate unchanged. The pacquet (Rust) resolver applies the
same behavior.
* Sun Jun 28 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.9.0:
* Minor Changes
- bae694f: Some registries generate tarballs on-demand and
cannot provide an integrity checksum in their package
metadata. In that case pnpm now computes the integrity from
the downloaded tarball and stores it in the lockfile, so the
entry is verifiable on subsequent installs instead of being
written without an integrity (which would fail the next
install). This also applies to --lockfile-only: the tarball
is downloaded so its integrity can be computed. A lockfile
entry that is still missing its integrity is rejected as a
ERR_PNPM_MISSING_TARBALL_INTEGRITY lockfile verification
violation (the install fails closed) rather than being
silently re-fetched.
- 6c35a43: Added --exclude-peers to pnpm sbom. With
auto-install-peers (the default), peer dependencies resolve
into the lockfile and are otherwise indistinguishable from
the package's own dependencies. The flag drops peer
dependencies (and any transitive subtree reachable only
through them) from the SBOM. CycloneDX 1.7 has no scope or
relationship that expresses "consumer-provided peer", so
omission is the only spec-clean handling. The flag name
matches pnpm list --exclude-peers; note the SBOM flag prunes
a peer's exclusive subtree, which is stricter than pnpm list
(which only hides leaf peers).
* Patch Changes
- 25a829e: pnpm audit --fix now writes a single combined
minimumReleaseAgeExclude entry per package (e.g. axios@0.18.1
|| 0.21.1) instead of one entry per version, matching the
format documented for the setting. Existing per-version
entries in pnpm-workspace.yaml are merged into the combined
form rather than left as duplicates. Installs that
auto-collect immature versions into minimumReleaseAgeExclude
now report the same combined entries, so the "Added N
entries" message matches what is written to the manifest
[#12534].
- 1cbb5f2: Fixed non-deterministic peer resolution that could
add or remove an optional transitive peer — for example
@babel/core, reached through styled-jsx — from a package's
peer-dependency suffix across otherwise identical installs,
churning the lockfile and causing intermittent pnpm dedupe
- -check failures in CI. When a package's children are
resolved by one occurrence (the "owner") and reused by a
deeper consumer, whether that consumer inherited the owner's
missing peers depended on whether the owner's resolution had
finished yet — a race under concurrent resolution. The
decision is now a function of the dependency graph's
structure rather than resolution-completion order.
- d577eea: Fixed a Windows flakiness in pnpm dlx where a failed
install could surface a spurious EBUSY: resource busy or
locked error. The cleanup of a partially-populated dlx cache
is now best-effort with retries and no longer masks the
original error.
- ec7cf70: Shortened the pnpm dlx cache path so deep dependency
trees no longer overflow Windows' MAX_PATH, which could make
a dependency's lifecycle script fail with spawn cmd.exe
ENOENT.
- 05b95ab: Fixed pnpm hanging (and crashing with an unhandled
promise rejection) when a non-retryable network error such as
SELF_SIGNED_CERT_IN_CHAIN occurs while fetching from a
registry. The error is now rejected through the returned
promise instead of being thrown inside the detached retry
callback.
- d3f68e2: Fix a pnpm audit performance regression on lockfiles
that contain dependency cycles. The reachable-vulnerability
pruning added in pnpm 11.5.1 only memoized acyclic subtrees,
so any node whose subtree touched a cycle — together with all
of its ancestors — was recomputed on every query, making the
path walk quadratic. Reachability is now computed once per
node using Tarjan's strongly-connected-components algorithm,
so cyclic graphs are handled in linear time #12212.
- The audit path walk also no longer recurses, so a deeply
nested dependency graph can no longer overflow the call
stack, and the install path to each finding is tracked
without per-node copying, keeping memory linear in the graph
depth.
- 322f88f: Fix failed optional dependency updates so they don't
rewrite unrelated dependency specs #11267.
- 1488db1: When enableGlobalVirtualStore is toggled on for a
project that was previously installed without it, stale
hoisted symlinks under node_modules/.pnpm/node_modules are
now replaced instead of being left pointing at the old
per-project virtual store location #9739.
- 6545793: Fixed pnpm install --ignore-workspace overwriting
the allowBuilds map in pnpm-workspace.yaml. The ignored
builds of a package with a build script were auto-populated
into allowBuilds even though --ignore-workspace was passed,
clobbering committed true/false values with the set this to
true or false placeholder #12469.
- fbdc0eb: Fixed minimumReleaseAgeExclude and
trustPolicyExclude so multiple exact-version entries for the
same package behave the same as a single || disjunction
entry. Previously only the first matching rule's versions
were honored, so a config like [form-data@4.0.6,
form-data@2.5.6] could still flag form-data@2.5.6 as
violating minimumReleaseAge, while [form-data@4.0.6 || 2.5.6]
worked as expected #12463.
- fa7004b: The in-memory package metadata cache is now
populated on the exact-version disk fast path, so repeated
resolutions of the same package within one install no longer
re-read and re-parse the on-disk metadata. In large monorepos
this brings the time for adding a new package down from
minutes to seconds. The in-memory cache key now also includes
the registry, so a package of the same name served by two
different registries in a single install can no longer share
a cache slot and resolve the wrong tarball.
- 0a154b1: Fixed pnpm patch dropping the package name (and
leaking internal option fields) when the patched dependency
resolves to a single git-hosted version.
- 4d3fe4b: The pnpr resolver endpoints moved under the reserved
/-/pnpr namespace: POST /v1/resolve is now POST
/-/pnpr/v0/resolve and POST /v1/verify-lockfile is now POST
/-/pnpr/v0/verify-lockfile. The capability handshake at GET
/-/pnpr advertises protocol version 0 to match. This keeps
every pnpr-proprietary route in npm's reserved namespace, so
it can never collide with a package path.
- 0ec878d: Removing a runtime dependency now removes the
matching devEngines.runtime or engines.runtime entry that was
materialized from it. Blank runtime selectors are normalized
to latest.
- 17e7f2c: pnpm sbom now emits a CycloneDX issue-tracker
external reference for components (and the root) whose
package.json declares a bugs URL. Email-only bugs entries are
skipped, since the reference requires a URL.
- a84d2a1: Add @pnpm/resolving.tarball-url, which builds and
recognizes the canonical npm tarball URL of a package. It
vendors getNpmTarballUrl (previously the external
get-npm-tarball-url package) and adds
isCanonicalRegistryTarballUrl, the predicate the lockfile
writer uses to decide whether a tarball URL is derivable from
name+version+registry (and can therefore be omitted from
pnpm-lock.yaml).
- Exposing isCanonicalRegistryTarballUrl lets a custom resolver
(pnpmfile resolvers) fronting a proxy that serves tarballs on
a non-canonical path (e.g. an ephemeral localhost:<port>)
rewrite the resolved tarball to the canonical form, so
nothing host-specific is persisted to the lockfile.
Previously this logic was private to @pnpm/lockfile.utils.
- Two correctness fixes are included while consolidating the
logic: the scoped-package unescape now handles uppercase %2F
as well as %2f (percent-encoding is case-insensitive), and
protocol-insensitive comparison strips only a leading
http(s):// scheme instead of splitting on the first ://
(which could truncate URLs containing a later ://).
- 852d537: Lockfile verification no longer reports a registry
metadata fetch failure (for example a 403/401 on a private
registry, or a network error) as
ERR_PNPM_TARBALL_URL_MISMATCH. When the registry can't be
reached to verify an entry, the install now aborts with the
registry's own fetch error (such as ERR_PNPM_FETCH_403, which
already explains the authentication situation) instead of
mislabeling a transport failure as lockfile tampering.
Registry fetch errors no longer leak basic-auth credentials
embedded in the registry URL (https://user:pass@host/) into
their message.
* Sat Jun 20 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 11.8.0:
* Minor Changes
- c112b61: Added a --dry-run option to pnpm install. It runs a
full dependency resolution and reports what an install would
change, but writes nothing to disk (no lockfile, no
node_modules) and always exits with code 0. This mirrors the
preview semantics of npm install --dry-run #7340.
- 179ebc4: pnpm run --no-bail now exits with a non-zero exit
code when any of the executed scripts fail, while still
running every matched script to completion. This makes the
exit-code behavior of --no-bail consistent between recursive
and non-recursive runs (recursive runs already failed at the
end). Previously, a non-recursive pnpm run --no-bail always
exited with code 0, even when a script failed #8013.
- 0474a9c: Added support for generating Node.js package maps at
node_modules/.package-map.json during isolated and hoisted
installs. Added the node-experimental-package-map setting to
inject the generated map into pnpm-managed Node.js script
environments, and the node-package-map-type setting to choose
between standard and loose package maps.
- dcededc: pnpm sbom now marks components reachable only
through devDependencies with CycloneDX scope: "excluded" and
the cdx:npm:package:development property. The excluded scope
documents "component usage for test and other non-runtime
purposes", which matches the semantics of a devDependency;
the property is the CycloneDX npm-taxonomy marker emitted by
@cyclonedx/cyclonedx-npm, so both modern (scope) and existing
(property) consumers are covered. Components reachable at
runtime (including installed optionalDependencies) omit scope
and default to required.
- 1495cb0: Added per-package SBOM generation with --out and
- -split flags. Use --out out/%s.cdx.json to write one SBOM
per workspace package to individual files, or --split for
NDJSON output to stdout. When --filter selects a single
package, the SBOM root component now uses that package's
metadata. Workspace inter-dependencies (workspace: protocol)
and their transitive dependencies are included. Author,
repository, and license fall back to the root manifest when
the package doesn't define them.
- 293921a: feat(view): support searching project manifest
upward when package name is omitted When running pnpm view
without a package name, the command now searches upward for
the nearest project manifest (package.json, package.yaml, or
package.json5) and uses its name field. If the manifest
exists but lacks a name field, an error is thrown. This
change also replaces the find-up dependency with empathic for
improved performance and consistency across workspace tools.
* Patch Changes
- 29ab905: Fixed pnpm update overriding the version range
policy of a named catalog whose name parses as a version
(e.g. catalog:express4-21). The catalog: reference carries no
pinning of its own, so the prefix from the catalog entry
(such as ~) is now preserved instead of being widened to ^
[#10321].
- bee4bf4: Security: validate config dependency names and
versions from the env lockfile (pnpm-lock.yaml) before using
them to build filesystem paths. A committed lockfile with a
traversal-shaped configDependencies name (such as
../../PWNED) or version (such as ../../../PWNED) could
previously cause pnpm install to create symlinks or write
package files outside node_modules/.pnpm-config and the
store. Names must now be valid npm package names and versions
must be exact semver versions; the same validation is applied
to optional subdependencies of config dependencies, and to
the legacy workspace-manifest format before any lockfile is
written. See GHSA-qrv3-253h-g69c.
- 96bdd57: Fix link: workspace protocol switching to file:
after pnpm rm is run from inside a workspace package whose
target workspace dependency has its own dependencies, when
injectWorkspacePackages: true is set. Follow-up to #10575,
which fixed the same symptom for workspace packages without
dependencies.
- 302a2f7: No longer warn about using both packageManager and
devEngines.packageManager when the two fields pin the same
package manager at the same version with the same integrity
hash (e.g. both pnpm@11.5.1+sha512.…). Previously the hash
was stripped from the legacy packageManager field but not
from devEngines.packageManager, so even identical
specifications looked like a mismatch #12028.
The warning still fires on any genuine divergence, and
several cases now state the specific reason instead of a
single generic message: a different package manager, a
different version, or contradictory integrity hashes for the
same version.
- 3f0fb21: Fixed the progress line showing leftover characters
from external processes that write to the terminal between
progress updates (e.g. an SSH passphrase prompt would leave a
fragment like added 0sa':). The interactive reporter now
redraws each frame in place, erasing to the end of the
display before reprinting, so any such remnants are cleared
[#12350].
- 564619f: Fixed pnpm approve-builds reporting "no packages
awaiting approval" when a build-script dependency whose
approval was revoked (e.g. after git stash drops the
allowBuilds from pnpm-workspace.yaml) is re-added. The
revoked packages are now correctly recorded in .modules.yaml
so approve-builds can find them. #12221
- 3d1fd20: Skip the redundant "target bin directory already
contains an exe called node" warning on Windows when the
existing node.exe already matches the target (same hard link
or identical content) pnpm/pnpm#12203.
- 1b02b47: Fix macOS Gatekeeper blocking native binaries
(.node, .dylib, .so) by removing the com.apple.quarantine
extended attribute after importing them from the store.
When pnpm imports files from its content-addressable store
into node_modules, macOS preserves extended attributes,
including com.apple.quarantine. If this xattr is present on a
store blob (e.g. it was first written under a
Gatekeeper-enabled app such as a Git client), it propagates
to node_modules, and Gatekeeper blocks the native binary from
loading even though pnpm already verified the file's
integrity against the lockfile.
After importing a package, pnpm now strips
com.apple.quarantine from its native binaries, matching
Homebrew's behaviour of dropping quarantine from verified
downloads. The cleanup is macOS-only, runs in a single
batched xattr call per package, is restricted to native
binaries (other files are untouched), and is non-fatal (it
logs a warning on unexpected errors).
Fixes #11056
- 61969fb: Fix pnpm install with optimisticRepeatInstall
incorrectly reporting Already up to date when pnpm-lock.yaml
changed but project manifests did not. This affected
workflows such as checking out or restoring only the lockfile
[#12100].
Also fixes checkDepsStatus to use the correct lockfile path
when useGitBranchLockfile is enabled, so the optimistic
fast-path and lockfile modification detection work with
pnpm-lock.<branch>.yaml files instead of always stat'ing
pnpm-lock.yaml. Merge-conflict detection now reads the
resolved lockfile name as well, and with
mergeGitBranchLockfiles enabled every pnpm-lock.*.yaml is
scanned for modifications and conflicts. The git branch is
now resolved by reading .git/HEAD directly (no process spawn)
and uses the workspace directory rather than process.cwd().
- 5c12968: Fix recursive updates of transitive dependencies
when the update command mixes transitive dependency patterns
with direct dependency selectors. For example, pnpm up -r
"@babel/core" uuid now updates matching transitive
@babel/core dependencies even when uuid is a direct
dependency selector #12103.
- 9d79ba1: Register the pnpm update --no-save flag in the CLI
help and option parser.
- 0474a9c: Fixed pnpm import for Yarn v2 lockfiles when js-yaml
v4 is installed.
- 9e0c375: Fixed pnpm install repeatedly prompting to remove
and reinstall node_modules in a workspace package when
enableGlobalVirtualStore is enabled. The post-install build
step recorded a per-project node_modules/.pnpm virtual store
directory in node_modules/.modules.yaml, overwriting the
global <storeDir>/links value the install step had written.
The next install then detected a virtual-store mismatch
(ERR_PNPM_UNEXPECTED_VIRTUAL_STORE). The build step now
derives the same global virtual store directory as the
install step #12307.
- 223d060: Document the --cpu, --os and --libc flags in the
output of pnpm install --help. These flags were already
supported but were only documented on the website #12359.
- e85aea2: Avoid reading README.md from disk when publishing if
the publish manifest already provides a readme field. The
README is now only read lazily, inside
createExportableManifest, when it is actually needed.
- 3188ae7: Fixed pnpm peers check to accept loose peer
dependency ranges such as >=3.16.0 || >=4.0.0- when the
installed peer version satisfies the range #12149.
- 531f2a3: Fixed pnpm update rewriting a workspace: dependency
that points at a local path (e.g.
workspace:../packages/foo/dist) into a normalized link: or
version-range specifier. Such specifiers are now preserved
verbatim when the workspace protocol is preserved #3902.
- fe66535: Fixed a lockfile non-convergence bug where an
incremental install kept a duplicate transitive dependency
that a fresh install would not produce. When a package is
reused from the lockfile, its child edges are taken verbatim
and bypass the preferred-versions walk, so a transitive
dependency could stay pinned to an older version even after a
direct dependency resolved to a higher version that satisfies
the same range. The resolver now refreshes such a stale pin
to the higher direct-dependency version during resolution —
so the older version is never resolved or fetched, and the
incremental result converges to the fresh one.
- 6d35338: pnpm install detects changes inside local file
dependencies again. The optimistic repeat-install fast path
only tracks manifest and lockfile modification times, so
edits inside a local dependency's directory (or a repacked
local tarball) were reported as "Already up to date".
Projects with local file dependencies (file: and bare local
path or tarball specifiers, declared directly or through
pnpm.overrides) now always run a full install, which
refetches those dependencies, matching pnpm v10 behavior
[#11795].
- 4ca9247: Preserve the existing Node.js runtime version prefix
when resolving node@runtime:<range> to a concrete version.
- 30c7590: Create shorter CAFS temporary package directories to
leave room for lifecycle scripts that create IPC socket paths
under TMPDIR.
- 13815ad: Reporter output (warnings, progress) for pnpm store
and pnpm config subcommands now goes to stderr instead of
stdout. This fixes scripts that capture their stdout (e.g.
PNPM_STORE=$(pnpm store path), pnpm config list --json | jq)
from getting warnings mixed into the result.
- 1c05876: Avoid relinking unchanged child dependencies and
remove stale child links during warm installs.
- 817f99d: Fixed lockfile churn where a package's
transitivePeerDependencies could be dropped (and shift
between packages) when the package participates in a
dependency cycle. A cycle re-entry resolves against truncated
children, so it must not be cached as "pure"; otherwise
sibling occurrences of the same package short-circuit and
lose transitive peers depending on traversal order #5108.
- eba03e0: Fix pnpm install reporting "Already up to date"
after a catalog entry in pnpm-workspace.yaml was reverted to
a previous version. After an update modified a catalog, the
workspace state cache stored the pre-update catalog versions,
so reverting the entry back to its original version was not
detected as an outdated state #12418.
- 3b54d79: pnpm update now keeps lockfile overrides that
resolve through a catalog in sync with the catalog.
Previously, when an override referenced a catalog (e.g.
overrides: { foo: 'catalog:' }) and pnpm update bumped that
catalog entry, the lockfile's catalogs advanced while the
resolved overrides kept the old version. The resulting
lockfile was internally inconsistent, so a later pnpm install
- -frozen-lockfile failed with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
- 9d0a300: Fixed pnpm version --recursive so it honors the
workspace selection. In recursive mode the version bump now
applies to the packages resolved from the workspace filter
(selectedProjectsGraph), matching the behavior of pnpm
publish --recursive, instead of always bumping every
workspace package #11348.
- update to 11.7:
* Minor Changes
- Added a new setting frozenStore (--frozen-store) that lets
pnpm install run against a package store on a read-only
filesystem (e.g. a Nix store, a read-only bind mount, an OCI
layer). When enabled, pnpm opens the store's SQLite index.db
through the immutable=1 URI — bypassing the WAL/-shm sidecar
creation that otherwise fails on a read-only directory — and
suppresses every store-write path (the index.db writer and
the project-registry write). Pair it with --offline
- -frozen-lockfile against a fully-populated store. Under the
global virtual store, package directories live inside the
store, so if the store is missing the build output of a
package whose lifecycle scripts are approved (or that has a
patch), pnpm fails up front with
ERR_PNPM_FROZEN_STORE_NEEDS_BUILD rather than crashing
mid-build on a read-only write — seed the store with those
builds first. Incompatible with --force and with a configured
pnpr server, since both write into the store; the
side-effects cache is likewise not written under frozenStore.
If the store is missing its content directory, the install
fails fast with ERR_PNPM_FROZEN_STORE_INCOMPLETE rather than
attempting to initialize it. The read-only immutable=1 open
requires Node.js >=22.15.0, >=23.11.0, or >=24.0.0; on older
runtimes --frozen-store fails with a clear
ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODE error. Bin-linking
also tolerates a read-only store: under the global virtual
store a package's bin source lives inside the store, so the
chmod that makes it executable would be refused — with
EPERM/EACCES, or with EROFS on a genuinely read-only
filesystem. That chmod is redundant when the seed already
ships its bins executable with a normalized shebang, so it is
now skipped in that case, while a non-executable bin (or one
still carrying a Windows CRLF shebang) on a read-only store
still errors.
- When pacquet (the Rust port of pnpm) is declared in
configDependencies, pnpm now delegates dependency resolution
to it too — not just materialization — provided the installed
pacquet is new enough to support full resolving installs (>=
0.11.7).
Previously pacquet only ran in frozen-install mode: pnpm
always resolved the dependency graph itself (writing
pnpm-lock.yaml) and handed pacquet a finished lockfile to
fetch / import / link. With pacquet >= 0.11.7, a non-frozen
pnpm install (default isolated nodeLinker, plain install) is
delegated to pacquet end-to-end in a single pass — pacquet
resolves the manifests, writes the lockfile, and materializes
node_modules. pnpm detects the capability from the installed
pacquet's version; older pacquet releases keep the
resolve-then-materialize split, and add / update / remove
still resolve in pnpm (it has to mutate the manifests first).
This remains an opt-in preview of the Rust install engine
[#11723].
- Added a new opt-in --batch flag to pnpm publish --recursive
that sends all selected packages to the registry in a single
PUT /-/pnpm/v1/publish request instead of one request per
package. The target registry has to implement the batch
publish endpoint (pnpr does); registries that don't are
reported with a clear ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED
error. The batch is processed all-or-nothing by pnpr: if any
package in the batch fails validation, none of the packages
are published.
* Patch Changes
- Reject path-traversal and reserved dependency aliases (such
as ../../../escape, .bin, .pnpm, or node_modules) that come
from a lockfile rather than a freshly resolved manifest. A
crafted lockfile alias could otherwise be joined directly
under a hoisted node_modules directory, letting package files
be written outside the intended install root or overwrite
pnpm-owned layout.
The fix adds two layers:
- The nodeLinker: hoisted graph builder now validates each
alias at the directory sink (safeJoinModulesDir), matching
the validation pnpm already performs when resolving aliases
from manifests.
- The lockfile verification gate (verifyLockfileResolutions)
now runs an always-on, policy-independent check that
rejects any importer or snapshot dependency alias that is
not a valid package name, failing the install early —
before any fetch or filesystem work — for every node linker
at once.
- Made shared package child resolution deterministic when the
same package is reached through multiple contexts. pnpm now
chooses the shallowest occurrence, then importer order, then
parent path, instead of letting request timing decide the
child context and missing-peer report pnpm/pnpm#12358.
- Fix garbled summary line after submitting pnpm update -i and
pnpm audit --fix -i. The interactive checkbox prompt
previously printed every selected choice's full table row
(label, current/target versions, workspace, URL) joined by
commas, producing a wall of text after pressing Enter. The
summary now lists only the selected package names (or
vulnerability keys) by setting an explicit short per choice;
the in-progress selection UI is unchanged.
- Prevent pnpm patch-remove from removing files outside the
configured patches directory.
- Fixed pnpm publish ignoring strictSsl: false when publishing
to registries with self-signed certificates. The strictSSL
option is now forwarded to libnpmpublish / npm-registry-fetch
so that strict-ssl=false in .npmrc or strictSsl: false in
pnpm-workspace.yaml is respected during publish, the same way
it is for pnpm install pnpm/pnpm#12012.
- Fixed Cannot destructure property 'manifest' of
'manifestsByPath[rootDir]' as it is undefined regression
introduced in 11.6.0 when running pnpm add <pkg> outside a
workspace on Windows. selectProjectByDir was keying the
resulting ProjectsGraph by opts.dir instead of
project.rootDir, so downstream manifestsByPath lookups missed
when the two paths normalized differently (typically
drive-letter casing). pnpm/pnpm#12379
- Git dependencies that point to a subdirectory of a repository
(repo#commit&path:/sub/dir) keep their path in the lockfile
again. Since the integrity of git-hosted tarballs started
being pinned in the lockfile, any install that actually
downloaded the tarball rebuilt the lockfile resolution as {
integrity, tarball, gitHosted } and dropped the path field,
while installs served from the store kept it — so the field
disappeared seemingly at random. Without path, later installs
from that lockfile silently unpacked the repository root
instead of the subdirectory #12304.
- Fixed nondeterministic lockfile output that made pnpm dedupe
- -check fail intermittently in CI. When a locked peer
provider was pinned for a dependency that has no child
dependencies of its own, the pinned provider leaked into the
shared parent scope, so siblings resolved after it could pick
up an optional peer they should not see. Which siblings were
affected depended on resolution order, which varies with
network timing.
- Sped up pnpm install with a frozen lockfile by running
lockfile verification (the policy revalidation gate added for
minimumReleaseAge/trustPolicy and the tarball-URL anti-tamper
check) concurrently with fetching and linking instead of
blocking the whole install on it. Dependency lifecycle
scripts are still held back until verification succeeds, so
no script runs on an unverified lockfile: if verification
fails the install aborts before any dependency build, and if
linking finishes first the install waits for the verification
verdict before completing.
- User-defined npm_config_* environment variables are now
preserved during lifecycle script execution. Previously, all
npm_-prefixed env vars were stripped, which caused user-set
variables like npm_config_platform_arch to be lost
pnpm/pnpm#12399.
- pnpm can now use different auth tokens for different package
scopes, even when those scopes use the same registry URL.
Previously, auth was selected only by registry URL. If @org-a
and @org-b both used https://npm.pkg.github.com/, they had to
share the same token. This caused problems for registries
that issue tokens per organization or per scope.
Configure a scope-specific token by adding the package scope
after the registry URL in the auth key:
@org-a:registry=https://npm.pkg.github.com/
@org-b:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:@org-a:_authToken=${ORG_A_TOKEN}
//npm.pkg.github.com/:@org-b:_authToken=${ORG_B_TOKEN}
//npm.pkg.github.com/:_authToken=${FALLBACK_TOKEN}
pnpm login --registry=https://npm.pkg.github.com --scope=@org-a
writes the token to the same scope-specific auth key.
When installing or publishing @org-a/*, pnpm uses
ORG_A_TOKEN. For @org-b/*, pnpm uses ORG_B_TOKEN. Packages
without a matching scope continue to use the registry-wide
fallback token.
- pnpm setup no longer prompts to approve build scripts for
@pnpm/exe when installing the standalone executable. pnpm
links the platform-specific binary itself, so the package's
install scripts are skipped during the global self-install
[#12377].
- Close lockfile reads deterministically before rewriting
lockfiles and keep pacquet's virtual store directory length
aligned with pnpm on Windows.
- A 304 Not Modified answer from the registry now renews the
cached metadata file's mtime, so the minimumReleaseAge
freshness shortcut keeps serving resolutions from the cache.
Previously, once a cached packument grew older than
minimumReleaseAge, every subsequent install re-validated it
against the registry forever, because a 304 never rewrites
the file.
- Updated dependency ranges. Notably:
- @pnpm/logger peer dependency range moved to ^1100.0.0.
- msgpackr 1.11.8 → 2.0.4 (store index files remain
byte-compatible in both directions).
- open ^7.4.2 → ^11.0.0, memoize ^10 → ^11, cli-truncate ^5 →
^6, pidtree ^0.6 → ^1.
- @yarnpkg/core 4.5.0 → 4.8.0, @rushstack/worker-pool 0.7.7 →
0.7.18, @cyclonedx/cyclonedx-library 10.0.0 → 10.1.0,
@pnpm/config.nerf-dart ^1 → ^2, @pnpm/log.group 3.0.2 →
4.0.1, @pnpm/util.lex-comparator ^3 → ^4.
- Updated @zkochan/cmd-shim to v9.0.6.
- Fixed a Windows-only hang where a failed command could take
20–46 seconds to exit. On error, pnpm enumerates descendant
processes (via pidtree) to terminate them, which on Windows
shells out to wmic/PowerShell Get-CimInstance Win32_Process —
a lookup that is extremely slow on some machines. The lookup
is now bounded by a short timeout so it can no longer stall
the process exit.
* Fri Jun 12 2026 Avindra Goolcharan <avindra@opensuse.org>
- Update to 11.6.0:
* pnpm install completes without re-resolving when pnpm-lock.
yaml was deleted but node_modules is intact: the up-to-date
check now treats the current lockfile (node_modules/.pnpm/lock.
yaml) — the record of what the previous install materialized —
as the wanted lockfile, verifies the manifests still match it,
restores pnpm-lock.yaml from it, and reports "Already up to date".
Previously this scenario triggered a full resolution and a re-
verification of every locked package against the registry.
* 615c669: Added support for configuring URL-scoped registry
settings through npm_config_//… and pnpm_config_//…
environment variables, for example:
* Improved the warning printed when a project .npmrc uses an
environment variable in a registry/proxy URL or in registry
credentials. The message now explains why the setting was
ignored and how to migrate it to a trusted source — for example
by moving the line to the user-level ~/.npmrc or running pnpm
config set "<key>" <value> — with a link to https://pnpm.io/npmrc.
The pnpm config set example is only suggested when the key has
no ${...} placeholder, so the snippet is always safe to copy-paste.
* Print a "Lockfile passes supply-chain policies (verified 2h
ago)" message when lockfile verification is skipped because a
cached verdict for the same lockfile content and policy is reused.
Previously the cached short-circuit was completely silent,
which made it look like the policy gate never ran #12324.
* Platform-specific optional dependencies are now skipped even
when their os/cpu/libc fields are missing from the registry
metadata or the lockfile. Some registries strip these fields
from the package metadata, which made pnpm download and install
the binaries of every platform regardless of
supportedArchitectures. The missing platform fields of an
optional dependency are now inferred from its name (e.g. @nx/nx-
win32-arm64-msvc → os: win32, cpu: arm64), so foreign-platform
binaries are skipped without even downloading them #11702.
* Thu Jun 11 2026 Avindra Goolcharan <avindra@opensuse.org>
- Update to 11.5.3:
* ⚠️ Security fix — environment variables in a project .npmrc
* Stopped expanding environment variables in repository-
controlled registry/proxy request destinations and registry
credential values from .npmrc, and in workspace registry URLs
from pnpm-workspace.yaml. Move dynamic registry URL and token
configuration to trusted user, global, CLI, or environment config.
* Resolve package-manager bootstrap dependencies with trusted
user or CLI registry and network config, and reject package-
manager env-lockfile records that do not use registry package
paths with integrity-only resolutions before auto-switch execution.
* Avoid writing packageManagerDependencies to pnpm-lock.yaml
when package manager policy is set to onFail: ignore or
pmOnFail: ignore #12228.
* Avoid running dependency-status auto-install when the
dependency status is unavailable without a project manifest.
* Using the $ version reference syntax in overrides (e.g. "react": "$react")
now prints a deprecation warning. The syntax still works, but
catalogs are the recommended way to keep an overridden
version in sync with the rest of the workspace. Reference a
catalog entry with the catalog: protocol instead.
* Fixed pnpm config get globalconfig to return the global config
.yaml path again pnpm/pnpm#11962.
* Fixed bare --color so it does not consume the following CLI
flag, allowing command shorthands like --parallel to expand
correctly and forms like pnpm --color with current <command>
to dispatch the inner command instead of failing with MISSING_WITH_CURRENT_CMD.
* Fix pnpm install ignoring enableGlobalVirtualStore toggle by
including it in the workspace state settings check #12142.
* Security: pnpm now verifies the npm registry signature of a
package-manager binary before spawning it, so a cloned
repository cannot make pnpm download and execute an arbitrary native binary.
* Made peer-dependent deduplication deterministic. When a peer-
suffixed package variant was a subset of two or more mutually
incompatible larger variants, the variant it collapsed into
depended on the order importers were resolved in, which
varies between machines. This could resolve the same
workspace to different lockfiles on different platforms and
make pnpm dedupe --check alternate between passing and failing.
* Reject invalid package names and versions from staged tarball
manifests before deriving filenames for pnpm stage download.
* Clarified in CLI help that the pnpm store is trusted shared
state and store integrity checks are corruption detection,
not a tamper boundary for untrusted store writers.
* Reject reserved manifest bin names ("", ".", "..", and scoped
forms such as @scope/..) when resolving a package's bins.
These names previously passed the bin-name guard and, when
joined to the global bin directory during global remove/update
/add operations, could resolve to the global bin directory
itself or its parent and have it recursively deleted.
* Require trusted package identity before package-name
allowBuilds entries can approve lifecycle scripts for git, git
- hosted tarball, direct tarball, and local directory artifacts.
To approve one of those artifacts explicitly, use its peer-
suffix-free lockfile depPath as the allowBuilds key. Lockfile
verification now rejects lockfiles where a registry-style
dependency path (name@semver) is backed by a git, directory,
or git-hosted tarball resolution (
ERR_PNPM_RESOLUTION_SHAPE_MISMATCH), so the dependency path
is a reliable artifact identity by the time scripts can run.
* Security: pnpm now verifies the OpenPGP signature of a
downloaded Node.js runtime's SHASUMS256.txt before trusting
its integrity hashes.
* Fri Jun 05 2026 Avindra Goolcharan <avindra@opensuse.org>
- Update to 11.5.2:
* Find the full changelog and breaking changes at:
https://github.com/pnpm/pnpm/blob/v11.5.2/pnpm/CHANGELOG.md
* Fri May 22 2026 Elisei Roca <eroca@suse.com>
- update to 11.2.2:
* Find the full changelog and breaking changes at:
https://github.com/pnpm/pnpm/blob/v11.2.2/pnpm/CHANGELOG.md
* Automated migration steps:
https://pnpm.io/migration
- cd /path/to/your/project
- pnpx codemod run pnpm-v10-to-v11
* Non-automated steps you might have to do:
https://pnpm.io/migration#manual-follow-ups
* Requires at least Node.js v22.13
* Wed May 06 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.33.4:
* Patch Changes
- Pin the integrity of git-hosted tarballs
(codeload.github.com, gitlab.com, bitbucket.org) in the
lockfile so that subsequent installs detect a tampered or
substituted tarball and refuse to install it. Previously the
lockfile only stored the tarball URL for git dependencies, so
a compromised git host or a man-in-the-middle could serve
arbitrary code on later installs without lockfile changes.
- A new gitHosted: true field is recorded on git-hosted tarball
resolutions in the lockfile, letting every reader/writer
route them by a single typed check instead of
pattern-matching the tarball URL in each call site. Lockfiles
written by older pnpm versions are enriched on load (URL
fallback) so the field can be relied on uniformly across the
codebase.
- Fix a regression where pnpm --recursive --filter '!<pkg>'
run/exec/test/add would include the workspace root in the
matched projects. The workspace root is now correctly
excluded by default when only negative --filter arguments are
provided, matching the documented behavior. To include the
root, pass --include-workspace-root #11341.
- update to 10.33.3:
* Patch Changes
- When self-updating from v10's @pnpm/exe to v11+ on Intel
macOS (darwin-x64), pnpm self-update now transparently
switches to the JS-only pnpm package on npm instead of
installing @pnpm/exe@v11+ (which doesn't ship a working
binary for Intel Macs because of an upstream Node.js SEA bug
— see #11423 and nodejs/node#62893). Without this, the
self-update would silently leave the user with no working
pnpm binary. The new install requires Node.js to be available
on PATH; a warning is printed when the swap happens. All
other host/version combinations are unchanged.
- pnpm self-update (with no version argument) no longer
downgrades pnpm when the registry's latest dist-tag points to
an older release than the currently active version. Run pnpm
self-update latest to force a downgrade #11418.
* Sun Apr 26 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.33.2:
* Patch Changes
- Globally-installed bins no longer fail with
ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND when pnpm was installed
via the standalone @pnpm/exe binary (e.g. curl -fsSL
https://get.pnpm.io/install.sh | sh -) on a system without a
separate Node.js installation. Previously, when which('node')
failed during pnpm add --global, pnpm fell back to
process.execPath, which in @pnpm/exe is the pnpm binary
itself — and that path was baked into the generated bin shim,
causing the shim to invoke pnpm instead of Node #11291,
[#4645].
- Fix an infinite fork-bomb that could happen when pnpm was
installed with one version (e.g. npm install -g pnpm@A) and
run inside a project whose package.json selected a different
pnpm version via the packageManager field (e.g. pnpm@B),
while a pnpm-workspace.yaml also existed at the project root.
- The child's environment is now forced to
manage-package-manager-versions=false (v10) and
pm-on-fail=ignore (v11+), which disables the
package-manager-version handling in whichever pnpm runs as
the child.
- Fixes #11337.
- update to 10.33.1:
* Patch Changes
- When a project's packageManager field selects pnpm v11 or
newer, commands that v10 would have passed through to npm
(version, login, logout, publish, unpublish, deprecate,
dist-tag, docs, ping, search, star, stars, unstar, whoami,
etc.) are now handed over to the wanted pnpm, which
implements them natively. Previously they silently shelled
out to npm — making, for example, pnpm version --help print
npm's help on a project with packageManager: pnpm@11.0.0-rc.3
[#11328].
* Sun Mar 29 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.33:
* Minor Changes
- Added a new dedupePeers setting that reduces peer dependency
duplication. When enabled, peer dependency suffixes use
version-only identifiers (name@version) instead of full dep
paths, eliminating nested suffixes like
(foo@1.0.0(bar@2.0.0)). This dramatically reduces the number
of package instances in projects with many recursive peer
dependencies #11070.
* Patch Changes
- Fail on incompatible lockfiles in CI when frozen lockfile
mode is enabled, while preserving non-frozen CI fallback
behavior.
- When package metadata is malformed or can't be fetched, the
error thrown will now show the originating error.
- Fixed intermittent failures when multiple pnpm dlx calls run
concurrently for the same package. When the global virtual
store is enabled, the importer now verifies file content
before skipping a rename, avoiding destructive swap-renames
that break concurrent processes. Also tolerates EPERM during
bin creation on Windows and properly propagates
enableGlobalVirtualStore through the install pipeline.
- Fixed handling of non-string version selectors in hoistPeers,
preventing invalid peer dependency specifiers.
- Improve the non-interactive modules purge error hint to
include the confirmModulesPurge=false workaround.
- When pnpm needs to recreate node_modules but no TTY is
available, the error now suggests either setting CI=true or
disabling the purge confirmation prompt via
confirmModulesPurge=false.
- Adds a regression test for the non-TTY flow.
- Fixed false "Command not found" errors on Windows when a
command exists in PATH but exits with a non-zero code. Also
fixed path resolution for --filter contexts where the command
runs in a different package directory.
- When a pnpm-lock.yaml contains two documents, ignore the
first one. pnpm v11 will write two lockfile documents into
pnpm-lock.yaml in order to store pnpm version integrities and
config dependency resolutions.
- Fixed a bug preventing the clearCache function returned by
createNpmResolver from properly clearing metadata cache.
* Sun Mar 15 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.32.1:
* Patch Changes
- Fix a regression where pnpm-workspace.yaml without a packages
field caused all directories to be treated as workspace
projects. This broke projects that use pnpm-workspace.yaml
only for settings (e.g. minimumReleaseAge) without defining
workspace packages #10909.
- update to 10.32.0:
* Minor Changes
- Added --all flag to pnpm approve-builds that approves all
pending builds without interactive prompts #10136.
* Patch Changes
- Reverted change related to setting explicitly the npm config
file path, which caused regressions.
- Reverted fix related to lockfile-include-tarball-url. Fixes
[#10915].
- update to 10.31.0:
* Minor Changes
- When pnpm updates the pnpm-workspace.yaml, comments, string
formatting, and whitespace will be preserved.
* Patch Changes
- Added -F as a short alias for the --filter option in the help
output.
- Handle undefined pkgSnapshot in pnpm why -r #10700.
- Fix headless install not being used when a project has an
injected self-referencing file: dependency that resolves to
link: in the lockfile.
- Fixed a race condition when multiple worker threads import
the same package to the global virtual store concurrently.
The rename operation now tolerates ENOTEMPTY/EEXIST errors if
another thread already completed the import.
- When lockfile-include-tarball-url is set to false, tarball
URLs are now always excluded from the lockfile. Previously,
tarball URLs could still appear for packages hosted under
non-standard URLs, making the behavior flaky and inconsistent
[#6667].
- Fixed optimisticRepeatInstall skipping install when
overrides, packageExtensions, ignoredOptionalDependencies,
patchedDependencies, or peersSuffixMaxLength changed.
- Fixed pnpm patch-commit failing with "unable to access
'/.config/git/attributes': Permission denied" error in
environments where HOME is unset or non-standard (Docker
containers, CI systems).
- The issue occurred because pnpm was setting HOME and the
Windows user profile env var to empty strings to suppress
user git configuration when running git diff. This caused git
to resolve the home directory (~) as root (/), leading to
permission errors when attempting to access
/.config/git/attributes.
- Now uses GIT_CONFIG_GLOBAL: os.devNull instead, which is
git's proper mechanism for bypassing user-level configuration
without corrupting the home directory path resolution.
- Fixes #6537
- Fix pnpm why -r --parseable missing dependents when multiple
workspace packages share the same dependency #8100.
- Fix link-workspace-packages=true incorrectly linking
workspace packages when the requested version doesn't match
the workspace package's version. Previously, on fresh
installs the version constraint is overridden to * in the
fallback resolution paths, causing any workspace package with
a matching name to be linked regardless of version #10173.
- Fixed pnpm update --interactive table breaking with long
version strings (e.g., prerelease versions like
7.0.0-dev.20251209.1) by dynamically calculating column
widths instead of using hardcoded values #10316.
- Explicitly tell npm the path to the global rc config file.
- The parameter set by the --allow-build flag is written to
allowBuilds.
- Fix a bug in which specifying filter on pnpm-workspace.yaml
would cause pnpm to not detect any projects.
- Print help message on running pnpm dlx without arguments and
exit.
* Mon Mar 02 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.30.3:
* Patch Changes
- Fixed version switching via packageManager field failing when
pnpm is installed as a standalone executable in environments
without a system Node.js #10687.
- update to 10.30.2:
* Patch Changes
- Fix auto-installed peer dependencies ignoring overrides when
a stale version exists in the lockfile.
- Fixed "input line too long" error on Windows when running
lifecycle scripts with the global virtual store enabled
[#10673].
- Update @zkochan/js-yaml to fix moderate vulnerability.
- update to 10.30.1:
* Patch Changes
- Use the /-/npm/v1/security/audits/quick endpoint as the
primary audit endpoint, falling back to
/-/npm/v1/security/audits when it fails #10649.
- update to 10.30.0:
* Minor Changes
- pnpm why now shows a reverse dependency tree. The searched
package appears at the root with its dependents as branches,
walking back to workspace roots. This replaces the previous
forward-tree output which was noisy and hard to read for
deeply nested dependencies.
* Patch Changes
- Revert pnpm why dependency pruning to prefer correctness over
memory consumption. Reverted PR: #7122.
- Optimize pnpm why and pnpm list performance in workspaces
with many importers by sharing the dependency graph and
materialization cache across all importers instead of
rebuilding them independently for each one #10596.
- update to 10.29.3:
* Patch Changes
- Fixed an out-of-memory error in pnpm list (and pnpm why) on
large dependency graphs by replacing the recursive tree
builder with a two-phase approach: a BFS dependency graph
followed by cached tree materialization. Duplicate subtrees
are now deduplicated in the output, shown as "deduped (N deps
hidden)" #10586.
- Fixed allowBuilds not working when set via .pnpmfile.cjs
[#10516].
- When the enableGlobalVirtualStore option is set, the pnpm
deploy command would incorrectly create symlinks to the
global virtual store. To keep the deploy directory
self-contained, pnpm deploy now ignores this setting and
always creates a localized virtual store within the deploy
directory.
- Fixed minimumReleaseAgeExclude not being respected by pnpm
dlx #10338.
- update to 10.29.2:
* Patch Changes
- Reverted a fix shipped in v10.29.1, which caused another
issue #10571.
- Reverted fix: Fixed pnpm run -r failing with "No projects
matched the filters" when an empty pnpm-workspace.yaml exists
[#10497].
- update to 10.29.1 (10.29.0 was not released):
* Minor Changes
- The pnpm dlx / pnpx command now supports the catalog:
protocol. Example: pnpm dlx shx@catalog:.
- Support configuring auditLevel in the pnpm-workspace.yaml
file #10540.
- Support bare workspace: protocol without version specifier.
It is now treated as workspace:* and resolves to the concrete
version during publish #10436.
* Patch Changes
- Fixed pnpm list --json returning incorrect paths when using
global virtual store #10187.
- Fix pnpm store path and pnpm store status using workspace
root for path resolution when storeDir is relative #10290.
- Fixed pnpm run -r failing with "No projects matched the
filters" when an empty pnpm-workspace.yaml exists #10497.
- Fixed a bug where catalogMode: strict would write the literal
string "catalog:" to pnpm-workspace.yaml instead of the
resolved version specifier when re-adding an existing catalog
dependency #10176.
- Fixed the documentation URL shown in pnpm completion --help
to point to the correct page at https://pnpm.io/completion
[#10281].
- Skip local file: protocol dependencies during pnpm fetch.
This fixes an issue where pnpm fetch would fail in Docker
builds when local directory dependencies were not available
[#10460].
- Fixed pnpm audit --json to respect the --audit-level setting
for both exit code and output filtering #10540.
- update tar to version 7.5.7 to fix security issue
- Updating the version of dependency tar to 7.5.7 because the
previous one have a security vulnerability reported here:
CVE-2026-24842
- Fix pnpm audit --fix replacing reference overrides (e.g.
$foo) with concrete versions #10325.
- Fix shamefullyHoist set via updateConfig in .pnpmfile.cjs not
being converted to publicHoistPattern #10271.
- pnpm help should correctly report if the currently running
pnpm CLI is bundled with Node.js #10561.
- Add a warning when the current directory contains the PATH
delimiter character. On macOS, folder names containing
forward slashes (/) appear as colons (:) at the Unix layer.
Since colons are PATH separators in POSIX systems, this
breaks PATH injection for node_modules/.bin, causing binaries
to not be found when running commands like pnpm exec #10457.
* Tue Jan 27 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.28.2:
* Patch Changes
- Security fix: prevent path traversal in directories.bin
field.
- When pnpm installs a file: or git: dependency, it now
validates that symlinks point within the package directory.
Symlinks to paths outside the package root are skipped to
prevent local data from being leaked into node_modules.
This fixes a security issue where a malicious package could
create symlinks to sensitive files (e.g., /etc/passwd,
~/.ssh/id_rsa) and have their contents copied when the
package is installed.
Note: This only affects file: and git: dependencies. Registry
packages (npm) have symlinks stripped during publish and are
not affected.
- Fixed optional dependencies to request full metadata from the
registry to get the libc field, which is required for proper
platform compatibility checks #9950.
- update to 10.28.1:
* Patch Changes
- Fixed installation of config dependencies from private
registries.
Added support for object type in configDependencies when the
tarball URL returned from package metadata differs from the
computed URL #10431.
- Fix path traversal vulnerability in binary fetcher ZIP
extraction
- Validate ZIP entry paths before extraction to prevent
writing files outside target directory
- Validate BinaryResolution.prefix (basename) to prevent
directory escape via crafted prefix
- Both attack vectors now throw ERR_PNPM_PATH_TRAVERSAL error
- Support plain http:// and https:// URLs ending with .git as
git repository dependencies.
Previously, URLs like
https://gitea.example.org/user/repo.git#commit were not
recognized as git repositories because they lacked the git+
prefix (e.g., git+https://). This caused issues when
installing dependencies from self-hosted git servers like
Gitea or Forgejo that don't provide tarball downloads.
Changes:
- The git resolver now runs before the tarball resolver,
ensuring git URLs are handled by the correct resolver
- The git resolver now recognizes plain http:// and https://
URLs ending in .git as git repositories
- Removed the isRepository check from the tarball resolver
since it's no longer needed with the new resolver order
Fixes #10468
- pnpm run -r and pnpm run --filter now fail with a non-zero
exit code when no packages have the specified script.
Previously, this only failed when all packages were selected.
Use --if-present to suppress this error #6844.
- Fixed a path traversal vulnerability in tarball extraction on
Windows. The path normalization was only checking for ./ but
not .\. Since backslashes are directory separators on
Windows, malicious packages could use paths like
foo\..\..\.npmrc to write files outside the package
directory.
- When running "pnpm exec" from a subdirectory of a project,
don't change the current working directory to the root of the
project #5759.
- Fixed a path traversal vulnerability in pnpm's bin linking.
Bin names starting with @ bypassed validation, and after
scope normalization, path traversal sequences like ../../
remained intact.
- Revert Try to avoid making network calls with preferOffline
[#10334].
- Fix --save-peer to write valid semver ranges to
peerDependencies for protocol-based installs (e.g. jsr:) by
deriving from resolved versions when available and falling
back to * if none is available #10417.
- Do not exclude the root workspace project, when it is
explicitly selected via a filter #10465.
* Mon Jan 19 2026 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.28.0:
* Minor Changes
- Add support for a hook called beforePacking that can be used
to customize the package.json contents at publish time #3816.
- In some cases, a filtered install (i.e. pnpm install --filter
...) was slower than running pnpm install without any filter
arguments. This performance regression is now fixed. Filtered
installs should be as fast or faster than a full install
[#10408].
* Patch Changes
- Do not add a symlink to the project into the store's project
registry if the store is in a subdirectory of the project
[#10411].
- It should be possible to declare the requiredScripts setting
in pnpm-workspace.yaml #10261.
- update to 10.27.0:
* Minor Changes
- Adding trustPolicyIgnoreAfter allows you to ignore trust
policy checks for packages published more than a specified
time ago#10352.
- Added project registry for global virtual store prune
support.
- Projects using the store are now registered via symlinks in
{storeDir}/v10/projects/. This enables pnpm store prune to
track which packages are still in use by active projects and
safely remove unused packages from the global virtual store.
- Semi-breaking. Changed the location of unscoped packages in
the virtual global store. They will now be stored under a
directory named @ to maintain a uniform 4-level directory
depth.
- Added mark-and-sweep garbage collection for global virtual
store.
- pnpm store prune now removes unused packages from the global
virtual store's links/ directory. The algorithm:
- Scans all registered projects for symlinks pointing to the
store
- Walks transitive dependencies to mark reachable packages
- Removes any package directories not marked as reachable
- This includes support for workspace monorepos - all
node_modules directories within a project (including those in
workspace packages) are scanned.
* Patch Changes
- Throw an error if the value of the tokenHelper or
<url>:tokenHelper setting contains an environment variable.
- Git dependencies with build scripts should respect the
dangerouslyAllowAllBuilds settings #10376.
- Skip the package manager check when running with --global and
a project packageManager is configured, and warn that the
check is skipped.
- pnpm store prune should not fail if the dlx cache directory
has files, not only directories #10384
- Fixed a bug (#9759) where pnpm add would incorrectly modify a
catalog entry in pnpm-workspace.yaml to its exact version.
- update to 10.26.2:
* Patch Changes
- Improve error message when a package version exists but does
not meet the minimumReleaseAge constraint. The error now
clearly states that the version exists and shows a
human-readable time since release (e.g., "released 6 hours
ago") #10307.
- Fix installation of Git dependencies using annotated tags
[#10335].
- Previously, pnpm would store the annotated tag object's SHA
in the lockfile instead of the actual commit SHA. This caused
ERR_PNPM_GIT_CHECKOUT_FAILED errors because the checked-out
commit hash didn't match the stored tag object hash.
- Binaries of runtime engines (Node.js, Deno, Bun) are written
to node_modules/.bin before lifecycle scripts (install,
postinstall, prepare) are executed #10244.
- Try to avoid making network calls with preferOffline #10334.
- update to 10.26.1:
* Patch Changes
- Don't fail on pnpm add, when blockExoticSubdeps is set to
true #10324.
- Always resolve git references to full commits and ensure HEAD
points to the commit after checkout #10310.
* Mon Dec 15 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.26.0
* Minor Changes
- Semi-breaking. Block git-hosted dependencies from running
prepare scripts unless explicitly allowed in onlyBuiltDependencies #10288.
- Semi-breaking. Compute integrity hash for HTTP tarball
dependencies when fetching, storing it in the lockfile to
prevent servers from serving altered content on subsequent installs #10287.
- Added a new setting blockExoticSubdeps that prevents the
resolution of exotic protocols in transitive dependencies.
- Added support for allowBuilds, which is a new field that
can be used instead of onlyBuiltDependencies and
ignoredBuiltDependencies. The new allowBuilds field in your
pnpm-workspace.yaml uses a map of package matchers to
explicitly allow (true) or disallow (false) script execution.
This allows for a single, easy-to-manage source of truth for
your build permissions.
* Patch Changes
- Show deprecation in table/list formats when latest version
is deprecated #8658.
- Remove the injectWorkspacePackages setting from the
lockfile on the deploy command #10294.
- Normalize the tarball URLs before saving them to the
lockfile. URLs should not contain default ports, like :80 for
http and :443 for https #10273.
- When a dependency is installed via a direct URL that
redirects to another URL and is immutable, the original URL
is normalized and saved to package.json #10197.
* Sat Dec 13 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.25.0:
* Minor Changes
- Allow loading certificates from cert, ca, and key for
specific registry URLs. E.g.,
//registry.example.com/:ca=-----BEGIN CERTIFICATE-----....
Previously this was only working via certfile, cafile, and
keyfile.
These properties are supported in .npmrc, but were ignored by
pnpm, this will make pnpm read and use them as well.
Related PR: #10230.
- Added a new flag called --bare to pnpm init for creating a
package.json with the bare minimum of required fields #10226.
* Patch Changes
- Improved reporting of ignored dependency scripts #10276.
- pnpm install should build any dependencies that were added to
onlyBuiltDependencies and were not built yet #10256.
- pnpm publish -r --force should allow to run publish over
already existing versions in the registry #10272.
- Don't fail with a ERR_PNPM_MISSING_TIME error if a package
that is excluded from trust policy checks is missing the time
field in the metadata.
* Sat Dec 13 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.24.0:
* Minor Changes
- Increased network concurrency on machines with many CPU
cores. pnpm now automatically selects a network concurrency
between 16 and 64, based on the number of pnpm workers
(calculated as workers × 3). This improves performance on
high-core systems #10068.
* Patch Changes
- trustPolicy should ignore the trust evidences of prerelease
versions, when installing a non-prerelease version.
- Handle ENOENT errors thrown by fs.linkSync(), which can occur
in containerized environments (OverlayFS) instead of EXDEV.
The operation now gracefully falls back to fs.copyFileSync()
in these cases #10217.
- Reverted: pnpm self-update should download pnpm from the
configured npm registry #10205.
- Packages that don't have a package.json file (like Node.js)
should not be reimported from the store on every install.
Another file from the package should be checked in order to
verify its presence in node_modules.
- Correctly read auth tokens for URLs that contain underscores
[#17].
* Sun Nov 23 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.23.0:
* Minor Changes
- Added --lockfile-only option to pnpm list #10020.
* Patch Changes
- pnpm self-update should download pnpm from the configured npm
registry #10205.
- pnpm self-update should always install the non-executable
pnpm package (pnpm in the registry) and never the @pnpm/exe
package, when installing v11 or newer. We currently cannot
ship @pnpm/exe as pkg doesn't work with ESM #10190.
- Node.js runtime is not added to "dependencies" on pnpm add,
if there's a engines.runtime setting declared in package.json
[#10209].
- The installation should fail if an optional dependency cannot
be installed due to a trust policy check failure #10208.
- pnpm list and pnpm why now display npm: protocol for aliased
packages (e.g., foo npm:is-odd@3.0.1) #8660.
- Don't add an extra slash to the Node.js mirror URL #10204.
- pnpm store prune should not fail if the store contains
Node.js packages #10131.
* Tue Nov 18 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.22.0:
* Minor Changes
- Added support for trustPolicyExclude #10164.
You can now list one or more specific packages or versions
that pnpm should allow to install, even if those packages
don't satisfy the trust policy requirement. For example:
trustPolicy: no-downgrade
trustPolicyExclude:
- chokidar@4.0.3
- webpack@4.47.0 || 5.102.1
- Allow to override the engines field on publish by the
publishConfig.engines field.
* Patch Changes
- Don't crash when two processes of pnpm are hardlinking the
contents of a directory to the same destination
simultaneously #10179.
* Tue Nov 18 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.21.0:
* Minor Changes
- Node.js Runtime Installation for Dependencies. Added support
for automatic Node.js runtime installation for dependencies.
pnpm will now install the Node.js version required by a
dependency if that dependency declares a Node.js runtime in
the "engines" field. For example:
{
"engines": {
"runtime": {
"name": "node",
"version": "^24.11.0",
"onFail": "download"
}
}
}
If the package with the Node.js runtime dependency is a CLI
app, pnpm will bind the CLI app to the required Node.js
version. This ensures that, regardless of the globally
installed Node.js instance, the CLI will use the compatible
version of Node.js.
If the package has a postinstall script, that script will be
executed using the specified Node.js version.
Related PR: #10141
- Added a new setting: trustPolicy.
When set to no-downgrade, pnpm will fail installation if a
package’s trust level has decreased compared to previous
releases — for example, if it was previously published by a
trusted publisher but now only has provenance or no trust
evidence.
This helps prevent installing potentially compromised
versions of a package.
Related issue: #8889.
- Added support for pnpm config get globalconfig to retrieve
the global config file path #9977.
* Patch Changes
- When a user runs pnpm update on a dependency that is not
directly listed in package.json, none of the direct
dependencies should be updated #10155.
- Don't crash when two processes of pnpm are hardlinking the
contents of a directory to the same destination
simultaneously #10160.
- Setting gitBranchLockfile and related settings via
pnpm-workspace.yaml should work #9651.
* Sat Nov 01 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.20.0:
* Minor Changes
- Support --all option in pnpm --help to list all commands
[#8628].
* Patch Changes
- When the latest version doesn't satisfy the maturity
requirement configured by minimumReleaseAge, pick the highest
version that is mature enough, even if it has a different
major version #10100.
- create command should not verify patch info.
- Set managePackageManagerVersions to false, when switching to
a different version of pnpm CLI, in order to avoid subsequent
switches #10063.
- update to 10.19.0:
* Minor Changes
- You can now allow specific versions of dependencies to run
postinstall scripts. onlyBuiltDependencies now accepts
package names with lists of trusted versions. For example:
Related PR: #10104.
onlyBuiltDependencies:
- nx@21.6.4 || 21.6.5
- esbuild@0.25.1
- Added support for exact versions in minimumReleaseAgeExclude
[#9985].
You can now list one or more specific versions that pnpm
should allow to install, even if those versions don’t satisfy
the maturity requirement set by minimumReleaseAge. For
example:
minimumReleaseAge: 1440
minimumReleaseAgeExclude:
- nx@21.6.5
- webpack@4.47.0 || 5.102.1
- update to 10.18.3:
* Patch Changes
- Fix a bug where pnpm would infinitely recurse when using
verifyDepsBeforeInstall: install and pre/post install scripts
that called other pnpm scripts #10060.
- Fixed scoped registry keys (e.g., @scope:registry) being
parsed as property paths in pnpm config get when
- -location=project is used #9362.
- Remove pnpm-specific CLI options before passing to npm
publish to prevent "Unknown cli config" warnings #9646.
- Fixed EISDIR error when bin field points to a directory
[#9441].
- Preserve version and hasBin for variations packages #10022.
- Fixed pnpm config set --location=project incorrectly handling
keys with slashes (auth tokens, registry settings) #9884.
- When both pnpm-workspace.yaml and .npmrc exist, pnpm config
set --location=project now writes to pnpm-workspace.yaml
(matching read priority) #10072.
- Prevent a table width error in pnpm outdated --long #10040.
- Sync bin links after injected dependencies are updated by
build scripts. This ensures that binaries created during
build processes are properly linked and accessible to
consuming projects #10057.
- update to 10.18.2:
* Patch Changes
- pnpm outdated --long should work #10040.
- Replace ndjson with split2. Reduce the bundle size of pnpm
CLI #10054.
- pnpm dlx should request the full metadata of packages, when
minimumReleaseAge is set #9963.
- pnpm version switching should work when the pnpm home
directory is in a symlinked directory #9715.
- Fix EPIPE errors when piping output to other commands #10027.
- update to 10.18.1:
* Patch Changes
- Don't print a warning, when --lockfile-only is used #8320.
- pnpm setup creates a command shim to the pnpm executable.
This is needed to be able to run pnpm self-update on Windows
[#5700].
- When using pnpm catalogs and running a normal pnpm install,
pnpm produced false positive warnings for "skip adding to the
default catalog because it already exists". This warning now
only prints when using pnpm add --save-catalog as originally
intended.
- update to 10.18.0:
* Minor Changes
- Added network performance monitoring to pnpm by implementing
warnings for slow network requests, including both metadata
fetches and tarball downloads.
Added configuration options for warning thresholds:
fetchWarnTimeoutMs and fetchMinSpeedKiBps.
Warning messages are displayed when requests exceed time
thresholds or fall below speed minimums
Related PR: #10025.
* Patch Changes
- Retry filesystem operations on EAGAIN errors #9959.
- Outdated command respects minimumReleaseAge configuration
[#10030].
- Correctly apply the cleanupUnusedCatalogs configuration when
removing dependent packages.
- Don't fail with a meaningless error when scriptShell is set
to false #8748.
- pnpm dlx should not fail when minimumReleaseAge is set
[#10037].
* Tue Sep 23 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.17.1:
* Patch Changes
- When a version specifier cannot be resolved because the versions
don't satisfy the minimumReleaseAge setting, print this
information out in the error message #9974.
- Fix state.json creation path when executing pnpm patch in a
workspace project #9733.
- When minimumReleaseAge is set and the latest tag is not mature
enough, prefer a non-deprecated version as the new latest #9987.
* Fri Sep 19 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.17:
* Minor Changes
- The minimumReleaseAgeExclude setting now supports patterns.
For instance:
minimumReleaseAge: 1440
minimumReleaseAgeExclude:
- "@eslint/*"
* Patch Changes
- Don't ignore the minimumReleaseAge check, when the package is
requested by exact version and the packument is loaded from
cache #9978.
- When minimumReleaseAge is set and the active version under a
dist-tag is not mature enough, do not downgrade to a
prerelease version in case the original version wasn't a
prerelease one #9979.
- update to 10.16.1:
* Patch Changes
- The full metadata cache should be stored not at the same
location as the abbreviated metadata. This fixes a bug where
pnpm was loading the abbreviated metadata from cache and
couldn't find the "time" field as a result #9963.
- Forcibly disable ANSI color codes when generating patch diff
[#9914].
- update to 10.16:
* Minor Changes
- There have been several incidents recently where popular
packages were successfully attacked. To reduce the risk of
installing a compromised version, we are introducing a new
setting that delays the installation of newly released
dependencies. In most cases, such attacks are discovered
quickly and the malicious versions are removed from the
registry within an hour.
- The new setting is called minimumReleaseAge. It specifies the
number of minutes that must pass after a version is published
before pnpm will install it. For example, setting
minimumReleaseAge: 1440 ensures that only packages released
at least one day ago can be installed.
- If you set minimumReleaseAge but need to disable this
restriction for certain dependencies, you can list them under
the minimumReleaseAgeExclude setting. For instance, with the
following configuration pnpm will always install the latest
version of webpack, regardless of its release time:
minimumReleaseAgeExclude:
- webpack
- Added support for finders #9946.
In the past, pnpm list and pnpm why could only search for
dependencies by name (and optionally version). For example:
pnpm why minimist
prints the chain of dependencies to any installed instance of
minimist:
verdaccio 5.20.1
├─┬ handlebars 4.7.7
│ └── minimist 1.2.8
└─┬ mv 2.1.1
└─┬ mkdirp 0.5.6
└── minimist 1.2.8
What if we want to search by other properties of a
dependency, not just its name? For instance, find all
packages that have react@17 in their peer dependencies?
This is now possible with "finder functions". Finder
functions can be declared in .pnpmfile.cjs and invoked with
the --find-by=<function name> flag when running pnpm list or
pnpm why.
Let's say we want to find any dependencies that have React 17
in peer dependencies. We can add this finder to our
.pnpmfile.cjs:
module.exports = {
finders: {
react17: (ctx) => {
return ctx.readManifest().peerDependencies?.react === "^17.0.0";
},
},
};
Now we can use this finder function by running:
pnpm why --find-by=react17
pnpm will find all dependencies that have this React in peer
dependencies and print their exact locations in the
dependency graph.
@apollo/client 4.0.4
├── @graphql-typed-document-node/core 3.2.0
└── graphql-tag 2.12.6
It is also possible to print out some additional information
in the output by returning a string from the finder. For
example, with the following finder:
module.exports = {
finders: {
react17: (ctx) => {
const manifest = ctx.readManifest();
if (manifest.peerDependencies?.react === "^17.0.0") {
return `license: ${manifest.license}`;
}
return false;
},
},
};
Every matched package will also print out the license from
its package.json:
@apollo/client 4.0.4
├── @graphql-typed-document-node/core 3.2.0
│ license: MIT
└── graphql-tag 2.12.6
license: MIT
* Patch Changes
- Fix deprecation warning printed when executing pnpm with
Node.js 24 #9529.
- Throw an error if nodeVersion is not set to an exact semver
version #9934.
- pnpm publish should be able to publish a .tar.gz file #9927.
- Canceling a running process with Ctrl-C should make pnpm run
return a non-zero exit code #9626.
- update to 10.15.1:
* Patch Changes
- Fix .pnp.cjs crash when importing subpath #9904.
- When resolving peer dependencies, pnpm looks whether the peer
dependency is present in the root workspace project's
dependencies. This change makes it so that the peer
dependency is correctly resolved even from aliased npm-hosted
dependencies or other types of dependencies #9913.
* Wed Aug 20 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.15.0:
* Minor Changes
- Added the cleanupUnusedCatalogs configuration. When set to
true, pnpm will remove unused catalog entries during
installation #9793.
- Automatically load pnpmfiles from config dependencies that
are named @*/pnpm-plugin-* #9780.
- pnpm config get now prints an INI string for an object value
[#9797].
- pnpm config get now accepts property paths (e.g. pnpm config
get catalog.react, pnpm config get .catalog.react, pnpm
config get
'packageExtensions["@babel/parser"].peerDependencies["@babel/types"]'),
and pnpm config set now accepts dot-leading or subscripted
keys (e.g. pnpm config set .ignoreScripts true).
- pnpm config get --json now prints a JSON serialization of
config value, and pnpm config set --json now parses the input
value as JSON.
* Patch Changes
- Semi-breaking. When automatically installing missing peer
dependencies, prefer versions that are already present in the
direct dependencies of the root workspace package #9835.
- When executing the pnpm create command, must verify whether
the node version is supported even if a cache already exists
[#9775].
- When making requests for the non-abbreviated packument, add
* /* to the Accept header to avoid getting a 406 error on AWS
CodeArtifact #9862.
- The standalone exe version of pnpm works with glibc 2.26
again #9734.
- Fix a regression in which pnpm dlx pkg --help doesn't pass
- -help to pkg #9823.
* Fri Aug 01 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.14.0:
* Minor Changes
- Added support for JavaScript runtime installation
(Related PR: #9755.)
Declare Node.js, Deno, or Bun in devEngines.runtime (inside
package.json) and let pnpm download and pin it automatically.
Usage example:
{
"devEngines": {
"runtime": {
"name": "node",
"version": "^24.4.0",
"onFail": "download" // we only support the "download" value for now
}
}
}
How it works:
- pnpm install resolves your specified range to the latest
matching runtime version.
- The exact version (and checksum) is saved in the lockfile.
- Scripts use the local runtime, ensuring consistency across
environments.
Why this is better:
- This new setting supports also Deno and Bun (vs. our
Node-only settings useNodeVersion and
executionEnv.nodeVersion)
- Supports version ranges (not just a fixed version).
- The resolved version is stored in the pnpm lockfile, along
with an integrity checksum for future validation of the
Node.js content's validity.
- It can be used on any workspace project (like
executionEnv.nodeVersion). So, different projects in a
workspace can use different runtimes.
- For now devEngines.runtime setting will install the runtime
locally, which we will improve in future versions of pnpm
by using a shared location on the computer.
- Add --cpu, --libc, and --os to pnpm install, pnpm add, and
pnpm dlx to customize supportedArchitectures via the CLI
[#7510].
* Patch Changes
- Fix a bug in which pnpm add downloads packages whose libc
differ from pnpm.supportedArchitectures.libc.
- The integrities of the downloaded Node.js artifacts are
verified #9750.
- Allow dlx to parse CLI flags and options between the dlx
command and the command to run or between the dlx command and
- - #9719.
- pnpm install --prod should removing hoisted dev dependencies
[#9782].
- Fix an edge case bug causing local tarballs to not re-link
into the virtual store. This bug would happen when changing
the contents of the tarball without renaming the file and
running a filtered install.
- Fix a bug causing pnpm install to incorrectly assume the
lockfile is up to date after changing a local tarball that
has peers dependencies.
* Wed Jul 09 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.13.1:
* Patch Changes
- Run user defined pnpmfiles after pnpmfiles of plugins.
- update to 10.13.0:
* Minor Changes
- Added the possibility to load multiple pnpmfiles. The pnpmfile
setting can now accept a list of pnpmfile locations #9702.
- pnpm will now automatically load the pnpmfile.cjs file from any
config dependency named @pnpm/plugin-* or pnpm-plugin-* #9729.
- The order in which config dependencies are initialized should
not matter — they are initialized in alphabetical order. If a
specific order is needed, the paths to the pnpmfile.cjs files in
the config dependencies can be explicitly listed using the
pnpmfile setting in pnpm-workspace.yaml.
* Patch Changes
- When patching dependencies installed via pkg.pr.new, treat them
as Git tarball URLs #9694.
- Prevent conflicts between local projects' config and the global
config in dangerouslyAllowAllBuilds, onlyBuiltDependencies,
onlyBuiltDependenciesFile, and neverBuiltDependencies #9628.
- Sort keys in pnpm-workspace.yaml with deep #9701.
- The pnpm rebuild command should not add pkgs included in
ignoredBuiltDependencies to ignoredBuilds in
node_modules/.modules.yaml #9338.
- Replaced shell-quote with shlex for quoting command arguments
[#9381].
* Mon Jun 30 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.12.4:
* Patch Changes
- Fix pnpm licenses command for local dependencies #9583.
- Fix a bug in which pnpm ls --filter=not-exist --json prints
nothing instead of an empty array #9672.
- Fix a deadlock that sometimes happens during peer dependency
resolution #9673.
- Running pnpm install after pnpm fetch should hoist all
dependencies that need to be hoisted.
- Fixes a regression introduced in v10.12.2 by #9648; resolves
[#9689].
* Tue Jun 24 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.12.3:
* Patch Changes
- Restore hoisting of optional peer dependencies when installing
with an outdated lockfile. Regression introduced in v10.12.2 by
[#9648]; resolves #9685.
* Mon Jun 23 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.12.2:
* Patch Changes
- Fixed hoisting with enableGlobalVirtualStore set to true #9648.
- Fix the --help and -h flags not working as expected for the pnpm
create command.
- The dependency package path output by the pnpm licenses list
- -json command is incorrect.
- Fix a bug in which pnpm deploy fails due to overridden
dependencies having peer dependencies causing
ERR_PNPM_OUTDATED_LOCKFILE #9595.
* Sun Jun 08 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.12.1 (10.2.0 was yanked):
* Minor Changes
- Experimental. Added support for global virtual stores. When
enabled, node_modules contains only symlinks to a central
virtual store, rather to node_modules/.pnpm. By default, this
central store is located at <store-path>/links (you can find
the store path by running pnpm store path).
In the central virtual store, each package is hard linked
into a directory whose name is the hash of its dependency
graph. This allows multiple projects on the system to symlink
shared dependencies from this central location, significantly
improving installation speed when a warm cache is available.
This is conceptually similar to how NixOS manages packages,
using dependency graph hashes to create isolated and
reusable package directories.
To enable the global virtual store, set
enableGlobalVirtualStore: true in your root
pnpm-workspace.yaml, or globally via:
pnpm config -g set enable-global-virtual-store true
NOTE: In CI environments, where caches are typically cold,
this setting may slow down installation. pnpm automatically
disables the global virtual store when running in CI.
Related PR: #8190
- The pnpm update command now supports updating catalog:
protocol dependencies and writes new specifiers to
pnpm-workspace.yaml.
- A new catalogMode setting is available for controlling if and
how dependencies are added to the default catalog. It can be
configured to several modes:
- strict: Only allows dependency versions from the catalog.
Adding a dependency outside the catalog's version range
will cause an error.
- prefer: Prefers catalog versions, but will fall back to
direct dependencies if no compatible version is found.
- manual (default): Does not automatically add dependencies
to the catalog.
- Added two new CLI options (--save-catalog and
- -save-catalog-name=<name>) to pnpm add to save new
dependencies as catalog entries. catalog: or catalog:<name>
will be added to package.json and the package specifier will
be added to the catalogs or catalog[<name>] object in
pnpm-workspace.yaml #9425.
- Semi-breaking. The keys used for side-effects caches have
changed. If you have a side-effects cache generated by a
previous version of pnpm, the new version will not use it and
will create a new cache instead #9605.
- Added a new setting called ci for explicitly telling pnpm if
the current environment is a CI or not.
* Patch Changes
- Sort versions printed by pnpm patch using semantic versioning
rules.
- Improve the way the error message displays mismatched
specifiers. Show differences instead of 2 whole objects
[#9598].
- Revert #9574 to fix a regression #9596.
* Mon Jun 02 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.11.1:
* Patch Changes
- Fix an issue in which pnpm deploy --legacy creates unexpected
directories when the root package.json has a workspace
package as a peer dependency #9550.
- Dependencies specified via a URL that redirects will only be
locked to the target if it is immutable, fixing a regression
when installing from GitHub releases. (#9531)
- Installation should not exit with an error if
strictPeerDependencies is true but all issues are ignored by
peerDependencyRules #9505.
- Use pnpm_config_ env variables instead of npm_config_ #9571.
- Fix a regression (in v10.9.0) causing the --lockfile-only
flag on pnpm update to produce a different pnpm-lock.yaml
than an update without the flag.
- Let pnpm deploy work in repos with overrides when
inject-workspace-packages=true #9283.
- Fixed the problem of path loss caused by parsing URL address.
Fixes a regression shipped in pnpm v10.11 via #9502.
- pnpm -r --silent run should not print out section #9563.
* Tue May 20 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- add Requires for nodejs(abi) >= 18
* Wed May 14 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.11.0:
* Minor Changes
- A new setting added for pnpm init to create a package.json
with type=module, when init-type is module. Works as a flag
for the init command too #9463.
- Added support for Nushell to pnpm setup #6476.
- Added two new flags to the pnpm audit command, --ignore and
- -ignore-unfixable #8474.
Ignore all vulnerabilities that have no solution:
> pnpm audit --ignore-unfixable
Provide a list of CVE's to ignore those specifically, even if
they have a resolution.
> pnpm audit --ignore=CVE-2021-1234 --ignore=CVE-2021-5678
- Added support for recursively running pack in every project
of a workspace #4351.
Now you can run pnpm -r pack to pack all packages in the
workspace.
* Patch Changes
- pnpm version management should work, when
dangerouslyAllowAllBuilds is set to true #9472.
- pnpm link should work from inside a workspace #9506.
- Set the default workspaceConcurrency to
Math.min(os.availableParallelism(), 4) #9493.
- Installation should not exit with an error if
strictPeerDependencies is true but all issues are ignored by
peerDependencyRules #9505.
- Read updateConfig from pnpm-workspace.yaml #9500.
- Add support for recursive pack
- Remove url.parse usage to fix warning on Node.js 24 #9492.
- pnpm run should be able to run commands from the workspace
root, if ignoreScripts is set tot true #4858.
* Sun May 11 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.10.0:
* Allow loading the preResolution, importPackage, and fetchers
hooks from local pnpmfile.
* Fix cd command, when shellEmulator is true #7838.
* Sort keys in pnpm-workspace.yaml #9453.
* Pass the npm_package_json environment variable to the
executed scripts #9452.
* Fixed a mistake in the description of the --reporter=silent
option.
* Mon Apr 21 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.9.0:
* Minor Changes
- Added support for installing JSR packages. You can now
install JSR packages using the following syntax:
add jsr:<pkg_name>
or with a version range:
pnpm add jsr:<pkg_name>@<range>
For example, running:
pnpm add jsr:@foo/bar
will add the following entry to your package.json:
{
"dependencies": {
"@foo/bar": "jsr:^0.1.2"
}
}
When publishing, this entry will be transformed into a format
compatible with npm, older versions of Yarn, and previous
pnpm versions:
{
"dependencies": {
"@foo/bar": "npm:@jsr/foo__bar@^0.1.2"
}
}
Related issue: #8941.
Note: The @jsr scope defaults to https://npm.jsr.io/ if the
@jsr:registry setting is not defined.
- Added a new setting, dangerouslyAllowAllBuilds, for
automatically running any scripts of dependencies without the
need to approve any builds. It was already possible to allow
all builds by adding this to pnpm-workspace.yaml:
neverBuiltDependencies: []
dangerouslyAllowAllBuilds has the same effect but also allows
to be set globally via:
pnpm config set dangerouslyAllowAllBuilds true
It can also be set when running a command:
pnpm install --dangerously-allow-all-builds
* Patch Changes
- Fix a false negative in verifyDepsBeforeRun when nodeLinker
is hoisted and there is a workspace package without
dependencies and node_modules directory #9424.
- Explicitly drop verifyDepsBeforeRun support for nodeLinker:
pnp. Combining verifyDepsBeforeRun and nodeLinker: pnp will
now print a warning.
* Mon Apr 14 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- udate to 10.8.1:
* Patch Changes
- Removed bright white highlighting, which didn't look good on
some light themes #9389.
- If there is no pnpm related configuration in package.json,
onlyBuiltDependencies will be written to pnpm-workspace.yaml
file #9404.
- The patch file path saved by the pnpm patch-commit and
patch-remove commands should be a relative path #9403.
* Tue Apr 08 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.8:
* Minor Changes
Experimental. A new hook is supported for updating
configuration settings. The hook can be provided via
.pnpmfile.cjs. For example:
module.exports = {
hooks: {
updateConfig: (config) => ({
...config,
nodeLinker: "hoisted",
}),
},
};
Now you can use the pnpm add command with the --config flag
to install new configurational dependencies #9377.
* Patch Changes
- Do not hang indefinitely, when there is a glob that starts
with !/ in pnpm-workspace.yaml. This fixes a regression
introduced by #9169.
- pnpm audit --fix should update the overrides in
pnpm-workspace.yaml.
- pnpm link should update overrides in pnpm-workspace.yaml, not
in package.json #9365.
* Tue Apr 01 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.7.1:
* Patch Changes
- pnpm config set should convert the settings to their correct
type before adding them to pnpm-workspace.yaml #9355.
- pnpm config get should read auth related settings via npm CLI
[#9345].
- Replace leading ~/ in a path in .npmrc with the home directory
[#9217].
* Wed Mar 26 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.7:
* Minor Changes
- pnpm config get and list also show settings set in
pnpm-workspace.yaml files #9316.
- It should be possible to use env variables in
pnpm-workspace.yaml setting names and value.
- Add an ability to patch dependencies by version ranges. Exact
versions override version ranges, which in turn override
name-only patches. Version range * is the same as name-only,
except that patch application failure will not be ignored.
For example:
patchedDependencies:
foo: patches/foo-1.patch
foo@^2.0.0: patches/foo-2.patch
foo@2.1.0: patches/foo-3.patch
The above configuration would apply patches/foo-3.patch to
foo@2.1.0, patches/foo-2.patch to all foo versions which
satisfy ^2.0.0 except 2.1.0, and patches/foo-1.patch to the
remaining foo versions.
[!WARNING]
The version ranges should not overlap. If you want to
specialize a sub range, make sure to exclude it from the
other keys. For example:
[#] pnpm-workspace.yaml
patchedDependencies:
[#] the specialized sub range
'foo@2.2.0-2.8.0': patches/foo.2.2.0-2.8.0.patch
[#] the more general patch, excluding the sub range above
'foo@>=2.0.0 <2.2.0 || >2.8.0': 'patches/foo.gte2.patch
In most cases, however, it's sufficient to just define an
exact version to override the range.
- pnpm config set --location=project saves the setting to a
pnpm-workspace.yaml file if no .npmrc file is present in the
directory #9316.
- Rename pnpm.allowNonAppliedPatches to
pnpm.allowUnusedPatches. The old name is still supported but
it would print a deprecation warning message.
- Add pnpm.ignorePatchFailures to manage whether pnpm would
ignore patch application failures.
- If ignorePatchFailures is not set, pnpm would throw an
error when patches with exact versions or version ranges
fail to apply, and it would ignore failures from name-only
patches.
- If ignorePatchFailures is explicitly set to false, pnpm
would throw an error when any type of patch fails to apply.
- If ignorePatchFailures is explicitly set to true, pnpm
would print a warning when any type of patch fails to
apply.
* Patch Changes
- Remove dependency paths from audit output to prevent
out-of-memory errors #9280.
* Wed Mar 19 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.6.5:
* Patch Changes
- Remove warnings after having explicitly approved no builds
[#9296].
- When installing different dependency packages, should retain
the ignoredBuilds field in the .modules.yaml file #9240.
- Fix usages of the catalog: protocol in injected local
workspace packages. This previously errored with
ERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER. #8715
- Setting workspace-concurrency to less than or equal to 0
should work #9297.
* Mon Mar 17 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.6.4:
* Patch Changes
- Fix pnpm dlx with --allow-build flag #9263.
- Invalid Node.js version in use-node-version should not cause
pnpm itself to break #9276.
- The max amount of workers running for linking packages from
the store has been reduced to 4 to achieve optimal results
[#9286]. The workers are performing many file system
operations, so increasing the number of CPUs doesn't help
performance after some point.
* Thu Mar 13 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.6.3:
* Patch Changes
- pnpm install --prod=false should not crash, when executed in
a project with a pnpm-workspace.yaml file #9233. This fixes
regression introduced via #9211.
- Add the missing node-options config to recursive run #9180.
- Removed a branching code path that only executed when
dedupe-peer-dependents=false. We believe this internal
refactor will not result in behavior changes, but we expect
it to make future pnpm versions behave more consistently for
projects that override dedupe-peer-dependents to false. There
should be less unique bugs from turning off
dedupe-peer-dependents.
See details in #9259.
* Mon Mar 10 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.6.2:
* Patch Changes
- pnpm self-update should always update the version in the
packageManager field of package.json.
- Fix running pnpm CLI from pnpm CLI on Windows when the CLI is
bundled to an executable #8971.
- pnpm patch-commit will now use the same filesystem as the
store directory to compare and create patch files.
- Don't show info output when --loglevel=error is used.
- peerDependencyRules should be set in pnpm-workspace.yaml to
take effect.
* Fri Mar 07 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.6.1:
* Patch Changes
- The pnpm CLI process should not stay hanging, when --silent
reporting is used.
- When --loglevel is set to error, don't show installation
summary, execution time, and big tarball download progress.
- Don't ignore pnpm.patchedDependencies from package.json
[#9226].
- When executing the approve-builds command, if package.json
contains onlyBuiltDependencies or ignoredBuiltDependencies,
the selected dependency package will continue to be written
into package.json.
- When a package version cannot be found in the package
metadata, print the registry from which the package was
fetched.
* Thu Mar 06 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.6.0:
* Minor Changes
- pnpm-workspace.yaml can now hold all the settings that .npmrc
accepts. The settings should use camelCase #9211.
pnpm-workspace.yaml example:
verifyDepsBeforeRun: install
optimisticRepeatInstall: true
publicHoistPattern:
- "*types*"
- "!@types/react"
- Projects using a file: dependency on a local tarball file
(i.e. .tgz, .tar.gz, .tar) will see a performance improvement
during installation. Previously, using a file: dependency on
a tarball caused the lockfile resolution step to always run.
The lockfile will now be considered up-to-date if the tarball
is unchanged.
* Patch Changes
- pnpm self-update should not leave a directory with a broken
pnpm installation if the installation fails.
- fast-glob replace with tinyglobby to reduce the size of the
pnpm CLI dependencies #9169.
- pnpm deploy should not remove fields from the deployed
package's package.json file #9215.
- pnpm self-update should not read the pnpm settings from the
package.json file in the current working directory.
- Fix pnpm deploy creating a package.json without the imports
and license field #9193.
- pnpm update -i should list only packages that have newer
versions #9206.
- Fix a bug causing entries in the catalogs section of the
pnpm-lock.yaml file to be removed when
dedupe-peer-dependents=false on a filtered install. #9112
* Thu Feb 27 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.5.2:
* The pnpm config set command should change the global .npmrc
file by default.
This was a regression introduced by #9151 and shipped in pnpm
v10.5.0.
* Wed Feb 26 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 10.5.1:
* Throw an error message if a pnpm-workspaces.yaml or
pnpm-workspaces.yml file is found instead of a
pnpm-workspace.yaml #9170.
* Fix the update of pnpm-workspace.yaml by the pnpm
approve-builds command #9168.
* Normalize generated link paths in package.json #9163
* Specifying overrides in pnpm-workspace.yaml should work.
* pnpm dlx should ignore settings from the package.json file in
the current working directory #9178.
* Tue Feb 25 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.5.0:
* The pnpm.* settings from package.json can now be specified in
the pnpm-workspace.yaml file instead #9121.
* Added support for automatically syncing files of injected
workspace packages after pnpm run #9081. Use the sync-injected
- deps-after-scripts setting to specify which scripts build
the workspace package. This tells pnpm when syncing is needed.
The setting should be defined in a .npmrc file at the root of
the workspace.
* The packages field in pnpm-workspace.yaml became optional.
* pnpm link with no parameters should work as if --global is
specified #9151
* Allow scope registry CLI option without --config. prefix such
as --@scope:registry=https://scope.example.com/npm #9089
* pnpm link <path> should calculate relative path from the root
of the workspace directory #9132
* Fix a bug causing catalog snapshots to be removed from the
pnpm-lock.yaml file when using --fix-lockfile and --filter. #8639
* Fix a bug causing catalog protocol dependencies to not re-
resolve on a filtered install #8638
* Mon Feb 17 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.4.1:
* Throws an error when the value provided by the --allow-build
option overlaps with the pnpm.ignoredBuildDependencies list #9105.
* Print pnpm's version after the execution time at the end of the console output.
* Print warning about ignored builds of dependencies on repeat install #9106.
* Setting init-package-manager should work.
- includes 10.4.0:
* pnpm approve-builds --global works now for allowing
dependencies of globally installed packages to run
postinstall scripts.
* The pnpm add command now supports a new flag, --allow-build,
which allows building the specified dependencies.
* pnpm approve-builds should work after two consecutive pnpm install runs #9083.
* Fix instruction for updating pnpm with corepack #9101.
* The pnpm version specified by packageManager cannot start with v.
* Tue Feb 11 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.3.0:
* Added a new setting called strict-dep-builds. When enabled,
the installation will exit with a non-zero exit code if any
dependencies have unreviewed build scripts (aka postinstall scripts) #9071.
* Fix a false negative of verify-deps-before-run after pnpm
install --production|--no-optional #9019.
* Print the warning about blocked installation scripts at the
end of the installation output and make it more prominent.
* Sat Feb 08 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.2.1:
* Don't read a package from side-effects cache if it isn't
allowed to be built #9042.
* pnpm approve-builds should work, when executed from a
subdirectory of a workspace #9042.
* pnpm deploy --legacy should work without injected dependencies
* Add information about how to deploy without "injected
dependencies" to the "pnpm deploy" error message.
- includes 10.2.0:
* Packages executed via pnpm dlx and pnpm create are allowed to
be built (run postinstall scripts) by default.
* Quote args for scripts with shell-quote to support new lines
(on POSIX only) #8980.
* Fix a bug in which pnpm deploy fails to read the correct
projectId when the deploy source is the same as the workspace directory #9001.
* Proxy settings should be respected, when resolving Git-hosted
dependencies #6530.
* Prevent overrides from adding invalid version ranges to
peerDependencies by keeping the peerDependencies and
overriding them with prod dependencies #8978.
* Sort the package names in the "pnpm.onlyBuiltDependencies"
list saved by pnpm approve-builds.
* Fri Jan 31 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.1.0:
* Added a new command for printing the list of dependencies
with ignored build scripts: pnpm ignored-builds #8963.
* Added a new command for approving dependencies for running
scripts during installation: pnpm approve-builds #8963.
* Added a new setting called optimistic-repeat-install. When
enabled, a fast check will be performed before proceeding to
installation. This way a repeat install or an install on a
project with everything up-to-date becomes a lot faster. But
some edge cases might arise, so we keep it disabled by
default for now #8977.
* Added a new field "pnpm.ignoredBuiltDependencies" for
explicitly listing packages that should not be built. When a
package is in the list, pnpm will not print an info message
about that package not being built #8935.
* Verify that the package name is valid when executing the
publish command.
* When running pnpm install, the preprepare and postprepare
scripts of the project should be executed #8989.
* Allow workspace: and catalog: to be part of wider version
range in peerDependencies.
* pnpm deploy should inherit the pnpm object from the root
package.json #8991.
* Make sure that the deletion of a node_modules in a sub-
project of a monorepo is detected as out-of-date #8959.
* Fix infinite loop caused by lifecycle scripts using pnpm to
execute other scripts during pnpm install with
verify-deps-before-run=install #8954.
* Replace strip-ansi with the built-in util.
stripVTControlCharacters #9009.
* Do not print patched dependencies as ignored dependencies
that require a build #8952.
* Fri Jan 10 2025 Avindra Goolcharan <avindra@opensuse.org>
- update to 10.0.0:
* Lifecycle scripts of dependencies are not executed during
installation by default! This is a breaking change aimed at
increasing security. In order to allow lifecycle scripts of
specific dependencies, they should be listed in the pnpm
onlyBuiltDependencies field of package.json #8897
* The pnpm link command now adds overrides to the root package.json. #8653
* Secure hashing with SHA256
* Configuration updates
* Changes to the global store
* The # character is now escaped in directory names within
node_modules/.pnpm. #8557
* Running pnpm add --global pnpm or pnpm add --global @pnpm/exe
now fails with an error message, directing you to use pnpm
self-update instead. #8728
* Dependencies added via a URL now record the final resolved
URL in the lockfile, ensuring that any redirects are fully
captured. #8833
* The pnpm deploy command now only works in workspaces that
have inject-workspace-packages=true. This limitation is
introduced to allow us to create a proper lockfile for the
deployed project using the workspace lockfile.
* Removed conversion from lockfile v6 to v9. If you need v6-to-
v9 conversion, use pnpm CLI v9.
* pnpm test now passes all parameters after the test keyword
directly to the underlying script. This matches the behavior
of pnpm run test. Previously you needed to use the -- prefix. #8619
* node-gyp updated to version 11.
* pnpm deploy now tries creating a dedicated lockfile from a
shared lockfile for deployment. It will fallback to
deployment without a lockfile if there is no shared lockfile
or force-legacy-deploy is set to true.
* Added support for a new type of dependencies called
"configurational dependencies". These dependencies are
installed before all the other types of dependencies (befor
"dependencies", "devDependencies", "optionalDependencies").
* New verify-deps-before-run setting. This setting controls how
pnpm checks node_modules before running scripts #8836
* On repeated installs, pnpm performs a quick check to ensure
node_modules is up to date. #8838
* pnpm add integrates with default workspace catalog: #8640
* pnpm dlx now resolves packages to their exact versions and
uses these exact versions for cache keys. This ensures pnpm
dlx always installs the latest requested packages #8811
* No node_modules validation on certain commands. Commands that
should not modify node_modules (e.g., pnpm install --lockfile-
only) no longer validate or purge node_modules. #8657
* for full changes, see https://github.com/pnpm/pnpm/releases/tag/v10.0.0
* Mon Jan 06 2025 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 9.15.3:
* Fixed the Regex used to find the package manifest during
packing #8938.
* pnpm update --filter <pattern> --latest <pkg> should only
change the specified package for the specified workspace, when
dedupe-peer-dependents is set to true #8877.
* Exclude .DS_Store file at patch-commit #8922.
* Fix a bug in which pnpm patch is unable to bring back old patch
without specifying @version suffix #8919.
* Mon Dec 30 2024 Johannes Kastl <opensuse_buildservice@ojkastl.de>
- update to 9.15.2:
* Fixed publish/pack error with workspace dependencies with
relative paths #8904. It was broken in v9.4.0 (398472c).
* Use double quotes in the command suggestion by pnpm patch on
Windows #7546.
* Do not fall back to SSH, when resolving a git-hosted package if
git ls-remote works via HTTPS #8906.
* Improve how packages with blocked lifecycle scripts are
reported during installation. Always print the list of ignored
scripts at the end of the output. Include a hint about how to
allow the execution of those packages.
* Sun Dec 22 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.15.1:
* pnpm remove should not link dependencies from the workspace,
when link-workspace-packages is set to false #7674
* Installation with hoisted node_modules should not fail, when
a dependency has itself in its own peer dependencies #8854
* Sat Dec 07 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.15.0:
* Metadata directory version bumped to force fresh cache after
we shipped a fix to the metadata write function. This change
is backward compatible as install doesn't require a metadata cache
* pnpm update --global should not crash if there are no any
global packages installed #7898
* Fix an exception when running pnpm update --interactive if
catalogs are used.
* Sat Nov 30 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.14.4:
* Don't ever save mutated metadata to the metadata cache
- includes 9.14.3:
* Some commands should ignore the packageManager field check of
package.json #7959
* Wed Nov 20 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.14.2:
pnpm publish --json should work #8788
- includes 9.14.1:
* Added support for pnpm pack --json to print packed tarball
and contents in JSON format #8765
* pnpm exec should print a meaningful error message when no
command is provided #8752
* pnpm setup should remove the CLI from the target location
before moving the new binary #8173
* Fix ERR_PNPM_TARBALL_EXTRACT error while installing a
dependency from GitHub having a slash in branch name #7697
* Don't crash if the use-node-version setting is used and the
system has no Node.js installed #8769
* Convert settings in local .npmrc files to their correct types.
For instance, child-concurrency should be a number, not a string #5075
* pnpm should fail if a project requires a different package
manager even if manage-package-manager-versions is set to true
* pnpm init should respect the --dir option #8768
- includes 9.14.0:
* chore: use verify-deps-before-run
* fix(init): --dir option should be respected (#8768)
* feat: support json format output in pnpm pack (#8765)
* fix: pnpm exec should specify command (#8774)
* fix: proper types of settings in local .npmrc files (#8775)
* fix: ERR_PNPM_TARBALL_EXTRACT when the URL's hash contains a slash
* fix: the CLI should fail if a different package manager is
required by the project
* fix: ETXTBSY error on running setup (#8780)
* feat: add linux-riscv64 build (#8779)
* fix: remove link to X from update notifier (#8773)
* docs: update sponsors
* fix: upgrade cross-sapwn (#8782)
* fix: don't crash when use-node-version is set and there is no node.js
* docs: update changesets
* Sat Nov 16 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.13.2:
* Detection of circular peer dependencies should not crash with
aliased dependencies #8759. Fixes a regression introduced in
the previous version.
* Fix race condition of symlink creations caused by multiple
parallel dlx processes.
* Thu Nov 14 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.13.1:
* Fixed some edge cases where resolving circular peer
dependencies caused a dead lock #8720
* Wed Nov 13 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.13.0:
* The self-update now accepts a version specifier to install a
specific version of pnpm.
* Fix Cannot read properties of undefined (reading 'name') that
is printed while trying to render the missing peer
dependencies warning message #8538
* Fri Nov 08 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.12.3:
* Don't purge node_modules, when typing "n" in the prompt that
asks whether to remove node_modules before installation #8655
* Fix a bug causing pnpm to infinitely spawn itself when manage-
package-manager-versions=true is set and the .tools directory is corrupt
* Use crypto.hash, when available, for improved performance #8629
* Fixed a race condition in temporary file creation in the
store by including worker thread ID in filename. Previously,
multiple worker threads could attempt to use the same
temporary file. Temporary files now include both process ID
and thread ID for uniqueness #8703
* All commands should read settings from the package.json at
the root of the workspace #8667
* When manage-package-manager-versions is set to true, errors
spawning a self-managed version of pnpm will now be shown
(instead of being silent)
* Pass the find command to npm, it is an alias for npm search
- includes 9.12.2:
* When checking whether a file in the store has executable
permissions, the new approach checks if at least one of the
executable bits (owner, group, and others) is set to 1.
Previously, a file was incorrectly considered executable only
when all the executable bits were set to 1. This fix ensures
that files with any executable permission, regardless of the
user class, are now correctly identified as executable #8546
* Tue Oct 08 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.12.1:
* pnpm update --latest should not update the automatically
installed peer dependencies #6657
* pnpm publish should be able to publish from a local tarball #7950
* Prevent EBUSY errors caused by creating symlinks in parallel
dlx processes #8604
* Fix maximum call stack size exceeded error related to
circular workspace dependencies #8599
* Fri Oct 04 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.12.0:
* Fix peer dependency resolution dead lock #8570. This change
might change some of the keys in the snapshots field inside
pnpm-lock.yaml but it should happen very rarely.
* pnpm outdated command supports now a --sort-by=name option
for sorting outdated dependencies by package name #8523
* Added the ability for overrides to remove dependencies by
specifying "-" as the field value #8572
* Fixed an issue where pnpm list --json pkg showed "private":
false for a private package #8519
* Packages with libc that differ from pnpm.
supportedArchitectures.libc are not downloaded #7362
* Prevent ENOENT errors caused by running store prune in parallel #8586
* Add issues alias to pnpm bugs #8596
* Sat Sep 21 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.11.0:
* Experimental: added pnpm cache commands for inspecting the
metadata cache #8512
* Fix a regression in which pnpm deploy with node-linker=hoisted
produces an empty node_modules directory #6682
* pnpm deploy should work in workspace with shared-workspace-lockfile=false #8475
* Don't print a warning when linking packages globally #4761
* Wed Sep 11 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.10.0:
* Support for a new CLI flag, --exclude-peers, added to the
list and why commands. When --exclude-peers is used, peer
dependencies are not printed in the results, but dependencies
of peer dependencies are still scanned #8506.
* Added a new setting to package.json at pnpm.auditConfig.
ignoreGhsas for ignoring vulnerabilities by their GHSA code #6838.
* Throw an exception if pnpm switches to the same version of itself.
* Reduce memory usage during peer dependencies resolution.
* Mon Sep 02 2024 Virinas-code <Virinas-code@opensuse.org>
- update to version 9.9.0:
* https://github.com/pnpm/pnpm/compare/v9.8.0...v9.9.0
* Minor breaking change. We had to optimize how we resolve peer
dependencies in order to fix some infinite loops and
out-of-memory errors during peer dependencies resolution.
* pnpm deploy should write the node_modules/.modules.yaml to the
node_modules directory within the deploy directory #7731
* Don't override a symlink in node_modules if it already points
to the right location pnpm/symlink-dir#54
- changes from 9.8.0:
* https://github.com/pnpm/pnpm/compare/v9.7.1...v9.8.0
* Added a new command for upgrading pnpm itself when it isn't
managed by Corepack: pnpm self-update. This command will work,
when pnpm was installed via the standalone script from the pnpm
installation page #8424
* CLI tools installed in the root of the workspace should be
added to the PATH, when running scripts and use-node-version is
set
* pnpm setup should never switch to another version of pnpm
* Ignore non-string value in the os, cpu, libc fields, which
checking optional dependencies #8431
* Remember the state of edit dir, allow running pnpm patch-commit
the second time without having to re-run pnpm patch
- changes from 9.7.1:
* https://github.com/pnpm/pnpm/compare/v9.7.0...v9.7.1
* Fixed passing public-hoist-pattern and hoist-pattern via env
variables #8339
* pnpm setup no longer creates Batch/Powershell scripts on Linux
and macOS #8418
* When dlx uses cache, use the real directory path not the
symlink to the cache #8421
* pnpm exec now supports executionEnv #8356
* Remove warnings for non-root pnpm field, add warnings for
non-root pnpm subfields that aren't executionEnv #8143
* Replace semver in "peerDependency" with workspace protocol
[#8355]
* Fix a bug in patch-commit in which relative path is rejected
[#8405]
* Update Node.js in @pnpm/exe to v20
- changes from 9.7.0:
* https://github.com/pnpm/pnpm/compare/v9.6.0...v9.7.0
* Added pnpm version management. If the
manage-package-manager-versions setting is set to true, pnpm
will switch to the version specified in the packageManager
field of package.json #8363
* Added the ability to apply patch to all versions #8337
* Change the default edit dir location when running pnpm patch
from a temporary directory to
node_modules/.pnpm_patches/pkg[@version] to allow the code
editor to open the edit dir in the same file tree as the main
project #8379.
* Substitute environment variables in config keys #6679
* pnpm install should run node-gyp rebuild if the project has a
binding.gyp file even if the project doesn't have an install
script #8293
* Print warnings to stderr #8342
* Peer dependencies of optional peer dependencies should be
automatically installed #8323
* Thu Jul 25 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.6.0:
* Support specifying node version (via pnpm.executionEnv.nodeVersion
in package.json) for running lifecycle scripts per each package
in a workspace #6720
* Overrides now support the catalogs: protocol #8303
* The pnpm deploy command now supports the catalog: protocol #8298
* The pnpm outdated command now supports the catalog: protocol #8304
* Correct the error message when trying to run pnpm patch
without node_modules/.modules.yaml #8257
* Silent reporting fixed with the pnpm exec command #7608
* Add registries information to the calculation of dlx cache hash #8299
* Tue Jul 09 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.5.0:
* Added support for catalogs #8122
* Read authentication information from .npmrc in the current
directory when running dlx #7996
* Updated @pnpm/tabtab to v0.5.4, enabling zsh autocomplete
lazy loading #8236
* Installation with filtering will now work, when dedupe-peer-
dependents is set to true #6300
* Fixed dlx not actually using the Node.js version specified by
- -use-node-version.
* Sat Jul 06 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.4.0:
* Some registries allow the exact same content to be published
under different package names and/or versions. This breaks
the validity checks of packages in the store. To avoid errors
when verifying the names and versions of such packages in the
store, you may now set the strict-store-pkg-content-check
setting to false #4724
* Fix package-manager-strict-version missing in config #8195
* If install is performed on a subset of workspace projects,
always create an up-to-date lockfile first. So, a partial
install can be performed only on a fully resolved (non-partial)
lockfile #8165
* Handle workspace protocol with any semver range specifier,
when used in peer dependencies #7578
* Fri Jun 14 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.3.0:
* Semi-breaking. Dependency key names in the lockfile are
shortened if they are longer than 1000 characters. We don't
expect this change to affect many users. Affected users most
probably can't run install successfully at the moment. This
change is required to fix some edge cases in which installation
fails with an out-of-memory error or "Invalid string length
(RangeError: Invalid string length)" error. The max allowed
length of the dependency key can be controlled with the peers-
suffix-max-length setting #8177.
* Set reporter-hide-prefix to true by default for pnpm exec. In
order to show prefix, the user now has to explicitly set reporter
- hide-prefix=false #8174.
- changes from 9.2.0:
* If package-manager-strict-version is set to true, pnpm will
fail if its version doesn't exactly match the version in the
"packageManager" field of package.json.
* Update @yarnpkg/pnp to the latest version, fixing issue with
node: imports #8161.
* Deduplicate bin names to prevent race condition and corrupted
bin scripts #7833.
* pnpm doesn't fail if its version doesn't match the one
specified in the "packageManager" field of package.json #8087.
* exec now also streams prefixed output when --recursive or
- -parallel is specified just as run does #8065.
- changes from 9.1.4:
* Improved the performance of the resolution stage by changing
how missing peer dependencies are detected #8144.
- changes from 9.1.3:
* Fix a bug in which a dependency that is both optional for one
package but non-optional for another is omitted when optional=false #8066.
* Clear resolution cache before starting peer dependencies resolution #8109.
* Reduce memory usage by peer dependencies resolution #8072.
* Tue May 21 2024 draskmont@protonmail.com
- update to version 9.1.2
- require nodejs >= 18
* Sat Apr 27 2024 Avindra Goolcharan <avindra@opensuse.org>
- update to version 9.0.6:
* Lockfiles that have git-hosted dependencies specified should
be correctly converted to the new lockfile format #7990.
* Don't upgrade the lockfile format on pnpm install
- -frozen-lockfile #7991.
- includes version 9.0.5:
* Lockfiles with local or git-hosted dependencies are now
successfully converted to the new lockfile format #7955.
* Resolve peer dependencies correctly, when they have
prerelease versions #7977.
* Fix aliased dependencies resolution on repeat install with
existing lockfile, when the aliased dependency doesn't
specify a version or range #7957.
* The lockfile should be saved in the new format even if it is
up-to-date.
- run spec-cleaner
* Sat Apr 20 2024 draskmont@protonmail.com
- update to version 9.0.4
- Added shell completion subpackages for bash, zsh and fish
- Removed python2 dependency
- Prevent packaging of executable files for other OS.
- Cleaned up specfile:
* Removed some unused tags, macros and redudant requirements
* Added licensing header
* Fixed rpmlint errors and warnings
- Removed unused _service file
- Restored changelog
* Thu Jan 25 2024 Marcel Kuehlhorn <tux93@opensuse.org>
- update to version 8.14.3
* https://github.com/pnpm/pnpm/compare/v8.4.0...v8.14.3
/usr/share/fish /usr/share/fish/vendor_completions.d /usr/share/fish/vendor_completions.d/pnpm.fish
Generated by rpm2html 1.8.1
Fabrice Bellet, Thu Nov 12 22:26:22 2026