pnpm 12.19.0
pnpm 12.19.0 adds pnpm fund, lets the loaded node linker handle packages that need a build, and adds the projectScopedOptionalPeers setting and an experimental JSON lockfile format. It also includes a security fix: an offline install error no longer prints registry credentials. pnpm now checks the engines and platform fields of every workspace project, and many hoisted-linker and frozen-lockfile bugs are fixed.
Minor Changes#
-
Added
pnpm fund, which lists the funding URLs declared by the installed dependencies in the same treenpm fundprints. Use--jsonfor thenpm fund --jsonreport and--recursiveor--filterto report on workspace projects.pnpm fund <pkg>opens the funding URL of an installed package, and--whichpicks one when it lists several #7354. -
With
nodeLinker.type: loaded, packages that need a build no longer have to be listed innodeLinker.excluded. A package with a matching side-effects cache entry loads from the store. If side-effects cache reads and writes are enabled, pnpm builds an approved package once, saves the result to the side-effects cache, and then loads it from the store. This includes a build that changes no files in its package, so its script does not run again in other projects that use the same store. List a package insideEffectsCache.excludeif its build must run in every project, such as a git hook installer. pnpm materializes other cache misses, packages with native.nodeaddons, and their dependencies automatically.pnpm also scans JavaScript sources for filesystem imports and for
__dirnameorimport.meta, and materializes matching packages. The scan does not detect computed imports. List such packages innodeLinker.excluded. -
Added the
projectScopedOptionalPeerssetting. When it istrue, a project gets a missing optional peer dependency only if the project's own dependencies install a version of it. Another workspace project's dependencies, such as itsdevDependencies, no longer supply one.dedupePeerDependentskeeps deduplicating everything else #16666. -
Added the experimental
lockfile.format: v13setting. With it, pnpm writespnpm-lock.yamlin a JSON-like syntax, indented with tabs, with a trailing comma after every entry. The file is still valid YAML. The file has onepackagesentry per package version in place of thepackagesandsnapshotssections, and each entry records the versions of its peer dependencies as its properties. Updating a peer dependency then changes only the lines that record its version. pnpm reads lockfiles in both formats whatever the setting is. -
pnpm store addnow accepts directories of tarballs, such aspnpm store add ./tarballs, and adds every.tgz,.tar.gz, and.tarfile inside them recursively.pnpm store addis now faster when it adds many packages or local tarballs #17875. -
With
supportedArchitecturesset,pnpm installandpnpm fetchalso store, without linking them, the dependencies of the archives kept for the other listed platforms. A platform added to the setting gets its archives and their dependencies on the next install. Optional dependencies a listed platform cannot run are not stored, and one that fails to download is skipped. Dependencies from git repositories are not stored for other platforms. -
When
supportedArchitecturesnames exactly one platform and notcurrent, pnpm links that platform's variant of a package published with platform variants, together with that variant's dependencies. This holds for the list form and for theos,cpu, andlibcform. A variant whose floor or ABI the listed platform does not state is not linked for it: pnpm links another variant that fits, or the version's own archive. Changing the setting, or the platform of the host, such as anode_modulesmounted into a musl container or a Node.js with another ABI, relinks the package on the next install, even from a frozen lockfile. -
pnpm now provides a prebuilt binary for NetBSD 10 on x64 #17803.
Patch Changes#
Security#
- The error for a tarball that is missing from the store in offline mode no longer prints the credentials or token in the tarball URL.
Installs that failed or hung#
-
pnpm install,pnpm ci, andpnpm install-testoutside a workspace now fail withERR_PNPM_NO_PKG_MANIFESTwhen the directory has nopackage.json,package.json5, orpackage.yaml. They no longer create apackage.json, a lockfile, andnode_modulesthere #17727. -
Fixed
pnpm installhanging when a concurrent tarball download or manifest read is cancelled while other tasks are waiting on the same archive. -
Running a global command such as
nodein a project that pins its version no longer hangs while pnpm installs that version. The hang kept starting new processes until the machine ran out of memory. -
A global command such as
nodenow runs the version a project pins whenPNPM_CONFIG_LOCKFILE_DIR,PNPM_CONFIG_VIRTUAL_STORE_ONLY, orPNPM_CONFIG_SYMLINKis set in the environment. Before, the dispatch failed with "the installed node executable is not under" the store. -
pnpm installnow fetches fresh metadata for a package when a version allowed byminimumReleaseAgeExcludeis missing from the cached metadata. Before, it could fail withERR_PNPM_NO_MATCHING_VERSIONwithout contacting the registry #17723. -
A
${VAR}placeholder in a registry URL inpnpm-workspace.yamlor the project.npmrcnow fails the command. pnpm does not expand environment variables in these files. Before, pnpm ignored such a URL and resolved packages from another registry, such as npmjs.org. pnpm now warns when it ignores a placeholder in a proxy,pnprServer, oruserAgentsetting inpnpm-workspace.yaml#17762. -
Fixed local package installs failing while a workspace lockfile is being written.
-
pnpm self-updateand switching to thepackageManagerversion now work behind a proxy that ends a response by closing the TLS connection without aclose_notifyalert. The registry signature check failed there withERR_PNPM_PNPM_ENGINE_IDENTITY_UNVERIFIABLE#17763. -
A package tarball that keeps failing to download now fails the install after
fetchRetriesretries. pnpm used to download it again with a second full set of retries, which doubled the wait. The error now names the actual download failure. -
When the native binary of the pnpm version a project pins fails to download, pnpm now reports the download error. It used to report only that no native binary was found for the host.
-
POSIX bin shims and the
pnpm,pn,pnpx, andpnxlaunchers now run inside a Nix build on macOS with the sandbox enabled. Installing again replaces the shims already innode_modules#17750. -
Fixed pnpm leaving process pipes open after it exits on macOS pnpm/tasks#229.
Engines and platform checks#
-
pnpm install,pnpm add,pnpm update, andpnpm removenow check theengines,os,cpu, andlibcfields of every workspace project, including the workspace root.pnpm run -randpnpm exec -rcheck them before running anything. An incompatibleengines.pnpmfails the command. An incompatibleengines.nodeprints a warning, or fails the command withengineStrictenabled. An unsupported platform prints a warning. A project that downloads its own Node.js throughdevEngines.runtimeis checked against that Node.js #17751 pnpm/tasks#281 pnpm/tasks#299. -
Fixed missing warnings when a required dependency does not support the current Node.js version.
-
An optional dependency that declares no
os,cpu, orlibcfield is no longer skipped because its name contains only an operating system word, such asget-windowson macOS. pnpm now infers its platform from the name only when the name also has a cpu or libc token, such as-win32-x64#17774.
Peer dependencies#
-
With
dedupePeerDependents: false, a missing optional peer is no longer resolved from a version that only another workspace project depends on. The workspace root's dependencies still count ifresolvePeersFromWorkspaceRootistrue. Before, a tool in one project'sdevDependencies, such aswebpack-cli, could become an optional peer of another project's production dependencies #16666. -
pnpm installno longer gives a project an optional peer dependency that it skipped because a required peer would conflict with the project's dependencies. Before,dedupePeerDependentsmerged the project's copy of the package that declares the peer with another project's copy that resolves it #17781. -
pnpm installno longer fills in a missing optional peer dependency if a required peer it brings in conflicts with a version the project depends on. Before, such a peer was installed along with it and reported as unmet #17718. -
pnpm install --filterin a sparse checkout now counts the dependencies of workspace projects that are missing from disk when it resolves peer dependencies and marks packages as optional. The lockfile it writes no longer differs from a full checkout in those entries #17791.
Frozen lockfile and workspace links#
-
pnpm install --frozen-lockfilenow succeeds when a workspace project recorded inpnpm-lock.yamlhas an empty directory, such as a git submodule that was not checked out #17712. -
pnpm install --frozen-lockfilenow fails withERR_PNPM_OUTDATED_LOCKFILEwhen a workspace package linked through a plain semver range, such as^7.9.5, has changed to a version outside that range #17806. -
pnpm install --frozen-lockfileandverifyDepsBeforeRunnow accept aworkspace:dependency on a project whose version is not valid semver, such asworkspace:1on a project at version1. Before, they reported the lockfile thatpnpm installhad just written as out of date. -
pnpm install --frozen-lockfilenow accepts a lockfile when an injected workspace package declares afile:orlink:dependency with backslashes, such asfile:..\vendor, or with a slash after the protocol, such asfile:/./vendor. Before, the install reported the lockfile as out of date right afterpnpm installhad written it #17720. -
When a package injected through a path, such as
file:../libor../lib, gives one of its dependencies acatalog:specifier,pnpm install --frozen-lockfilenow reports the lockfile as outdated. For a bare path such as../lib,pnpm installnow also fails the way a fresh resolution does. Before, these installs accepted the existing lockfile #17722. -
Dependencies declared as
workspace:entries in catalogs now remain in the lockfile whenexcludeLinksFromLockfileis enabled. Frozen installs can restore these links afternode_modulesis removed pnpm/tasks#260. -
With
dedupeDirectDeps,pnpm installnow compareslink:dependencies by the directory they point at. A project'slink:dependency is removed when the workspace root links the same directory under a different path. It is kept when the root'slink:dependency has the same text but points at another directory. pnpm reports a dependency the root provides as removed only once, when it removes the project's link. The summary shows the package version of a removedlink:dependency pnpm/tasks#293.
Hoisted node linker and custom modulesDir#
-
With
nodeLinker: hoisted, a package that runs a lifecycle script or has a patch is now cloned or copied intonode_modules, as with the isolated linker. A build that rewrites one of its files no longer changes the store copy shared with other projects. -
With
nodeLinker: hoisted, pnpm no longer deletes a directory outsidenode_moduleswhen it removes a package whose scope directory is a symlink. -
With
nodeLinker: hoisted,pnpm install --filterin a sparse checkout now drops previously installed workspace projects that are missing from disk from the hoistednode_modulestree. It no longer recreatesnode_modulesinside a missing project's directory. Packages that only the missing project used are also removed from the rootnode_modules. -
With
nodeLinker: hoistedanddedupeDirectDeps,pnpm installnow removes a project'slink:dependency that the workspace root provides, and reports eachlink:dependency it removes. Removing the last scopedlink:dependency of a scope also removes the empty@scopedirectory. -
With
nodeLinker: hoistedand a custommodulesDir, pnpm now installs a workspace project's dependencies that cannot be hoisted to the root into the project'smodulesDir. The next install that relinks the project removes the copies that earlier installs left in itsnode_modules. A later install restores the project'smodulesDirwhen it was removed. Package directories that pnpm did not install are now also moved to.ignoredin a custommodulesDir. -
Fixed installs in a workspace whose
modulesDiris an absolute path, including one that goes through a symlink such as/tmpon macOS, or a path that starts with... Dependencies of workspace projects no longer end up outside the workspace, and the root project's dependencies,link:dependencies, and commands are linked into the configured directory.pnpm bin,pnpm root,pnpm run,pnpm exec, andpnpm versionnow use that directory too pnpm/tasks#285.
Store and builds#
-
Concurrent installs into a global virtual store no longer wait on each other. A package that needs a build is built in a private directory and moved into the store once its build has finished, so another project never links a half-built package. An install with
--ignore-scriptslinks the packages it does not build from their own store directories, andpnpm store pruneremoves the build directories of installs that were interrupted #15568. -
Packages that an older pnpm version added to the store now run their install scripts. Before, pnpm could skip those scripts and hardlink the package's files to the store.
-
pnpm installnow keeps patches to bundled dependencies when it restores a package from the side-effects cache, including files a patch adds #17734. -
Local installs on macOS now keep cached clones separate from the global virtual store.
pnpm store pruneremoves this cache, so unused cached packages no longer accumulate #17907. -
pnpm store prunenow stops if a dependency link cannot be read. This keeps installed packages from being removed after an incomplete scan. -
A repeat
pnpm installnow runs a full install when theruntimeOnFailsetting changes. Before, it printed "Already up to date" and did not install the runtime a project declares indevEngines.runtimepnpm/tasks#303. -
The sync of an injected workspace dependency now copies a file whose source has reached the filesystem's limit on hard links.
pnpm patch-commitnow copies a file into its scratch directory when the filesystem refuses to hard link it, unless the path is missing or already occupied pnpm/tasks#283.
Speed#
-
pnpm installis faster when it clones files from the store, which it does by default on macOS. On a Mac with endpoint security software, a cold install of a project with about 1,350 packages took 29% less time pnpm/tasks#280. -
pnpm installunpacks packages that contain many files faster. Unpackingtypescripttook 30% less time in our measurements pnpm/tasks#288. -
Resolving against a registry that sends
Cache-Control: no-cache, such as Azure Artifacts, is fast again when the lockfile already decides the version. pnpm reads the cached metadata for a pinned version, or for a range the lockfile version is proven to win, without a registry request #17758. -
POSIX bin shims start fewer processes. Running a dependency's bin through its shim from Git Bash on Windows starts 7 processes where it started 15. Installing again replaces the shims already in
node_modules.
Windows#
-
pnpm now installs dependencies and runs lifecycle scripts from projects on mapped Windows network drives. When a network share refuses the links in
node_modules, the error now suggests settingnodeLinker: hoistedor moving the project to a local drive #17747. -
packageImportMethod: autonow tries hardlinking before cloning, as it does on Linux. Installs from a warm store on a Dev Drive are faster. An install no longer fails when the store refuses a hardlink with an access-denied error, and pnpm tries cloning or copying the file next #17809. -
pnpm runandpnpm setupno longer clear the visible terminal when a project path contains non-ASCII characters. -
A failed attempt to create a directory junction in
node_modulesno longer leaves a.pnpm-junction-*directory behind. -
pnpmstarts faster. It loads system libraries such asuser32.dllandshell32.dllthe first time a command uses them, sopnpm --versionloads 11 libraries where it loaded 25.
pnpm deploy#
-
pnpm deploynow runs the build scripts of a workspace project that it installs as afile:dependency, without a machine-specificallowBuildskey. AnallowBuildsentry that denies the project still applies #17740. -
pnpm deployno longer fails when the deployed project depends on a workspace package that setspublishConfig.directory. pnpm installs that package from its publish directory and runs its build scripts #17771. -
pnpm deploywithout a shared workspace lockfile now resolves a relativeworkspace:path, such asworkspace:../lib, from the source project's directory. It used to be resolved from the deploy target and fail #17804. -
pnpm deploy --offline --trust-lockfilenow reuses dependencies pinned in the source lockfile from a populated store when registry metadata is unavailable. This works with per-project lockfiles,--legacy, and npm aliases #17888.
Other commands and settings#
-
pnpm dist-tagandpnpm unpublishnow offer browser-based authentication when the account's two-factor authentication uses a security key. Before, they asked for a one-time password in that case #17832. -
pnpm publishwith provenance now downloads Sigstore's current signing configuration before signing. A failed or slow download is retried with the same retry policy and deadline as the signing requests. -
pnpm packandpnpm publishnow apply the first rule of a.npmignoreor.gitignorethat starts with a UTF-8 BOM. -
pnpm outdatednow checks dependencies when the project has no lockfile, for example withlockfile: false. TheCurrentcolumn shows the installed version, or the lowest version allowed by the declared range if the dependency is not installed #17811. -
pnpm self-updatenow updates the project'sengines.pnpmrange along with its pnpm pin, keeping the^or~operator #17732. -
On Linux and macOS,
pnpm runno longer sometimes reports a lifecycle failure afterCtrl+Cinterrupts a script. -
Config dependencies use the declared registry
serverTypewhen recording and installing packages pnpm/tasks#236. -
Relative directory selectors set on
config.filterorconfig.filterProdby anupdateConfighook now resolve from the workspace root even when--diris passed orpnpmruns from a subdirectory #17906. -
Commands that add settings to
pnpm-workspace.yamlnow fail with an error if the file contains onlynull, and leave the file unchanged. Before, they wrote invalid YAML. -
Shell tab-completion now completes directory and file paths for path options such as
-C,--dir,--prefix,--store-dir, and--env-file. Before, options without a fixed list of values offered command names #17884.