Update dependency pnpm to v12 #6
Loading…
Reference in a new issue
No description provided.
Delete branch "renovate/pnpm-12.x"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR contains the following updates:
11→12Release Notes
pnpm/pnpm (pnpm)
v12.15.0: pnpm 12.15Compare Source
This release lets
pnpm-workspace.yamldelegate settings to other files and config dependencies withinclude, addslockfile.integrityTarget: tarand tar-registry support, accepts zstd-compressed registry responses, and honorsSOURCE_DATE_EPOCHinpnpm deployandpnpm install. It also rejects registry metadata that names a different package than the one requested and speeds up workspace installs and macOS file linking.Minor Changes
pnpm-workspace.yamlcan now delegate settings to other YAML files and to config dependencies through a newincludefield. Each source may define only the settings the root manifest delegates to it. Every setting has one owner. Commands that change settings write each one to the file that owns it. A relative path in an included file is relative to that file. See Splitting Settings Across Files.A config dependency supplies its settings through the YAML file named in the
pnpm.configfield of itspackage.json. The newbootConfigsetting sets the release-age and trust policy pnpm uses to install config dependencies and the pnpm version frompackageManager.Added the
lockfile.integrityTargetsetting. WithintegrityTarget: tar,pnpm-lock.yamlpins the hash of every package's uncompressed tar, so the lockfile no longer changes when a registry recompresses its tarballs or serves them as tars. pnpm learns a version's tar hash from the store or by downloading it once.pnpm audit signaturesties the tar hash to the signed.tgzintegrity.pnpm can now install from a registry that serves uncompressed tars with zstd or gzip content coding.
pnpm-lock.yamlrecords the hash of each tar. Declare such a registry withserverType: pnprinregistriesto keep tarball URLs out of the lockfile. See Tar registries.pnpm now decodes the content coding of tarball downloads itself. It rejects a zstd response whose window is larger than 8 MiB.
Added the
maxTarballUnpackedSizesetting. It caps the bytes the files of one package may unpack to, 4 GiB by default.Registry revisions now always pin the hash of the uncompressed tar, the original included. pnpm records an original selected through the revision protocol, for example with a
+r0specifier, asrevision: 0in the lockfile and fetches it from the registry's digest route. pnpm checks a revision's tar whether the registry sends it as a tar, gzip, or zstd. Revision metadata must list revision 0 and mark the selected revision. pnpm rejects incomplete revision metadata withERR_PNPM_MALFORMED_METADATA.pnpm now accepts zstd-compressed responses. Registries that serve zstd send package metadata in fewer bytes than with gzip, most of all the full metadata that settings such as
minimumReleaseAgeandtrustPolicyread.pnpm deployandpnpm installnow use theSOURCE_DATE_EPOCHenvironment variable as an upper bound for timestamps innode_modules/.modules.yamlandnode_modules/.pnpm-workspace-state-v1.json. With an equivalently prepared deploy target and an epoch no later than the timestamps that would otherwise be recorded, deploying the same project twice produces identical files #17171.pnpm update --interactivenow highlights the row under the cursor in cyan while keeping the colors of its version changes.Patch Changes
Security
pnpm now fails with
ERR_PNPM_REGISTRY_NAME_MISMATCHif a registry returns metadata for a package other than the one requested. Before, pnpm installed the package under the name in the metadata, soallowBuildsrules for the requested package did not apply to it.minimumReleaseAgeExcludePruneandtrustPolicyExcludePrunenow keep exclusions for resolved configuration dependencies and their transitive dependencies #17142.Resolving dependencies
When
pnpm installresolves 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, useoverrides#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.
A package that lists the same dependency in
dependenciesand 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 --prodandpnpm deploy --prodno longer drop 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 installwith an existing lockfile and a secondpnpm dedupenow keep optional peer bindings that point into a dependency cycle or back into the dependent package. Before, the second run dropped the optional peer binding from the lockfile snapshot key.pnpm installandpnpm dedupenow preserve own-dependency and peer-dependency bindings when reusing a lockfile whose peer suffixes were hashed bypeersSuffixMaxLength.Upgrading a direct dependency in one workspace project now refreshes matching transitive pins in sibling projects during the same
pnpm installpass.With
resolvePeersFromWorkspaceRoot: false, a workspace project no longer binds a peer to a version that only another project's dependencies provide. pnpm now auto-installs a version that satisfies the peer range for that project. Before, the project got a version outside the range, with no peer warning.pnpm install,pnpm add, andpnpm updatenow reuse the lockfile's resolution for dependencies that take part in a dependency cycle, such asmllyandpkg-types. They re-resolved those packages and everything that depends on them from the registry before pnpm/tasks#130.pnpm now measures a
pnpm.overridesentry written as aworkspace:path, such asworkspace:./packages/foo, from the directory holdingpnpm-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 installnow removes config dependencies that are no longer declared, including when the entireconfigDependenciessetting is removed. Frozen installs reject a lockfile that still records removed config dependencies.pnpm install --update-checksumsnow re-resolves every locked package and records the integrity the registry reports now.pnpm addandpnpm installnow acceptgist:<id>dependencies and GitHub Gist URLs, and install them from the gist's archive.pnpm pkg fixnormalizes agist:repository the waynpm pkg fixdoes,pnpm licenseslinks a gist package to its gist page, andpnpm sbomreports the repository of a package whoserepositoryfield is agist:shorthand.pnpm addnow reads the package name correctly from a tarball URL under a registry URL that has a path, such ashttps://example.com/npm/, and installs a registry tarball URL with a malformed percent escape as a plain tarball URL. Before, the registry path became part of the name, so a scoped package failed withERR_PNPM_INVALID_PACKAGE_NAME.With
minimumReleaseAgeset, pnpm now revalidates a package's cached full metadata with a single ETag request so an unchanged registry answers with304, even when the registry does not list a publish time for every version. Previously, pnpm downloaded that metadata again whenever the cached copy was older thanminimumReleaseAge. WithresolutionModeset tolowest-directortime-based, pnpm also reuses that metadata across every workspace project that depends on the package.When pnpm revalidates cached registry metadata, it now sends
If-Modified-Sincewhen the registry's modification time is a date without a time, and no longer crashes when the modification time is before 1970.Added protocol versioning between the pnpm client and pnpr server, verified applied resolution policies in resolution responses, and preserved structured error codes across the wire.
Performance
Sped up
pnpm installon macOS. On a 12-core Mac, installing 1352 packages from a warm store took 1.3 seconds, down from 2.2. pnpm now writes package files intonode_moduleswith 4 threads on macOS, whatever the number of CPU cores. Set thePNPM_MACOS_LINK_THREADSenvironment variable to use a different number of threads, or to0to write files with the threads pnpm uses for the rest of the install.pnpm installis faster and uses less memory in large workspaces withsharedWorkspaceLockfile: false. Each project's install no longer reads the manifests of every other workspace project again. Installs that wait for their workspace dependencies now count towardworkspaceConcurrency#16966.Sped up workspace project discovery, dependency resolution, lockfile up-to-date checks, and peer dependency checks in large workspaces #16839 pnpm/tasks#9. On a workspace with 6,833 projects, the lockfile up-to-date check uses about 1 second less CPU time. The peer dependency check at the end of an install now checks linked workspace projects against the manifests already in memory, without reading each
package.jsonagain.Sped up bin linking and build planning during
pnpm install. Package binary targets and shebang runtimes are now precalculated during tarball extraction and served inpnprregistry metadata alongside verifiedrequiresBuild, tarball size, and file-count metadata.Lowered peak memory when pnpm downloads many packuments, such as during lockfile verification with
trustPolicyon Linux. In one 1,800-entry workspace, a lockfile update now peaks at 1.8 to 2.0 GB. It peaked at 2.6 to 3.7 GB before #17020.pnpm installin a Cargo workspace skips crates that an earlier install already unpacked into the store. Before, a repeat install processed every crate inCargo.lockagain.pnpm install --forceunpacks every crate again pnpm/tasks#69.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
ENOTEMPTYwhen its copy of the package collided with the copy another install had already placed pnpm/tasks#48.pnpm installnow succeeds whennode_modulesis a symlink to an existing directory. Dependency hoisting previously rejected a symlinkednode_modulesas an invalid destination #17157.A filtered first install under the isolated linker now links every workspace project by name into
node_modules/.pnpm/node_modules, not only the selected ones pnpm/tasks#155.With
nodeLinker: hoisted, a filtered install such aspnpm --filter app add <pkg>keeps the rootnode_moduleslinks and bins of the workspace projects it did not select. A project with no dependencies of its own lost them until the next full install.pnpm install --filterwithnodeLinker: hoistedand--prodor--devnow preserves previously installed dependency groups for unselected workspace projects pnpm/tasks#164. Repeat filtered installs also re-resolve when a transitive injected workspace dependency or a linked dependency's version changes.With
sharedWorkspaceLockfile: false,pnpm installnow runs the workspace root's pnpmfile for every project, after the project's own pnpmfile. Before, it ran only the pnpmfile in the project's directory.pnpm installandpnpm peers checknow detect peer dependency issues and report linked package versions in linked projects that usepackage.json5orpackage.yamlpnpm/tasks#172.pnpm installandpnpm peers checkno longer report an unmet peer when a workspace package's peer is provided by a remote tarball URL. pnpm now compares the peer range with the version in the tarball's manifest #17037.pnpm prunenow removes extraneous direct packages fromnode_moduleswhenpackage.jsonandpnpm-lock.yamlare already in sync.pnpm prunealso honors--filter,--recursive,sharedWorkspaceLockfile: false, and thetrustLockfilesetting and--trust-lockfileflag. With the lockfile trusted,pnpm prune --prodworks offline when every package is in the store #16999.Injected workspace packages
pnpm install --frozen-lockfilenow fails withERR_PNPM_OUTDATED_LOCKFILEwhen a local directory dependency or injected workspace package changes a dependency to a different tarball URL or git commit #17129. It also fails when an injected workspace package changes a dependency's named-registry range, such aswork:^2.0.0, to one the locked version no longer satisfies.pnpm install --frozen-lockfilenow accepts the lockfile it wrote for afile:dependency on a directory without apackage.json. It also accepts the lockfile for an injected workspace package that bundles a dependency inbundledDependencies, lists a dependency in bothdependenciesandoptionalDependencies, or publishes from apublishConfig.directorywhosepackage.jsonlists other dependencies than the project's own. Before, these failed withERR_PNPM_OUTDATED_LOCKFILE.The lockfile now records a
catalog:peer dependency of an injected workspace package as written in itspackage.json. It recorded the catalog's range before, so editing the catalog changed the consumer's lockfile entry pnpm/tasks#153.pnpm installnow resolves a git, tarball, or tag dependency of an injected workspace package again when its spec changes. Before, it kept the old version.Repeat installs and
verifyDepsBeforeRunnow re-check projects that inject a modified workspace package, including whensharedWorkspaceLockfileisfalse#17007, and accept aliasedworkspace:ranges on linked dependencies.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
syncInjectedDepsAfterScriptsrebuilds it. Before, these refreshes copied every file of the package directory into the injected copy, including files thatfilesor.npmignoreleave out. WithdeployAllFiles: truethe copy still gets every file.A script listed in
syncInjectedDepsAfterScriptsnow keeps a package's links to its own files as links in the injected copies, the waypnpm installwrites them, and updates those copies whensharedWorkspaceLockfileisfalse. Before, a directory link the script added became a real directory or went missing withdeployAllFiles, and withsharedWorkspaceLockfile: falsethe copies kept the files from the last install.Running scripts and checking dependencies
Pressing Ctrl+C during
pnpm -r runorpnpm --filter <pkg> runno longer reportsERR_PNPM_RECURSIVE_RUN_FIRST_FAIL. pnpm now ends with the interrupted script's status, such as 130 for a script that SIGINT ended on Linux or macOS #16964.With
enableGlobalVirtualStore, a Node.js resolve hook that a script registers now sees the importer ascontext.parentURLafter pnpm'sNODE_PATHfallback fails to find a bare specifier. Hooks that resolve tsconfigpathsaliases from the importer failed withERR_MODULE_NOT_FOUND#16979.pnpm run,pnpm exec, andpnpm restartnow pass the install flags they were given, such as--lockfile-dir,--modules-dir,--store-dir,--offline, and--ignore-scripts, to thepnpm installthatverifyDepsBeforeRun: installstarts. When that install leaves the dependencies out of date, pnpm now prints a warning.A repeated
pnpm install --filternow finishes without reinstalling when the selected projects are up to date, and so doespnpm installonce filtered commands have installed every project. After a filtered install,pnpm runandpnpm execnow install a selected project whose dependencies changed in between #17011, require the workspace root when a filtered install withpruneDirectDependencies: trueskipped it pnpm/tasks#181, and preserve the recorded ignored build scripts of unselected projects.The check that runs before a script now holds the workspace projects that the selected project depends on, including with
sharedWorkspaceLockfile: falseand for workspace dependencies declared withlink:.--filter=<project>...now selectslink:dependencies as well. In a workspace that has nopackage.jsonat its root,pnpm runandpnpm execno longer reinstall before every command when the dependencies are up to date.verifyDepsBeforeRunand repeatpnpm installruns now applyoverridesbefore comparing a modifiedpackage.jsonwithpnpm-lock.yaml#17127.pnpm installwithoptimisticRepeatInstallnow re-runs whenconfigDependencieschanged,verifyDepsBeforeRunreportsnode_modulesout of date whenuseGitBranchLockfileis enabled and the branch lockfile is missing, and in a single-project repository both checks record a successful lockfile check so later runs skip it until the lockfile changes #16993.pnpm installandverifyDepsBeforeRunnow compare manifests, lockfiles, and patch files by content hash. They notice an edit that kept the file's modification time, ascp -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, packing, and other commands
pnpm deploynow deploysfile:dependencies that live outside the workspace directory. It failed with "Refusing to deploy unsafe lockfile path" #16983.pnpm deploynow records the peer dependencies of the workspace packages it copies into the deployed lockfile. Before, pnpm rejected that lockfile with--frozen-lockfilewhen a deployed workspace package declared a peer andinjectWorkspacePackageswas off #17093.pnpm install --frozen-lockfileandverifyDepsBeforeRunnow succeed inside a directory created bypnpm deploy.pnpm deploywrites self-contained workspace and lockfile settings, prunes unusedpatchedDependencies, and preserves workspace-level dependency resolutions on deployed directory dependencies.On macOS,
pnpm deployno longer writes a second, unused copy of every package tonode_modules/.pnpm/@in the deploy directory #17116.pnpm patch-commitandpnpm installnow apply a patch that renames or moves a file of the patched package. Such a patch failed with "rename/copy operations in patches are not yet supported".In workspaces with
sharedWorkspaceLockfile: false,pnpm patch,pnpm patch-commit, andpnpm patch-removenow resolve patch files andpatchedDependenciesentries at the workspace root.pnpm installchecks configured patches across all workspace projects before reporting unused patches.pnpm packandpnpm publishno longer include editor swap files such as.*.swp, macOS._*files,.lock-wscript,.wafpickle-*,build/config.gypi, orarchived-packages/in the tarball. Naming one of these files infilesstill includes it #16955.pnpm packandpnpm publishnow support extended glob patterns and brace ranges such aspart{1..3}.jsandpart{a..c}.js, including descending ranges, steps, and zero padding, infiles,.npmignore, and.gitignore. Explicitly listed internal directory symlinks are preserved below excluded directories, entry points honor nested ignore files, ignore negations can re-include default-excluded logs and backup files, and nested lockfiles follow the package's file selection rules.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 deprecateandpnpm unpublishnow work for packages whose registry metadata is larger than 10 MiB, such astypescript. They accept up to 50 MiB, the same limit aspnpm owner.pnpm owner lsnow lists the owners of a package on the npm registry. It reported every package as not found.pnpm licenses list --prodnow includes optional dependencies, including transitive ones. Pass--no-optionalto leave them out.pnpm licenses listalso reports no licenses for workspace projects not recorded in the lockfile, errors whenpnpm-lock.yamlis missing, and formats empty results as the requested text or JSON pnpm/tasks#174.pnpm whynow reads each selected workspace project's lockfile and modules directory whensharedWorkspaceLockfileisfalsepnpm/tasks#169.pnpm pkg fixnow converts arepositorystring such asgithub:pnpm/exampleto the object form, asnpm pkg fixdoes. It also normalizes a GitHub, GitLab, or Bitbucket repository URL the waynpm pkg fixdoes #16980.Configuration and setting up pnpm
Registry credentials and TLS settings passed with
--config, such as--config.//registry.example.com/:_authToken=<token>, are now applied. They take precedence over the same settings in.npmrcfiles.pnpm now reads only the settings it needs to decide whether to switch to the pnpm version a project pins. A setting outside them can no longer fail that decision or change its outcome. For example,
pnpm --versionno longer fails in a pinned project whosepnpm-workspace.yamlhas a setting this pnpm cannot read. Projects that pin neither pnpm nor a runtime skip this check without reading any settings.When pnpm switches to the version a project pins, an option only the pinned pnpm knows now reaches that pnpm when it comes immediately before the command name, as in
pnpm --new-flag install. When switching forpnpm --versionor an unrecognized command line, pnpm also honors--color,--no-color,--reporter, and--loglevelwherever they appear on the command line.pnpm self-updatenow updates the project's pnpm pin when Homebrew installed pnpm or Corepack runs it, leaving the global pnpm alone #17001. Run inside a workspace package,pnpm self-updateupdates the pin in the workspace root's manifest, including inpackage.yamlorpackage.json5.pnpm setupnow completes when Windows registry environment values cannot be read or written. It prints PowerShell commands to configurePNPM_HOMEandPATHwithout registry access #17167.pnpm setupnow succeeds when it cannot tell which shell configuration file to update, for example in a container whereSHELLis not set. It installs pnpm and prints thePNPM_HOMEandPATHlines to add yourself. Previously it failed withERR_PNPM_UNKNOWN_SHELL, and so did the installer script at get.pnpm.io #16973.In StackBlitz WebContainers,
pnpmno longer crashes intermittently withParking not supported on this platform, and running a command that does not exist now reports that the command was not found. Previously it sometimes failed with a generic error.Platinum Sponsors
Gold Sponsors
v12.14.0: pnpm 12.14.0Compare Source
This release adds the
sideEffectsCache.excludesetting, stopspnpm installfrom waiting on the daily update check, and keeps pnpm from aborting with a core dump when the reader of its output exits.Minor Changes
Added the
sideEffectsCache.excludesetting for packages whose build depends on something pnpm cannot include in the cache key, such as the system libraries a native addon links against. pnpm builds the listed packages in every project and never restores or saves their builds through the side-effects cache, locally or on aremoteCacheserver. Under the global virtual store, each project gets its own copy of these packages and of the packages that depend on them. Entries use the samename,@scope/*, andname@versionpatterns asminimumReleaseAgeExclude#5271.pnpm loginnow prints thenpm-noticeresponse header during web-based login. Registries use it to show the verification code that completes the sign-in in the browser.Patch Changes
Installing packages
pnpm installandpnpm addno longer wait for the daily update check. If the command finishes first, pnpm skips the update notice and checks again next time. On a registry that accepted the connection but never answered, an install that had nothing to do used to take minutes #16839.pnpm install,pnpm addandpnpm updatenow reusepnpm-lock.yamlwhen the workspace configures a catalog entry that no dependency references, such as an unused entry or one whose package anoverridesentry replaces. Before, every resolution in such a workspace resolved all dependencies again from the registry #16923.pnpm installnow looks up a git dependency's branch or tag once per install, even when many workspace projects depend on it. WithlinkWorkspacePackagesenabled, it used to rungit ls-remoteonce per project #16899.A filtered install, such as
pnpm install --filter <pkg>, now keeps thepnpm-lock.yamlentries 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.pnpm now decides whether a dependency that a package also declares as an optional peer is linked as a dependency or as a peer from the shallowest path that reaches the package. When an existing lockfile records such a dependency as a regular dependency and a newly added dependency now provides the peer, pnpm records it as a peer, as a fresh install does. The lockfile entry of such a package can change on the next install #16901.
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.
Running commands and scripts
pnpm no longer aborts with a core dump when the reader of its output has exited, as in
pnpm list | head -1orpnpm start 2>&1 | head -1. The command finishes with its usual exit code, and a failing script's exit code is kept. A write to stdout that fails for another reason, such as a full disk, ends the command with exit code 1 #16904 pnpm/tasks#132.On Windows,
pnpm exec,pnpm run, and commands such aspnpm nxnow return when the command they run exits, even if it left a detached process running and pnpm's output is redirected or piped. Previously a shell reading that output waited until the detached process, such as the Nx daemon, exited #16930.Commands that do not write the project manifest now leave a missing
package.jsonabsent. This covers queries such aspnpm whyandpnpm outdated,pnpm fetch,pnpm dedupe --check, and commands that exit with an error, such aspnpm rt ls. These commands used to create one from the initialization template.pnpm sbomrun from a lockfile alone now reports the root component asunknownwith version0.0.0#16907.pnpm runnow reports a failed script as a JSON record under--reporter=ndjson. It printed a plain[ELIFECYCLE]line to stderr.pnpm runno longer prints[ELIFECYCLE] Command failed with exit code N.to stderr when a script fails under--reporter=silent,--silent, or--loglevel=silent.pnpm pipeline --dry-runno longer writes a run report when no projects are selected. With--jsonit prints{"tasks": []}pnpm/tasks#126.Packing and publishing
pnpm packandpnpm publishnow ship the whole gitignored directory thatmain,browser, orbinpoints into, as npm does. Previously only themainandbinfiles themselves were packed, and a gitignoredbrowserfile was left out #16908.pnpm packandpnpm publishnow honor a.npmignoreor.gitignoreinside a subdirectory whenpackage.jsonhas afilesfield. The package root's ignore files and those inherited from the workspace are still skipped #16909.Setting up pnpm and toolchains
--registry,--https-proxy,--http-proxy, and--no-proxynow apply when pnpm resolves and downloads the pnpm version a project pins inpackageManager. Before, these lookups went tohttps://registry.npmjs.org/, which a firewall that only allows a private registry blocks #16911.On Windows,
pnpm installnow installs the Rust toolchain for rustup's default host, so a machine set up withrustup set default-host x86_64-pc-windows-gnugets the MinGW toolchain. Without a default host, pnpm installs the MSVC toolchain for the machine's architecture, as rustup does. This includes a pnpm built for MinGW, such as the MSYS2 package, which has installed the MinGW toolchain since 12.13.0. An x64 pnpm on ARM64 Windows now installs the ARM64 toolchain #16854.Platinum Sponsors
Gold Sponsors
v12.13.0: pnpm 12.13.0Compare Source
This release adds the
pnpm profileandpnpm tokencommands, thestoreIndexFormat: filessetting for stores shared over a network filesystem, and faster Python dependency resolution.Minor Changes
Added the
pnpm profilecommand to view and update registry user profile settings.Added
pnpm tokento list, create, and revoke authentication tokens on the registry.Added the
storeIndexFormatsetting. Set it tofilesto keep a store's package index as one file per package instead of the SQLiteindex.db, for a store on a network filesystem that several machines use at once. SQLite's locking does not work across machines, so concurrent installs on such a store could corruptindex.db.Added the
nodePackageMapsetting, which groups the package-map settings.nodePackageMap.mode: write-onlywritesnode_modules/.package-map.jsonfor other tools without making Node.js load it.mode: loadalso makes Node.js load the map, likenodeExperimentalPackageMap: true.nodePackageMap.typereplacesnodePackageMapType. The old settings still work #14937.Patch Changes
Setting up pnpm
pnpm setupnow works when pnpm was installed with npm on macOS. It installed pnpm from npm'sbindirectory and left a$PNPM_HOME/bin/pnpmthat could not run #16855.pnpm runs again after
pnpm setupwhenglobalShimsenables pnpm. Every command failed with "the global target of the pnpm shim points back at the shim" #16874.A pnpm built for the MinGW target on Windows, such as the MSYS2 package, now installs the MinGW Rust toolchain that
rust-toolchain.tomlnames. It used to install the MSVC toolchain.Installing packages
On macOS,
pnpm installno longer fails withERR_PNPM_LOCKFILE_WRITE_FILEwhen the project is on a virtiofs or SMB share. pnpm now falls back to a regularfsyncwhen the file system rejects a full sync #16846.On macOS,
pnpm install,pnpm patch-commit, and other commands that load a pnpmfile no longer hang at random.pnpm installnow puts the executables of a Python build backend's requirements on thePATHwhile the backend runs. Projects built withuv_buildnow install. Before, they failed with "uv-build was not properly installed".Removing a
link:dependency now also removes its symlink fromnode_moduleswhennodeLinkerishoisted#16884.pnpm-lock.yamlno longer records the tarball URL of a package installed from a scoped registry, such as one set by@types:registry=, when pnpm can rebuild the URL from that registry #16866.Packing, auditing, and pipelines
pnpm packandpnpm publishnow pack onlypackage.json, the README, the license, and the files named inmainandbinwhenfilesis an empty array. They used to pack every file in the package #16889.pnpm auditandpnpm audit signaturesno longer check runtimes installed throughdevEngines.runtime, such asnode@runtime:24.21.0, against the npm package of the same name.pnpm audit signaturesreported these runtimes as having an invalid registry signature #16869.pnpm pipelinenow runs the root's tasks whenpnpm-workspace.yamllists only the root inpackagesor omitspackages, aspnpm -r rundoes. A change to a root file in such a workspace no longer triggers a full-run warning. When a full run has no projects to run,pnpm pipelinenow reports that no projects are selected #16849.Performance
pnpm installresolves Python dependencies faster. With--lockfile-only, a project with 10 dependencies now resolves in about 4 seconds, down from about 40. When the index publishes a metadata file beside each wheel (PEP 658), as PyPI does, pnpm downloads only the wheels of the releases it selects.A repeat install against an existing lockfile no longer reads registry metadata for the dependencies it takes from that lockfile. On a warm store this removes thousands of metadata lookups from a repeat install of a large workspace.
pnpm installwithenableModulesDir: falsenow verifies the packages already in the store while the dependencies resolve, and checks a package's files in parallel. On a 5,100-package install with the store on a network filesystem, this removed a 19-second pause between resolution and fetching.Platinum Sponsors
Gold Sponsors
v12.12.1: pnpm 12.12.1Compare Source
This release adds OIDC authentication to
pnpm dist-tagand thetool:protocol.pnpm publishnow checks which registry anNPM_ID_TOKENwas issued for before using it.Patch Changes
pnpm publishnow uses anNPM_ID_TOKENonly if itsaudclaim matches the selected registry, such asnpm:registry.npmjs.org. A token for another registry is skipped with a warning, and pnpm falls back to the configured credentials.pnpm dist-tag add,rm, andlsnow support OIDC authentication in CI. Enable "Allow npm dist-tag" in the package's trusted publishing settings to manage tags without a stored npm token.Added the
tool:protocol for the tools pnpm downloads itself: Node.js, Bun, Deno, and Yarn.pnpm add bun@tool:1.3.0,pnx node@tool:22, andpnx yarn@tool:4work like theruntime:spelling, which stays supported as an alias. A lockfile written forruntime:stays up to date whenpackage.jsonswitches totool:, and the other way around.A named registry can no longer be called
tool.Platinum Sponsors
Gold Sponsors
v12.12.0: pnpm 12.12.0Compare Source
This release shares task results and dependency builds through a remote cache, adds the
addMissingPeerTypessetting, and letspnpm add --configandpnpm updatemanage configuration dependencies. It also fixesdangerouslyAllowAllBuildsrunning the build scripts of packages thatallowBuildsdenies.Minor Changes
pnpm pipelinecan share task results between machines. A result is a signed artifact stored on a pnpr server or on a server that speaks the Turborepo Remote Cache API, such as Vercel Remote Cache.The remote side-effects cache can also store dependency builds on a Turborepo Remote Cache server. Both caches read the new
remoteCachesetting, which names the server and holds the signing keys.pnpm pipeline --reportrecords the run under the organizationremoteCache.orgnames.Added the
addMissingPeerTypessetting, which links the@typespackage of a peer dependency next to every package that peer-depends on it. For example, a package that peer-depends onreactgets the project's@types/reactlinked next to it. This lets TypeScript find the types of a peer dependency when the global virtual store is on #15689.pnpm add --configandpnpm updatenow handle configuration dependencies the way they handle other dependencies #16814.pnpm add --configsaves a version range inpnpm-workspace.yamlby the same rules aspnpm add, sopnpm add --config my-config@latestsaves a range such as^1.2.0. It also resolves the package again whenpnpm-lock.yamlalready locks an older version.pnpm updateupdates configuration dependencies within their ranges, andpnpm update --latestmoves them to their latest version. A selector such aspnpm update my-configupdates only the configuration dependencies it names.Patch Changes
A package set to
falseinallowBuildsno longer runs its build scripts whendangerouslyAllowAllBuildsistrue. The two settings together now allow every build except the denied ones. Previously,dangerouslyAllowAllBuildsignored the denials.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
.npmrcfile, or_auth, or apnpm-workspace.yamlit could not parse, stopped every command,pnpm --versionincluded.pnpm updateandpnpm whyaccept--depth Infinityagain. They failed withinvalid value 'Infinity' for '--depth <DEPTH>'#16817.pnpm deploywithoutinjectWorkspacePackagesnow binds a peer dependency of a linked workspace package to the version its parent provides, as an injected install does. Previously the deploy failed withERR_PNPM_DEPLOY_AMBIGUOUS_PEERwhenever other packages in the deployed graph depended on different versions of that peer #16807.pnpm deploy --legacyno longer fails withERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVERwhen a workspace dependency of the deployed project declares aworkspace:peer dependency that the project does not depend on itself. pnpm now copies that peer from the workspace into the deploy directory.An auto-installed peer declared with
workspace:now always resolves to the workspace project. Previously,workspace:*andworkspace:^1.0.0peers could be installed from the registry #16806.Repeat installs no longer resolve again when an injected workspace package lists a dependency also as a peer dependency.
Sped up frozen installs of unchanged injected workspace packages that use hardlinks.
Sped up workspace discovery with subtree exclusions such as
!generated/**.A key under a registry URL in the
_authsetting 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 anauthTokenobject but lacks its@, such asorgfor@org, is still an error.Platinum Sponsors
Gold Sponsors
v12.11.2: pnpm 12.11.2Compare Source
This release fixes
--workspace-concurrency=Infinity, filters set by anupdateConfighook, and store fetches withenable-modules-dir=false.Patch Changes
--workspace-concurrency=Infinitynow runs workspace projects with no concurrency limit. It used to fail withinvalid digit found in string#16793.pnpm installand other recursive commands now apply thefilterandfilterProdthat anupdateConfighook sets. They used to run on every workspace project #16792.enable-modules-dir=falsenow also fetches the packages an install reuses from an existing lockfile, so the store holds every package the lockfile lists.Platinum Sponsors
Gold Sponsors
v12.11.1: pnpm 12.11.1Compare Source
This release fixes two ways
pnpm installcould fail, runs tools of any Rust release throughpnx, and treatsregistry.npmjs.comas an alias of the npm registry.Patch Changes
pnpm installno longer fails whenpackageManagerpins the pnpm version that is already running and the registry does not publish that version. pnpm warns and continues. A registry mirror that has not synced a release no longer blocks the commands of a project pinned to it.pnpm installno longer fails withERR_PNPM_CMD_SHIM_CHMODwhennode_modules/.binholds a shim that another user created, if everyone can already execute it and it is not world-writable. This happens when several users share one checkout.enable-modules-dir=false(enableModulesDir: falsethrough the Node.js addon) fetches the registry packages the host can install into the store again, as pnpm v10 did, while still writing nothing undernode_modules. The setting exists for anode_modulesthat something else mounts from the store, such as a FUSE daemon, and that consumer no longer has to download each package on first access. A plain--lockfile-onlyrun still fetches nothing.pnpm packandpnpm publishno longer put.npmignoreand.gitignorefiles in the tarball. Afilesentry that names one still ships it.pnpm now treats
https://registry.npmjs.com/as an alias ofhttps://registry.npmjs.org/. Registry requests, credentials, and trusted publishing use the canonical hostname.pnx --package=rust@<channel> <tool>runs a tool of that Rust release, for examplepnx --package=rust@nightly-2026-01-01 cargo build. pnpm installs the release with the components and targets fromrust-toolchain.tomland the target of each--targetargument.pnpm installnow links agent skills for more coding agents. It detects the agent fromANTIGRAVITY_AGENT,COPILOT_AGENT,COPILOT_CLI,CODEX_THREAD_ID,CODEX_SANDBOX,AI_AGENT, andCLAUDE_CODEpnpm/tasks#116.When the registry rejects
pnpm stage publish, the error message now starts with "Failed to stage package".Platinum Sponsors
Gold Sponsors
v12.11.0: pnpm 12.11.0Compare Source
This release adds Rust toolchain management, links the agent skills that dependencies ship, adds the
permissionssetting, and keeps the colors of streamed script output.Minor Changes
pnpm installnow links the agent skills that direct dependencies ship underskills/<name>/SKILL.mdinto the project's agent skill directories, such as.claude/skills. A package's skills are linked only after you approve them withpnpm approve. Theskills.dirssetting chooses the directories pnpm/rfcs#35.Added the
permissionssetting, which records what each dependency may do. Itsbuildcapability works likeallowBuildsand takes precedence over it.pnpm approve-buildswrites topermissionswhenpnpm-workspace.yamlalready has it, and toallowBuildsotherwise.Added
pnpm permissions, which lists the granted and denied permissions and the packages awaiting approval.pnpm approvereviews build scripts and agent skills in one prompt pnpm/rfcs#36.pnpm now installs and runs Rust toolchains.
cargo.enabled,pnpm installinstalls the toolchain named inrust-toolchain.toml. pnpm verifies the release signature, stores the toolchain once per machine, and links it into.pnpm/rust.pnpm runandpnpm execput itscargoandrustcon thePATH.pnpm add -g rust@<channel>installs a toolchain globally. Thecargoandrustccommands run it outside projects that pin their own.pnpm update -g,pnpm ls -g, andpnpm remove -gmanage it like any global package.pnpm add rust@<channel>pins the toolchain inrust-toolchain.toml.pnpm shim add rustadds project-aware shims forcargo,rustc, and the other Rust tools. In a project with arust-toolchain.toml, they run the toolchain the file names and install it on first use. Elsewhere, the next command of the same name onPATHruns, such as rustup's.pnpm runandpnpm execnow keep the colors of script output that they print under the project's name, such as with--stream. pnpm setsFORCE_COLOR=1for these scripts when its own output is in color, unlessFORCE_COLORis already set.Script output is also rendered more cleanly:
\rshows only its last state.pnpm -r runno longer garbles its live output when a script fails while other scripts are still running.Patch Changes
Installing packages
pnpm no longer panics with "unexpected error when polling the I/O driver" when it runs under QEMU user-mode emulation, such as a
linux/amd64container on an Apple Silicon Mac #16696.pnpm view,pnpm update, and other commands that read registry metadata now work behind proxies that end a response by closing the connection without a TLSclose_notifyalert #16704.pnpm now switches to the version a project pins in
packageManagerordevEngines.packageManagereven whenpnpm-workspace.yamlhas a setting the running pnpm cannot read, such as alockfile.includeResolutionSettingssection. If pnpm does not switch, it still reports that setting #16675.When the
pnpmpackage has to download its native binary on first run, it now uses the registry and credentials from.npmrcand from thenpm_config_registryandpnpm_config_registryenvironment variables.COREPACK_NPM_REGISTRYstill takes precedence. A project.npmrcis not read whenCOREPACK_INTEGRITY_KEYSturns off the signature check #16655.pnpm installandpnpm add --confignow applyminimumReleaseAgewhen they resolve a config dependency. A config dependency range resolves to the newest version that is old enough, so a later cleanpnpm install --frozen-lockfileaccepts the lockfile #16660.pnpm removewithcatalogPruneno longer removes catalog entries thatpnpm-lock.yamlstill records for workspace projects missing from disk. Before, a following frozen install failed withERR_PNPM_LOCKFILE_CONFIG_MISMATCH#16679.With
cargo.enabled,pnpm installnow writes the source replacement for vendored crates into.pnpm/crates/config.tomland includes it as optional from.cargo/config.toml. A checkout without.pnpmbuilds with plain Cargo #16659.With
nodeLinker.typeset toloaded, Node.js now stops withERR_PNPM_LOADER_UNSUPPORTED_NODEwhen it preloads the store loader on a version the loader cannot serve. The supported versions are^24.18.0 || >=26.2.0. On other versions, CommonJS packages imported from ESM failed withCannot find moduleon their first relativerequire().On Windows,
pnpm installno longer fails with "The filename, directory name, or volume label syntax is incorrect" when a package contains a file whose name is invalid on Windows, such asicon.svg?as=metadata.d.ts. pnpm removes the invalid characters from the name and prints a warning that lists the renamed files.On Windows, hoisting no longer fails intermittently with link errors when a junction is created or replaced concurrently pnpm/tasks#53.
Resolving dependencies
pnpm install --no-optionalnow installs the peer dependencies a project declares whenautoInstallPeersis on. The lockfile marked such a peeroptional: truewhen another dependency had it as an optional peer.pnpm installandpnpm dedupenow link an optional peer to the workspace package that the workspace root depends on when the picked version matches it. 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 inpeerDependenciesMeta#16681.pnpm addand other installs that re-resolve dependencies now keep the lockeddevEngines.runtimeversion while it still satisfies the declared range #16764.pnpm audit --fix updatenow updates only the dependencies whose locked version is vulnerable #14928.pnpm outdatedandpnpm update --interactivenow applyoverridesbefore they look up the latest version. Before, a dependency overridden to an npm alias was compared with the latest version of the package the override replaces #16719.Patched dependencies
Patches saved with CRLF line endings now apply, including a patch that creates or deletes a file. pnpm rejected the git headers of such a patch with
ERR_PNPM_INVALID_PATCHand the messageinvalid file mode: 100644#16641.A patch that changes a file's mode, such as adding or removing the executable bit, now applies the new mode on Unix pnpm/tasks#110.
Injected dependencies and deploy
With
sharedWorkspaceLockfile: false, an injected workspace package installed in the same run as its dependent now holds only the files itsfilesfield selects. The copy also held other files of the project, such astsconfig.json#16683.A script listed in
syncInjectedDepsAfterScriptsno longer fails when it rewritespackage.jsonwhile pnpm is copying its edits into the injected copies. The sync after the script now replaces a half-copied manifest.pnpm deploywith a shared lockfile no longer fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHforsettings.dedupeInjectedDepsorsettings.dedupePeerDependentswhenlockfile.includeResolutionSettingsis enabled. The deployed lockfile now records both settings asfalse, the values the deploy installs with.pnpm deploynow writes the dependencies in the deployedpackage.jsonsorted by name. It also sorts theallowBuildsentries in the deployedpnpm-workspace.yaml. Repeated deploys of the same lockfile now produce identical files #16687.Packing and publishing
pnpm packandpnpm publishnow match.npmignoreand.gitignorerules the way npm does. A negation such as!lib/**or!lib/**/!(*.map)re-includes files under a directory that an earlier*rule excluded #16743.pnpm packandpnpm publishno longer always include root files that merely start withREADME,LICENSE, orLICENCE, such asREADME_INTERNAL.md. OnlyREADME,LICENSE,LICENCE, andCOPYING, with or without an extension, ship regardless offilesand.npmignore, as in npm #16753.pnpm publish --provenance=falseand--no-provenancenow turn off provenance under trusted publishing, so a package can be published from a self-hosted runner. Settingprovenance: falseinpnpm-workspace.yamldoes the same #16721.Speed and resource use
pnpm installhardlinks files from a group-writable or world-writable store again. pnpm copied every file from such a store intonode_modules#16677.A repeat
pnpm installno longer imports patched packages and packages with build scripts again when nothing changed. Their builds no longer run again either. This happened whenrecursiveInstallwasfalseor the project had afile:dependency #16705.Verifying the lockfile against
trustPolicyandminimumReleaseAgeuses less memory. pnpm no longer keeps every published version's manifest of each checked package in memory until the install ends #16656.pnpm rebuild <pkg>andpnpm rebuild --pendingno longer read the manifest of every installed package to find build scripts. Only the selected packages are inspected and built. On a largenode_modulesserved lazily, such as over a network or FUSE mount, this turned a rebuild of a few packages into a fetch of every package.pnpm rebuildno longer removes and recreatesnode_moduleswhen the settings recorded innode_modules/.modules.yamldiffer from the current configuration. The rebuild runs the build scripts against the installed packages as they are.pnpm store prunenow compacts the store'sindex.dbafter removing package entries, so the file shrinks again #16717.Registry commands
pnpm whoami,pnpm bugs,pnpm docs,pnpm repo,pnpm star,pnpm unstar, andpnpm starsnow honor the--registryoption.--registrynow takes precedence over a scope's configured registry for scoped packages inpnpm access,pnpm view,pnpm repo,pnpm star,pnpm unstar,pnpm owner,pnpm deprecate,pnpm undeprecate,pnpm unpublish,pnpm dist-tag,pnpm team, andpnpm stage. Without--registry,pnpm accessnow sends a scoped package or scope to the registry configured for that scope.Registry commands now keep the path of a registry URL such as
https://example.com/npm/when they build request URLs. This applies topnpm access,pnpm owner,pnpm ping,pnpm search,pnpm star,pnpm stars,pnpm team,pnpm unstar, andpnpm whoami.pnpm viewnow honors the network retry settings.pnpm reponow fetches the repository URLs of several packages in parallel.Platinum Sponsors
Gold Sponsors
v12.10.1: pnpm 12.10.1Compare Source
This release fixes
pnpm installfailures after anoverrideschange and on a filtered frozen install withcatalogPrune. It also fixes several bugs in the experimentalnodeLinker.type: loaded, which now keeps its generated files innode_modules.Patch Changes
With
nodeLinker.type: loaded, pnpm now writes its generated files tonode_modules, which projects already ignore in git. The store manifest and loader arenode_modules/.pnpm/.store-manifest.jsonandnode_modules/.pnpm/.store-loader.mjs. Bin shims are innode_modules/.bin.Earlier versions wrote
.pnpm-store.jsonand.pnpm-store-loader.mjsto the project root, and a.pnpmdirectory to the root and to each workspace package. Delete them after reinstalling.With
nodeLinker.type: loaded, packages that ship their ownnode_modulesdirectory, such asnpmwith its bundled dependencies, now load from the store. Before, one such package in the install stopped every Node.js process from starting.With
nodeLinker.type: loaded, scripts can now run a Node.js runtime installed throughdevEngines.runtime. Before, every script that callednodere-ran its own shim until it failed with "Argument list too long".With
nodeLinker.type: loaded, Node.js processes start faster. In a project with 13,000 stored files, the startup overhead per process dropped from 67 ms to 18 ms.pnpm installno longer fails withERR_PNPM_NO_MATCHING_VERSIONafter a change tooverrideswhen the lockfile resolves an optional peer dependency to an npm alias of another package #16654.A frozen install with
catalogPruneno longer removes catalog entries thatpnpm-lock.yamlstill records. Before,pnpm install --frozen-lockfile --filterfailed withERR_PNPM_LOCKFILE_CONFIG_MISMATCHwhen some workspace projects were missing from disk #16638.pnpm install --fix-lockfileno longer removes thedeprecatedandhasBinfields from lockfile entries #6600.With
enableGlobalVirtualStore, an install that updatesnode_modulesnow repairs a package in the global virtual store that an interrupted install left without some of its dependency links or package files. Before, such an install kept the incomplete package if the project'snode_modulesalready recorded it #16642.pnpm installnow skips the Cargo and Python projects inside a nested directory that has its ownpnpm-workspace.yamlor.gitdirectory, such as a git worktree of the same workspace or a separate clone.The
Request tookwarning for package metadata now starts timing when pnpm sends the request. Before, it also counted the time the request waited for a free request slot, so large installs printed it for requests the registry answered quickly.Platinum Sponsors
Gold Sponsors
v12.10.0: pnpm 12.10.0Compare Source
This release adds an experimental
loadednode linker, letspnpm-lock.yamlrecord resolution settings, and reads cached registry metadata faster. It also carries several security fixes, including one that stops a dependency version from writing files outside the global virtual store.Minor Changes
Added experimental
nodeLinker: { type: loaded }installation. Compatible dependencies load directly from the content-addressable store through an automatically registered Node.js loader.nodeLinker.excludedselects packages and their dependency trees to install in the global virtual store.lockfile.includeResolutionSettings: truemakespnpm-lock.yamlrecordautoDedupe,dedupeInjectedDeps,dedupePeerDependentsandlinkWorkspacePackages. Installs then treat a lockfile that records other values as outdated. A lockfile that recordsautoDedupeis reused by later installs on any machine, sopnpm runafterpnpm install --frozen-lockfileno longer starts another install #16583.Patch Changes
Security
pnpm installnow prevents dependency versions with path traversal from writing files outside the global virtual store.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
variationsresolution against the registry. Aname@versionlockfile entry with an emptyvariationsresolution is now rejected.pnpm audit signaturesnow verifies signatures against the integrity recorded in the lockfile. Packages without a recorded integrity cannot pass signature verification.pnpm installandpnpm publishnow reject archive metadata larger than 64 MiB before reading it into memory. Publishing a pre-built tarball also rejects manifests and README files larger than 64 MiB.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.The warning about an ignored project
.npmrcregistry setting no longer prints the username and password of a URL-scoped key such as//user:password@registry.example.com/${PATH}/:_authToken.Installing and resolving dependencies
pnpm installnow fails withERR_PNPM_UNSUPPORTED_PROTOCOLwhen a dependency uses a specifier with a protocol pnpm does not support, such as Yarn'spatch:. On Windows, such a specifier failed withos error 123. On other platforms, pnpm linked it to a directory that does not exist. Reading apackage.jsonthat fails now names the file #16590.pnpm installnow fails withERR_PNPM_PACKAGE_MANIFEST_INVALID_ATTRIBUTEwhen a project declares a dependency whose specifier is not a string, such as"is-positive": 42. Before, the dependency was silently left out of the lockfile. AreadPackagehook can still correct the specifier.Fixed
pnpm installfailing withERR_PNPM_CMD_SHIM_RESOLVE_PATHwhen an executable's parent directory contains a dangling symlink.pnpm install --frozen-lockfileno longer fails withERR_PNPM_RESOLUTION_SHAPE_MISMATCHwhen aname@versionlockfile entry has a resolution served by a custom fetcher pnpm/tasks#108.pnpm install --fix-lockfilerepairs a lockfile whose importer references a package that has no snapshot entry, as left by a badly merged lockfile. It failed withERR_PNPM_LOCKFILE_MISSING_DEPENDENCYsince 12.8.0 #16618.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 installandpnpm dedupekept the newer locked version of the peer, so the lockfile held two copies of it, for example two copies ofvuepnpm/tasks#61.pnpm dedupenow reads registry metadata for a dependency pinned to an exact version, aspnpm installdoes. If the registry metadata disagreed with the package'spackage.json, the lockfile it wrote depended on whetherminimumReleaseAgewas set #16615.Speed and size
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--offlineis set.pnpm cache prunealso removes the metadata cache that older pnpm versions wrote under<cache-dir>/v11/#13512.Package metadata requests no longer wait behind queued tarball downloads when
maxSocketsor a proxy limits the connections to a registry. Large installs resolve faster and print fewerRequest tookwarnings.Sped up installs in large workspaces on macOS when the dependency links already exist. pnpm now keeps a link that already points at the right package without trying to create it first. Relinking the direct dependencies of 1,000 workspace projects took 45 ms, down from 116 ms pnpm/tasks#65.
Commands in a project that pins a different pnpm version start about 13 ms faster on macOS. pnpm now runs the pinned version's binary directly, without the shell script in front of it pnpm/tasks#66.
The pnpm binary is about 0.9 MB smaller, and the arm64 Linux binary is about 1 MB smaller still.
Running scripts and commands
pnpm runno longer prints[ELIFECYCLE] Command failed ...after Ctrl+C ends the script. pnpm still exits the way the script's shell did: on Windows with the shell's exit code (cmdreports-1073741510, PowerShell1), on Unix by re-raisingSIGINT#16579.pnpm run "/<regex>/"now accepts JavaScript regular expression syntax such as lookahead and lookbehind. A selector like"/^hello:(?!b).*$/"failed withERR_PNPM_NO_SCRIPT#16604.pnpm runandpnpm execnow forward--config.*command-line flags to the install started byverifyDepsBeforeRunpnpm/tasks#60.On Windows, a process started by
pnpm runorpnpm execcan again start a child withCREATE_BREAKAWAY_FROM_JOB. That child keeps running after pnpm exits, even if the command fails #16628.Empty
nodeOptionsvalues from command-line flags and environment variables now override lower-priority settings. Scripts retainNODE_OPTIONSfrom the parent environment orextraEnvwhennodeOptionsis empty.pnpm now reads the
failIfNoMatchsetting frompnpm-workspace.yaml, so a filter that matches no workspace project exits with code 1 when the setting istrue. The new--no-fail-if-no-matchflag turns the setting off for one command #16577.Configuration, setup, and pnpm versions
pnpm config get --globalandpnpm config list --globalnow show only the global configuration, also when run inside a project. Settings from the project'spnpm-workspace.yamland.npmrcwere included before. The same applies to--location=global#16598.pnpm now prints config warnings, such as an unset environment variable in
.npmrc, when loading the config fails.pnpm 11 releases older than 11.28.4 can run pnpm 12 again when the
packageManagerfield pins it. Since 12.9.0 they failed withSyntaxError: Invalid or unexpected token#16594.On Windows,
pnpm self-updateno longer runs the update a second time when it replaces apnpm.cmdlinked by pnpm 12.8 or older. cmd.exe read on in the replacedpnpm.cmd, printed an error about a command that is not recognized, and ran the new pnpm once more #16573.pnpm setupnow puts$PNPM_HOME/binfirst onPATHin login shells that inherited it further down, such as the VS Code terminal on macOS. Before, anothernodetook precedence over the one installed bypnpm runtime set node -g. Runpnpm setupagain to update the block in your shell config #16635.pnpm setupnow names the shell config file even if it is already up to date #16608.Updating, auditing, and publishing
pnpm update --latestnow applies thesavePrefixsetting when it rewrites a dependency whose range has no operator of its own, such as<2.0.0.The interactive
pnpm audit --fixpicker now shows each patched version with thesaveExactandsavePrefixstyle that the override is written with #13209.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.Output and messages
A warning about a project's
devEnginesorpackageManagerpin is now printed to stderr. A command such aspnpm cache pathorpnpm list --jsonkeeps only its own output on stdout #16584.pnpm listnow reports the correct package paths whennodeLinkerishoisted#9593.Resolution errors now name the failing dependency and its parent packages. Fatal errors appear as structured error records with their error codes when using
--reporter=ndjson.The error for an invalid git repository in the lockfile now has the code
ERR_PNPM_INVALID_GIT_REPOSITORY. Its message now lists every rejected form of the value.pnpm runtime --helpandpnpm help runtimenow name thesetsubcommand and the runtimes it accepts #16580.Platinum Sponsors
Gold Sponsors
v12.9.1: pnpm 12.9.1Compare Source
This release moves the WebContainer build into a separate
@pnpm/wasmpackage, shrinks thepnpmpackage back to about 4 MB, and fixespnpm publishwith provenance from GitLab CI.Patch Changes
The WebAssembly build for StackBlitz WebContainers now ships as a separate
@pnpm/wasmpackage. Thepnpmand@pnpm/exepackages no longer include it, which brings their unpacked size back from about 55 MB to about 4 MB. In a WebContainer, install@pnpm/wasmwith npm to get thepnpmcommand.pnpm publishwith provenance from GitLab CI is no longer rejected by the npm registry with a 422 error. The provenance statement now includes the GitLab CI variables ininvocation.parameters, as npm does #16551.pnpm audit signaturesnow uses the TLS settings of the redirect target when a registry redirects its signing-keys request, for example to registry.npmjs.org. Acafilescoped to a private registry no longer makes the redirected request fail #16541.Fixed
pnpm install --frozen-lockfilerejecting an up-to-date lockfile when an injected workspace package uses a catalog entry inpeerDependencies#16557.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.It also detects changes in projects whose directory names contain non-ASCII characters. Such a change used to be credited to the parent project.
changedFilesIgnorePatternandtestPatternnow match changed files whose names contain non-ASCII characters.The
pnpmexecutable is about 10% smaller. On macOS arm64 it went from 45.1 MB to 40.3 MB.Sped up trust downgrade checks for packages with long release histories.
With
optimisticRepeatInstall: false,pnpm installnow runs the projects' own lifecycle scripts, such asprepare, even whennode_modulesis already up to date #16545.pnpm self-updatenow fails for Homebrew-installed pnpm and prints thebrew upgradecommand for the installed formula, such asbrew upgrade pnpmorbrew upgrade pnpm@11. It used to install a second copy of pnpm that the Homebrew one kept shadowing #16547.Platinum Sponsors
Gold Sponsors
v12.9.0: pnpm 12.9Compare Source
This release runs pnpm in StackBlitz WebContainers, adds a per-registry
networkConcurrencysetting, and records every installed project in the store. It also carries a security fix forpnpm login.Minor Changes
pnpm now automatically uses WebAssembly in StackBlitz WebContainers, including when installation scripts are disabled. Native installations continue to use the native executable when installation scripts are enabled.
A
registriesentry can now setnetworkConcurrency, the most requests pnpm keeps in flight to that registry's origin. Requests to other registries keep the overall limit. The setting may live inpnpm-workspace.yamlor the globalconfig.yaml.pnpm installnow records every project it installs in the store'sprojectsdirectory, as a symlink to the project directory. A--frozen-storeinstall without the global virtual store still records nothing. Only projects that used the global virtual store were recorded before #6929.Patch Changes
pnpm loginno longer forwards credentials in its request body to another origin during redirects.Installing packages
Fixed
pnpm installfailing on Android withERR_PNPM_STORE_DIR_ACQUIRE_OPERATION_LOCK#16508.pnpm install --frozen-lockfileagain succeeds when a workspace project recorded inpnpm-lock.yamlhas no directory, such as a project left out of a Docker build context. It still fails if the project's directory exists without apackage.json#16453.pnpm install --frozen-lockfileno longer requires apnpm-lock.yamlin a project that has no dependencies. It also succeeds whenpnpm-lock.yamlrecords only the pinned pnpm version, as other commands write it when they run before the first install #16477.Fixed
pnpm install --frozen-lockfilerejecting a fresh lockfile when an injected workspace dependency has an optional peer supplied by another workspace project #16428.With
nodeLinker: hoisted, a filtered install now keeps the packages of the workspace projects an earlier install put innode_modules. This also covers the install thatpnpm --filter <selector> runandpnpm --filter <selector> execstart before the command. Before, these installs removed every package that only the unselected projects needed #16483.pnpm installwithnodeLinker: hoistednow refreshes directories supplied by custom fetchers when reinstalling. pnpm also keeps the symlinks inside those directories.With
enableGlobalVirtualStoreon, scripts can run entry points that a CommonJS require hook loads again, such asts-node index.ts. They failed withERR_UNKNOWN_FILE_EXTENSIONon Node.js versions without built-in TypeScript support #16436.pnpm now keeps each project's current lockfile and hidden hoisted dependencies in its own
node_modules/.pnpmwhenvirtualStoreDirpoints at a shared global virtual store.--virtual-store-dirnow sets the global virtual store's location too pnpm/tasks#47.pnpm cleanno longer deletes the project whenvirtualStoreDirorglobalVirtualStoreDiris set to the project directory. It also leaves a directory outside the project alone when the setting reaches it through a symlink. It now removes a global virtual store thatglobalVirtualStoreDirplaces inside the project, as it does forvirtualStoreDir.Optional dependencies
pnpm installno longer fails when a dependency of an optional dependency is missing from the registry. Like npm, pnpm now leaves out the nearest optional dependency above it, together with its subtree #16511.When an optional dependency fails to build, pnpm now removes its link from
node_modules. A repeatpnpm installthen reports "Already up to date" and no longer reruns the failing build #16468.pnpm installnow prints a warning with the error when an optional dependency cannot be fetched and is skipped. The skipped package is no longer counted in thePackages: +Nsummary. Thepnpm:skipped-optional-dependencylog reports the skip with thefetch_failurereason #16514.Resolving dependencies
Fixed
pnpm installchanging an unchanged project's direct dependency to a sibling workspace's pinned version when its dependency tree contains a cycle #16417.With
autoDedupeenabled, downgrading a dependency in one workspace project now moves the other projects to that version when it satisfies their ranges. This also applies to a filteredpnpm --filter <project> add#16432.pnpm installandpnpm dedupenow move an optional peer to the version already in the dependency graph when no other package provides its locked version anymore. After a bump such asvue3.5.40 to 3.5.43, the lockfile kept a second copy of@vue/server-rendererfor@vue/test-utils#16443.pnpm dedupe --checkno longer fails right afterpnpm installwhen a project's optional peer is satisfied by a package another workspace project installs.pnpm dedupenow picks the same versions for that package's dependencies aspnpm install#16447.pnpm installno longer re-resolves an up-to-date lockfile on every run when a patched package is a peer in a peer cycle #16418.With
autoDedupeenabled,pnpm install --lockfile-onlyno longer resolves the dependency graph again when nothing changed since an earlier--lockfile-onlyinstall deduplicated the lockfile. Such an install keeps the lockfile even if versions were published since it was written, or if only a setting such asresolutionModechanged. Runpnpm dedupeto apply such a change #16458.Speed and network
A repeat
pnpm installin a large workspace reports "Already up to date" faster #16487.Sped up dependency resolution of workspaces with many peer dependencies.
pnpm installsends fewer registry metadata requests when the lockfile already decides which version a range resolves to. This now also covers ranges that several locked versions satisfy when one of them outranks the others, and direct dependencies kept at their locked version. Packages thatminimumReleaseAgeExcludelists without a version now reuse cached registry metadata the same way they do whenminimumReleaseAgeis not set #16458.pnpm no longer downloads every packument again on each install from a registry whose metadata responses forbid caching, such as
Cache-Control: no-store. pnpm revalidates the cached metadata with a conditional request, so a registry that supports conditional requests answers with a 304 when the package has not changed #16528.Cached metadata for a package published within
minimumReleaseAgeis now revalidated with its ETag, so the npm registry can answer304 Not Modified. Before, the next install that checked the cache downloaded the whole document again #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.
Sped up
pnpm install --offlinewhen the version a range picks is not in the store. While it looks for a version the store holds, pnpm now reads only the versions the range admits #16495.pnpm install --offlinenow reuses config dependency tarballs that are already present in the store pnpm/tasks#46.Running scripts
pnpm -s <script>runs the script again, with-smeaning--sequentialas it does forpnpm run -s <script>. pnpm rejected it with "unexpected argument '-s' found" #16446.pnpm runandpnpm execnow warn and run the command when the install thatverifyDepsBeforeRunstarts 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 runorpnpm execnow finds dependencies out of date when a workspace dependency of a selected project has nonode_modulesdirectory, as after a filtered install. WithverifyDepsBeforeRun: install, pnpm installs that dependency before running the command pnpm/tasks#45.Scripts run without a terminal no longer start a second
sheach. One watchdog per pnpm command now ends every script's process group if pnpm is killed, sopnpm -r runacross many projects starts half as many processes #16489.Terminate batch job (Y/N)?no longer appears after pressing Ctrl+C in a script started withpnpmfrom PowerShell or cmd on Windows #16502.pnpm rebuildandpnpm approve-buildsrefresh 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.When
pnpm run <script>orpnpm <script>finds nothing to run and--filterfollows the script name, the error now suggests putting the filter option before the script name #4655.Package-name filters now support
?to match one character #2817.The pinned pnpm and
pnpm self-updatepnpm no longer downloads the project's pinned pnpm version again on every command when
nodeVersioninpnpm-workspace.yamlnames a different Node.js major than thenodeonPATH. Before, each of those commands took about a second longer and failed without network access #16497.Several
pnpmcommands started at once in a project that pinspackageManagerno longer fail withThe process cannot access the file because it is being used by another processon Windows while the pinned pnpm is being installed.pnpm can now switch to a
packageManagerversion below 11 on x64 musl Linux, such as Alpine #16467.A
devEngines.packageManagerrange no longer makes pnpm replace the version recorded inpnpm-lock.yamlwith the running pnpm while the recorded version still satisfies the range. When pnpm does record a version, it records the running pnpm only if it meetsminimumReleaseAge. Otherwise it records the newest version in the range that meets it, or the running pnpm if none does #16431.On Windows,
pnpm self-updatenow replaces apnpm.exeleft inPNPM_HOMEor inPNPM_HOME\bin. Windows ran that executable in place of the updatedpnpm.cmdshim, sopnpm --versionkept printing the old version after a successful update. If the executable was inPNPM_HOME,self-updatenow asks you to runpnpm setup#9094.pnpm self-updatenow checks that a version installed as the JavaScriptpnpmcan start before making it the globalpnpm. If Node.js is missing, the update fails and the currentpnpmstays in place.Other commands
pnpm deploynow finds patches and local dependencies when the target directory sits under a symlink, such as/tmpon macOS. It failed withERR_PNPM_PATCH_NOT_FOUND#16470.pnpm deploy --legacynow resolves the deployed project's relativefile:,link:, and path dependencies from the project's own directory #16475.pnpm update --globalnow removes hard-linked executables fromPNPM_HOMEwhen migrating packages from the old global layout #16420.pnpm store pruneno longer fails on store index entries that pnpm 11 wrote for git-hosted packages without apackage.json. Entries that still cannot be read are kept and counted in the prune summary.pnpm store prunenow aborts when an error other than a missing directory occurs while scanning project directories in the mark phase.pnpm config getandpnpm config listnow report a setting given on the command line with--config.<name>=<value>. Before, a value such as--config.node-linker=hoistedreached the install but was absent from the reported configuration #16276.pnpm -r pkg getnow 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.The install summary shows a
link:dependency as+ name <- path, and the Node.js API'shideLinkedPkgsDiffreporter option leaves matching linked dependencies out of the summary.The
--forcehelp text ofpnpm installandpnpm addnow says that--forcekeeps skipping optional dependencies built for other platforms. It points toforceIgnoresPlatformand the--os,--cpu, and--libcoptions for installing them #16435.The
homepagefield of the publishedpnpmpackage points to https://pnpm.io again.Platinum Sponsors
Gold Sponsors
v12.8.2: pnpm 12.8.2Compare Source
pnpm 12.8.2 fixes a startup crash on Linux ppc64le and
UnknownIssuererrors on systems without CA certificates.pnpm runno longer installs before every script on CI whenautoDedupeis enabled, and resolution and hoisted installs on macOS are faster.Patch Changes
Platforms and environments
Fixed pnpm crashing on startup on Linux ppc64le #16380.
Fixed installs failing with
UnknownIssueron Linux systems without CA certificates, such asnode:24-slim, whenNODE_EXTRA_CA_CERTSis set. The extra certificates now extend the bundled CA roots #16365.pnpm now creates its store operation locks and other per-user lock files in
$XDG_RUNTIME_DIRwhen it points to a directory only the user can write to. Otherwise, pnpm still uses/tmpon Linux and macOS. Sandboxes that block writes to/tmpcan pointXDG_RUNTIME_DIRat a writable directory #16390.POSIX bin shims and the
pnpm,pn,pnpx, andpnxlaunchers now run inside a Nix build, where the system default path holds none of the utilities they call. Installing again replaces the shims already innode_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 rejected the option before switching, so
pnpm install --auto-dedupefailed with "Unknown option" even though the pinned pnpm supports it #16353.Installing and resolving dependencies
pnpm install --frozen-lockfilenow fails whenCargo.lockdoes not satisfy a dependency requirement inCargo.toml. The error names the crate and the version the lockfile holds #16355.pnpm installreturns "Already up to date" again in a workspace with injected workspace dependencies and a shared lockfile. Since 12.7.0 every repeat install in such a workspace ran the full install and copied the injected projects again.With
injectWorkspacePackages: true, a freshpnpm installnow records a workspace dependency aslink:when its injected copy differs from the project only by an optional peer that peer-dependent dedupe merges. It was recorded as a peer-suffixedfile:copy #16354.pnpm dedupe --checknow passes right afterpnpm dedupewhen deduplication merges variants of a package that differ only in their peers. A lockfile key whose peer suffix named a merged variant now names the variant that replaced it #16356.When
minimumReleaseAgehides the version thatlatestpoints 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. Before, while a new1.0.0was too new,latestfell back to an old0.0.1even though1.0.0-beta.4had beenlatestuntil then #16388.Git-hosted dependencies now respect
pmOnFail. If it is set to anything other thandownload, a git-hosted dependency that pins a pnpm version is prepared by the running pnpm, and pnpm does not download the pinned version #16376.pnpmfile hooks such as
readPackagenow run once for a dependency that several packages request at the same time. They could run twice for it before.childConcurrencynow defaults to 5, the documented value. It used to be capped at 4 and to follow the host's CPU count.Running scripts
pnpm runandpnpm execno longer install dependencies before every script on CI whenautoDedupeis enabled.pnpm install --frozen-lockfilenow keeps the deduplication record left by an earlier install #16374.On macOS and Linux, lifecycle scripts and
pnpm runnow always getPATHfrom thePATHvariable. When the environment also held aPathvariable, a script sometimes gotPath's value, and failed withnode: not found#16308.pnpm runnow exits after aSIGTERMin a container where pnpm is PID 1 and the script runs pnpm again, as"start": "pnpm serve"does. Since 12.6.0 it kept waiting after the script had shut down, until the container runtime killed it.Other commands and settings
pnpm config get globalShims,pnpm shim list, and global installs no longer readglobalShimsfrom a project'spnpm-workspace.yaml. Only the global config file, the pnpm home's ownpnpm-workspace.yaml, andPNPM_CONFIG_GLOBAL_SHIMSset it, so a repository cannot choose which globally installed packages get project-aware shims.pnpm config set --location=projectrefuses a machine-level setting such asstateDirorscopewithERR_PNPM_CONFIG_SET_NOT_A_PROJECT_SETTING, which names where the setting belongs.pnpm config deletestill clears such a key from a project'spnpm-workspace.yaml.pnpm deployno longer fails withERR_PNPM_DEPLOY_AMBIGUOUS_PEERin a workspace withinjectWorkspacePackages: truewhen a workspace package lists its peer dependency as a dev dependency too #16375.pnpm deployno longer copies the workspace root'spackageManageranddevEngines.packageManagerfields into the deployedpackage.json#16403.pnpm publishnow includes bareREADMEfiles and README files with Markdown extensions such asreadme.markdownin registry metadata #12704.pnpm store prunenow removes the packages that only expiredpnpm dlxcache entries used. They were left in the store until the nextpnpm store prune#16383.Performance
Sped up dependency resolution in large workspaces, and when many dependencies request different ranges of the same package. Resolution also uses less memory.
Sped up
pnpm installwithnodeLinker: hoistedon macOS when the lockfile is re-resolved, such as withautoDedupeenabled #16397.Sped up extracting package tarballs.
pnpm installwithout--frozen-lockfileis faster on some machines in projects with apnpm-workspace.yaml. Those installs linked with one worker thread per core, half of what a frozen install uses.On Windows, warm
pnpm install --frozen-lockfileruns are 4-5% faster on 4- and 8-core machines. pnpm now links with one worker thread per core on Windows, between 4 and 16. This changes frozen installs and installs in projects without apnpm-workspace.yamlon machines with 3 to 15 cores.Platinum Sponsors
Gold Sponsors
v12.8.1: pnpm 12.8.1Compare Source
pnpm 12.8.1 fixes
pnpm install --frozen-lockfilerejecting lockfiles with injected workspace packages that have peers, restores the executable bit on files of local directory dependencies, makespnpm dedupeconverge, and uses less CPU on many-core machines.Patch Changes
pnpm install --frozen-lockfileno longer rejects a freshly generated lockfile when an injected workspace package has peer dependencies #16332.Executable files in a
file:directory dependency or an injected workspace package keep their executable bit again. Since 12.8.0, pnpm installed these files without the permissions they have in their project.pnpm dedupenow reaches a stable lockfile when a package's peer suffix is long enough to be hashed. Before, each run could switch that package's key between the hashed and the spelled-out suffix, sopnpm dedupe --checkalways failed #16331.pnpm install --frozen-lockfile, the default in CI, now uses less CPU on machines with more than 8 cores. Warm installs on many-core Windows machines got up to 10% faster. Frozen installs now link with at most 16 worker threads.verifyDepsBeforeRunno longer reports dependencies as outdated after a filtered install just becausepnpm-lock.yamlhas a newer modification time. It checks the lockfile against the packages that install put in place. Before,pnpm runreinstalled the whole workspace with lifecycle scripts on, for example after a DockerCOPYbrought in a lockfile with a newer mtime #16322.After a filtered install,
verifyDepsBeforeRunnow also checks that the install put the selected projects' dependencies in place. Anode_modulesdirectory alone no longer counts as proof.pnpm runandpnpm execno longer install a project that has never been installed and has nothing to install. Such a project declares no dependencies, no peer dependencies thatautoInstallPeerswould fetch, and no install lifecycle scripts. The command now runs without writingnode_modulesorpnpm-lock.yaml#16313.pnpm update -g --latestnow upgrades globally installed packages beyond their saved version ranges #16320.Platinum Sponsors
Gold Sponsors
v12.8.0: pnpm 12.8Compare Source
pnpm 12.8.0 warns when
pnpm packorpnpm publishwould ship a.envfile thatfilesdoes not list, installssharedWorkspaceLockfile: falseworkspaces concurrently, applies every setting passed as--config.<name>=<value>, and no longer leaves the Windows terminal stuck after Ctrl+C in a script.Minor Changes
pnpm packandpnpm publishnow warn when the tarball includes a.envor.env.*file that thefilesfield ofpackage.jsondoes not list. Templates such as.env.exampleare not reported. List the file infilesto publish it on purpose, or exclude it in.npmignoreor.gitignore#7826.pnpm packnow honors--silent,--reporter=silent, and--loglevel=silentto hide the tarball contents and summary. With--json, lifecycle script output and the final JSON output remain visible #10297.Patch Changes
Installing packages
Installing through a
pnprserver now records the pnpmfile checksum in the lockfile, so a laterpnpm install --frozen-lockfileaccepts that lockfile #14460. A frozen install through the pnpr server now fails if the pnpmfile changed. If the pnpmfile defines areadPackage,afterAllResolvedorpreResolutionhook or custom resolvers, pnpm resolves dependencies locally and prints a warning that the pnpr server was not used.Installing through a
pnprserver also links a workspace project at the directory itspublishConfig.directorynames. A server that does not forward the setting makes the install fail withERR_PNPM_PNPR_PUBLISH_DIRECTORY_MISMATCH, so pnpm never writes a lockfile that points at the wrong directory. The server rejects apublishConfig.directorythat points outside its project.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.A git-hosted dependency that is a pnpm workspace with no committed lockfile is now detected as a pnpm project #14011.
pnpm install --devandpnpm fetch --devnow install the optional dependencies of devDependencies, such as the platform binaries of Biome and oxlint. The project's ownoptionalDependenciesare still skipped #9678.pnpm install --offlineandpnpm add --offlinenow 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 withERR_PNPM_NO_OFFLINE_TARBALLwhen 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. The error also carries the
ERR_PNPM_NO_OFFLINE_METAcode.pnpm cache prune --helpnow says that pnpm 11.26 and earlier, and pnpm 12.3 and earlier, depend on the directories it removes #15656.Running
pnpm installnow refreshes dependencies when a package declared with a localfile:directory changes its dependencies #4623.A repeat
pnpm installnow keeps its fast up-to-date check when an override replaces a declared localfile:dependency #12892.pnpm installnow removes an optional dependency fromnode_modulesif its install script fails. Code that checks whether the package is installed no longer finds a package that cannot load #8756.With
nodeLinker: hoisted,pnpm installnow restores a workspace project'snode_modulesafter it was deleted. Before, the install printed "Already up to date" and left the project without the dependencies nested under it. On Windows, the install also no longer fails with "Access is denied" when another project's copy of a shared dependency links to the deleted directory.Under
nodeLinker: hoisted,pnpm installnow clears orphaned package directories that an interrupted or failed install leaves in a project'snode_modules. A directory recorded by the previous install is removed, while an unrecorded directory is moved tonode_modules/.ignored. A copy already in.ignoredis never overwritten #13676.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 write.
Resolving and linking dependencies
pnpm installno longer aborts on a failed allocation of many gigabytes when peer dependency ranges combine overlapping||alternatives #15867.pnpm installno longer fails when a package from the registry declares afile: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 aslink:<root>/typings/css-tree#9141.An
npm:alias written byoverridesnow stays in place when a change elsewhere makes pnpm re-resolve the aliased dependency. Before, pnpm could look up the alias name at the aliased version, which failed withERR_PNPM_NO_MATCHING_VERSIONor locked an unrelated package #16309.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 dedupeno longer changes the lockfile on every run when a nested peer dependency is provided through an npm alias #15709.With
resolutionMode: time-basedandminimumReleaseAgeboth set,pnpm installno longer reports a subdependency as too new when only the time-based cutoff excludes it. Such subdependencies used to fail a strict install withERR_PNPM_NO_MATURE_MATCHING_VERSION, or were added tominimumReleaseAgeExclude#13569. A transitive dependency that has no matching version published before the time-based cutoff now resolves to the lowest matching version allowed byminimumReleaseAge. pnpm picks a version younger thanminimumReleaseAgeonly if no older version matches #16298.pnpm installretries registry metadata fetches that fail with a timeout, a dropped connection, or an interrupted response body before it appliestrustPolicyorminimumReleaseAge. A transient fetch failure is not reported asTRUST_DOWNGRADEorMINIMUM_RELEASE_AGE_VIOLATION#12031.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 onpnpm installorpnpm update. User-configuredpackageExtensionsstill apply to project manifests #11700.Packages in an external
virtualStoreDircan resolve the project's direct dependencies selected byhoistPattern. Runpnpm install --forceto repair an existing installation #5652.pnpm installnow links the executables of auto-installed peer dependencies into the workspace root'snode_modules/.bin, including after a frozen-lockfile reinstall #8511.Lockfiles and frozen installs
pnpm install --frozen-lockfilenow works on a detached HEAD whengitBranchLockfileis enabled. The install reads the lockfiles of the local and remote-tracking branches that contain the checked-out commit. It still writes the sharedpnpm-lock.yaml#7672.pnpm install --frozen-lockfilenow accepts a lockfile that has no importer entry for a workspace package without dependencies. Such a package added after the lockfile was written made the install fail withERR_PNPM_PACKAGE_MANAGER_NO_IMPORTER#15875.pnpm installnow fails withERR_PNPM_LOCKFILE_MISSING_DEPENDENCYwhen an importer references a dependency version that has no snapshot entry. Before, the install succeeded and left anode_modulessymlink pointing at a missing virtual-store directory #14764.pnpm installon CI now fails on an outdated lockfile whenpreferFrozenLockfileis explicitly set totrue. Setting it totrueused to let CI update the lockfile #9072.With
gitBranchLockfileenabled, each emoji or other character outside the Basic Multilingual Plane in a branch name now becomes!!in the lockfile name. Before, each such character became one!.Workspaces and filtering
pnpm installin a workspace withsharedWorkspaceLockfile: falsenow installs projects concurrently, up toworkspaceConcurrencyat a time #14480. A project is resolved, fetched, and written to its virtual store without waiting for the workspace projects it depends on. It waits for them only before it links its dependencies and runs its lifecycle scripts, so its scripts still run after theirs. A project with apreinstallorpnpm:devPreinstallscript, or with an injected orfile:workspace dependency, waits for its workspace dependencies before it starts.The installs of the projects also share their package metadata, lockfile verification, and store caches, so they use less CPU and memory when several projects depend on the same packages. An install with a pnpmfile no longer starts an extra Node.js process when the pnpmfile has no
preResolutionhook.With
enableGlobalVirtualStoreandsharedWorkspaceLockfile: false, each project now keeps its current lockfile and its hidden hoisted dependencies in its ownnode_modules/.pnpm. Before, every project wrote them to the workspace root'snode_modules/.pnpm, so each repeat install treated the other projects' packages as its own and relinked them #14480.pnpm rebuild,pnpm approve-builds, andpnpm ignored-buildsnow work on the current project'snode_moduleswhen they run inside a project of a workspace withsharedWorkspaceLockfile: false. They used to read the workspace root'snode_modules, sopnpm rebuilddid not rebuild the project's dependencies and created a second virtual store at the workspace root #9402.pnpm installno longer creates anode_modulessymlink inside thepublishConfig.directoryof a workspace package linked withlinkDirectory. A build tool that cleaned its output directory through that symlink deleted the files of the package's dependencies.pnpm installalso removes a symlink that an earlier install left there #16226. It also no longer fails withERR_PNPM_CMD_SHIM_RESOLVE_PATHwhen such a package has abinfield and itspublishConfig.directorydoes not exist yet.pnpm installno longer fails for an injected workspace dependency whose package publishes from apublishConfig.directorythat its ownpreparescript builds. The injected copy now picks up that directory oncepreparefinishes building it.pnpm install --frozen-lockfileno longer reports the dependency as outdated while the directory has not been built yet #7811.An in-place edit to the source of an injected workspace package now shows up in its injected copy, unless a build writes to that package or
packageImportMethodis set. pnpm hardlinks such packages under the default import method #4410. Scripts listed insyncInjectedDepsAfterScriptsnow update injected dependencies while they run, so a watcher on the injected package, such as a dev server, sees each change before the script exits.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.injectWorkspacePackagesnow hard links a workspace dependency declared with a relative path, such asworkspace:../foo, the same way it already does forworkspace:*#10446.Workspace discovery prunes dot-prefixed directories, so a
packagespattern such as**no longer matches projects inside.cacheand other hidden directories #16250.pnpm importin a workspace now keeps the versions pinned by ayarn.lockinside a workspace project #4385.Store and caches
Files imported from the store now follow the umask of the install that writes them. Installing with a umask of
077no longer leaves imported files readable by the group and others #3807.pnpm installkeeps the owner, group, and mode of files already in a shared store, includingindex.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 installrepairs a store file that was modified through a hard link innode_modules, the repair now keeps the file's inode on Linux and macOS, so hard-linked copies in other projects are healed at the same time. On Windows the repair still replaces the file, so other projects are healed on their next install #3445.pnpm installnow reports a full store at once when writing package files fails. It no longer retries the tarball #8581.pnpm now warns when it cannot hard link packages from an existing store in the pnpm home directory and falls back to 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.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.
The global virtual store and the side-effects cache now key built packages by the Node.js version that the root project's
devEngines.runtimeorengines.runtimepins. That is the Node.js their build scripts run with. A dependency that declares its ownengines.runtimeno longer changes the key for every other package.With
enableGlobalVirtualStore, an install into a freshnode_modulesno longer runs the build scripts of a dependency whose global virtual store slot an earlier install already built.pnpm rebuildstill runs them #14480.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.
A warm
pnpm installreuses 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 updatestill fetches current metadata #13976.pnpm no longer revalidates cached registry metadata when the registry sends
Cache-Control: max-age=0,no-cache, orno-store. It downloads the metadata again, so a version newly published to such a registry is visible on the next install #13487.pnpm installhonorsCache-Controlfor dependencies named with anhttp:orhttps:tarball URL. A fresh response is taken from the store with no request, and a stale one is revalidated withIf-None-Match#15648.Patched dependencies
pnpm installnow repairs apnpm-lock.yamlwhose(patch_hash=<hash>)dependency paths disagree with itspatchedDependenciesmap, including paths that lack the hash their patch calls for. Before, pnpm accepted such a lockfile as up to date and kept the old patched files.pnpm install --frozen-lockfilenow fails on such a lockfile withERR_PNPM_INCONSISTENT_PATCH_HASH. It fails withERR_PNPM_UNCHECKABLE_PATCH_HASHwhen 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 installwithnodeLinker: hoistednow applies a patch once to each copy of a patched dependency in a workspace. Before, a copy that several workspace projects shared could receive the patch twice and end up with the patched content duplicated #7565.pnpm installandpnpm fetchnow fail withERR_PNPM_PATCH_NOT_FOUNDwhen a patch file listed inpatchedDependenciesdoes not exist #5268.engineStrictnow checks the patchedpackage.jsonwhen apatchedDependenciesentry changesengines. A patch that relaxesengines.nodeno longer fails the install against the published range #9603.pnpm patchnow applies the existing patch file to the edit directory of a git-hosted dependency, as it already does for packages from the registry #9699.Adding, updating, and removing dependencies
pnpm add <dir>now warns when the added directory declares peer dependencies, aspnpm linkdoes. The directory is saved as alink:dependency, and its peers are not resolved from the project that adds it. Use thefile:protocol to have them resolved #5523.pnpm add --save-typesno longer adds a@types/*package whose resolved version is deprecated. DefinitelyTyped publishes such stubs for packages that ship their own types, such as@types/typescriptfortypescript#15636.pnpm version,pnpm add, andpnpm pkg setkeep JSON5 style when they updatepackage.json5. ASCII identifier keys stay unquoted, strings keep JSON5 quotes, and indented files keep trailing commas #15717.Running scripts and commands
pnpm runandpnpm execno longer install dependencies automatically when the rootpackage.jsonstill keepsoverrides,packageExtensions,patchedDependencies, orignoredOptionalDependenciesin itspnpmfield. 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 topnpm-workspace.yaml#16278.When
verifyDepsBeforeRuntriggers an install before a filteredpnpm runorpnpm 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 -r run /regexp/now honors thetasksdependsOndeclared 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.pnpm runexits 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 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.
pnpm runand lifecycle scripts use the configuredscriptShell, including Git Bash on Windows, whenshellEmulatoris also enabled.shellEmulatorstill runs scripts whenscriptShellis not set. Extra arguments passed topnpm runare quoted for the shell that runs the script, so a Windows path stays intact #14719.With
enableGlobalVirtualStore, dependency build scripts now see the workspace root'snode_modules/.bin, as they do with a local virtual store. Apostinstallscript that runsnodefinds the Node.js installed bydevEngines.runtimeand no longer fails with "command not found" on machines without a system Node.js #15652. Dependency build scripts also see the bins of privately hoisted dependencies.Dependency install scripts now find the node-gyp bundled with pnpm when pnpm runs through a symlink, such as
node_modules/.bin/pnpmor thepnpmthatnpm install -g pnpmlinks. They used to fail withnode-gyp: command not foundon macOS #15694.pnpm runand lifecycle scripts now setnpm_config_node_gypto the bundlednode-gypentry point. Tools that read the variable resolve the samenode-gyppnpm builds with. Annpm_config_node_gypvalue the environment already sets is kept as is #16270.Scripts now see the
npm_commandenvironment variable that npm sets. It holdsrun-scriptwhen the command runs a script, and the command's own name otherwise #16265.Commands run from a POSIX shell through a dependency's own
node_modules/.bin, such asnode_modules/vite/node_modules/.bin/esbuild, no longer fail withMODULE_NOT_FOUND#10189.pnpx --versionandpnpm dlx --versionnow print the pnpm version. Other unknown options before the command are reported as errors. Before, pnpm tried to download a package named after the option #16259.pnpm dlxnow keeps the virtual store of its cached installs innode_modules/.pnpm, like every other install #13955.pnpm pack-appnow names the manifest of its runtime install directorypnpm-pack-app-<target>.Publishing, packing, and deploying
pnpm packandpnpm publishnow ship a file that thefilesfield names even when another entry excludes the directory holding it. For example,["**", "!dist", "dist/index.d.ts"]shipsdist/index.d.ts#16213.pnpm packprunes a directory that afilesfield exclusion names, such as!**/test, excluding the directory and its contents from the packed package #15738.pnpm publishnow 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 --prodno longer fails withERR_PNPM_OUTDATED_LOCKFILEwhen the deployed project declares adevEngines.runtimewithonFail: download. The runtime stays out of the deployednode_moduleswith the rest of the dev dependencies #15703.pnpm deploywith a shared lockfile now copies workspace dependencies into the deploy directory, even whenpackageImportMethodis set tohardlink. Before, their files were hard-linked to the workspace sources, so editing a source file also changed the deployed copy #12176.pnpm deploy --legacyno longer leaves broken links to nested local dependencies of workspace packages #9575.Configuration and pnpmfile hooks
Every setting pnpm supports can now be set with
--config.<name>=<value>on the command line, not only the ones whose command also carries a matching flag. Before,pnpm install --config.frozen-lockfile=truedropped the setting and rewrotepnpm-lock.yamlas though the install had not been frozen #16276.Settings given on the command line, such as
--registryand--store-dir, now take precedence over the values a pnpmfileupdateConfighook sets #14063.pnpm config set --location=projectandpnpm config delete --location=project, run from a package inside a workspace, now write settings that belong inpnpm-workspace.yamlto the workspace root'spnpm-workspace.yaml. Before, they created a newpnpm-workspace.yamlin the current package, which made that package the workspace root. Settings stored in.npmrcare 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_DIRstill works as a fallback #16275.pnpm now fails with
ERR_PNPM_AUTH_INVALID_BASE64when a registry's_passwordin.npmrcis not valid base64. Before, it sent the value as the raw password. Ausernameor_passwordleft empty, for example by an unset environment variable, now supplies no credential #16273.proxy=falsenow turns proxying off even whenHTTP_PROXY,HTTPS_PROXY, orALL_PROXYis set. pnpm no longer sends requests through a proxy named only inALL_PROXY.pnpm installnow runs the install hooks of a config dependency plugin's pnpmfile, includingreadPackage,afterAllResolved, and custom resolvers. Its pnpmfile is also counted inpnpmfileChecksum. Before, only the plugin'supdateConfighook ran, so a plugin could not change the resolved dependencies.A pnpmfile
fetchershook now runs once per package on a fresh install when it handles a resolution with a customtypeor delegates a git-hosted one to the same subdirectory #15584. These packages were fetched a second time for installation, so the installed files could come from a different archive than the one their dependencies were read from. The hook also no longer runs twice when aresolvershook returns a tarball resolution without a manifest #15025.pnpm installnow re-fetches a package from a custom resolver when theintegrityof its resolution changes, with or withoutenableGlobalVirtualStore. It used to update the lockfile but keep the old files innode_modules#15670.pnpm installnow rejects invalid results from areadPackagehook. A hook that returns a non-object value fails withERR_PNPM_BAD_READ_PACKAGE_HOOK_RESULT#15730. A hook that sets a dependency range to a value other than a string, such asundefined, fails with an error that names the dependency, the package and the pnpmfile. Delete the property to remove a dependency #15705.Global packages, pnpm versions, and runtimes
pnpm update --globalnow reinstalls the global packages that pnpm 10 installed into the previous global directory,<global-dir>/5, so their commands are linked into the pnpm homebindirectory again andpnpm list --globallists them. Once every package is migrated, pnpm deletes the previous directory and the commands pnpm 10 linked into the pnpm home #11528.A signal sent to pnpm, such as
SIGTERM, now reaches the pnpm that pnpm switches to because ofpackageManagerordevEngines.packageManager, and the one thatpnpm withruns. The signal used to be dropped, so scripts running under that pnpm never got to shut down #9948.On arm64 musl Linux, such as Alpine on ARM, switching to a pinned pnpm older than 12 now runs the JavaScript
pnpmpackage. The standalone executable of those versions crashed at startup on that platform #10443.Global shims such as
nodenow work when pnpm runs through a relative symlink, as with a Homebrew install. They were copies of that symlink and did not resolve from the global bin directory #15691.pnpm env remove --globaldeletes Node.js versions that pnpm installed into its own store, including when another tool installed pnpm #8357.pnpm self-updateno longer suggests a downgrade whenminimumReleaseAgeholds back the registry'slatestrelease. It now says that release is still within the cutoff #12006.Windows
Interrupting a script with Ctrl+C on Windows no longer leaves the terminal stuck #14860. A script that runs through a batch shim, as
vite devdoes throughvite.CMD, made cmd.exe wait forever on its "Terminate batch job (Y/N)?" answer, and every following keystroke went to that prompt. pnpm now ends a cmd.exe script shell once it has sat for a second after the interrupt with nothing running under it. A script that takes longer to shut down is still waited for. A second Ctrl+C ends the script's shell at once.On Windows,
pnpm runnow passes the arguments after the script name to the script as typed. Before,cmdexpanded%VAR%in them and backslashes arrived doubled. Line breaks still arrive as the two characters\n, becausecmdcannot pass them. The command line pnpm prints for the script quotes the arguments the same way on every platform #16257.The Windows
pnpm.exeruns on a clean Windows install that does not have the Visual C++ Redistributable. It used to exit immediately on startup because that runtime was missing #15723.On Windows, the
.cmdcommand shims innode_modules/.binnow keep a%in the project path. Before, cmd.exe expanded it as a variable reference, so the command received a mangledNODE_PATH#15716. Command shims also run tools whose paths contain non-ASCII characters #6999, including the PowerShell shims in Windows PowerShell 5.1 #16217.Bin shims in
node_modules/.binrun from Cygwin on Windows again. The shims passed a/cygdrive/c/...path to the Windowsnodefound onPATH, so Node.js failed withCannot find module 'C:\cygdrive\c\...'#12845.On Windows, installing pnpm with npm inside a project now writes
node_modules/.binshims that runpnpm.exe. A global install withnpm install --location=globalnow gets the same shims asnpm install -g#15688.pnpm installno longer fails withERR_PNPM_WORKSPACE_INVALID_GLOBon Windows for a wildcard pattern such asplugins/*/*inpnpm-workspace.yamlwhen the workspace is on a different drive than the pnpm cache or state directory #16239.On Windows,
pnpm installno longer skips a dependency's build script on a later install when the package ships an executable file and the script changes nothing inside the package directory #15667.pnpm setupno longer writes thepn.ps1,pnpx.ps1, andpnx.ps1PowerShell wrappers. It also removes the ones an earlier setup wrote. PowerShell now runspn,pnpx, andpnxthrough their.cmdwrappers, likepnpmitself. Before, these aliases failed with a "not digitally signed" error wherever the execution policy blocks unsigned scripts #8444.pnpm setupon Windows no longer panics when an unrelated environment variable has a name containing a non-ASCII character. It skips that variable #15684.On Windows,
pnpm setuprepairs thePNPM_HOMEregistry type left by older pnpm versions, even when the configured directory has not changed.On Windows, the
ERR_PNPM_BAD_ENV_FOUNDerror ofpnpm setupnow shows the valuePNPM_HOMEis currently set to. Before, it showed the directory pnpm wanted to set.On Windows, pnpm expands nested
%VAR%references inPNPM_HOMEand the other directory environment variables it uses for its home, store, cache, state, and config directories. pnpm fails with an error when a%VAR%reference remains after expansion #13236.On Windows, if the global bin directory is not in
PATHand aPATHentry still contains an unexpanded variable such as%PNPM_HOME%, the error now names that entry. A variable referenced from the userPathmust be set to a full path and stored as a plain string (REG_SZ) for the entry to expand #5283.Inspecting dependencies
pnpm auditandpnpm audit signaturesnow fail with an error when the lockfile contains unresolvable dependency references #13638.pnpm licenses listnow reports the actual on-disk package locations when usingnodeLinker: hoistedorshamefully-hoist: true#8589. With--json, itspathsarray now includes every installed copy of a package, including hoisted copies and isolated installations with different peer dependencies.pnpm rootnow prints the configuredmodulesDir. It used to printnode_modulesregardless of the setting. A project's ownmodulesDirfrompackageConfigsis printed too #9113.Output and messages
With the default and append-only reporters, installs with
--loglevel warnor--loglevel errornow 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.When a dependency fails to resolve, the error now shows the cause. For example, a Node.js runtime download behind a proxy that re-signs TLS now reports
invalid peer certificate: UnknownIssuer#9556.When installing a git dependency over SSH fails with
Permission denied (publickey), pnpm suggests checking the loaded keys withssh-add -l. Resolving an SSH URL that refuses the key also shows a local HTTPS rewrite that leaves the recorded URL alone #13743.Lockfile verification now fails with
ERR_PNPM_TARBALL_URL_MISMATCH,ERR_PNPM_TARBALL_REVISION_MISMATCH, orERR_PNPM_MISSING_NAMED_REGISTRYwhen every rejected entry failed that check. These failures were reported as the genericERR_PNPM_LOCKFILE_RESOLUTION_VERIFICATION.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 installno longer prints an extraProgress:line after the progress line is markeddone#16184.Platinum Sponsors
Gold Sponsors
v12.7.0: pnpm 12.7Compare Source
pnpm 12.7.0 ships with
.nvmrcand.node-versionsupport in the globalnodeshim,pnpm install --allow-build,pnpm publish --publish-wait-timeout, andpnpm-workspace.yamlcreated from theworkspacesfield.pnpm install --forceno longer installs optional dependencies built for other platforms. This release also carries security fixes for bin shims on Nix, for lifecycle scripts of packages in astoreDirinside the workspace, and foruserAgentplaceholders inpnpm-workspace.yaml.Minor Changes
pnpm install --forcenow keeps skipping optional dependencies whoseos,cpuorlibcdo not match the host. It still refetches every package and liftsengineStrict. The newforceIgnoresPlatformsetting restores the previous behaviour, installing optional dependencies of every platform under--force#6133.The global
nodeshim created by pnpm now uses the Node.js version from the nearest.nvmrcor.node-versionfile when the project does not declare a Node.js runtime indevEngines.runtimeorengines.runtime#4471. The nearest directory with a Node.js runtime declaration decides the version. Within one directory,package.jsontakes precedence over.node-version, which takes precedence over.nvmrc. An.nvmrcvalue that only nvm can act on, such assystemor a custom alias, is ignored.pnpm installnow supports the--allow-buildoption to selectively allow or deny package lifecycle scripts and record them inpnpm-workspace.yaml#15388.Added
pnpm publish --publish-wait-timeout <milliseconds>to wait for published versions and their tarballs to become available from the registry. SetpublishWaitTimeoutinpnpm-workspace.yamlto configure a default. A value of0disables the check.Recursive publishing confirms availability before publishing dependent packages. If confirmation times out, the command fails.
When
pnpm publish -r --report-summaryfails after some uploads were accepted, the summary file now lists those packages.pnpm installnow createspnpm-workspace.yamlfrom theworkspacesfield of the rootpackage.jsonwhen the repository has nopnpm-workspace.yaml. The projects the field lists are linked on that same install. An existingpnpm-workspace.yamlis never changed. With--ignore-workspace, no file is created. If theworkspacesfield later differs frompackagesinpnpm-workspace.yaml, pnpm prints a warning #2255.When a project pins a pnpm version or a runtime that another pnpm process is installing at that moment, pnpm now waits a few seconds and then installs and runs a private copy of its own. It used to wait up to five minutes and then use the shared install directory without the lock. The private copy is removed once the command has run.
pnpm store pruneremoves any private copy that a killed process left behind #15413.pnpm now keeps the blank lines between entries of
package.jsonwhen it updates the file, for example onpnpm add#5602.Patch Changes
Security
pnpm no longer expands environment variables in a
userAgentset in a project'spnpm-workspace.yaml. AuserAgentwith a placeholder in that file is now ignored. Before this fix, pnpm sent the variable's value to the configured registry #15415.On Nix, a dependency's bin named like a system utility such as
sedcan no longer redirect a POSIX bin shim or thepnpm,pn,pnpx, andpnxlaunchers. The shims and launchers now ignorenode_modulesand relativePATHentries while they locate their own files. Installing again replaces the shims already innode_modules#14883.pnpm no longer treats manifests inside its store, cache, state, or modules directories as workspace projects. Before, a
storeDirinside the workspace could let lifecycle scripts of packages in the store run withoutallowBuildsapproval #15033.Packages that run a lifecycle script are no longer hard-linked into the virtual store, so a build script can no longer rewrite the workspace source of an injected package or the store copy it was imported from #15483.
Installing packages
Fixed
pnpm install,pnpm add,pnpm remove, andpnpm peers checkrunning out of memory when many packages share a missing peer dependency. This mostly affected projects withautoInstallPeers: false#15362.pnpm no longer hangs for up to 5 minutes after a pnpm process was killed while setting up the pnpm version pinned in
packageManagerordevEngines#15360, #15393. The killed process left behind a lock that every later pnpm command in the project waited on. pnpm now detects that the process holding a lock is gone and takes the lock over at once. The same applies to the locks pnpm takes while installing a managed runtime or writing the global bin directory. Two pnpm processes that are both still running keep waiting for each other as before.Requests to a registry or tarball server whose TLS certificate fails verification now fail at once. Such requests were retried for more than a minute without any output #9134.
On macOS, pnpm now falls back to its bundled CA roots when system trust evaluation is unavailable, such as in a sandbox or when macOS cannot create an SSL policy for a registry connection. Installs failed or crashed on the first registry request in that case. Custom
cacertificates are now honored directly #15329, #14461.pnpm installnow caps concurrent connections to a proxy at 50 sockets by default #15280. It also immediately retries transient connection resets when downloading package archives.pnpm installnow reuses a package already present in the store when an existing lockfile entry satisfies the dependency, avoiding registry requests that fail without authorization #2522.Installing or adding dependencies no longer fails when a previously installed local tarball file was deleted from disk #8367.
pnpm installnow installs the new version of a local tarball dependency whose file was replaced at the same path #2437.pnpm install --frozen-lockfilerejects such a changed tarball, even when the previous archive contents are in the store #1889.pnpm installnow fetches committed submodules of git dependencies #1470.pnpm installnow applies patches produced bypnpm patch-commitwhen an edit removes the trailing lines of a file along with its newline. The install no longer fails withERR_PNPM_INVALID_PATCH("expected end of hunk") #12451.pnpm installnow preserves existingnode_modulesdirectories when a cross-device move reportsEXDEV#14504.pnpm installno 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.Interrupting
pnpm installwith Ctrl+C or SIGTERM no longer leaves a temporary lockfile (.pnpm-lock.yaml.*.tmp) behind in the project #1418.pnpm installnow relinks a direct dependency whose link innode_modulespoints to a missing target. Before, it reported "Already up to date" and left the broken link #9758.pnpm installuses less CPU when it links packages from a warm store. On Windows, a warm install could take several times longer than with pnpm 11 #15439.pnpm installnow runsnode --versiononce per run. A workspace whose projects keep their own lockfiles (sharedWorkspaceLockfile: false) previously ran the probe once or twice for every project, and on macOS the concurrent launches waited on each other, so a project could wait several seconds before its linking started.A repeat
pnpm install --frozen-lockfilewithnodeLinker: hoistedin a workspace no longer re-linksnode_moduleswhen nothing changed.Custom fetcher hooks no longer run a second time during installation when an archive was already fetched during dependency resolution #15025.
Fixed a package resolved by a
resolverspnpmfile hook installing without its own dependencies. This happened when the hook returned nomanifestand afetchershook handled the resolution #15552.pnpm install --prodand other installs that skipdevDependenciesno longer run thepnpm:devPreinstallscript #7065. They skippreparelifecycle scripts too, as do installs given package arguments.pnpm prune --prodand production installs now remove devDependencies whenlockfile: falseis configured #2677.pnpm install --prod,pnpm fetch --prodandpnpm deploy --prodno 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 sbomandpnpm auditleave it out of--prodresults too. The same applies to--dev. A peer that is not optional is still installed and audited #15344.pnpm installno longer skips optional dependencies that the Node.js version locked for adevEngines.runtimerange supports, when the range usesonFail: download. An explicitly setnodeVersionstill takes priority #14628.pnpm fetchnow also installs the pnpm version thatpnpm-lock.yamlpins, when it differs from the running pnpm. A laterpnpm install --offlinethat switches to the pinned version no longer fails because that version is missing from the store #11808.A dependency that ships a
binding.gypand setsgypfile: falseno longer gets thenode-gyp rebuildinstall script pnpm synthesizes for it. Such a dependency needs noallowBuildsentry and is no longer listed under "Ignored build scripts".pnpm installno longer addsallowBuildsplaceholder entries topnpm-workspace.yamlwhen it runs in CI or without a terminal. Interactive installs still add them #11574.pnpm now detects the same CI environments as pnpm 11, including AWS CodeBuild, which does not set
CI. On these servicespnpm installuses a frozen lockfile by default and fails withERR_PNPM_OUTDATED_LOCKFILEwhen the lockfile is outdated.Resolving and linking dependencies
Installing through a pnpr server now installs a project's peer dependencies when
autoInstallPeersis enabled. A project that declared only peer dependencies failed withERR_PNPM_OUTDATED_LOCKFILEor skipped its peers #14833.pnpm now installs a dependency that a package also declares as an optional peer dependency, for example
lightningcssin some vite builds. The dependency was missing fromnode_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
overridesnow 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.packageExtensionsandoverridesentries with a ranged selector (such as@<Xor@*) no longer match a dependency that has nopackage.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 withERR_PNPM_TRUST_DOWNGRADEeven when an older version satisfied its range.pnpm self-updatepicks its target version the same way. A request for an exact version still fails #14176.pnpm installnow re-resolves a dependency when its manifest range is updated from a prerelease to a stable version. The lockfile previously retained the prerelease version and caused--frozen-lockfileto fail #15528.pnpm install --ignore-pnpmfileno longer removespnpmfileChecksumfrom an up-to-datepnpm-lock.yaml.pnpm install --frozen-lockfile --ignore-pnpmfileno longer fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHwhen the lockfile records apnpmfileChecksum. A command that resolves dependencies with the pnpmfile ignored still writes the lockfile without it #10944.pnpm installandpnpm peers checknow use local tarball packages' actual versions when checking peer dependencies. Compatible packages no longer fail withstrictPeerDependenciesenabled.pnpm peers checkand the install-time peer dependency check now resolve peer dependencies from the workspace root whenresolvePeersFromWorkspaceRootis enabled #14982.autoDedupeandpnpm dedupenow move transitive dependencies to the version acatalog:dependency pins, as they already did for versions written directly inpackage.json. Previously they could move those dependencies to a higher version and keep both versions in the lockfile.pnpm dedupenow produces a stable lockfile when a dependency's range matches both a direct dependency and annpm:alias of the same package. The dependency resolves to the version of the direct dependency. Repeated runs previously alternated between two lockfiles #15588.Merging lockfiles now preserves recorded configuration fields such as
overrides,neverBuiltDependencies,patchedDependencies,packageExtensionsChecksum,settings, andcatalogs#8366.A lockfile entry whose resolution is unchanged now keeps its recorded
deprecatedmessage #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.Tarball URLs recorded in the lockfile now strip default HTTP and HTTPS ports (
:80and:443) #15539.node_modules/.package-map.jsonno longer contains entries that point at directories that do not exist. Such entries appeared for packages installed only with peer dependencies, most visibly withenableGlobalVirtualStore#14938.With
nodeLinker: hoisted,hoistWorkspacePackagesnow links each workspace project thathoistPatternorpublicHoistPatternselects into the rootnode_modules, unless a hoisted package or a root dependency already uses its name. The project's bins are linked into the rootnode_modules/.bin#7553.With
nodeLinker: hoisted,pnpm installnow removes the commands of the packages it removes fromnode_modules/.bin, such as a nested copy deduped into the rootnode_modules#7568.pnpm installno longer puts a dependency's bin onPATHfor that dependency's own lifecycle scripts before the bin's file exists. pnpm links such a bin after the dependency's build has run. It also removes such a bin left by an earlier install. This fixes installing thenodepackage on Windows #15501.Dependencies and executable binaries are now correctly linked and accessible for workspace packages using
publishConfig.directoryandpublishConfig.linkDirectory#8338.Bin linking leaves workspace and linked dependency files outside node_modules unchanged. Already executable bin files no longer receive redundant permission changes.
Workspaces and filtering
pnpm installnow finds workspace projects reached through a symlink, such as apackagesdirectory that links to a folder outside the workspace. It installs their dependencies, and the links in theirnode_modulesresolve #1044.A dependency declared with
catalog:now counts as a workspace dependency when its catalog entry points at a workspace project, for exampleworkspace:*#15587. WithlinkWorkspacePackagesenabled, so does annpm:alias of a workspace project, such as"math-alias": "npm:math@^1.0.0".pnpm -r runruns that project first.--filter <pkg>...selects it.A
workspace:dependency now resolves to a workspace project whose version is not valid semver, such as1or1.0.workspace:*,workspace:^, andworkspace:~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.3matches a project at0.5.6-next.3+f60facc#6483.Secondary dependencies now prefer the version resolved by the local project's direct dependencies over versions from sibling workspace projects #7191.
pnpm installnow 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-lockfilenow fails withERR_PNPM_OUTDATED_LOCKFILEwhenpnpm-lock.yamllists 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 -rnow installs every workspace project whenrecursiveInstallis set tofalseinpnpm-workspace.yaml#7504.pnpm installwith--filternow installs only the dependencies of the selected projects when usingnodeLinker: hoisted#8882.pnpm installnow updates an injected workspace dependency after that package's own dependencies change, whenshared-workspace-lockfileisfalse#7209.pnpm installnow copies the output of a workspace package's ownprepare,install, orpostinstallscript into the injected copies of that package. Before, the injected copies kept only the files that existed before the script ran.syncInjectedDepsAfterScriptsnow also works whenmodulesDiris set #9464.syncInjectedDepsAfterScriptsnow copies files into injected dependencies whennode_modulesis 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.A
modulesDirwith several path segments, such aswww/modules, now puts each workspace project's dependencies in<project>/www/moduleson both fresh and frozen installs, andpnpm binprints<project>/www/modules/.bin#15484.With
nodeLinker: hoisted, pnpm now installs the root project's dependencies into a custommodulesDirinstead ofnode_modules. With a custommodulesDir, the virtual store and itslock.yamlnow default to<modulesDir>/.pnpm.A repeat
pnpm installin a workspace with a custommodulesDirnow takes the up-to-date fast path. Before, pnpm looked for each workspace project's dependencies innode_modulesand ran a full install every time.pnpm now warns when a workspace install covers a project that has its own
pnpm-workspace.yaml. The nested file's settings, such aspatchedDependencies, do not apply when the outer workspace installs that project. pnpm reads settings only from thepnpm-workspace.yamlat the workspace root #11724.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 inpnpm-workspace.yaml#8718. It also selects projects that files were moved out of when git detects the move as a rename #15481.--filternow evaluates selectors in order, so later inclusion filters can re-include packages that an earlier exclusion filter excluded #9354.Adding, updating, and removing dependencies
pnpm addnow saves changes topackage.jsonbefore running lifecycle scripts, so a postinstall script failure leaves the added dependency inpackage.json#8627.pnpm addnow saves the requested exact version when adding a dependency, even when the manifest already contains a version range #6040.pnpm add <pkg>@<version>andpnpm update <pkg>@<version>now move the catalog entry onto the named version when the entry's range already covers it. For example,^7.22.17becomes^7.29.6, the same waypnpm update <pkg>moves an entry to the version it resolves #13715.pnpm addandpnpm installkeep an emptypeerDependencies,dependencies,devDependencies, oroptionalDependenciesfield that was already inpackage.json. pnpm still drops such a field when it removes the last entry itself, aspnpm removedoes #5096.pnpm updatenow keeps a version range whose shape has no save prefix, such as<= 3.0.0or>=1.0.0 <2.0.0, when the updated version still satisfies it. Before,<= 3.0.0became^3.0.0#6714.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 asminimumReleaseAgeused to reject that version before the update could replace it #9953.pnpm update --prodno longer installs devDependencies when run in a project installed with--prod#8038.pnpm update --interactive --workspacenow allows external dependencies to be updated.pnpm outdatedandpnpm updatenow applyminimumReleaseAgeto GitHub Actions.minimumReleaseAgeExcludeentries match action names such asactions/checkout#13923.pnpm removenow accepts--trust-lockfileand--no-trust-lockfileto control supply-chain policy checks while removing a package #14406.pnpm unlinknow removes thelink:dependency thatpnpm link <dir>added topackage.json. The linked package is removed fromnode_modulesand the lockfile. Alink:dependency to another directory is kept #4219.pnpm installnow prunes unreferenced catalog entries frompnpm-workspace.yamlwhencatalogPrune: trueis configured #15273.minimumReleaseAgeExcludePruneandtrustPolicyExcludePrunenow work in workspaces withshared-workspace-lockfile=false. Once every project has been installed, pnpm drops an entry only if no project lockfile records it. UndecidedallowBuildsentries are pruned the same way #14612.Exclude entries that pnpm writes to
pnpm-workspace.yamlnow match the file's list indentation and dominant quote style #15571, #15079.pnpm importnow converts dependencies that use Yarn'spatch:protocol. The dependency keeps the version it patches, and the patch file is added topatchedDependenciesinpnpm-workspace.yaml. If the patch file is missing, pnpm prints a warning and imports the dependency without the patch #10278.pnpm importin a workspace now keeps the versions pinned by the rootyarn.lock,package-lock.json, ornpm-shrinkwrap.jsonwhen another workspace project's range allows a newer version. Before, the root project got the newest version in its range #4385.pnpm patch,pnpm patch-commit, andpnpm patch-removenow work in a project of a workspace withsharedWorkspaceLockfile: false.pnpm patchfailed there withERR_PNPM_PATCH_NO_LOCKFILEafter a successful install. The reinstall after committing or removing a patch left the project's ownnode_modulesunchanged #9926.pnpm patch-commitnow resolves default patch directory locations when passed a package name or package specifier (such aspnpm patch-commit <pkg>orpnpm patch-commit <pkg>@<version>).pnpm patch-commitnow updates the lockfile snapshot and prunes removed dependencies when the patch modifiespackage.json#6866.pnpm patch-commitnow falls back to copying package files when hard linking fails.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
webServerdoes 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>andpnpm -r <command>now run a command installed in the selected projects' dependencies when none of them has a script by that name. This matchespnpm <command>in a single project.pnpm runwith--filteror-rstill reports the missing script #10151.pnpm execandpnpm dlxnow setnpm_execpath,INIT_CWD,npm_node_execpath, andNODEin child environments when Node.js is available. Stale inheritedNODEandnpm_node_execpathvariables are cleared when Node.js cannot be found on PATH #7037. Scripts thatpnpxandpnxrun now get pnpm itself asnpm_execpath. A script that ran$npm_execpath installthere ranpnpm dlx install.pnpm execnow sets thePWDenvironment variable to the directory the command runs in. Shells and tools that readPWDnow report the logical path of a workspace package reached through a symlink #1550.A script that runs
pnpm runno longer adds duplicatenode_modules/.binandnode-gyp-binentries toPATH#5352.Concurrent
pnpm runandpnpm execcommands now serialize their dependency installs #14551.pnpm runandpnpm execwithverifyDepsBeforeRunnow accept a moved project whose store is on the project's volume. Before, the check reported that the workspace structure had changed whenever the default store was not on the home volume.verifyDepsBeforeRunchecks now account for project-specificpackageConfigsoverrides in workspaces withsharedWorkspaceLockfile: false#15545.pnpm restartnow 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 dlxnow 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.pnpm pipelineno longer fails when run in a project outside a Git work tree or on a system withoutgit. Tasks in those projects run without caching, and pnpm prints a warning explaining why #15601.A
runtime:version range that contains||or a space, such as adevEngines.runtimeversion of^22.18.0 || ^24.0.0, now installs the requested runtime. pnpm used to install the npm package with the same name, such asnode#14817.When the configured
scriptShelldoes 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.A script killed by a signal now fails with an error that names the signal, such as
Command failed with signal SIGKILL.#9821.Publishing, packing, and deploying
pnpm publishnow resolvesworkspace:dependencies from workspace manifests whennode_modulesis not installed. Previously, publishing withoutnode_modulesfailed withERR_PNPM_CANNOT_RESOLVE_WORKSPACE_PROTOCOL#6567.pnpm publishnow honorspublishConfig["@scope:registry"]for a package in that scope. It takes precedence over the registry set for the same scope in.npmrcand overpublishConfig.registry#12071.pnpm packandpnpm publishnow include bundled dependencies when using the isolated linker. This covers workspace packages and the dependencies of each bundled package. Bundled dependencies are also included whenpublishConfig.directoryselects 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 packleaves out symlinks that point outside the package #8208.pnpm packnow preserves file executable permissions in the packed tarball when source files are executable on disk.pnpm publishandpnpm packnow report a missingversionornamefield on a workspace dependency. Previously, pnpm reported that the dependency was not installed #4164.pnpm publishandpnpm packnow report an error when a bin script has a shebang line ending with CRLF #7311.pnpm deploynow copies thepackageManageranddevEngines.packageManagerfields of the workspace rootpackage.jsoninto the deployedpackage.json, unless the deployed project pins a package manager itself #9079.pnpm deploynow puts the virtual store atvirtualStoreDir, resolved against the deploy directory. A shared-lockfile deploy recordsvirtualStoreDirin the deployedpnpm-workspace.yaml. With the global virtual store enabled or an absolutevirtualStoreDir, the deploy still usesnode_modules/.pnpm#8787.pnpm deploynow respects--package-import-methodpassed on the command line and reports the package import method correctly #7593.pnpm deploydoes not run thepreparescripts of the deployed project #7282.pnpm deploy --legacyno longer rewrites the source workspace'snode_modules/.pnpm-workspace-state-v1.jsonto describe only the deployed project #15352.Manifests and configuration files
pnpm now reads and updates
package.json5project manifests. Manifest updates retain comments, and workspace discovery preferspackage.json, thenpackage.json5, thenpackage.yaml#15129.pnpm packincludes exactly onepackage.jsonin the archive when the project uses an alternative manifest format, even when.npmignoreorfilesexcludes the source file.Git-hosted dependencies that use a
package.yamlorpackage.json5manifest now honor itsfilesfield #7906.Fixed
pnpm versionfailing on projects using apackage.yamlmanifest.Fixed
pnpm initcreating an extrapackage.jsonwhenpackage.yamlis already present.pnpm versionnow applies pending bumps to private workspace packages. A private package's changelog is written to its committedCHANGELOG.md, also whenversioning.changelog.storageisregistry#13736, #13519.pnpm initnow supports the--bareoption. It creates apackage.jsonfile with only the required fields #15538.The
reportersetting is now honored when it is configured inpnpm-workspace.yaml, the global configuration, or thePNPM_CONFIG_REPORTERenvironment variable. Configuredreporter: silentmakes silent output the default. An explicit--reportertakes precedence #4879..npmrcandpnpm-workspace.yamlfiles now support npm's${VAR?}placeholder. It expands to the value ofVAR, or to an empty string without a warning whenVARis unset #14404.pnpm now expands environment variables in
_auth.authTokenvalues loaded from globalconfig.yamlandpnpm_config__auth.pnpm now keeps the configured default registry when
_authholds 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 parses the first setting in a
.npmrcthat starts with a UTF-8 byte order mark. Previously, the leading byte order mark caused the first line's key to be ignored #15353.pnpm now prints a warning when a
.npmrc,auth.ini, or the file set bynpmrcAuthFileexists but cannot be read. The settings in such a file were ignored without any message. A.npmrcthat contains invalid UTF-8 is now read #5065.pnpm config setandpnpm config deletenow preserve comments and repeated keys such asca=in.npmrc#14851.pnpm loginnow 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, asnpm logindoes #12055.Commands that do not use the store no longer create a temporary file in the project directory when they load their settings. These include
pnpm view,pnpm config,pnpm root,pnpm bin,pnpm exec,pnpm run, the script shortcuts such aspnpm test, and the registry commands such aspnpm whoami,pnpm dist-tag, andpnpm search.pnpm exec,pnpm run, and the script shortcuts still create one in projects that declareconfigDependencies.Global packages, pnpm versions, and runtimes
Global commands such as
pnpm add --global,pnpm list --global, andpnpm bin --globalnow run with the pnpm you invoked, even in a project that pins another pnpm version. Previously, a pin withonFail: "download"switched them to the pinned pnpm, and a pinned pnpm 10 or older failed because its global bin directory was not inPATH#14531.pnpm add -g,pnpm update -g, andpnpm remove -gno longer fail withERR_PNPM_PACKAGE_MANIFEST_IO_ERRORwhen 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 --globalnow skips a global package installed from afile:path that no longer exists, prints a warning, and updates the remaining global packages. Previously the whole update failed withERR_PNPM_LINKED_PKG_DIR_NOT_FOUND#12533.pnpm self-updaterun in a project that pins pnpm throughpackageManagerordevEngines.packageManagernow also updates the global pnpm, as it does outside a project #14747.pnpm self-updateno longer leaves the previous pnpm in the global packages when it was installed as@pnpm/exe.pnpm ls -gnow lists a single pnpm #14709.pnpm setupnow installs pnpm under the package namepnpmtoo, so both commands leave the same shims in the global bin directory. After a self-update on Windows, PowerShell ran pnpm throughpnpm.cmdand asked "Terminate batch job (Y/N)?" on Ctrl+C #15567.pnpm setupfailed withText file busy (os error 26)when$PNPM_HOME/binalready heldpn,pnpx, orpnxas links to the running pnpm executable. It now replaces those files and completes #15494.pnpm setupno longer deletes aliases and other lines that sit between a# pnpmcomment and the pnpm block in a shell startup file #7067.When pnpm switches to the version a project pins, the
minimumReleaseAgeapprovals for that version are now added tominimumReleaseAgeExcludein the project'spnpm-workspace.yaml. A project without that file gets one. Global commands leave the project's settings unchanged #15396.pnpm env removenow cleans up dangling Node.js executables and shims. Surviving global commands remain intact.Node.js runtime resolution now supports Windows ARM64. Node.js 20 and newer resolve native
win-arm64builds, and older versions fall back towin-x64under emulation #7123.Windows and WSL
On Windows,
pnpm cleanand installs no longer fail immediately when another process uses a package innode_modules. pnpm waits up to a minute for an open file. It waits up to 5 seconds for a running program #15081.pnpm installin WSL now waits out Windows file locks on a Windows drive such as/mnt/c, as it already does on Windows. Before, an antivirus or indexer scan holding a file open could fail the install withEACCES#6155.On Windows, pnpm now retries saving
pnpm-lock.yamlfor up to a minute while another process holds the file open. The save used to fail at once withEPERM,EBUSY, or "Access is denied" #9461.pnpm now escapes trailing dots and spaces in
node_modules/.pnpmdirectory names. Windows strips these characters, so a dependency such as"parent-pkg": "file:../"created a directory that could not be deleted or failed to install #8101.On Windows, bin shims run from Git Bash, MSYS2, or Cygwin now pass
NODE_PATHto Node.js as Windows paths. A project installed from cmd or PowerShell gave its bins aNODE_PATHunder the Git install directory when they ran from Git Bash. Installing again replaces the shims already innode_modules#3360.Fixed scripts failing with errors such as
'an-compile' is not recognizedwhenscriptShellis set tocmd.exeon Windows #7181.On Windows, the error for a
node_modulesdirectory that pnpm cannot move out of the way now names the directory and says that a file in it is probably in use by another process #7505.Inspecting dependencies
pnpm auditandpnpm audit signaturesnow 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 auditnow 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 --fixnow prunes redundant overrides when one vulnerable range is a subset of another for the same package #8577.Running
pnpm listinside a workspace package without--recursiveor a filter now lists only the current package #14494.pnpm licenses listdoes the same. Use--recursiveor--filterto list the licenses of other workspace projects #5689.pnpm list --only-projectsnow prints every project selected with--filteror--recursive, including a project that has no workspace dependencies #9770. It also lists the workspace projects whensharedWorkspaceLockfileisfalse#7151, and a project that setspublishConfig.directory#10635. It no longer reports packages innode_modulesthat are missing from the lockfile #9528.pnpm licenses listfailed or reported nothing in a workspace withsharedWorkspaceLockfile: false. It now reads the lockfile of each selected project #10140.With
nodeLinker: hoisted,pnpm licenses listreported every license asUnknownand listed paths undernode_modules/.pnpmthat do not exist. It now reads each package from the directory where the hoisted linker placed it #8589.pnpm outdatedandpnpm -r outdatednow fail withERR_PNPM_NO_PACKAGE_IN_DEPENDENCIESwhen a requested package selector does not match any dependency in the inspected projects #2319.pnpm -r outdated --jsonnow 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 examplevue@2.7.14 (dev)#7693.pnpm sbomfiltered to a single workspace project now takes theauthor,description,license,repository, andbugsfields from the workspace rootpackage.jsonwhen the project does not declare them. A field the project declares is never taken from the root, even when it is blank ornull#14882.pnpm store statusno longer reports a package as modified when it has build or postinstall scripts, peer dependencies, or skipped optional dependencies #15383. When packages were mutated, it now lists only those packages and no longer suggests runningpnpm install --force#919.pnpm peers checkand theERR_PNPM_PEER_DEP_ISSUESerror now group peer dependency issues under the workspace project they were found in #15351.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 #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.Fatal peer dependency errors and their hints are now written to stderr #5419.
pnpm runwith--loglevelset towarn,error, orsilent(or the sameloglevelsetting) no longer prints the$ <command>line before a script, nor the summary of the install thatverifyDepsBeforeRunruns first. Both are info-level output #8944.pnpm runandpnpm execnow printNo projects matched the filters in "<workspace>"when--filterselects no project #8408.pnpm dedupenow counts each package reused from the store once in its progress output #15303.pnpm addnow warns when replacing an existing dependency with a specifier pointing to a different source #14869.pnpm linknow warns when linking a package that declares one or more peer dependencies, explaining that the linked dependency will not resolve peer dependencies from the targetnode_modulesand suggesting thefile:protocol instead.pnpm importnow warns when package.json lists projects in a "workspaces" array and there is no "pnpm-workspace.yaml". Without that file, the import writes a lockfile for the root project only #5240.Bash completion now completes script names that contain a colon, such as
pnpm run test:utopnpm run test:unit#5482.Platinum Sponsors
Gold Sponsors
v12.6.0: pnpm 12.6Compare Source
pnpm 12.6.0 ships with automatic dependency deduplication, relocatable node_modules, package.yaml manifest editing, and --save-types support.
Minor Changes
autoDedupededuplicates compatible dependency versions during installation #7258. Enable it inpnpm-workspace.yamlor usepnpm install --auto-dedupeorpnpm add --auto-dedupe. Frozen installs leave the lockfile unchanged.pnpm install,pnpm run, andpnpm execon macOS and Linux now reuse anode_modulesdirectory and bin shims that moved or were copied together with their project #6937. The first command after the move checks the tree and records its new location, so project commands innode_modules/.binkeep working.pnpm add --save-typessaves available@types/*packages indevDependenciesalongside registry dependencies #3868. Packages that declare bundled TypeScript types are skipped. SetsaveTypes: trueinpnpm-workspace.yamlto enable this by default.package.yamlmanifests can now be updated bypnpm add,pnpm update,pnpm remove,pnpm pkg,pnpm link,pnpm set-script, andpnpm version#2008. Existing comments and key order are preserved.Catalog entries can now use the
file:andlink:protocols #8642. A relative path or bare path in an entry, such as./tarballs/foo.tgz, is measured from the directory holdingpnpm-workspace.yaml.pnpm tasks statuslists running and waiting tasks in each concurrency group, and waiting tasks now take available slots in arrival order with higherprioritytasks going first #15208. If workspaces use different limits for the same group, a later task can take a free slot that earlier tasks cannot use. A package script namedtaskstakes precedence; usepnpm pm tasks statuswhen that script exists.pnpm cache prunedeletes registry metadata cache directories that this version of pnpm can no longer read #15046.pnpm cache prune --dry-runlists what it would delete without removing anything.macosBackup.excludeModulesDirandmacosBackup.excludeStoreDiron macOS can now exclude newly created modules, virtual-store, and package-store directories from Time Machine #6440. Set either totruein global configuration or using thePNPM_CONFIG_MACOS_BACKUP_EXCLUDE_MODULES_DIRandPNPM_CONFIG_MACOS_BACKUP_EXCLUDE_STORE_DIRenvironment variables.pnpm add --tildeis now an alias for--save-prefix=~#12863. The Yarn-Tshorthand is not supported.progresssetting and--no-progressoption now turn off dependency and download progress lines #14065. Warnings, lifecycle output, and the dependency summary are still printed.Patch Changes
Security
POSIX bin shims now take
cygpathandwslpathfrom the system default path on Cygwin, MSYS2, and WSL2 so a dependency cannot redirect another package's shim #14866.pnpm installwarnings no longer carry the text of a package's deprecation notice, naming only the deprecated package and version #15099. A deprecation warning names the newest non-deprecated version when one exists, and control characters and line separators are stripped from package identifiers and warnings.pnpm installand other commands that report configuration warnings now warn when environment variables in project.npmrccredentials are ignored #15051.Installing packages
pnpm install --frozen-lockfilenow succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile #3960.pnpm install --frozen-lockfileno longer installs dependencies of projects removed frompnpm-workspace.yaml#15248. Missing local tarballs used only by those projects no longer fail the install.pnpm cinow emptiesnode_modulesbefore installing in a project that declares acleanscript #15276.pnpm install --forcenow re-imports every package into the virtual store #15030 and removes obsolete dependency links inside virtual-store packages when their dependencies change #15039.preinstallscript for the root project now runs before dependencies are resolved and linked #3760.pnpm installnow runspnpm:devPreinstallwhen the root project usespackage.yaml#15168.pnpm installnow enforces the root project'sengines.noderange whenengineStrictis enabled #3016.pnpm installnow uses the running Node.js whendevEngines.runtimedeclares a range withoutonFail: download#15230.pnpm installno longer hangs when a git dependency is fetched over SSH and ssh prompts for a passphrase or host key confirmation, running ssh in batch mode instead #2227.pnpm installnow installs git-hosted dependencies without preparing them when their builds are explicitly denied byallowBuilds#10522.pnpm installnow reuses an in-flight tarball download when another resolution of the same archive still needs itspackage.json#15037.pnpm install --prodno longer downloads registry packages that only a devDependency reaches #881.pnpm install --no-runtime --frozen-lockfilewithnodeLinker: hoistedno longer fails on repeated runs with a broken lockfile #15212.Resolving and linking dependencies
pnpm installandpnpm updatenow resolve a dependency range to the newest matching version that is not deprecated #15128.pnpm add <pkg>without a version now uses the catalog entry when the workspace already catalogs that package #14865.pnpm installnow links workspace dependencies declared with plain version ranges whenexcludeLinksFromLockfileandlinkWorkspacePackagesare enabled #15133.pnpm installnow resolves local tarball dependencies whose absolutefile:paths contain..consistently and skips reinstallation on repeat installs #15190.pnpm installnow installs dependencies when a custom resolver returns a local or git-hosted tarball without a manifest #15016.pnpm.overridesentries written as a bare path, such as./local-dep, are now measured from the directory holdingpnpm-workspace.yaml#11131.pnpm update --no-saveno longer bypasses version-scoped overrides when a dependency selector specifies a version #14923.pnpm peers checkand strict peer dependency checks no longer reject compatible versions from named registries #15225.pnpm outdatedandpnpm update --interactive --latestnow include named-registry dependencies such aswork:2.1.0and preserve their registry prefix #15226.Workspace projects selected by
hoistPatternorpublicHoistPatternare now hoisted on every install #3642.Workspace packages with SemVer build metadata are no longer skipped when they match the requested range and have the same version precedence as the registry package #2812.
Sped up
pnpm dedupeandpnpm installin projects with many convergence overrides by checking overrides concurrently #15175.minimumReleaseAgeis no longer skipped for packages served by registries returning matching ETags for abbreviated and full package metadata #14925.Running scripts and tasks
pnpm runsignal handling no longer delivers a redundant secondSIGINTto child scripts onCtrl+Cin a terminal, and properly forwards termination signals when running non-interactively without a terminal #7374.pnpm runandpnpm execin workspaces withsharedWorkspaceLockfile: falsenow verify dependencies in the selected projects rather than expecting a root workspace state #15272.pnpm testnow forwards--filterarguments to the test script when the option follows the shortcut #15217.Recursive runs now start scripts matched by a
/pattern/selector in parallel withinworkspaceConcurrency#14933.pnpm deploy,pnpm rebuild,pnpm rb, andpnpm setupnow prefer apackage.jsonscript of the same name #14976.modulesDircustom directory names now support executable lookup and CommonJS plugin resolution acrosspnpm run,pnpm exec,pnpm versionhooks, and lifecycle scripts #3604.pnpm install-testnow accepts--no-baildirectly and in recursive runs #3777.Workspace and project configuration
pnpmcommands run in a project not included in the workspace now act on that project alone #3561.pnpm-workspace.yamledits now preserve scalar YAML anchors and aliases #8245.pnpm-workspace.yamlnow expands environment variable placeholders with fallback syntax in enum-valued settings such asnodeLinker#14914.pnpmfileconfiguration now loads a.jsfile as CommonJS or an ES module, following the nearestpackage.json#15141.updateConfighook settings are now honored bypnpm peers check,why,list,ll,licenses,audit,sbom,fetch,patch,patch-commit,patch-remove,approve-builds, andruntime#15047, #15049.readPackagehook changes or removal now take added dependencies out ofpnpm-lock.yamland update dependencies when an existing lockfile is present #3735, #15136.package.yamlprojects now record their pinned pnpm underpackageManagerDependenciesinpnpm-lock.yaml#15167.packageManagerDependenciespinning@pnpm/exebesidepnpmis no longer rewritten inpnpm-lock.yaml#14926.pnpmnow preserves CRLF line endings when modifying project manifests #3529.loglevelsetting is now honored when configured inpnpm-workspace.yaml, global configuration, orPNPM_CONFIG_LOGLEVEL#3122.storeDirvalues loaded from global configuration orPNPM_CONFIG_STORE_DIRnow expand a leading~/to the user's home directory #6560.--shared-workspace-lockfilenow produces a warning when passed on the command line outside a workspace #1617.Windows
pnpm installon Windows now runs dependency build scripts from long global virtual store paths and normalizes scoped package paths in lifecycle scriptPATHentries #15111.pnpm installacross projects sharing a global virtual store on Windows no longer fails withAccess is denied, file-exists errors, or transient sharing violations #15114, #15176, #15171.pn,pnpx,pnx, andpnpmnow run when Git Bash, MSYS2, or Cygwin launches them through a Windows path #14884.pnpm dlxnow reuses cached packages when Windows creates directory junctions for its cache links #15171.pnpm pipeline --watchnow resolves Windows short paths so multiple path representations share the build cache #15105.CLI commands and output
pnpm removenow runs the project's ownpreuninstall,uninstall, andpostuninstallscripts #3276.pnpm remove -rnow fails before modifying manifests if any requested dependency is absent from all selected projects #2319.pnpm update --peernow updates ranges inpeerDependencies#8081.pnpm updatenow movesdevEngines.runtimeandengines.runtimeversion ranges to the resolved Node.js version #14988.pnpm update -gno longer reinstalls unchanged packages #12002.pnpm add -g,pnpm update -g, andpnpm remove -gnow recover a global package group whosenode_modulesdirectory was deleted #15093.pnpm add -gnow installs local tarballs whenPNPM_HOMEcontains..path segments #15118.pnpm versionnow readstagVersionPrefixfrompnpm-workspace.yaml, global config, orPNPM_CONFIG_TAG_VERSION_PREFIXwhen creating and reading Git tags #15044.pnpm publishnow allows a detached Git HEAD in CI environments #5894.pnpm store prunenow removes unreferenced files and packages from the content-addressable store #3635, as well as expired or supersededpnpm dlxcache data #15171.pnpm cache list-registriesnow prints decoded registry URLs #15046.pnpm deployno longer triggers an install when running scripts in a read-only deployed filesystem #11617.pnpm -r list --jsonnow outputs a single JSON array whensharedWorkspaceLockfileisfalse, and--longand--parseableread each project's own modules directory #15011.pnpm sbomnow validates SPDX identifiers and expressions before emitting them as CycloneDX license IDs or expressions, falling back to a license name for non-SPDX values such asUNLICENSED#14786.pnpm change checknow validates pending change intents in.changeset/#15183.pnpm --filterandpnpm -Fshell completion now suggests workspace package names #15216. Completion candidates containing control or invisible formatting characters are omitted so package and script names cannot inject terminal escape sequences.pnpm runandpnpm run-scriptshell completion now suggests package scripts #15034.pnpm --versionno longer creates a temporary file in the project directory during store detection #15264.pnpm setupnow describes displayed configuration changes as "The following configuration changes were made" #15100.minimumReleaseAgeapproval prompts inpnpm installandpnpm update -gnow count and display each package version once #15083, #15091..npmrcauthentication warnings now report when an empty environment variable removes an auth token and name the affected key #4806.The install summary now names the version each dependency resolved to when
node-linkerishoisted#15161.pnpm installnow re-links a package's global virtual store slot afterallowBuildschanges #15117.Platinum Sponsors
Gold Sponsors
v12.5.1: pnpm 12.5.1Compare Source
Patch Changes
pnpm now reports an unknown task setting in
pnpm-workspace.yamland carries on. It used to refuse to start, so a project could not use a task setting that only the pnpm version itspackageManagerpins reads. The setting is still an error when the running pnpm is that pinned version.Python interpreter installation now retries historical release metadata requests. It caches the release list for up to 24 hours and refreshes it once after a lookup miss. When a release omits the current platform, the search samples at most eight other releases before reporting that the lookup is inconclusive.
Python
registriesentries now route packages by exact names or trailing-prefix patterns inpackages. Registry declaration order no longer affects resolution. A matched package resolves exclusively from its assigned registry, including transitive and build dependencies. Usepackages: ["*"]to declare the default index.pnpm installno longer fails with "Too many levels of symbolic links" when a Cargo configuration file above the workspace is a symlink, such as a~/.cargo/config.tomllinked from a dotfiles repository.pnpm installnow returns "Already up to date" in a workspace wherededupeDirectDepsleft a project without anode_modulesdirectory of its own. Such a project forced a full install on every run.pnpm installno longer refuses the repeat-install fast path just because a changedpnpm-lock.yamlis 16 MiB or larger. Such a lockfile forced a full install on the run after every change.Platinum Sponsors
Gold Sponsors
v12.5.0: pnpm 12.5Compare Source
pnpm 12.5.0 makes Python a first-class ecosystem, accepts Package URLs in
pnpm add, names whole platforms insupportedArchitectures, and gives tasks machine-wide concurrency limits. It also fixes an install that could reuse one package's downloaded tarball for another.Minor Changes
Installing packages
pnpm addaccepts a Package URL in place of a package name.pnpm add pkg:npm/express@4.18.2savesexpresstopackage.json.pnpm add pkg:cargo/serde@1.0.188savesserdetoCargo.toml.pnpm add pkg:pypi/requests@2.31.0savesrequeststopyproject.toml.pkgis now a reserved specifier prefix, whatever case it is written in, so a named registry can no longer be calledpkg.A
registriesentry can now name the ecosystem it serves.ecosystemacceptsnpm,cargoandpypi. An entry that does not name one serves npm, as every entry did before.An ecosystem with several indexes searches them in the order they are declared. The first index that has a package supplies it, so the one declared last answers what none before it had.
A
registriesentry may not carry credentials. pnpm reads them from.npmrc, matched by origin, for a PyPI index as for every other package source.Configuring pnpm
supportedArchitecturesnow accepts a list of platforms, in place of theos,cpuandlibcaxes.An install prepares for the platforms the list names, and for those only. A platform reads as
<os>-<cpu>, with a C library on Linux, as inlinux-x64-muslorlinux-x64-manylinux_2_28. The Rust target triple of the same machine is accepted too, sox86_64-unknown-linux-gnunames the platformlinux-x64names. A Linux platform that names no C library is the glibc platform.currentis the platform the install runs on.The
os,cpuandlibcmapping keeps working and keeps its meaning.Added concurrency groups for tasks. A task in
pnpm-workspace.yamlcan name aconcurrencyGroup. The newconcurrencyGroupssetting gives each group a limit. At most that many tasks of the group run at once on the machine, counted across every pnpm process,pnpm pipelineincluded. A task past the limit waits for a running one to finish. A script that callspnpm runfor a task of the same group runs under the slot its parent holds.toolsnames the programs pnpm downloads, andmirrorsays where each one comes from.node,bunandpythoncan be named. Any other tool is refused.mirroris the base a tool's own layout hangs off.channelssends one release channel elsewhere. A channel neither it nornode-mirror:<channel>names is left tomirror. Onlynodepublishes channels, so naming them for another tool is refused.Set it in the global
config.yamlor inPNPM_CONFIG_TOOLS. Apnpm-workspace.yamlthat names a tool mirror is ignored.pnpm pack-appdownloads the Node.js it embeds throughtools.node.node-mirror:<channel>keeps working and names the same thing as an entry underchannels.Python interpreters and environments
pnpm installnow chooses a Python interpreter for each project instead of installing every project with one interpreter #14945. A project is installed with the first interpreter on the machine that itsrequires-pythonaccepts, so a workspace can hold projects that support different Python versions. pnpm reads.python-versiontoo, and prefers the version it asks for. Setpython.executableinpnpm-workspace.yamlto name one interpreter for every project.pnpm installnow installs a Python interpreter when no interpreter on the machine fits the project #14945. The builds are python-build-standalone's, which uv and rye install too. One interpreter is shared by every project on the machine, and a later install uses it without downloading anything.runtimeOnFaildecides what an install with no interpreter that fits does, the way it does for a Node.js runtime.errorreports the project instead of installing one.warnandignoreinstall with an interpreter the machine has that the project'srequires-pythonrejects.tools.python.mirrornames a mirror.Python environments now live in the store. Each project keeps only its
.venvlink, which points at the project's current environment generation underpython-envsin the store. A repository with many Python projects no longer holds a.pnpm/python-envsdirectory in each of them. The next install relinks a.venvthat an earlier release published. The old.pnpm/python-envsdirectory is left in place, since a running program may still use it, and can be deleted once none does. WithfrozenStoreset, pnpm writes nothing to the store, so environments stay in the project's.pnpm/python-envs#15014.Python environments now use
packageImportMethodto import wheel files from the store. Useclone-or-copyfor copy-on-write clones with a copy fallback, orcopyfor independent files. Hardlinked files share writes with the store and other environments.Isolated Python build environments keep backend writes private with copy-on-write clones or copies.
Python projects and workspaces
pnpm installnow installs a Python project's own package, so the project can be imported and the commands in[project.scripts]run right after an install #14945. The installed package points at the source tree, so an edit to a module takes effect without another install. pnpm installs the package of a project that declares a[build-system].tool.uv.packageoverrides that either way.pnpm installnow installs a Python project in the workspace from its own source. Declare it under[tool.uv.sources], asshared = { workspace = true }orshared = { path = "../shared", editable = true }. pnpm builds the project with the backend it declares. It installs the build editable, so an edit to the project takes effect without another install.Approve the build backend under
allowBuildsinpnpm-workspace.yamlas a Package URL, aspkg:pypi/hatchling: true. An install that has not approved a backend does not build the projects that need it. The message names the key to add.pnpm installnow refuses a requirement that names a project in the workspace when nothing declares where it comes from. It used to take that name from the index.The members of a uv workspace can now share one Python environment. Set
shared-environment = trueunder[tool.pnpm.python]in thepyproject.tomlthat declares[tool.uv.workspace].pnpm installthen resolves every member as one graph into onepylock.tomland one.venvat the workspace root. Two members that require versions of a distribution no release satisfies at once are refused with an error naming both. Each project still gets an environment of its own by default #15015.Python projects can now select extras and dependency groups through
[tool.pnpm.python]inpyproject.toml#14945. Workspacepython.extrasandpython.groupsdefaults now skip names a project does not define.pnpm installnow reads dynamic Python project metadata from the build backend #14945. Projects with only arequirements.txtfile now get a Python environment and lockfile.Python dependencies and lockfiles
pnpm can now resolve
pylock.tomlfor several platforms and Python versions at once.supportedArchitecturesnames the platforms to lock for andpython.versionsthe versions. Every platform is paired with every version. One committed lockfile then serves Linux CI and macOS or Windows contributors #14945.The lockfile pins the wheel each environment takes for a distribution. It marks a package only some environments install.
pnpm installtakes the packages and wheels of the environment its interpreter matches, and refuses an interpreter none of them stand for. pnpm resolves a project that declares environments itself, not through the serverpnprServernames. Naming neither setting locks for the interpreter running the install.python.overridesandpython.constraintspin the versions a Python resolution may pick #14945. pnpm reads uv's own overrides and constraints frompyproject.tomltoo.pnpm installnow supports Python dependencies from Git repositories #14945. Direct wheel URLs are also supported. Sources can be declared in[tool.uv.sources]. Git dependencies requireallowBuildsapproval.pnpm installcan install a Python release that publishes no wheel this interpreter accepts, by building the source distribution the index serves beside it #14945. The archive is pinned inpylock.tomlby name and SHA-256. A later install replays it from the store, offline included. Building a source distribution runs the release's own build backend. Approve it withpkg:pypi/<distribution>: trueunderallowBuilds.A resolution that finds no version of a distribution now says why. It tells apart a distribution no index publishes, one whose releases publish nothing this interpreter can install, and one whose versions the project's requirements exclude.
Patch Changes
Installing packages
pnpm no longer reuses one package's downloaded tarball for another package whose resolution pins a different integrity hash to the same URL #15021.
pnpm installandpnpm addnow report an error whenpackage.json,pnpm-lock.yaml,pyproject.tomlor another file they snapshot before installing is a named pipe or a device. The command used to wait forever for something to write to it.pnpm install --prodandpnpm install --devnow record every dependency group inpnpm-lock.yaml.node_modulesstill holds only the groups the filter selects. They used to write the filter into the lockfile, so a laterpnpm install --frozen-lockfilerejected it.pnpm prune --prod,pnpm prune --dev, andpnpm prune --no-optionalbehave the same way #14912.POSIX bin shims now convert a Windows-form path such as
C:\node_modules\.bin\tsccorrectly. The shim mangled the backslashes in such a path and could not reach the package it runs. Installing again replaces the shims already innode_modules#14867.Two pnpm processes installing one workspace at the same time no longer fail on Windows with "Access is denied" while writing
node_modules/.pnpm-workspace-state-v1.json. The write now retries the transient lock the other process holds, as pnpm's other file writes do.pnpm now reads the manifest from the tarball when a pnpmfile
resolvershook returns a resolution without one. Such a package installed alone, with none of its own dependencies and no warning #15000.pnpm installnow merges Git conflict markers inpnpm-lock.yaml. It parses both sides of the conflict and keeps the versions they locked. A conflict in the config dependencies recorded at the top of the lockfile is merged too #14880.Cargo projects
pnpm installcan now generateCargo.lockfor workspaces with path or Git[patch]and[replace]overrides. Adding, removing, and updating crates also preserve these overrides #14950.Cargo lockfile resolution blocks unsupported Git transport helpers declared by transitive dependencies.
pnpm installnow vendors recursive Git submodules for Cargo dependencies at their pinned commits. Cargo builds can use these sources offline. Set Git'sprotocol.file.allowtoalwaysto fetch local file submodules. pnpm fetches cached Git crates again on the first online install #14951.pnpm installnow generatesCargo.lockfor workspaces with Git dependencies, including a dependency that omits a package version. It also downloads the Rust standard library's dependencies when Cargo configuration enablesbuild-std#14944.pnpm installnow handles weak Cargo features, writtencrate?/feature. Resolution failed when one dependency turned on an optional crate and another asked for a weak feature of it #14960. The generatedCargo.locknow also includes the dependencies weak features reference, which Cargo rejected with--lockedfor crates such asuuid#14978.pnpm installnow generatesCargo.lockwhen a crate version it considers depends on a release the registry carries only as yanked. pnpm rules that version out and resolves the rest of the graph. Resolution failed with an error such asno non-yanked version of napi-build satisfies ^3.0.0-beta#14952.pnpm installnow falls back to an older semver-incompatible version of a crate when the newest one a dependency range allows cannot be resolved. Ranges such as>=1, <3span several of them #14962.Python projects
pnpm installnow honors uv workspace members when discovering Python projects. When no uv workspace declares a project, pnpm skips projects under conventional example, demo, documentation, template,test,tests, and test fixture directories #15058.pnpm install --filter <selector>now installs only the Python projects the selection asks for. A Python project that shares a directory with an npm workspace project is selected with that project. A Python project in a directory of its own is selected by the distribution it declares, by its path, or through the[tool.uv.sources]entries that reach it. Under--fail-if-no-match, a selector that names only a Python project is a match.pnpm add --filter <selector> pypi:<package>writes the requirement to every selected project #14945.pnpm installnow installs wheels whoseRECORDhashes disagree with their contents. The wheel archive's locked SHA-256 hash remains verified. pnpm writes correct hashes to the installedRECORD#15061.pnpm installnow installs a Python wheel whoseWHEELfile lists tags that differ from the ones in its filename. A wheel whose filename tags were changed after the build, such asmysql-connector-python, was rejected #14945.A Python release whose wheel metadata declares a requirement pnpm cannot read no longer fails the install. pnpm now resolves the project against the other releases of that package, and reports the unreadable requirement when none of them works.
pnpm installno longer fails when a Python index lists a file pnpm cannot use, such as a release with no SHA-256 digest or an unreadable wheel filename. That file is left out and the project resolves against the remaining releases.pnpm add pypi:<package>in a directory that has nopyproject.tomlnow names the missing file and says where to run the command. It used to fail with a bareNo such file or directory (os error 2)#14945.Performance
pnpm auditno longer hangs on dependency graphs with many shared dependencies #15005.Sped up
pnpm installin Python workspaces with many projects. Projects now prepare concurrently. Projects with identical registry requirements also share fresh dependency resolutions #14945.Repeat installs through the Node-API bindings now return "Already up to date" when the project manifests still match
pnpm-lock.yaml. Before, every such install reinstalled the whole tree. An install also no longer reinstalls whenpnpm-lock.yamldiffers from the installed dependencies only by packages no project depends on or by top-level keys pnpm does not define.Other commands
pnpm deploynow links commands exposed by workspace dependencies into the deployed project'snode_modules/.bindirectory #14899.pnpm dlxandpnxnow prompt to approve dependency build scripts in interactive terminals #14943. Cached packages with pending builds also prompt for approval. Without an interactive terminal, use--allow-buildto allow the required builds.pnpm add -gandpnpm update -gnow ignore incomplete unrelated global package groups when every command from the replaced group is retained. Operations that could remove a global command still require complete ownership information.pnpm packnow writes tarball entries grouped by file extension and file name, the order npm uses. Packages that ship many same-named files, such as template collections, pack much smaller #14766.pnpm outdated --longfills the Details column with the package homepage again #14886.Platinum Sponsors
Gold Sponsors
v12.4.2: pnpm 12.4.2Compare Source
pnpm 12.4.2 includes security fixes for executable shims and GitHub Actions links, more reliable installs, faster peer dependency checks in workspaces, and Python lockfiles that work across compatible targets.
Patch Changes
Security
Dependency executables can no longer take over another package's POSIX bin shim through its shell helpers. Reinstall dependencies to replace existing shims #14837.
On Cygwin, MSYS2, and WSL, shims still use
PATHfor Windows path conversion, so dependency executables can still redirect them there.GitHub Actions homepage links no longer expose server credentials. GitHub server URLs now require HTTPS, with HTTP allowed only for loopback hosts.
Installing packages
pnpm no longer crashes at startup on FreeBSD and other Unix-like platforms. Platforms other than Windows and macOS use
~/.local/share/pnpm/storeby default #14859.pnpm installon Windows no longer fails withERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIRwhen clearingnode_modulescontaining linked dependencies, such as when changingnodeLinker#14790.pnpm install <pkg>now accepts--prodand--dev, including--prod=false#14868.pnpm installandpnpm updatenow honor--ignore-workspacein nested projects excluded from the surrounding workspace. The flag also skips that workspace's settings during thepackageManagercheck #14809.pnpm installon macOS no longer reuses stale files forfile:tarball or git-hosted tarball dependencies.pnpm installin a single-project directory now detectspackage.jsonedits made while the previous install was finishing #14890.pnpm install --frozen-lockfilenow removes packages no longer reachable from any project inpnpm-lock.yaml. This also prevents repeated lifecycle script execution and unnecessary installs beforepnpm runandpnpm execwithverifyDepsBeforeRun#14891.Resolving and updating dependencies
Node.js runtime resolution now reports network failures from unofficial-builds.nodejs.org. These failures previously omitted musl builds from
pnpm-lock.yaml, making its contents depend on network access #14813.pnpm installnow rejects invalidpeerDependenciesspecifiers withERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION. A value such as"foo": "foo@1.0.0"previously created a broken directory link #14791.pnpm deploynow writes plain registry versions in the deployedpackage.json, without peer dependency suffixes. The lockfile retains peer bindings, and npm aliases retain their target package names #14873.pnpm add <git repository>now names repositories without apackage.jsonas@owner/repo, allowing dependencies on equally named repositories from different owners #14870.Peer dependency resolution now deduplicates packages whose child dependency resolves an optional peer in only some workspace projects, such as
nextwithstyled-jsx's optionalbabel-plugin-macrospeer #14800.pnpm updatenow settles the lockfile in one run when an upgrade removes the package providing an optional peer dependency #14895.pnpm update --no-savenow preserves override-applied specifiers for dependencies it is not updating, preventing subsequent frozen installs from failing withERR_PNPM_OUTDATED_LOCKFILE#14836.pnpm update --no-savenow succeeds underminimumReleaseAgeStrictwhen every resolved version is old enough #14835.Performance
Workspace installs and
pnpm peers checkare faster when projects depend on each other, fixing a slowdown introduced in 12.3.0. Unmet peer dependencies of workspace packages are now reported only under projects that link them directly #14906.Hoisted installs use less memory when packages are cached. Frozen-lockfile hoisted installs on macOS are also faster when reusable package directories are cached.
Python projects
pnpm install --frozen-lockfilenow reusespylock.tomlacross compatible Python targets, including after kernel updates. Reuse requires unchanged requirements, index, andrequires-python, compatible wheels, and a locked dependency graph matching the target's markers #14843.The lockfile's
environmentsmarker now includes only the interpreter version and marker variables used by the dependency graph. Without--frozen-lockfile, pnpm warns and resolves again when the locked graph no longer matches the target.Python resolution no longer fails on malformed
Requires-Pythonvalues, such as the trailing comma inopenpyxl3.0.x. pnpm treats these releases as declaring no interpreter range #14910.pnpm add pypi:...now rejects unsupported--save-prefixvalues before editing the manifest or resolving dependencies.Workspaces and scripts
Scripts listed in
syncInjectedDepsAfterScriptsno longer fail withERR_PNPM_INJECTED_DEPS_SYNC_READ_DIRwhen the lockfile contains an injected package copy that no project depends on.shellEmulatornow expands${VAR},${VAR:-default}, and${VAR:+alternative}in scripts #14814.Cargo and Python project discovery now honors
!exclusions inpnpm-workspace.yamlpackages, skipping both parsing and generated source configuration for excluded projects #14844.pnpm --filter "./packages/{app,lib}"now selects either alternative. Brace alternatives can nest, span path separators, and combine with other wildcards.GitHub Actions updates now stop if an action reference changes during version resolution, and preserve unrelated workflow edits.
CLI and output
pn,pnpx, andpnxnow run the pnpm installed alongside them, even when that directory is absent fromPATHor another pnpm comes first #14803.pnpm --versionnow reports failures to install or record a project's pinned pnpm, then prints the running CLI's version. It also honors--store-dirand--store#14831.pnpm self-updateno longer reinstalls the active version when it was installed by the standalone installation script #14823.pnpm tandpnpm tstwork again as aliases forpnpm test.pnpm sbomnow emits valid repository URLs in CycloneDXexternalReferences[].urland SPDXhomepage. Shorthands such asvercel/msbecomegit+httpsURLs, embedded credentials are removed, and invalid repository values are omitted #14773.Platinum Sponsors
Gold Sponsors
v12.4.1: pnpm 12.4.1Compare Source
pnpm 12.4.1 fixes installs that failed on filesystems refusing hard links or clones, on Android, and under
nodeLinker: hoisted. Repeat installs are faster.Patch Changes
Installing packages
pnpm installno longer fails withOperation not permittedwhen the filesystem refuses a hard link or a copy-on-write clone #14722. UnderpackageImportMethod: autoandclone-or-copy, pnpm copies the file instead. EdenFS checkouts, which have no hard links, and rootless containers, which refuse the clone syscall, both hit this. An explicitpackageImportMethod: hardlinkorclonestill reports the error.pnpm also copies a package file whose store entry has reached the filesystem's limit on names for one file, 1024 on NTFS and 65000 on ext4. Such a file failed the install under
packageImportMethod: hardlink, and underautoit stopped pnpm hard linking for the rest of the install.pnpm installno longer writes a package file through a symlink left at the path it is importing to. Copying such a file overwrote whatever the link pointed at, and created that file when the link pointed nowhere. An executable package file also made the link's target executable.Fixed
pnpm installandpnpm dlxon Android. Registry requests crashed because pnpm found no system CA certificates, so pnpm uses bundled ones there #14777. Imports also failed with "Permission denied" on filesystems that deny hard links and reflinks, and now fall back to copying #14780.pnpm installno longer fails with "Invalid cross-device link" while preserving a package's nestednode_modulesdirectory during a Docker build #14758.pnpm installno longer fails on a package tarball that carries a file at the archive root, such as the._*entries macOStaradds #14701. The file is installed at the root of the package.A
file:tarball packed without the usualpackage/directory is now recorded under the name and version from its ownpackage.json. It was recorded under the alias the dependency was given, at version 0.0.0.Under
nodeLinker: hoisted,pnpm installno longer re-imports packages that are already in place. A repeat install replaced the wholenode_modulestree and reportedPackages: +N. A package is still imported when its directory is missing, when itspackage.jsonno longer carries the installed version, when it is afile:dependency, and when it is patched. Lifecycle scripts no longer run again for a package left in place, andpnpm rebuildand a change toallowBuildsstill reach it.pnpm installnow runs a dependency's build scripts again when its side-effects cache entry has no files to restore #14717. Such builds were skipped and nothing was put in their place, so a script whose whole effect lands outside its own package directory, such as a git hook installer, never took effect. pnpm no longer publishes empty artifacts to the shared side-effects cache either.Resolving and linking dependencies
pnpm install,pnpm add, andpnpm dedupenow applyignoredOptionalDependencies#14729. Matching optional dependencies are left out of the lockfile and are not installed. pnpm 12 installed them whenever it resolved dependencies from scratch.pnpm installno longer links a transitive dependency to a workspace package whenlinkWorkspacePackagesistrueand the dependency is declared with a plain version range #14781. EnablingpreferWorkspacePackagesdoes not change this. SetlinkWorkspacePackages: deepto link them.pnpm installno longer leaves dangling dependency links in workspace packages located above the workspace root #14726.pnpm installandpnpm addno longer leave a dangling symlink innode_moduleswhen a project starts depending directly on a package that the lockfile holds only as a transitive dependency with resolved peer dependencies #14714.pnpm dedupenow keeps a compatible auto-installed peer when another workspace project depends on a newer major #14697. Repeated runs alternated between compatible and incompatible peer versions.pnpm peers checkno longer reports a peer dependency declared asworkspace:^,workspace:~, or a bareworkspace:as unmet #14770. pnpm reported these as unmet whatever version the linked workspace project supplied.Performance
Sped up repeat installs #14540. pnpm checks the store's files only for the packages it links into
node_modules, instead of every package in the lockfile. Creating the command shims innode_modules/.binmakes about 1,500 fewer filesystem calls in a 76 project workspace. Installs that use the global virtual store read their slot paths from the cache directory instead of deriving them every time. Verifying a large lockfile also allocates less memory.Sped up
pnpm installin Cargo workspaces with many member crates. Repeated installs reuse verified Cargo checksum metadata.Installing several packages from the same Git repository and commit now downloads the source once per install #14725. Each package still runs its prepare scripts in its own copy of the checkout.
Running scripts and tasks
pnpm now passes Ctrl+C on to the script or command it started and waits for it to shut down #14723. pnpm exited first, so a script that was still writing landed on the shell prompt.
pnpm run "/pattern/" --no-bailnow lets every matched script finish after one of them fails #14718. The command exits withERR_PNPM_RUN_FAILED, and its message lists the scripts that failed in the order they were selected.pnpm pipelineno longer fails on a project that tracks a symlink, such as aCLAUDE.mdpointing atAGENTS.md#14692. Changing a symlinked input's target invalidates that task's cache, andpnpm pipeline --no-cacheno longer hashes task inputs.Commands
pnpm add -g,pnpm update -g, andpnpm remove -gno longer change global bins or install directories after reading only part of an installed package group #13796. If any declared package manifest is missing, malformed, or unreadable, pnpm now fails before it activates or removes anything and leaves the existing global installation intact.pnpm dedupenow processes every workspace project by default, including workspaces that keep a separate lockfile per project #14732. Workspace filters select which projects it processes, and--fail-if-no-matchexits with an error when no project matches.pnpm update <name>@<version>now keeps the range operator the manifest declares #14745. Runningpnpm update react@19.3.0on"react": "^19.2.8"writes"react": "^19.3.0". Ajsr:entry keeps itsjsr:prefix, and a plainpnpm updatenow moves ajsr:range the way it moves an npm range.pnpm --filterdirectory selectors now support?wildcards and character classes such as[ab]. A*or?wildcard no longer selects a directory whose name starts with a dot, as on pnpm 11.pnpm deploy --legacynow prefers the dependency versions pinned in the source workspace lockfile when they still satisfy the deployed project's range #13857.pnpm sbomnow leaves out a package's author field when the manifest author name is empty or contains only whitespace #14685. In a filtered or split workspace run, only a project with noauthorfield inherits the workspace root's author.pnpm sbom --sbom-format spdxnow writescreationInfo.createdwith whole seconds, such as2026-09-08T10:38:21Z#14684. The fractional seconds it carried were rejected by strict SPDX consumers.Configuration
The
updateConfigpnpmfile hook now receives the resolved configuration, including settings that came from.npmrc, the command line, or a default #14676. Scoped registries are reported underregistriesByScope, and a hook may rewrite that map to change where packages are fetched from. Registry credentials are reported underconfigByUri, as pnpm 11 reports them. An unset setting is left out rather than reported asnull.pnpm audit --fixand theminimumReleaseAgeStrictapproval prompt now keep the comments inminimumReleaseAgeExcludewhen they append an entry to it inpnpm-workspace.yaml. The rest of the list is left as written, and thetrustPolicyExcludePruneandminimumReleaseAgeExcludePrunecleanups keep the comments of the entries they retain.pnpm installandpnpm dedupenow run those cleanups too #14759. Onlypnpm add,pnpm update, andpnpm removepruned the entries that the freshly written lockfile no longer resolves.pnpm config set --global node-download-mirrorsno longer rejects the key #13611. The global config file already acceptednodeDownloadMirrors, but the command refused to write it.NO_PROXYentries that start with a dot, such as.npmjs.org, now bypass the proxy for the domain and its subdomains #14686.pnpm no longer creates a project
pnpm-lock.yamlwhendevEngines.packageManager.onFailisdownloadand lockfile writing is off throughlockfile: falseor--no-lockfile#14728. pnpm still switches to the pinned version.pnpm now writes
node_modules/.package-map.jsononly whennodeExperimentalPackageMapis enabled. Nothing reads the file without that setting, and an install that stops writing the map removes the one a previous install left.Windows
pnpm pipelineno longer fails with intermittent access denied errors when concurrent tasks save their cache entries on Windows.Windows filesystem operations now retry permission errors for up to one second #14682. A permanent permission error delayed the failure by a minute. Sharing and lock violations keep their one minute retry budget.
Messages and output
pnpm now warns when the root
package.jsondeclares a non-emptyworkspacesarray and the project has nopnpm-workspace.yaml#2255. Such an install linked no project and said nothing about why.ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIRnow names the file or directory innode_modulesthat pnpm could not clean up. It reported only the underlying OS error, such as "Access is denied (os error 5)".pnpm --helpno longer describes pnpm as experimental.Platinum Sponsors
Gold Sponsors
v12.4.0: pnpm 12.4Compare Source
Minor Changes
pnpm can now manage npm, Python, and Cargo dependencies in the same workspace. Enable
python.enabledorcargo.enabledinpnpm-workspace.yaml, then usepnpm installto install them together.pnpm add pypi:<package>. pnpm usespyproject.toml,pylock.toml, and a managed.venv. Frozen and offline installs are supported, andpnpm runandpnpm execmake the environment's executables available #14566.pnpm add crate:<package>. pnpm supports crates.io and custom sparse registries configured withcargo.indexUrl. Registry authentication supports pnpm credentials and, for crates.io,CARGO_REGISTRY_TOKENor$CARGO_HOME/credentials.toml.Both ecosystems support faster dependency resolution through
pnprServer, with local resolution as a fallback when the server does not support it.Added
pnpm pipeline [name]to install frozen dependencies and run workspace tasks declared inpipelines. It selects affected projects, runs their task graph, and continues running tasks after a task fails.Tasks support
inputs,outputs,env, andcachesettings. Cached results restore task outputs and replay logs. Cargo tasks can reuse local build state between worktrees withtasks.<name>.cargoTargetDir. SetincludeWorkspaceRoot: trueto include root tasks.Use
pnpm pipeline --dry-runto preview the task graph without installing configuration dependencies or running workspace hooks.Added support for Android on arm64 and x64, FreeBSD on x64, and Linux on ppc64le, s390x, and RISC-V (riscv64 with glibc) #14431, #14597, #7582.
Added
trustPolicyExcludePruneto automatically remove unused versions and packages fromtrustPolicyExcludewhen runningpnpm add,pnpm update, orpnpm remove. It is disabled by default. Package name patterns such as@scope/*are kept, and cleanup is skipped whensharedWorkspaceLockfileisfalse.Added
pnpm change checkfor CI validation of package versions against theversioning.epicsbands andversioning.fixedgroups inpnpm-workspace.yaml. It reports all violations, including packages that are not part of the current release.Patch Changes
Registry metadata is now kept separate for registries with different URL paths or schemes. This prevents installs from using another registry's package versions or tarball URLs, and keeps metadata fetched over HTTP from being reused for HTTPS #13558.
The first install after upgrading refetches registry metadata. The package store is unchanged.
pnpm cache viewnow shows full registry URLs. Scripts that parse the directory names frompnpm cache list-registriesorpnpm cache listneed updating.Patches that add build scripts or a
binding.gypnow trigger a build, subject to build approval. Unapproved builds appear under "Ignored build scripts" #14648.Build scripts can now be rejected before installing a package with
pnpm add --allow-build=!<pkg>, including global installs.pnpm approve-builds <pkg>andpnpm approve-builds !<pkg>also save decisions when no packages are awaiting approval. They warn if the named package is not awaiting approval #14067.A registry configured in
.npmrcnow takes precedence over registry settings saved bypnpm loginin the globalconfig.yaml. This fixes installs using the wrong registry after login #14614.Large downloads over slow connections no longer time out while data is still arriving.
fetch-timeoutnow limits how long a request can go without making progress #14604.Sped up installs in workspaces with many projects when reusing a warm global virtual store #14540.
pnpm deployis faster in large workspaces and no longer fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHwhen the project includes a.pnpmfile.mjs#14539, #14671.pnpm add --workspace <pkg>works again. It saves the dependency with theworkspace:protocol and links it from the workspace. The command fails if no workspace project provides the package #14602.pnpm addandpnpm installnow accept protocol-prefixed selectors such asjsr:@scope/pkg,npm:pkg@^1.0.0, andworkspace:pkg@*#14590. Installs with JSR dependencies in the lockfile also no longer fail withERR_PNPM_META_FETCH_FAIL#14649.Boolean flags now accept explicit inline values. For example,
pnpm install --prod=falseinstalls devDependencies, while--prod=trueskips them #14553.pnpm install <pkg>now accepts--offlineand--prefer-offline, aspnpm add <pkg>already did #14194.Fixed
pnpm install --frozen-lockfilerejecting a freshly generated lockfile when overrides use relativefile:orlink:paths in a workspace #14555.Fixed installs with config dependencies failing on symlinked lockfiles, such as those used by Bazel and Nix, when the config dependencies have not changed. Updates that would write through a symlink remain disallowed. Updating config dependencies also preserves lockfiles that start with a byte order mark #14372.
Fixed package manager version pins being written to the wrong lockfile when
lockfileDiris set. The pins also remain consistent across commands when version switching is disabled, avoiding unnecessary lockfile changes #14633, #14575.pnpm importnow respectslockfileDirand branch lockfiles without modifying other lockfiles. Failed imports restore the destination lockfile #14563.pnpm patch-commitnow produces valid patches when files are added or deleted.pnpm installalso accepts patches that delete files without listing their contents, and patch files with CRLF line endings #14559, #14557.Fixed version ranges with partial upper bounds. For example,
<=16now includes all 16.x versions, and>=0.11 <=3correctly accepts 3.0.1 #14419.Workspace package patterns now support
.and..segments and repeated slashes. Patterns such as./packages/*and exclusions such as!./packages/foonow match correctly #14571.packageConfigssettings now apply to the specified projects whensharedWorkspaceLockfileisfalse, includingoverrides,hoist,modulesDir,saveExact, andsavePrefix. Workspaces with a shared lockfile report which entries were ignored #14556.pnpm runandpnpm execno longer report a changed workspace structure after a successful install whensharedWorkspaceLockfileisfalseandverifyDepsBeforeRunis enabled #14588.Commands run from a project's subdirectory now find the nearest ancestor with a manifest. This fixes commands such as
pnpm binreturning paths under the wrong directory.pnpm initstill creates its manifest in the current directory, andpnpm execstill runs there #14622.Relative
scriptShellpaths inpnpm-workspace.yamlnow resolve from the workspace root, including when scripts run in nested packages. Bare command names such asbashstill usePATH#14422.Fixed installing the pnpm version pinned in
packageManagerwhennodeLinkerishoisted. Managed Node.js, Deno, and Bun installations also work when the global config usesnodeLinker: hoisted#14595.The JavaScript pnpm can again switch to a project's pinned pnpm version on platforms without a native binary for that version, such as Alpine Linux with pnpm 10 or Intel Macs with pnpm 11. If a native pnpm version does not support the platform, the error now names the missing target #13622.
Provisioning Yarn 6 now uses
GH_TOKENorGITHUB_TOKENwhen available to avoid GitHub's anonymous API rate limit in CI. Tokens are only sent whenstrict-sslis enabled.Fixed concurrent installs sharing a global virtual store on macOS failing with "failed to import ... No such file or directory" #14560.
Fixed
pnpm setupfailing withERR_PNPM_DIRECTORY_FETCHER_PATH_ESCAPEon Windows. Localfile:dependencies whose directories are symlinks or junctions are now packed correctly #14618.On Windows, installs now retry replacing command shims temporarily locked by another process #14549.
Fixed argument forwarding on Windows with
shellEmulatorenabled. Paths ending in a backslash, line breaks, and literal shell expressions are preserved #14548.Windows store paths now consistently use backslashes in
pnpm store pathoutput and in thestoreDirandvirtualStoreDirfields ofnode_modules/.modules.yaml.Invalid certificates in
caorcafileno longer cause anInvalid CA certificateerror. Valid certificates still apply, and blankcertorkeyvalues are treated as unset #14646.Installs now respect the archive extraction concurrency limit even after a download is abandoned #14585.
pnpm auditsummaries now exclude advisories ignored throughauditConfig.ignoreGhsasand report them separately. When all advisories are ignored, the summary says so #14535.pnpm pack --jsonnow reports errors as JSON. Lifecycle script output appears before the final JSON output.pnpm outdated -rnow wraps theDependentscolumn, keeping the table readable when many workspace projects use the same dependency #14591.Shell completions now support the
pnalias in bash, fish, pwsh, and zsh #11955.pnpm versionnow accepts-mas a short alias for--message#14567.Platinum Sponsors
Gold Sponsors
v12.3.4: pnpm 12.3.4Compare Source
Patch Changes
Sped up dependency resolution in large workspaces #14352.
pnpm 12 now accepts the boolean settings as command-line flags on every command that takes them in pnpm 11, for example
pnpm install --unsafe-perm,pnpm add foo --offline, andpnpm install --dangerously-allow-all-builds. pnpm 12 rejected them withunexpected argument, which failed every install on Vercel, whose build runspnpm install --unsafe-perm#14346.pnpm removenow accepts--unsafe-perm, the same flagpnpm install,pnpm add, andpnpm updatetake.Platinum Sponsors
Gold Sponsors
v12.3.3: pnpm 12.3.3Compare Source
Patch Changes
Fixed concurrent installs sharing a store occasionally failing with an ENOENT error while importing a package file #14353.
Sped up writing the lockfile in large workspaces #14352.
Sped up dependency resolution in large workspaces #14352.
pnpm now runs through Node.js when it was installed by a tool that skips build scripts, such as Vercel's
packageManagerprovisioning, Bun, Deno, ornpm install --ignore-scripts. Those installs previously failed withsyntax error near unexpected token ')'. They still cannot run pnpm on Windows. On macOS only a shell can start it #14346.Platinum Sponsors
Gold Sponsors
v12.3.2: pnpm 12.3.2Compare Source
Patch Changes
pnpm audit --fix updateno longer aborts when a vulnerable package has no safe version inside its declared range #14508. The run updates every package it can and lists the rest as remaining.pnpm installno longer reruns root lifecycle scripts when the global virtual store contains an unfinished-build marker in a package slot that the current lockfile does not use pnpm/pnpm#14485.Sped up installs that have no lockfile. pnpm now links packages whose dependency subtree has no peer dependencies into the virtual store while resolution is still running.
pnpm runandpnpm execnow start without reinstalling on filesystems that keep sub-millisecond mtimes, such as NTFS. Previously, every run on those filesystems reinstalled first pnpm/pnpm#14486.pnpm importnow keeps the versions recorded inpackage-lock.json,npm-shrinkwrap.json, oryarn.lockwhen it generatespnpm-lock.yaml. A range inpackage.json, a catalog, or an override still decides which versions are eligible, and the recorded version is preferred among them. The generated lockfile previously could pin newer versions than the source lockfile #14476.pnpm importin a workspace now imports every workspace project into the shared lockfile. It previously imported only the project in the current directory.pnpm importnow fails withERR_PNPM_LOCKFILE_NOT_FOUNDwhen none of the three source lockfiles is present. It also fails withERR_PNPM_YARN_LOCKFILE_PARSE_FAILEDwhen it cannot parseyarn.lock. It previously generated a lockfile from scratch in both cases.pnpm importalways resolves locally. It warns when--pnpr-serveror thepnpr-serversetting is given and does not use the server.Sped up installs in large workspaces. Discovering the workspace projects no longer enumerates every matched directory to learn which manifest files it holds #14352.
Sped up installs in large workspaces. The resolver and the peer pass allocate less for every dependency edge #14352.
pnpm self-update,pnpm with, and automatic package-manager version switching no longer wait through registry retry delays when a configured registry has no signatures andregistry.npmjs.orgis unavailable #14483.Sped up installs in large workspaces. Saving the lockfile is faster, and the install finishes without waiting for memory cleanup #14352.
pnpm installnow relinks workspace packages whenpublishConfig.linkDirectorychanges. Frozen installs report an outdated lockfile until it is regenerated pnpm/pnpm#14488.The pnpm npm wrapper keeps its placeholder shebang-less so pnpm 11 can install pnpm 12 through the version store. Wrapper installs must allow lifecycle scripts to install the native binary #14502.
Sped up dependency resolution when there is no lockfile, and for the dependencies a lockfile does not cover.
Sped up installs in large workspaces. Workspace
link:targets and importer ids are now derived from the paths' suffixes under the workspace root #14352.pnpm installnow reports "Already up to date" when local tarball dependencies have not changed #14495.pnpm updatenow accepts--ignore-scriptsand skips lifecycle scripts during the update pnpm/pnpm#14512.Sped up installs that restore a deleted
node_modulesfrom a warm global virtual store. pnpm no longer re-links packages that are already fully present in the global virtual store #14510.Platinum Sponsors
Gold Sponsors
v12.3.1: pnpm 12.3.1Compare Source
Patch Changes
Sped up installs in large workspaces: the anchor for re-rendering workspace
link:targets is now derived once per project instead of once per dependency edge, and project ordering hashes paths by their raw bytes #14352.After a self-update from pnpm 12.2 to 12.3, global commands such as
node,npm, andyarnfailed withunexpected argument '--shim' found. Global commands now launch normally, and their first launch migrates the global bin directory to native shims. When self-update downgrades to pnpm 12.2 or older, it keeps the newer native shims so those commands continue to work.Sped up installs in large workspaces. The check that verifies each project against the lockfile now runs the projects in parallel #14352.
Platinum Sponsors
Gold Sponsors
v12.3.0: pnpm 12.3Compare Source
Minor Changes
Every context-aware global command (
node,deno,bun, and the shims created withpnpm shim add) is now a native executable on every platform, so environment variables whose names are not valid shell identifiers reach these commands. On Windows,<name>.exereplaces the.cmdand.ps1shims for them. Shims written by earlier pnpm 12 releases are migrated on the next global install or self-update.pnpm removeandpnpm updatenow accept--trust-lockfile,--no-trust-lockfile,--trust-policy,--trust-policy-excludeand--trust-policy-ignore-after, the same flagspnpm installandpnpm addtake, so the supply-chain settings can be overridden for a single run.pnpm removeverifies the lockfile against the active policies the waypnpm installdoes, and--trust-lockfileskips that pass for every entry, not only the package being removed.pnpmnow also honors--config.trust-lockfile=<value>, and accepts the bare--trust-lockfile/--no-trust-lockfilespelling on the commands that previously took the setting from the config file alone.Patch Changes
pnpm add <local directory>,pnpm add <local tarball>,pnpm add file:<path>andpnpm add <tarball URL>work again. A specifier given without a<name>@prefix is no longer read as a registry package name and rejected withERR_PNPM_PACKAGE_MANAGER_ADD_RESOLVE_LATEST#14437.Fixed
pnpm deploy --legacyignoringallowUnusedPatchessupplied through--config.allow-unused-patchesor thePNPM_CONFIG_ALLOW_UNUSED_PATCHESenvironment variable pnpm/pnpm#14450.Fixed
pnpm install --lockfile-onlywriting a lockfile that referenced a missing peer-suffixed snapshot when an npm-aliased dependency took part in a cyclic peer dependency graph. The followingpnpm install --frozen-lockfilefailed withERR_PNPM_LOCKFILE_MISSING_DEPENDENCY#14449.pnpm confignow accepts-g/--global,--location, and--jsonbefore its subcommand pnpm/pnpm#14421.pnpm dedupenow converges in one pass when it re-resolves a lockfile created by pnpm 11, so a second run no longer changes the lockfile #14455.Fixed detached child processes being terminated on Windows when another program launches
pnpmdirectly, without a shell, asnrfrom@antfu/nidoes #14447.Fixed
pnpm docs <package>@<version>ignoring the requested version. It now opens the selected version's homepage and reports a missing version instead of opening the package-level homepage pnpm/pnpm#14428.Sped up installs in large workspaces.
pnpm-lock.yamlis now read while the workspace projects are being discovered #14352.Fixed filtered and recursive
pnpm runandpnpm exechanging when a script reads from the terminal. Interactive prompts work again in a script that pnpm never runs alongside another one, such as a single--filtered project,--workspace-concurrency=1, a dependency chain, or a task declaringconcurrency: 1#14397.Fixed false unmet peer errors for auto-installed peers in linked workspace packages.
Fixed npm global installs on Windows so the PowerShell shims invoke
pnpm.exe.Fixed
pnpm with current <command>when global options precede it, such aspnpm --workspace-root with current --versionpnpm/pnpm#14413.A short-option cluster that mixes a global flag with an option owned by the command, such as
pnpm -ro dist pack-app, is now parsed like the same options written after the command.An option written before the command name is now reported as an unknown option unless that command accepts it, instead of being taken for the command to run.
pnpm -P exec echoandpnpm -z exec echofail the waypnpm --tag next exec echodoes.Apply pure insertions in zero-context patches at the correct line instead of one line early.
Improved peer dependency resolution performance when many packages reuse the same peer ranges.
pnpm outdatedandpnpm updatenow follow local actions and reusable workflows referenced with GitHub's self-repository syntax (uses: $/.github/actions/setup) when looking for outdated GitHub Actions, the same way they follow./references.The
pnpm install --helpdescriptions of--prodand--devno longer claim that the flags take precedence overNODE_ENV. pnpm does not readNODE_ENVwhen selecting which dependency groups to install #14445.Sped up installs in large workspaces. The check that decides whether the lockfile needs updating no longer compares every project against every lockfile entry #14352.
Sped up dependency resolution in large workspaces that use
link:dependencies #14352.On Linux, pnpm now resolves registry hostnames through the system resolver (
getaddrinfo), as it already does on macOS and Windows and as pnpm 11 did. Previously, an/etc/resolv.confcontaining an option the bundled pure-Rust resolver did not recognize, such asoptions no_tld_query, made pnpm ignore the configured nameservers and silently query Google's public DNS instead #14469.Sped up dependency resolution in large workspaces. The resolver builds fewer lookup keys for each dependency #14352.
catalogModeand--save-catalogno longer move a local path, tarball, orworkspace:<path>specifier into a catalog. Such a specifier is resolved against the project that declares it, so one catalog entry cannot mean the same directory for every project that references it #14437.Sped up installs in large workspaces. The workspace dependency graph is now built once per run instead of twice #14352.
Sped up writing
pnpm-lock.yamlin large workspaces #14352.Fixed non-frozen installs through a pnpr server failing instead of regenerating a conflicted lockfile.
pnpm update --interactiverenders its checklist the way pnpm 11 does. Group headings and column headers are separators the cursor skips instead of checkboxes that select nothing. The columns of one group line up with the next.atoggles all andiinverts the selection. The confirmed selection is echoed as a list of package names #14423.Fixed
pnpm configcommands targeting global configuration to skip project package manager version switching, allowing registry authentication to be configured before pnpm downloads a project-pinned version pnpm/pnpm#14463.Fixed pnpm retaining the surrounding quotes in
.npmrcvalues, including auth tokens expanded from environment variables. This restores authentication with registries configured using:_authToken="${TOKEN}"pnpm/pnpm#14427.Fetch and tarball errors no longer print the secrets of the URL they name. Inline
user:pass@credentials and the query string or fragment of a signed URL are hidden, so a failed install orpnpm add <url>cannot leak them into terminal scrollback or CI logs.When
dist-tags.latestnames a version whose manifest pnpm cannot read, the error now names that version and the field it could not decode, instead of reporting the tag as empty.Retry transient Windows file-lock errors, including sharing violations, while linking dependencies with the default (isolated)
nodeLinker. This fixes pnpm/pnpm#14407.pnpm run,pnpm exec,pnpm rebuild, and the script shortcuts such aspnpm testnow load the pnpmfile, soupdateConfighook settings such asextraEnvandextraBinPathsreach the scripts they spawn #14433.The
pnpmexecutable of the npm package now works when the package was installed without running its install scripts, as under--ignore-scriptsor the default build-script block of pnpm and Bun #14346. In that case it runs through Node.js and, in a terminal, says how to switch to the native binary.Sped up installs in large workspaces. The resolver no longer copies the whole lockfile before resolving #14352.
minimumReleaseAgeStrictnow defaults totruewhenminimumReleaseAgeis explicitly configured, whether inpnpm-workspace.yaml, the globalconfig.yaml, aPNPM_CONFIG_*variable, or a CLI flag. The built-in 1440-minute default stays non-strict. Previously an explicit cutoff was treated as non-strict, so immature versions were silently added tominimumReleaseAgeExcludeinstead of being gated with a prompt #14409.Preserve environment variables whose names are not valid shell identifiers when launching Node.js installed by
pnpm runtime set node --globalon Unix pnpm/pnpm#14417.Fixed
pnpm repoandpnpm docsfailing to open the Windows browser from WSL pnpm/pnpm#14467.pnpm link,pnpm outdated, andpnpm importnow apply pnpmfileupdateConfighooks before resolving dependencies.Fixed standalone installations to preserve the bundled
node-gypfiles used to build native dependencies.Fixed resolution against registries whose version manifests carry
_npmUser,dist.attestations,dist.unpackedSize,dist.fileCount, orpeerDependenciesMetain a shape npm does not use. Such a version was skipped as though it had never been published, sopnpm addcould fail with "no version found for the latest tag" even though the registry served it.pnpm unpublishnow completes the two-factor authentication a registry asks for instead of failing withERR_PNPM_UNAUTHORIZEDwhile logged in. A 401 that is an OTP challenge starts the web-based authentication flow, or prompts for a classic one-time password. The obtained password is reused by every request of the run #14464.On Windows, pnpm now resolves host names through the system resolver instead of its own DNS client. The built-in client bound a UDP socket for every lookup, which made Windows Defender Firewall ask to allow
pnpm.exeagain after everypnpm self-update#14405.Platinum Sponsors
Gold Sponsors
v12.2.1: pnpm 12.2.1Compare Source
Patch Changes
pnpmexecutable target without a file extension so pnpm 12.1 and earlier can upgrade to newer pnpm 12 releases on POSIX systems.Platinum Sponsors
Gold Sponsors
v12.2.0: pnpm 12.2Compare Source
Minor Changes
workspace:protocol.Patch Changes
Fixed
pnpm audit --fixfailing withERR_PNPM_INVALID_FIX_OPTIONwhen used without a value, including when another flag follows it, as inpnpm audit --fix --json#13261. Fixedpnpm audit --fix=overrideignoring thesaveExactandsavePrefixsettings when writing vulnerability overrides #11523.Authenticate Node.js runtime downloads from
nodeDownloadMirrorswith URL-scoped npm registry credentials, including bearer tokens, basic auth, andtokenHelperpnpm/pnpm#14334.Fixed detached child processes being terminated after successful commands on Windows.
Sped up installs in large workspaces by resolving each named
workspace:dependency (workspace:*,workspace:^,workspace:1.2.3) once and reusing it across every project that declares it, instead of re-resolving it per project.Fixed
pnpm install --fix-lockfileto derive its repair and filtered-merge views from one lockfile snapshot.Load pnpmfile
updateConfighooks before packing so hook-provided catalogs resolve inpnpm pack,pnpm publish, andpnpm stage publishpnpm/pnpm#14377.pnpm deployno longer requiresinjectWorkspacePackagesto be enabled. A linked workspace dependency is rewritten to afile:dependency in the dedicated deploy lockfile, and the peer dependencies it declares are bound to the deployed graph's own resolution.When a peer resolves to more than one version in that graph the binding is ambiguous, and choosing between the candidates is exactly what injecting the package would have decided, so the deploy still fails — now with
ERR_PNPM_DEPLOY_AMBIGUOUS_PEER, which names the package, the peer, and the competing versions, instead of refusing every non-injected workspace up front, and suggests pinning the peer to one version with anoverridesentry as the way to keep deploying without injection #9386.Fixed global virtual store hashes for dependency cycles. Every package that transitively depends on an allowed build now includes the engine in its store path, independent of traversal order pnpm/pnpm#14341.
Fixed
ERR_PNPM_CMD_SHIM_CHMODwhen several installs run at once against a shared global virtual store. One install could remove a command shim while another was making it executable (pnpm/pnpm#14353).Fixed the PowerShell shim generated by
npm install -g pnpmon Windows so it invokes the nativepnpm.exebinary pnpm/pnpm#14362.Fixed context-aware global shims on WSL2 so native Linux installations dispatch through the project runtime.
pnpm installno longer writes globalminimumReleaseAgeExcludeentries to the project'spnpm-workspace.yamlpnpm/pnpm#14347.Fixed
catalog:ranges in workspace package peer dependencies being reported as unmet pnpm/pnpm#14361.globalDirandglobalBinDirare honored wherever they are set, sopnpm add -gno longer fails withERR_PNPM_GLOBAL_BIN_DIR_NOT_IN_PATHafterpnpm config set -g global-bin-dir#14336. The globalconfig.yamlis read again,PNPM_CONFIG_GLOBAL_DIR/PNPM_CONFIG_GLOBAL_BIN_DIRreach the directories derived from them, and a leading~/is expanded before that derivation. A project'spnpm-workspace.yamlstill cannot set either key.Fixed the install progress line reporting
added 0undernodeLinker: hoisted, even when packages were linked intonode_modules#14348.An auto-installed optional peer is now resolved to a version its declared peer range accepts, even when the workspace root depends on that package at a version outside the range. Previously the root's version was used and then reported as an unmet optional peer #13867.
Fixed
pnpm run "/pattern/"running matching scripts one at a time in a single project. Matching scripts now run concurrently up toworkspaceConcurrency, and their output is prefixed so concurrent lines remain distinguishable pnpm discussion 14357.Fixed a slowdown at the end of a resolving install in a large workspace. The peer-dependency report now inspects only the projects the resolution flagged, rather than every project in the lockfile (pnpm/pnpm#14359).
Speed up workspace discovery for literal directories and conventional trailing-star patterns.
Workspace patterns now follow the same dot-directory rule as pnpm 11: a wildcard no longer matches a dot-prefixed directory, so
packages/*and**skippackages/.cacheand.git. A pattern that names a dot-prefixed directory still matches it, aspackages/.cacheandpackages/.*do.pnpm auditnow ends its output with a trailing newline, including the--json,--fix, and--ignoreoutput.Retry transient Windows file-lock errors while replacing hoisted packages during installation.
This fixes pnpm/pnpm#14349.
Fixed command-line
--side-effects-cacheoverrides being ignored whenpnpm-workspace.yamluses the object form ofsideEffectsCachepnpm/pnpm#14338.Speed up workspace project discovery in large monorepos: workspace patterns are now probed concurrently and the discovered projects'
package.jsonfiles are read in parallel #14352.Fixed repeated
pnpm deduperuns alternating between peer resolutions when a peer is provided through an npm alias.Fixed
pnpm repo <package>andpnpm docs <package>resolving bare package names through thelatesttag, and prevented malformed package ranges from crashing registry selection.Fixed non-ASCII characters in configuration values being mangled during environment-variable substitution. Paths such as
storeDir: ./café-storeare now preserved #14383.Platinum Sponsors
Gold Sponsors
v12.1.0: pnpm 12.1Compare Source
Minor Changes
pnpm loginandpnpm addusernow record the granted token in the globalconfig.yaml, under the_authsetting, with--scope's scope routed to that registry underregistries.pnpm logoutremoves it from there, and still from anauth.inian earlier version wrote. Tokens already inauth.inikeep working.A
scopeset in a project'spnpm-workspace.yamlis now ignored, with a warning naming where to set it instead.pnpm loginrecords the scope as a@scope:registryroute in the machine-globalauth.ini, which outranks~/.npmrcin every project — so a repository-committed file could redirect a scope such as@acmefor all of a user's other projects after one routine login. Use--scope, thePNPM_CONFIG_SCOPEenvironment 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-fromskips exactly the work that passed during a matching interrupted or failedpnpm -r run/pnpm -r execinvocation. When no compatible state exists, pnpm retains its graph-based resume behavior.Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group.
Added per-task concurrency limits to workspace task orchestration. Set
tasks.<name>.concurrencyinpnpm-workspace.yamlto limit how many instances of that task may run across workspace projects at once:sideEffectsCachenow 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: true,sideEffectsCacheReadonly,remoteSideEffectsCache, and itsorganizationfield 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: truenow blocks writing to the cache, and setting it alongsidesideEffectsCache: falsegives 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 runandpnpm -r execnow 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
taskssection inpnpm-workspace.yamldeclares what a task depends on, using the^convention:^namemeans the named task in each of the project's workspace dependencies; a barenamemeans the task in the same project; an entry with nodependsOndeclares an empty dependency list. A task with no entry behaves asdependsOn: ['^<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:
ERR_PNPM_TASK_CYCLE) instead of silently running in an arbitrary order. SettingignoreWorkspaceCycles: truedowngrades the error to a warning: the cycle's tasks run in an arbitrary order relative to each other.--resume-fromnow skips exactly the transitive dependencies of the anchor package; work unrelated to the anchor still runs.--no-bail, tasks whose dependencies failed are reported as skipped, not failed, and do not add to the exit code.--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 theverifyDepsBeforeRuncheck);--jsonemits the tasks and their resolved dependency edges.--workspace-concurrency=1, or the graph forces the scripts to run one after another).Added macOS and Windows x64 and arm64 support to remote shared build artifacts pnpm/pnpm#13771.
Generalized the experimental shared-artifact protocol so candidates and signed payloads identify a discriminated subject. Dependency side effects use package and source-integrity subjects, while workspace tasks use project and task subjects.
This changes shared-artifact request bodies and signed payloads. A pnpr server and its clients have to be on matching versions.
Patch Changes
An
_authentry in the global config file no longer decides which registry packages come from when something else says. Aregistryorregistriesdeclared inpnpm-workspace.yamlor the global config now wins over the route inferred from a stored credential, which still applies where nothing else declares one. Thepnpm_config__authenvironment variable is unchanged: it stays the way to point a CI runner at a mandated proxy, and still overrides what a repository declares.Fixed
pnpm deploy --legacyto exclude dependencies that are only reachable from unselected workspace projects afterpnpm fetch.Fixed dependency-verification install logs corrupting
pnpm execoutput and ignoring--silentpnpm/pnpm#14197.pnpm clean/pnpm purgerun from a workspace subdirectory now remove each project's ownnode_modulesinstead of emptying the workspace root's for every project #14239. A custommodulesDiris resolved against each project directory too.pnpm dlx <pkg>@catalog:now resolves the specifier through the calling workspace's catalogs instead of failing withERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC#14294.Fixed
pnpm doctorreporting a version that does not matchpnpm --versionpnpm/pnpm#14225.Pacquet now strips exactly one leading path component from
./-prefixed tarball entries, matching pnpm and npm's tar extraction semantics and keeping shared store keys consistent.Installs whose lockfile carries platform or engine constraints are up to ~150 ms faster when resolution runs: the
node --versionprobe behind the installability checks now starts before the lockfile is parsed and finishes while dependencies resolve, instead of running afterwards.Treat empty scripts selected by a regular expression as missing before running dependent tasks.
Filter hidden scripts matched by a regular expression during recursive runs when a visible script also matches.
Fixed
.mjspnpmfile hooks failing to load on Windows, including hooks supplied by config dependencies pnpm/pnpm#14301.Fixed automatically switched pnpm versions forcing all descendant pnpm processes to use the same version pnpm/pnpm#14309.
Fixed
pnpm deploy --prodfailing when an excluded dev dependency was also declared as an optional peer dependency pnpm/pnpm#14302.Fixed
pnpm packto respect thefilesfield when deciding whether to include root-level changelog, history, and notice files.pnpm update -gno longer downgrades a global package.--latestresolves thelatestdist-tag, which can point at an older release than the one installed — afterpnpm add -g <pkg>@next, for instance #14270.pnpm update -galso no longer changes the pnpm version. pnpm's own global install belongs topnpm self-update#14270.When multiple versions of the same package expose the same binary, pnpm now links the binary from the highest version #14249.
pnpm updateno longer replaces the specifier a project declares for a dependency that is also listed inoverrides. Acatalog:reference stays acatalog:reference, and a declared range stays as written, instead of being rewritten to the version the override resolved to #12115.pnpm updateno longer moves the range a project declares for a dependency thatoverridesalso lists, even when the override repeats that range verbatim. Previously the updatedpackage.jsondisagreed with the lockfile, so the nextpnpm install --frozen-lockfilefailed with a specifier mismatch #14224.Allowed pnpm's shared-artifact client to connect to an artifact-only pnpr tier.
Rebuilding
node_modulesfrom an up-to-date lockfile is up to ~200 ms faster: thenode --versionprobe that installability checks and store keying need now runs concurrently with the store's warm-cache reads instead of before them.Remove the duplicate colon from the one-time password prompt.
Print errors as JSON on stdout when
--jsonis passed topnpm viewor its aliases (info,show, andv).Installs complete faster on workspaces with many projects: each project's
node_modulesis now linked concurrently.Fixed
patchedDependenciesmatching for git-hosted dependencies during fresh and frozen installs pnpm/pnpm#14273.pnpm pm <command>works again: thepmprefix, which forces pnpm's built-in command over apackage.jsonscript of the same name, is recognized instead of failing withERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL/Command "pm" not found.pnpm pm cleanandpnpm pm purgenow removenode_moduleseven when the project (or the workspace root) declares aclean/purgescript #14226.The settings that pnpm accepts as command-line flags are recognized again:
--package-import-method,--hoist-pattern,--public-hoist-pattern,--no-hoist,--global-dir,--virtual-store-dir,--modules-dir,--child-concurrency,--no-lockfile,--strict-peer-dependencies,--side-effects-cache,--side-effects-cache-readonly,--trust-policy,--trust-policy-exclude,--trust-policy-ignore-after, and--optimistic-repeat-install. Each is accepted anywhere on the command line, spelled either--setting=valueor--setting value, and overrides the same setting read frompnpm-workspace.yamlor.npmrc#14281.pnpm add,pnpm update, andpnpm removenow savepackage.jsonbefore failing withERR_PNPM_IGNORED_BUILDS. The dependency they were asked to change is already materialized by that point, so the manifest has to record it — otherwise the next install removes the packages again.The progress output no longer overwrites the lines above it once it grows taller than the terminal window #14270.
Restoring a dependency's build from the remote side-effects cache no longer downloads files the store already holds.
Recognize
pnpm install --fix-lockfile, including filtered installs, and regenerate broken lockfile metadata while preserving compatible locked versions pnpm/pnpm#14250.Fixed intermittent
Access is deniedfailures when concurrent global commands hand off the global bin lock on Windows.Fixed the
--shamefully-hoistCLI option being rejected pnpm/pnpm#14235.The environment variables for the remote side-effects cache are named for the setting they configure:
PNPM_SIDE_EFFECTS_CACHE_REMOTE_KEY_ID,..._BUILDER_ID,..._IMAGE_DIGEST,..._ARCHITECTURE_BASELINE,..._PRIVATE_KEY,..._BUILD_ENV,..._TRUSTED_KEYSand..._PUBLISH. ThePNPM_REMOTE_SIDE_EFFECTS_CACHE_*names keep working, and the new one wins when both are set.Installs that run no build scripts finish faster, especially in workspaces with many projects.
A
devEngines.packageManagerrange pin on pnpm is now recorded inpnpm-lock.yaml'spackageManagerDependencieswhen 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 thanpnpm add/pnpm self-update) left the project without the shared resolution the pin exists to provide.Workspace installs are substantially faster (~0.7 s on a 60-project workspace): after hoisting, pnpm now shims only the bins of publicly hoisted workspace packages instead of re-walking every project's
node_modulesto rediscover bins that were already linked.Fixed a large install-time regression on macOS for installs that rebuild
node_modulesfrom a warm store #14231. APFS serializes file-cloning and hard-linking syscalls volume-wide, so importing packages one file at a time from many threads was bounded by a per-volume ceiling and got slower the more CPU cores the machine had. On macOS,pnpm installnow materializes each package once into the store'slinksdirectory (the same canonical slotsenableGlobalVirtualStoreuses) and copies it intonode_modules/.pnpmwith a single copy-on-write directory clone per package, replacing tens of thousands of per-file syscalls with one per package. Applies with the defaultnodeLinker: isolatedwhenenableGlobalVirtualStoreis off andpackageImportMethodisauto,clone, orclone-or-copy; hoisted, global-virtual-store, and explicithardlink/copyinstalls are unchanged.Stop in-flight recursive
runandexeccommands when bailing after the first failure.Warm installs that rebuild
node_moduleson macOS are about 10% faster: creating each package's virtual-store directory now issues fewer filesystem calls.An
_authcredential in an.npmrcnow authenticates even when its base64 is written without the trailing=padding (or with extra padding, or with whitespace inside it), instead of failing with a 401. An_auththat is not valid base64, or that carries no:between the username and the password, now fails withERR_PNPM_AUTH_INVALID_BASE64/ERR_PNPM_AUTH_MISSING_SEPARATOR#14257.Colored output is no longer printed as raw escape sequences in the Windows Command Prompt #14292. Commands such as
pnpm listnow style their output there.Platinum Sponsors
Gold Sponsors
v12.0.0: pnpm 12Compare Source
Major Changes
Git dependencies on known hosts (GitHub, GitLab, Bitbucket) are now treated as identities rather than transport choices. Every representation of the same repository —
github:owner/repo,owner/repo,git+https://…,git+ssh://git@…— resolves through the host's canonical HTTPS URL, and the lockfile never records an SSH URL for them. Repositories whose archive endpoint is anonymously reachable resolve to the host's archive (fast tarball download); all others resolve to agitclone of the canonical HTTPS URL, which every machine with access to the repository can fetch.To reach a private hosted repository over SSH, configure the machine (not the project) with git's own URL rewriting, for example:
pnpm shells out to
git, so the rewrite applies to all of pnpm's git operations automatically. URLs of unknown hosts (self-hosted servers) are unaffected and keep their exact URL, including SSH. URLs with embedded credentials are also kept verbatim and never resolve to a host archive.This removes the network probing that previously decided between HTTPS and SSH at resolution time, which could record a transport that only worked on the machine that happened to run the resolution (e.g. an SSH URL that broke CI runners without SSH keys).
A project's
pnpm-workspace.yamlmay no longer carry a setting pnpm does not recognize. Such a setting used to be ignored in silence — a misspelledminimumReleaseAgedropped the policy it was meant to set, and nothing said so. Now it is reported, suggesting the closest real setting name when the key looks like a typo, and it fails the command withERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGSwhen the project pins a pnpm version the running pnpm satisfies: with the pin honored, the setting cannot be meant for a different pnpm version, so it is a mistake to fix rather than a key to ignore. Everywhere else it is a warning, so a project that has yet to be cleaned up keeps working.The
pnpm configsubcommands never fail on such a setting, so a broken file can still be inspected and repaired, andpnpm config get <key>prints the value with no warnings at all. Keys the global config file cannot set are likewise split between workspace-only settings (still directed topnpm-workspace.yaml) and settings unknown to this version.Dependency cycles are now broken canonically during peer resolution: the members of each cycle are ordered by package id, and the edges that close a cycle are always cut at the same place, no matter where the installation walks into the cycle from. Previously the cut depended on the walk path, so installing the same dependencies could produce different lockfiles depending on importer order or resolution order #13846, and a peer-resolution verdict computed for one occurrence of a cyclic package could be wrongly reused at another #13865.
With canonical cycle breaking the lockfile is a pure function of the dependency graph: repeated installs, reordered importers, and reordered dependencies all produce byte-identical lockfiles. Peer dependencies of packages inside a cycle keep nearest-wins resolution along the canonical order, and a dependency edge that closes a cycle references an occurrence of its target resolved at the importer level. On large cycle-heavy workspaces peer resolution is 2–3× faster, uses about 25% less memory, and produces a substantially smaller lockfile (fewer redundant peer variants).
Existing lockfiles keep working: headless (
--frozen-lockfile) installs consume them unchanged, and installs that skip resolution leave them untouched. The first install that actually re-resolves (for example after a dependency change) re-keys walk-order-dependent peer variants of cyclic packages once.packageImportMethod: autonow tries hardlinks before cloning on Linux. A reflink materializes a new inode and copies extent bookkeeping inside the filesystem's metadata trees, where a hardlink is one directory entry — on btrfs this roughly halves the time an install spends materializingnode_modulesfrom a warm store. ext4 installs are unchanged (cloning was never supported there, soautoalready hardlinked), and macOS keeps clone-first, where APFSclonefileis the platform's cheap primitive. Cloning remains the fallback when the store refuses hardlinks, and remains available explicitly viapackageImportMethod: clone.Under
engineStrict, an install fails when an incompatible package is reached through a regulardependenciesedge of an installable package, even when that whole subtree hangs off anoptionalDependenciesentry. pnpm v11 installs the package and emits an install-check warning instead. Packages reachable only through optional edges, or through a package that was itself skipped, are still skipped in both versions #13286.pnpm install --frozen-lockfile falseis no longer supported. Usepnpm install --no-frozen-lockfileto disable frozen-lockfile mode. To enable it, usepnpm install --frozen-lockfilewithout a separatetrueargument #14741.Minor Changes
Globally installed bins can now follow the project you run them in. The new
globalShimssetting is a record of package names to policies that selects which globally installed packages get project-aware shims; it defaults to{ node: true, deno: true, bun: true }and merges key-wise, soglobalShims: { bun: false }switches one default off andglobalShims: { typescript: true }adds another package. With the default, a project that pins Node.js throughdevEngines.runtimeorengines.runtimegets the pinned stable release — authenticated against the Node.js release-team signatures — downloaded on first use and run whenever you typenodeinside the project, with no shell hooks. Candidates that are not signature-verified (Deno, Bun, Node.js prereleases, and ordinary package bins you enable) ask "Do you trust this project?" once per candidate and remember the answer machine-locally; the record values name the policy per package:"auto"(or its shorthandtrue) defers to artifact authentication,"always"switches without ever asking (useful in CI), and"prompt"always asks, even for authenticated candidates. SetglobalShims: falseto disable the feature, orPNPM_SHIM_BYPASS=1to bypass it for one invocation. On Windows, programs can keep spawning the globalnode.exedirectly, without a shell.pnpm installs the other package managers now, not just itself: npm, Yarn Classic, Yarn Berry, Yarn 6 (
yarnpkg/zpm), and Bun. Each is resolved and fetched through the trusted package-manager registries, and an npm-published one is verified against npm's signature for its exact version before it is executed.Three things use it:
packageManager/devEngines.packageManagerpin is honored, and ayarn.lockwritten by Yarn Classic no longer gets installed by Yarn Berry. pnpm provides that package manager when the dependency pinned a version, or when the host cannot satisfy what the dependency needs — so a repository built with Yarn now installs on a machine that has only pnpm, while a host that already has a suitable one keeps using its own.pnpm dlx(pnx) runs one of them for a single command:pnx yarn@4 install,pnx npm@11 ci,pnx bun@1.3.0 install. Naming a package manager, or a runtime (node,deno,bun), there now provisions the real thing instead of installing the npm package that shares its name — unless the specifier locates a package rather than asking for a released version (pnx yarn@npm:yarn@1.22.22,pnx yarn@yarnpkg/berry), which installs what it names —pnx yarn@4was previously a missing version, since Yarn 4 is published as@yarnpkg/cli-dist, andpnx node@22now runs that Node.js release rather than a wrapper that downloads one.--packagenaming a package manager picks which of its commands to run, sopnx --package npm@11 npx create-somethingruns that npm'snpx.pnpm shim add yarnlinks ayarncommand that runs whatever version the current project pins, andpnpm shim rm/pnpm shim lsmanage those shims. It works for any package, not only package managers. Shims are never created as a side effect ofpnpm setupor an install — a shim shadows the rest of yourPATH, so pnpm only writes one when asked.Installing a package manager globally (
pnpm add -g yarn) now makes it follow a project's pin too, the way a globally installed Node.js already followsdevEngines.runtime: the pinned version runs where a project pins one, and the globally installed copy is the fallback everywhere else. An explicitglobalShimsentry, includingfalse, is left as you set it.pnpm addfollows the same rule about what a name means.pnpm add -g yarn@4installs Yarn Berry — it used to fail, because npm'syarnpackage stops at Classic — andpnpm add -g node@22/pnpm add -g deno@2install that Node.js or Deno release rather than a wrapper package that downloads one. In a project, naming a package manager records which one the project uses instead of installing it as a dependency, and naming a runtime records it underengines.runtimeasnode@runtime:22already did.The declaration goes where the package manager reads it. Yarn is started from a project pin by corepack, which reads only
packageManagerand only accepts an exact version there, sopnpm add yarn@4resolves the line and writes"packageManager": "yarn@4.18.0"— the same thingcorepack use yarn@4writes, down to the+sha512.…integrity for the Yarn Classic line that corepack pins its tarball with. Every other package manager is recorded indevEngines.packageManager, which holds a range. Only one of the two fields is ever left behind: they declare the same thing, and corepack refuses to run a project whose declarations disagree.A JavaScript package manager on a machine without Node.js gets a managed LTS runtime to run on.
What changes for a project coming from v11:
pnpm add yarnrecords the project's package manager instead of installing the npm package that shares the name (that package is still reachable aspnpm add yarn@npm:yarn@1.22.22),pnpm add -g yarninstalls the current Yarn line rather than Classic,pnpm add -g node/pnpm add -g denoandpnx node/pnx denoinstall a Node.js or Deno release rather than a wrapper package, and a globally installed package manager defers to a project's pin where there is one.Added an opt-in proof of concept that lets installs reuse a dependency's build output across machines, by publishing and restoring signed, organization-scoped artifacts through pnpr instead of running the lifecycle scripts locally.
Configure it with the new
remoteSideEffectsCachesetting. A workspace names the eligibleorganizationandpackages; everything describing the act of signing —publish,keyId,builderId,trustedKeys,privateKeyand the provenance fields — is refused inpnpm-workspace.yamland read from the global config file or the environment instead.Added the
audit.ignorePrunesetting. When set totrue,pnpm audit --fixremoves ignored GHSA entries that no longer appear in the audit report.pnpm initnow 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 owndevEngines.packageManager/packageManagerpin #7490.The version is read from the
latesttag on the package-manager registries. When that lookup cannot answer — no network, an unreachable or slow registry,offline, or alatestthat theminimumReleaseAge/trustPolicysettings reject —pnpm initpins the running version as before, and never fails or hangs on the lookup. Alatestthat is older than the running pnpm is never pinned either.Allowed
pnpm update --patchesto refresh registry revisions through a configured pnpr server while retaining locked package versions.Added explicit registry revision selection with
<version>+rNandpnpm update --patchesfor refreshing revision artifacts without changing package versions. Registry-backed lockfile policy checks recognize historical revisions, and pnpr now preserves safe revision histories from upstream registries.Added support for registry replacement tarballs using standard integrity values, explicit revision fields, registry routing from the
registriessetting, non-redirecting integrity-addressed URLs, canonical safe-integer revision numbers, and pnpr proxying for immutable upstream revision artifacts.Running
pnpm setup,pnpm self-update, or a command that modifies the global installation (such aspnpm add --global) throughsudonow fails withERR_PNPM_SUDO_NOT_SUPPORTEDinstead of silently operating on the root user's home directory. pnpm keeps global packages and configuration in the invoking user's home directory, so these commands never need root permissions. Read-only global commands (such aspnpm bin --global) still work under sudo.pnpm stage approvenow approves several staged packages at once. Run it without a stage id to pick from the staged versions interactively, or pass a list of stage ids. The whole batch is approved with a single one-time password, and pnpm asks for a new one only once the registry stops accepting it. Inside a workspace, the selected packages are approved in dependency order, and a package whose workspace dependency could not be approved is skipped instead of being published against a dependency that never reached the registry.Patch Changes
Deprecated the pnpmfile
filterLoghook in pnpm v12. The Rust CLI ignores it and emits a warning.The built-in compatibility database no longer adds dependencies that were detected by static analysis of published packages. Those entries named packages that are only imported for their types, so installing them was at best unnecessary and at worst broke the dependent:
@typescript-eslint/typesgained atypescriptdependency resolved to the newest release, which put TypeScript 7 under older@typescript-eslintversions and made ESLint fail with "Cannot read properties of undefined (reading 'Intrinsic')". The database keeps its@yarnpkg/extensionsentries and pnpm's own curated ones.When no directory above the project accepts a hard link — inside an AI agent sandbox that only grants write access to the project, or a container with just the project mounted writable — the default store is now created at
<project>/node_modules/.pnpm-storeinstead of in the pnpm home directory. In those environments the home store is either read-only or on another volume, which forces every package to be copied instead of hard linked #13525.Platinum Sponsors
Gold Sponsors
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.
Pull request closed