Repository navigation
Releases
pnpm 12.20
pnpm 12.20.0 records the specifiers of local directory dependencies in the experimental v13 lockfile and fixes several nodeLinker: hoisted, peer dependency, and overrides bugs. The bundled node-gyp no longer ships the undici version that security scanners flag. Remote resolution and shared build caches no longer work with pnpr 0.1.0-alpha.19 or older.
Minor Changes
- With
lockfile.format: v13,pnpm installrecords the dependency specifiers of local directory packages. Frozen installs reject changed specifiers even when the locked versions still satisfy them.
Patch Changes
Upgrading and security
-
Remote resolution, shared build caches, and release notes no longer work with pnpr 0.1.0-alpha.19 or older. pnpm now reads the capabilities a pnpr server advertises under
pnpr.protocols. Installs fail when the server does not confirm the requested resolution and verification policies. -
The
node-gypbundled with pnpm no longer ships undici 6, which security scanners flag for CVE-2026-84947. On Node.js 26,node-gypdownloads through a proxy or with a custom CA file no longer crash with "invalid onError method" #17941.
Resolving dependencies
-
Version-selector
overridesentries such as"ms@^2.0.0": "2.1.1"now apply to dependencies declared with thecatalog:protocol when the catalog entry's range matches the selector #17922. -
A root
overridesentry that points a package at aworkspace:path, such asworkspace:./packages/foo, now also applies when a registry package depends on that package pnpm/tasks#270. -
Git dependencies now accept npm's
::separator between fragment parameters, such asgithub:owner/repo#v1.0.0::path:packages/foo. Before, pnpm read the whole fragment as a branch name and failed to resolve it #17827. -
pnpm dedupenow keeps a locked runtime version, fromdevEngines.runtimeor from a dependency'sengines.runtime, when the declared range still admits it.pnpm dedupe --checkno longer fails each time a newer Node.js release in the range comes out #17984. -
With
autoInstallPeersFromHighestMatch, conflicting non-semver peer dependencies now remain missing, and identical non-semver peer specifiers are preserved. Preferred registry versions no longer replace local paths or protocol-prefixed specifiers when pnpm installs missing peers pnpm/tasks#272.
Hoisted node linker
- With
nodeLinker: hoisted, a package now keeps its nested copy of a dependency that it resolved at a version other than the top-level one, including a peer dependency its parent supplies. Before, pnpm could remove that copy as a duplicate or give the top-level slot to a peer variant found deeper in the graph. The package then loaded the top-level version.
Lockfile and peer dependency graphs
-
Fixed
pnpm installkeeping stale peer bindings after dependency changes. Required dependencies no longer remain marked optional when another workspace project removes a shared package pnpm/tasks#221. -
Repeated installs now keep the same dependency links when packages form cycles with several peer dependency variants pnpm/tasks#221.
-
Fixed writing
lockfile.format: v13for dependency cycles with missing, optional, or transitive peers. Frozen installs also accept unchanged injected packages whose peers use catalogs. With--no-optional, frozen installs reject required catalog children recorded only as optional dependencies pnpm/tasks#221. -
pnpm deploypreserves an injected workspace package's resolved dependencies when they differ from the workspace project's own dependencies. This also applies to packages withpublishConfig.directorypnpm/tasks#221.
Running scripts and runtimes
-
Scripts that pnpm runs in a process group of their own are now ended when pnpm is killed right after starting them. A parallel run that was killed before pnpm registered its scripts left all of them running.
-
When a command name matches no script and pnpm falls back to running it as a command, the install that runs before scripts now runs once. Its errors and warnings print once #17977.
-
Failed downloads by project-aware global runtime commands now explain which configuration applies. Node.js failures also list the global mirror settings and environment variables to check.
Other commands and settings
-
Fixed
pnpmoccasionally crashing on the first HTTPS request, mostly on Windows virtual machines under load. pnpm now seeds TLS randomness from the operating system's random number generator pnpm/tasks#239. -
Fixed
pnpm deprecate,pnpm undeprecate, andpnpm unpublishfailing with revision conflicts when a registry cache serves stale package metadata. -
pnpm sbomnow reports malformed workspace manifests. Before, it omitted their packages pnpm/tasks#59. -
Fixed
pnpm store statusreporting approved builds of git dependencies as modified when the store contains legacy build metadata. -
pnpm now recognizes Capawesome Cloud and RWX as CI environments.
Platinum Sponsors
|
|
|
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
pnpm 12.19
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](#1...
pnpr 0.1.0-alpha.19
pnpr 0.1.0-alpha.19 stores package metadata in PostgreSQL or MySQL as well as SQLite, stores identical archives once, and adds pnpr gc to reclaim unreferenced archives while serving. It does not migrate alpha.18 data, so read the upgrade steps below before you upgrade. It also includes a security fix: pnpr no longer logs the credentials of some malformed URLs. The release also tightens account, team, and publish validation.
Minor Changes
Upgrading from alpha.18
-
pnpr does not migrate alpha.18 data. At startup it refuses an alpha.18 metadata database or SQL auth database, including a local
tokens.db, and leaves it unchanged. To upgrade:- Back up these databases, then delete pnpr's tables or point pnpr at new, empty databases.
- Point
storageor thes3:prefix at a fresh location. Hosted archives use a new layout. A fresh localstoragedirectory also gives you a fresh SQLite metadata file. - Replace the
backendandmetadatablocks withdatabase.url. pnpr refuses to start while either block is present. - Rename
auth.htpasswdtoauth.users. If accounts stay in the local account file, which is the case withoutdatabase.urlor with asqlite:one, runpnpr import-htpasswd --source <old htpasswd>to bring over existing passwords. With any otherdatabase.url, recreate the accounts. - Passwords now need at least 8 characters, and pnpr checks this at login too. Run
pnpr add-user <username>to set a new password for an account with a shorter one. - List account IDs, not usernames, in
auth.admins, access lists, and OIDC bindings. Give each entry underteamsanameandmembers. - Replace
pnpr oci-gcandpnpr verify --prune-orphanswithpnpr gc. - Recreate accounts and tokens, republish packages, and reconcile the SCIM directory with the identity provider. Recreated accounts get new IDs.
-
pnpr now runs numbered schema migrations for its metadata and auth tables. Each configured SQL database records its applied steps in its own
schema_migrationstable, starting at version1.
Storage
-
pnpr can store package metadata in PostgreSQL and MySQL. Set
database.urlto share accounts, tokens, and package metadata in one database. -
A hosted registry stores identical bytes once per org and ecosystem, whichever packages, versions, or image repositories publish them. A publish whose bytes are stored already stores nothing pnpm/tasks#269.
-
pnpr gcremoves the archives that no hosted package maps, in every hosted npm, PyPI, Cargo, and OCI registry. It runs while the registry keeps serving and other replicas keep publishing. It removes the bytes of unpublished versions and packages, image blobs that no retained manifest reaches, and the archives of publishes that stopped or were refused at their commit. It also finishes interrupted reclaims. The newgc.leasesetting sets how long an interrupted reclaim is left before another run finishes it. It defaults to1h.pnpr verifyno longer removes orphaned blobs. It still fails on missing or corrupted npm package blobs, and reports orphaned blobs in every hosted registry. Its--prune-orphansand--min-ageoptions are removed.
Clients
-
The server applies the
projectScopedOptionalPeerssetting that a client sends, and records it in the lockfile it returns. -
Added
oci.chunkMaxBytesto advertise an upload chunk size to OCI clients that supportOCI-Chunk-Max-Length.
Patch Changes
Security and access
-
pnpr no longer logs the credentials of a URL whose
?or#appears before@. It now redacts such a URL as<scheme>://redactedpnpm/tasks#301. -
Renaming an account preserves its permissions and package ownership. Reusing a deleted username or team name starts with fresh permissions.
-
User lookups now require authentication. Team names and rosters are visible only to registry team members and administrators. New accounts cannot use usernames that look like an existing username.
-
pnpr now refuses stale authorization when a team roster, managed access rule, or SCIM directory read exceeds five seconds. A stalled roster or rule read no longer delays SCIM refreshes #17836.
-
Fixed MySQL-backed authentication failing to read case-sensitive usernames.
-
pnpr now keeps package ownership after unpublish, team rosters, rule overrides, SCIM users, and staged-publish metadata in its metadata database. An unpublish records ownership and removed versions atomically.
Publishing npm packages
-
npm publishes now reject package names that npm disallows for new packages, including uppercase letters and an
@in the package part. Existing upstream packages remain readable. -
pnpr rejects publishes when a version key or manifest version is not valid semver pnpm/tasks#267. Publishing a version key with build metadata returns an explicit error.
DELETErequests for invalid versions return400. -
A tarball
DELETEof a version that the package still lists unpublishes that version. -
Retrying an identical publish of an unpublished version now returns
403. Batch publish retries also fail if any published member has been unpublished. -
Stale partial unpublish and deprecation requests now fail with
409. Deprecation requests preserve dist-tags and other package metadata. -
Dist-tag writes now reject names that parse as semver ranges and targets that are not published versions. Removing
latestis rejected while published versions exist. -
Release note edits and deletions no longer retain earlier text for restoration.
Other ecosystems
-
Deleting the last manifest of an OCI repository now removes the repository from
/v2/_catalog, andGET /v2/<name>/tags/listanswers404 NAME_UNKNOWNuntil another manifest is pushed.DELETE /v2/<name>/blobs/<digest>now answers403 DENIEDwhen a retained manifest still references the blob. -
Deleting an OCI manifest by digest now invalidates the saved batch publish outcomes that include it. Deleting an OCI tag leaves batch replay intact. A deleted OCI manifest can be published again.
-
The Python registry proxy now supports core-metadata declarations and caches
.metadatadownloads. pip and uv can read dependency metadata without downloading whole wheels. -
Python uploads now reject unreadable archives and metadata that names a different project or version. Publishers can change the yank status of individual files. Administrators can delete files from the index and disable their downloads. Deletion retains stored archive bytes.
-
Cargo package claims now cover both
-and_spellings. Requests for a hosted crate cannot fall through to an upstream registry under the alternate spelling. New versions must use the spelling of the crate's first published version. -
Cargo sparse index files now send ETags and return
304 Not Modifiedwhen the client already has the current content.
Speed and caching
-
Large package metadata requests no longer block unrelated registry requests. Repeated requests reuse rendered metadata while the package content is unchanged.
-
Sped up publishing with an
Idempotency-Keyheader. -
Responses for public packages read through a named registry, such as
/npm/~npmjs/<package>, are now cacheable by shared HTTP caches. Packages that need authentication still getCache-Control: private, no-store. PyPI project pages now sendVary: Accept. -
A tarball that pnpr downloads from an upstream registry is no longer cut off after the upstream
timeoutwhile it is still arriving. Thetimeoutnow limits how long pnpr waits for the upstream to answer and for each further part of the download. Before, a tarball that took longer than 30 seconds to download from a slow upstream failed every time. -
Response body timeouts no longer open an upstream's circuit breaker. Circuit-open responses name the upstream and include
Retry-After.
Responses and logs
-
npm clients now display the server's explanation when a registry request fails. Authentication errors distinguish invalid tokens from missing credentials. A
401response includes aWWW-Authenticatechallenge. -
Package metadata responses no longer include the internal
formatVersionfield oraccess: null. -
Fixed
npm searchfailing on hosted packages with maintainer names. Search results for hosted packages no longer link to an external package website. -
pnpr logs scoped package URLs such as
https://registry.npmjs.org/@babel/cliwith their registry host intact. It previously logged them ashttps://redacted@babel/cli.
pnpm 12.18
pnpm 12.18.0 packs, publishes, and installs packages with platform variants on pnpr registries. It also adds per-skill approval of agent skills and pnpm store add for offline installs from downloaded tarballs. Repeat installs and verifyDepsBeforeRun no longer read the package.json of unchanged workspace projects. The release fixes several peer dependency resolution bugs too.
Minor Changes
Agent skills
-
pnpm now names the link to a dependency's agent skill after the skill alone. A skill that the
@acme/kitpackage ships inskills/auth/is linked as.claude/skills/auth, not.claude/skills/pnpm-@acme+kit-auth. The next install replaces links with the old names. pnpm lists the linked skills in a# pnpmblock of each agent skill directory's.gitignore#17455.When two approved packages ship a skill of the same name, or the name is already taken in an agent skill directory, pnpm links the skill as
pnpm-<package>-<skill>. Before, the install failed. -
pnpm approvenow asks which agent skills to link when a package ships more than one. pnpm records the chosen skills as a list, such aspermissions.<pkg>.skills: [guide, migrate], and does not offer the other skills for approval again. The first time a project approves a skill,pnpm approvealso asks which agent directories to link skills into, and records the answer underskills.dirs. -
pnpm links agent skills nested in category directories, such as
skills/engineering/implement/SKILL.md, up to three levels belowskills/. Before, it linked only the skills directly underskills/.
Platform variants
These features need a registry declared with serverType: pnpr.
-
pnpm packpacks one archive per entry ofpublishConfig.variants, each platform variant as<name>-<version>~<tag>.tgz.pnpm pack --variant <target>packs only the archive whose entry lists that target.pnpm pack --jsonprints theintegrityandsizeof every archive it writes. -
pnpm publishpublishes a package'spublishConfig.variants. pnpm uploads every archive before the publish request that references them. Publishing variants to any other registry is refused unless--no-variantsis given, which publishes only the archive that targetsany. -
pnpm publish --upload-variant <target>uploads the archive of onepublishConfig.variantsentry to the registry's staging area and prints<target>=<integrity>.pnpm publish --variant-integrity <target>=<integrity>then publishes the version from archives that other jobs uploaded. With--recursive, each value names its package as<name>~<target>=<integrity>. -
pnpm publish -r --batchnow uploads each package archive on its own request and skips archives the registry already holds, so the batch request no longer grows with the archives it publishes. A batch of more than 256 packages still carries every archive inline. Packages withpublishConfig.variantscan be published in a batch. A batch with variants that would reference more than 256 archives on one registry fails withERR_PNPM_BATCH_PUBLISH_TOO_MANY_ARCHIVESpnpm/tasks#244. -
pnpm publish --provenancesigns one statement that names every archive of a version withpublishConfig.variants. A platform variant's archive is named by the version's package URL with avariantqualifier. -
Rerunning
pnpm publishafter the version was already published now succeeds when the published version is the same release. pnpm compares the archives, the provenance statement when the rerun signs one, and the dist-tag. When the published version differs, the publish fails withERR_PNPM_PUBLISHED_RELEASE_DIFFERS, which names what differs. -
pnpm installs the platform variant of a package that fits the host, or the package's own archive when none does. The lockfile records every variant. A version published with variants only, and no archive for other hosts, installs on a host one of the variants fits. On any other host, an optional dependency is skipped, and any other dependency fails with an error that lists the platforms the version is published for.
-
supportedArchitecturesplatform lists accept platform tags, the same tagspublishConfig.variantsentries name. An entry can state floors and an ABI, such aslinux-x64-manylinux_2_28,darwin-arm64-macos_14_0,linux-x64-napi8orlinux-x64-node127, and any Node.js OS and CPU, such asfreebsd-x64. The spellings accepted before still work. -
With
supportedArchitecturesset, pnpm also fetches into the store the variants that the other listed platforms need, when it downloads a package with platform variants.
pnpr registries
-
pnpm downloads less metadata from a registry declared with
serverType: pnpr. A large package document leaves out the manifests of rarely picked versions, and pnpm fetches one only when it picks that version. In a benchmark that resolves 787 packages, the package documents shrank from 30.3 MB to 2.5 MB gzipped. pnpm caches these documents inv12/metadata-pnpr, and thepnpm cachecommands cover them.pnpm warns when a registry that is not declared
serverType: pnprturns out to be pnpr. For a registry declaredserverType: pnpr, aminimumReleaseAgelonger than 30 days fails withERR_PNPM_UNSUPPORTED_MINIMUM_RELEASE_AGE. -
pnpm publishsends release notes to registries declared withserverType: pnpr. A package's notes are the section for its version inCHANGELOG.md, or the notes thatpnpm version -rcomposed from change intents.--release-notes <file>gives the notes from a file, and--no-release-notessends none.--publication-namenames a release published with--batch.In a workspace that uses pnpm's versioning,
pnpm version -r --release-notes <file>puts a summary above the composed notes of the release.pnpm view <pkg>@<version> releaseNotes,pnpm outdated --notes, andpnpm update --interactiveshow the release notes from these registries.
Store and hooks
-
pnpm store addnow prepares the store for offline installs from package tarballs downloaded some other way. Afterpnpm store add ./tarballs/*.tgz,pnpm install --offline --frozen-lockfileinstalls each registry package whose lockfile entry pins one of those tarballs #2978. -
Custom pnpmfile fetchers can use
fetchers.gitHostedTarball,fetchers.git, andfetchers.binarypnpm/tasks#191.
Patch Changes
Installing packages
-
Running
nodeorpnpm installin a project that pins a Node.js version pnpm has not downloaded yet no longer hangs when aglobalPnpmfileis configured #17530. -
pnpm installwithenableGlobalVirtualStoreno longer fails on Windows for a dependency from a named registry, such asgh:ornpmjs:#17665. -
Concurrent installs that share a global virtual store no longer fail with
ERR_PNPM_CMD_SHIM_SYMLINK_BINwhen they link the same executable at the same time #17587. -
pnpm installandpnpm cinow succeed when dependency skills are approved but no agent skill directory exists. The skills stay unlinked until a directory exists #17574. -
Commands run from a workspace package now use the same default store as the workspace root. Before,
pnpm addin a package could fail withERR_PNPM_UNEXPECTED_STOREwhen the workspace root was its own mount point and the home store was on another volume, as in a sandbox or a container.A workspace installed with the old behavior may need one
pnpm install --forcefrom the workspace root. -
pnpm deploynow keeps a symlink in the project whose target is missing from the source, such as a link into anode_modulesdirectory that was removed after the build. Deploy used to drop such a link silently #17571. -
pnpm installnow links the runtime that an earlierpnpm install --no-runtimeskipped, andpnpm install --no-runtimeremoves a runtime that an earlier install linked #17542. -
With
enableGlobalVirtualStore,pnpm installagain runs a dependency's build scripts in a project whose global virtual store slot another project already built, unless the side-effects cache has build output to restore for it. A build that writes only outside its own package, such as a git hook installer, now takes effect in every project #17526. -
Commands of a package whose
binpaths contain excess parent directory segments, such as../dist/cli.js, are no longer missing. Such paths now resolve inside the package #17586. -
Generated POSIX shell shims now quote their paths, so shell special characters in a path stay literal.
Lockfile and local paths
-
Packages from the default registry keep plain lockfile keys, even when a prefix such as
npmjspoints at the registry set asregistry. A dependency written with such a prefix shares the plain entry of the same package. The next install rewrites keys that earlier versions qualified this way #17575. -
pnpm install --frozen-lockfile,pnpm approve-builds, andpnpm rebuildnow accept a lockfile that resolves a*dependency to a prereleaselatestversion. pnpm wrote such a lockfile and then reported it as outdated #17594. -
pnpm install --frozen-lockfilenow accepts a lockfile written withexcludeLinksFromLockfileandlinkWorkspacePackageson, when a project depends on a ...
pnpm 11.28.11
pnpm 11.28.11 fixes frozen installs with excludeLinksFromLockfile, pnpm link in workspaces, pnpm deploy with dangling symlinks, and verifyDepsBeforeRun starting needless installs.
Patch Changes
Installing packages
-
pnpm deploynow keeps a symlink in the project whose target is missing from the source, such as a link into anode_modulesdirectory that was removed after the build. Deploy used to fail withENOENTon such a link #17571. -
A package with
bundledDependenciesand a build script now keeps its bundled dependencies when it is restored from the side-effects cache #17523. -
pnpm installnow links the runtime that an earlierpnpm install --no-runtimeskipped #17542. -
Commands of a package whose
binpaths contain excess parent directory segments, such as../dist/cli.js, are no longer missing. Such paths now resolve inside the package #17586. -
Generated POSIX shell shims now quote their paths, so shell special characters in a path stay literal.
-
A repeat
pnpm installnow rewrites the bin shims afterextendNodePathchanges. Before, it reported "Already up to date" and the shims kept theNODE_PATHof the previous setting.
Lockfile and resolution
-
pnpm install --frozen-lockfilenow accepts the lockfile it wrote withexcludeLinksFromLockfileandlinkWorkspacePackageson, when a project depends on a workspace package through a plain version range or acatalog:entry. It used to fail withERR_PNPM_OUTDATED_LOCKFILE. -
pnpm install --frozen-lockfile,pnpm approve-builds, andpnpm rebuildnow accept a lockfile that resolves a*dependency to a prereleaselatestversion. pnpm wrote such a lockfile and then reported it as outdated #17594. -
Packages from the default registry keep plain lockfile keys, even when a prefix such as
npmjspoints at the registry set asregistry. A dependency written with such a prefix shares the plain entry of the same package. The next install rewrites keys that earlier versions qualified this way #17575. -
Relative local paths in root
overridesnow resolve from the workspace root when each project has its own lockfile #17691.Installing a local directory dependency no longer fails when another project's install is writing a lockfile in that directory.
-
Peer deduplication no longer gives a project different copies of a dependency across direct and transitive paths when an optional peer has no provider #17666.
-
A Node.js version range such as
>=26.13, for example indevEngines.runtime, now resolves to the newest stable release that satisfies it. pnpm picks a prerelease only when no stable release matches #17516.
verifyDepsBeforeRun
-
With
verifyDepsBeforeRun,pnpm runandpnpm execnow report dependencies as out of date after a dependency is added to thepackage.jsonof a local directory dependency, such as"lib": "file:../lib". Before, the command ran without the new dependency installed. -
verifyDepsBeforeRunno longer reports dependencies as out of sync when only theversionfield of a single-project repository changed #17560. The same holds for a workspace project that no dependency can resolve to. A script run after such a change no longer starts an install first.
pnpm link
-
In a workspace,
pnpm link <dir>now adds thelink:dependency to thepackage.jsonof the project where the command runs, not to the workspace root'spackage.json. The link is created in that project'snode_modules, as documented. The override is still written to the rootpnpm-workspace.yaml. -
pnpm linkwith several paths now records every linked package in theoverridesofpnpm-workspace.yaml. Some of them could be missing before. -
pnpm linknow fails without changingpackage.jsonorpnpm-workspace.yamlwhen the linked package's name is not a valid npm package name. It used to write that name to both files, which made every later install fail #17475.
pnpm update
-
pnpm updatewith only negated selectors, such aspnpm update '!foo', now leaves the dependencies listed inupdate.ignoreDepsunchanged pnpm/tasks#243. -
pnpm update --depth 0without selectors now does nothing when every direct dependency is listed inupdate.ignoreDeps. Before, it failed withERR_PNPM_NO_PACKAGE_IN_DEPENDENCIESpnpm/tasks#251.
Store and self-update
-
pnpm store prunenow keeps packages linked from a configured modules directory pnpm/tasks#230. -
pnpm store statusno longer reports untouched packages as modified when the global virtual store is enabled. If the installed global-store layout cannot be verified, the command reportsERR_PNPM_STORE_STATUS_UNVERIFIABLEand asks for a reinstall #17631. -
pnpm store addnow fails right away withERR_PNPM_FROZEN_STORE_WRITEwhenfrozenStoreis enabled. It used to retry each download for minutes before failing, or fail with a SQLite error on an empty store. -
pnpm self-updatenow reinstalls a cached engine if its executable cannot run. This repairs shared store entries damaged by an earlier install #17582.
Platinum Sponsors
|
|
|
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
pnpr 0.1.0-alpha.18
pnpr 0.1.0-alpha.18 keeps hosted package metadata in SQLite or a shared libSQL database, logs release records in transparency logs, and accepts packages with platform variants. The documents metadata backend is gone, so packages hosted by an earlier pnpr have to be published again.
Minor Changes
Metadata storage
-
pnpr now keeps hosted package metadata in SQL only. By default it uses a SQLite database at
<storage>/.metadata/metadata.sqlite. Themetadatablock names another database by URL, such asurl: sqlite:./metadata.sqlite. Thedocumentsmetadata backend is removed, and a config that setsmetadata.backendormetadata.pathis refused.pnpr does not import the metadata of an existing store. A store that kept its metadata as documents starts with empty metadata, so republish its packages after upgrading. An
s3:store that hosts packages needs a libSQLmetadata.url.migrate-storagemoves archives only and needs that libSQL database to hold the metadata already. -
pnpr can keep hosted package metadata in a libSQL database, such as Turso, shared by several pnpr instances. Set
metadata.urlto alibsql://URL. It works with ans3:block, so archives stay in object storage. Withmetadata.replicaPath, each instance reads package documents from a local replica that syncs everymetadata.syncInterval. Permission checks and the reads a write depends on always read the primary. -
pnpr refuses any change to a published npm version's manifest, and never accepts an unpublished version again. pnpr records who moved each dist-tag, and when. Hosted npm package responses carry a
pnpr-generationheader. -
A reader sees all of a publish or batch publish, or none of it. A retried publish gets its original response instead of a 409. Batch publishes accept an
Idempotency-Keyheader. pnpr records each publish as a publication and serves it at/-/publications/<org>/<id-or-name>. -
pnpr stores release notes for package versions and publications. A publish can include them in a
notesfield. Publishers can edit or delete a note later. npm clients read a version's note at/npm/<name>/<version>/notes. A package's notes are listed at/npm/<name>/-/notes. A publication's note is at/-/publications/<org>/<id-or-name>/notes.
Publishing
-
A hosted pnpr registry rejects a publish whose version metadata disagrees with the
package.jsoninside its archive. Its400response names the package, the version, and each differing field. pnpr checks the dependencies, the platform fields,bin, and the install scripts. pnpr also rejects a publish whose archive does not decode or holds more than onepackage.json.pnpr accepts the rewrites that npm and older clients make while publishing, such as a
binstring expanded into an object. pnpr publishes a version without semver build metadata. pnpr serves a version's checked fields as its archive states them, withhasInstallScriptand_hasShrinkwrapalways set. Other fields, such asdescription, are served as submitted. -
pnpr accepts release archives uploaded by integrity before the publish that references them, through
PUT /-/pnpr/v0/artifacts/blob?integrity=<sha512-…>without an owner #17608. An npm publish, alone or in a batch, can then reference a staged archive: the attachment carries the archive'sintegrityin place ofdata. A version and its platform variants therefore no longer have to fit in one request body. An archive the publishing account has not staged, or one that has expired, fails the whole publish. -
Trusted-publishing workload credentials can stage archives for the packages they are configured for, and send batch publish commits for those packages. A batch entry for any other package, or of another ecosystem, is refused.
-
A hosted registry that declares
tarballs: tarnow stores each published version as one tar compressed with zstd at level 19, and no.tgz. A client that accepts zstd gets those bytes, so even its first download is at level 19. Any other client gets the tar decoded, gzip-encoded if it asks for gzip.pnpr now refuses to start when a router without
tarballs: tarlists such a registry as a source. Versions that an earlier pnpr published to such a registry are no longer served. Publish them again.
Package documents and platform variants
-
pnpr serves its own package document to clients that ask for
application/vnd.pnpr.install-v2+json. A large document leaves out the manifests of rarely picked versions. It serves each version's release record at<name>/<version>+r<revision>. npm's documents served by pnpr no longer carrydist.pnpr,dist.revision, ordist.revisions. The newpackageDocumentssetting configures the split threshold and the publish-time window. -
pnpr accepts platform variants with a published version and serves each one at
<name>@<version>/variants/<tag>. Only pnpr's own package document lists them, so npm clients install the version's own archive. -
pnpr accepts a version published with platform variants only, without an archive for every other host. It generates the archive that npm's documents point to, whose
package.jsonlists the variants' platforms, so npm reports an unsupported platform instead of installing an empty package. -
pnpr rejects a version published with a provenance statement that does not name each of its archives by its package URL and SHA-512. A platform variant's archive is named by the version's package URL with a
variantqualifier, which its release record entry lists aspurl.
Release records and transparency logs
-
A hosted npm version is accepted with its release record, and
<name>/<version>+r0serves the record's bytes as they were accepted. The record starts with the fields every release record has:schema,purl,revision,interpretation, the profile its manifest was derived under, andinputs, the digests of the.tgzand thepackage.jsonit was derived from. Each platform variant lists its owninputs. -
pnpr logs the release records of the npm versions it hosts. Each hosted organization has a transparency log per ecosystem, in the C2SP tiled log format. A version's log entry is recorded in the same transaction that accepts the version. With
releaseRecords.logSigningKeyset, pnpr then adds the entry to the log's tiles and signs a new checkpoint, keeping both in the hosted store. -
A hosted registry serves the transparency log of its release records under
/-/log/: the latest checkpoint, the hash tiles, and, for callers who may read every package of the organization, the entry bundles. A release record's evidence,<name>/<version>+r<revision>/evidence, holds the record's digest, the inclusion proof of its log entry against the log's checkpoint, and the version's provenance bundle.GET /-/pnprlistsrecordsandmanifestProfileswhen the records are logged. -
With
releaseRecords.statementSigningKeyset, a release record's evidence carries the registry's acceptance statement: a signed in-toto Statement saying the registry accepted the record under its profile. pnpr signs it from the accepted record each time it is read, so rotating the key signs every statement again.
Patch Changes
-
A resolve request keeps plain lockfile keys for packages from its default registry, even when a prefix such as
npmjspoints at that registry #17575. -
pnpr no longer frees a package name when a package deletion fails partway. pnpr no longer restores an unpublished version when it finishes an interrupted publish at startup.
-
pnpr config checknow prints the S3 key prefix as plain text, such asprefix: pnpr/, and leaves it out when none is set. It printedprefix: Some("pnpr")orprefix: None.
pnpm 12.17
pnpm 12.17.0 adds pnpm peers check --global, dependency-group selectors and wildcards in overrides, and runtime, package manager, Cargo, and Python updates in pnpm outdated and pnpm update. It also fixes files that other users could not read in Docker images, and repeat installs that missed changes to local dependencies.
Minor Changes
-
pnpm peers checknow accepts--global(-g) to check globally installed packages. After a global install or update, the peer dependency warning now suggestspnpm peers check -g#17504. -
pnpm outdatedandpnpm updatenow inspect and update package manager and runtime versions indevEngines.packageManager,packageManager,devEngines.runtime,engines.runtime, andruntime:dependencies. Withcargo.enabledorpython.enabled,pnpm outdatedandpnpm updatealso inspect and update Cargo dependencies inCargo.tomlandCargo.lockand Python dependencies inpyproject.tomlandpylock.toml. -
overridesselectors now support:prod,:optional, and:peerpseudo-selectors that target a single dependency group. They also support*wildcard patterns on parent and target package names. An override with a non-range value, such asnpm:orfile:, no longer adds a peer dependency todependencies. Removing an optional dependency with"-"now also removes the duplicate entry fromdependencies#5391.
Patch Changes
Installing packages
-
Files that pnpm creates in a project now get the permissions the umask gives any new file, such as
0644. Before, several of them were readable only by the user who ran pnpm, which broke Docker images that run as a different user #17435.The fix applies to:
node_modules/.pnpm-workspace-state-v1.json. A different user runningpnpm runreinstalled the dependencies because it could not read this file.pnpm-workspace.yaml- the files written by
pnpm deploy - patch files from
pnpm patch-commit - the lockfile restored by
pnpm dedupe --check - the Cargo and Python files that pnpm writes
When pnpm edits an existing file, the file keeps its permissions.
-
pnpm installno longer writes broken dependency links when the project'snode_modulesis a symlink to another directory, such as another project'snode_modules. The install reported success, but the links it wrote did not resolve #17448. -
Commands installed with
pnpm add --globalnow find the dependencies that pnpm hoists for their package. A package that loads a dependency it does not declare, such as a native addon's platform-specific package, failed with "Cannot find module". Reinstall such a package withpnpm add --globalto update its existing command #17503. -
When pnpm switches to the version a project pins, it now installs that version's native binary for the current host, even if the project's
supportedArchitecturesexcludes the host. Before, pnpm could not use the pinned version and kept running the installed one. pnpm also repairs a pinned version that an earlier install left without the host's binary. -
With the global virtual store, a change to a dependency of one package in a dependency cycle now gives every package in that cycle a new directory. Before, the other packages kept their directories and still linked the old copy of the changed package, so the project could load two copies of it #17466.
Packages in a dependency cycle, and packages that depend on one, move to new global virtual store directories once after this update. Those that run a build miss the side-effects cache once.
-
With
nodeLinker: hoisted, when two workspace projects need the same package version and it cannot be hoisted to the root, each project now keeps the copy installed in its ownnode_modules. Build scripts run in each of these copies. Before, the second project's copy was replaced with a symlink to the first project's, so every repeat install imported it again #17498. -
The Node.js
latestselector now chooses the newest stable release #17490. -
Hardlinking from the store now stats each store file once per install. Since pnpm 12.8.0, a store file was stat'ed again for every package that links it. On an offline install of a large workspace, that added up to 15% system CPU #17500.
Repeat installs and local dependencies
-
pnpm install --frozen-lockfileno longer reports an outdated lockfile right after the install that wrote it. This happened when a pnpmfilereadPackagehook orpackageExtensionschanged the dependencies or peer dependencies of a local directory dependency outside the workspace projects. It affected afile:dependency, an injectedworkspace:../dirdependency outside the workspace patterns, and an injected workspace package left out by--filter#17447 #17473.A repeat
pnpm installnow takes the fast path whenignoredOptionalDependenciesremoves an optional dependency of afile:directory dependency. It used to resolve again on every run. -
pnpm installnow resolves again after an edit to an injectedworkspace:../dirdependency outside the workspace patterns under a shared lockfile. It used to keep the lockfile unchanged. The nextpnpm install --frozen-lockfilethen failed #17476. -
pnpm installnow resolves a local directory again after itspackage.jsonchanges when an override or a package extension adds it to the dependencies of a registry package.pnpm install --frozen-lockfilefails on a lockfile that still records the old dependencies of such a directory #17486. -
With
injectWorkspacePackagesand per-project lockfiles,pnpm installnow updates a project's lockfile when a local tarball that an injected workspace project depends on is replaced. The project's lockfile kept the old version and integrity before. -
pnpm installnow detects a change to a local tarball or directory that only a pnpmfilereadPackagehook depends on. A repeat install reported "Already up to date" and kept the old contents before pnpm/tasks#227. -
pnpm installnow relinks aworkspace:orlink:dependency whose link was deleted fromnode_modules. It used to report "Already up to date" and leave the link missing #17485. -
A repeat
pnpm installreports "Already up to date" again when a pnpmfilereadPackagehook changes the dependencies of a workspace project. Since 12.15.0, every such install ran in full #17468.After an edit to a project's
package.json,pnpm installandverifyDepsBeforeRunnow run the pnpmfile'sreadPackagehook on the edited manifest before comparing it with the lockfile. They could report the project up to date when the hook rewrote the edited dependency to a specifier the lockfile did not record.
Adding and updating dependencies
-
pnpm update --latestwith package selectors now updates the dependencies of the selected packages within their declared ranges. Excluded packages and their dependencies keep their locked versions #17444. -
pnpm add <pkg>@<tag>now uses a catalog entry set to that same dist-tag. The dependency is saved ascatalog:and the entry stays as written pnpm/tasks#225.When
pnpm addadds a new dist-tag such asvue@legacyto a catalog, the catalog entry records the exact version the tag points to. The same applies to a dependency added without a version when thetagsetting names a dist-tag other thanlatest. -
pnpm addnow saves the resolved version range of an npm alias. For example,pnpm add v2@npm:vueandpnpm add v2@npm:vue@^3.0.0both savenpm:vue@^3.5.13.
Running scripts and commands
-
Programs that a script or
pnpm execcommand leaves running, such as a browser opened by theopenpackage, now outlive pnpm exactly when they would outlive the command run without pnpm. Before, on Windows, a failed command,Ctrl+C, or a closed terminal ended all of them. pnpm still ends them when it is killed or crashes.When a recursive or parallel run bails, the scripts still running now receive
SIGTERMon Unix and are killed only if they are still running two seconds later. On Windows, everything those scripts started is ended, including detached processes whose parent has already exited. Before, a bail killed them at once #17465. -
On Windows, the shell shim of a
.cmdor.batbin now runs it under Git Bash when MSYS argument conversion is off, such as withMSYS_NO_PATHCONV=1orMSYS2_ARG_CONV_EXCL='*'. The shim used to start an interactive cmd.exe prompt #17480. -
pnxandpnpm createnow run the command in the directory selected by--diror--workspace-root. Without either flag, the command still runs in the directory pnpm was started in. -
pnpm --env-filenow reads files the waynode --env-filedoes. Values are taken as written, so$VARis no longer expanded and Windows paths such asC:\Users\meload unchanged. A file is no longer rejected for its contents.
Settings and messages
pnpm versionno longer creates a git commit and tag when `gi...
pnpm 11.28.10
pnpm 11.28.10 stops pnpm store prune from deleting other stores when cacheDir points to their parent directory. It also fixes globally installed commands that load hoisted dependencies, and repeat installs that missed changes to local dependencies.
Patch Changes
Installing packages
-
pnpm store prunepreserves other stores and global package data whencacheDirpoints to their parent directory. -
Commands installed with
pnpm add --globalnow find the dependencies that pnpm hoists for their package. A package that loads a dependency it does not declare, such as a native addon's platform-specific package, failed with "Cannot find module". Reinstall such a package withpnpm add --globalto update its existing command #17503. -
pnpm prune --prodno longer fails withERR_PNPM_EXDEVwhennode_moduleswas copied from an earlier Docker build stage #17424. -
pnpm installnow retries a Node.js runtime zip download when the connection drops partway through. A download that still fails names its URL. Before, a single dropped connection ended the install withterminated#17411. -
With the global virtual store, a change to a dependency of one package in a dependency cycle now gives every package in that cycle a new directory. Before, the other packages kept their directories and still linked the old copy of the changed package, so the project could load two copies of it #17466.
Packages in a dependency cycle, and packages that depend on one, move to new global virtual store directories once after this update. Those that run a build miss the side-effects cache once.
-
pnpm installandpnpm rebuildnow refresh privately hoisted.binshims innode_modules/.pnpm/node_modules/.binafter a dependency build replaces a script target with a native executable #17402. -
The Node.js
latestselector now chooses the newest stable release #17490. -
The standalone
@pnpm/exebuilds now embed Node.js 26.13.0.
Repeat installs and lockfiles
-
pnpm installnow resolves a local directory again after itspackage.jsonchanges when an override or a package extension adds it to the dependencies of a registry package.pnpm install --frozen-lockfilefails on a lockfile that still records the old dependencies of such a directory #17486. -
With
injectWorkspacePackagesand per-project lockfiles,pnpm installnow updates a project's lockfile when a local tarball that an injected workspace project depends on is replaced. The project's lockfile kept the old version and integrity before. -
pnpm installnow detects a change to a local tarball or directory that only a pnpmfilereadPackagehook depends on. A repeat install reported "Already up to date" and kept the old contents before pnpm/tasks#227. -
pnpm installnow relinks aworkspace:orlink:dependency whose link was deleted fromnode_modules. It used to report "Already up to date" and leave the link missing #17485. -
Merging git branch lockfiles now keeps a workspace project whose directory is named
__proto__. It was dropped from the merged lockfile before pnpm/tasks#59.
Adding and publishing packages
-
pnpm addno longer rewrites a catalog entry set to the dist-tag that the dependency resolves through. For example, an entrybetastaysbetaafterpnpm add foo@betaorpnpm add foo. Before, the entry was replaced with the version the tag pointed to pnpm/tasks#225. -
pnpm packandpnpm publishnow always include the binary file declared by a stringbinfield inpackage.json, even whenfilesor.npmignorewould otherwise exclude it. -
When the version or range given to
pnpm unpublishmatches every published version, the error now says so and shows the same command with--force. It suggested runningpnpm unpublish <pkg>@<version>, the command that had just failed.
Running commands
-
Every command, including
pnpm --version,pnpm self-update, and invocations with unknown flags, now applies the samepackageManageranddevEngines.packageManagersettings when switching to the pinned pnpm version or recording it inpnpm-lock.yaml.--config.<key>flags andlockfileDirnow reach the version switch and lockfile sync on every entry point. Unreadable settings inpnpm-workspace.yamlor the globalconfig.yamlthat do not affect the pin decision no longer block the version switch pnpm/tasks#222. -
On Windows, the shell shim of a
.cmdor.batbin now runs it under Git Bash when MSYS argument conversion is off, such as withMSYS_NO_PATHCONV=1orMSYS2_ARG_CONV_EXCL='*'. The shim used to start an interactive cmd.exe prompt #17480. -
pnxandpnpm createnow run the command in the directory selected by--diror--workspace-root. Without either flag, the command still runs in the directory pnpm was started in. -
The error for a dependency with an invalid name in a project's manifest now names the project's directory. It used to say only "The current package", which did not tell which workspace project to fix #17475.
Platinum Sponsors
|
|
|
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
pnpm 12.16.1
This release fixes pnpm install hanging on a cold store in workspaces that install Cargo crates, and pnpm pack shipping a file that files excludes by name.
Patch Changes
-
pnpm installno longer hangs on a cold store in a workspace that also installs Cargo crates or a Rust toolchain. -
pnpm packandpnpm publishnow apply afilesentry that names a file after the glob and directory entries, as npm does. A file excluded by name, such as!dist/index.js, stays out even when a laterdist/**matches it. A file named by its path, such aslib/secret.js, ships even when a later!lib/*.jsmatches it. -
pnpm installno longer leaves a broken symlink in an injected workspace dependency when the package links to a file outside its published files and pnpm imports by hardlink. Installing a git dependency now skips internal directory symlinks when importing its files into the store. -
pnpm installnow relinks a command that a workspace project renames into the projects that depend on it whensharedWorkspaceLockfileisfalsepnpm/tasks#224. -
pnpm installandpnpm rebuildnow refresh privately hoisted.binshims innode_modules/.pnpm/node_modules/.binafter a dependency build replaces a script target with a native executable #17402. -
pnpm installnow reads a package's manifest to decide its peer dependencies when it updates an existing lockfile, as it does when it resolves from scratch. -
Commands,
pnpm --version,pnpm self-update, and invocations with unknown flags now apply the samepackageManageranddevEngines.packageManagersettings when switching to the pinned pnpm version or recording it inpnpm-lock.yaml.--config.<key>flags andlockfileDirnow reach the version switch and lockfile sync on every entry point. Unreadable settings inpnpm-workspace.yamlor the globalconfig.yamlthat do not affect the pin decision no longer block the version switch pnpm/tasks#222. -
pnpm now accepts the
--cache-dir <dir>flag, as inpnpm install --cache-dir /tmp/pnpm-cache#17422. -
When the version or range given to
pnpm unpublishmatches every published version, the error now says so and shows the same command with--force. Before, the error suggested runningpnpm unpublish <pkg>@<version>, the command that had just failed.
Platinum Sponsors
|
|
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
|
|
|
|
|
pnpm 12.16
This release adds a global --env-file flag, updates to a symlinked pnpm-lock.yaml, and pnpm setup --no-modify-path. It also fixes two security issues, in zstd decompression and in signing.
Minor Changes
-
Added a repeatable global
--env-file <path>flag to load environment variables from dotenv-style files before running a command. Later files override earlier files, variables already in the environment are preserved, and loaded variables are visible to user-level.npmrcinterpolation and lifecycle scripts #7111. -
pnpm can now update a
pnpm-lock.yamlthat is a symlink. List the directories the lockfile may link into in thesymlinkedLockfileTargetDirssetting, in the global config or withPNPM_CONFIG_SYMLINKED_LOCKFILE_TARGET_DIRS. pnpm writes the update to the file the symlink points to if that file is inside one of these directories. The symlink stays in place. pnpm ignores this setting inpnpm-workspace.yaml#17222. -
Added a
--no-modify-pathoption topnpm setup. It installs pnpm without adding it toPATH, leaving the shell config file, the Windows registry, and the GitHub Actions environment files unchanged.pnpm setupthen prints the lines to add yourself get.pnpm.io#37.
Patch Changes
Security
-
Fixed memory exposure and allocation handling vulnerabilities when processing zstd-compressed data #17388.
-
Signing in pnpm and pnpr no longer uses arithmetic that may leak the private key through timing. This covers shared artifacts, OCI registry tokens, and OIDC login state advisory.
Installing packages
-
A dependency symlink that pnpm cannot point at the wanted target now fails the install with an error that names the link. This happens when the filesystem still serves the old link after pnpm removed it, as a
node_modulesserved by a virtual filesystem does. pnpm used to remove the link again without end and crashed with a segmentation fault once the thread ran out of stack. -
Concurrent workspace installs no longer lose store-index rows when initializing the shared SQLite index #17388.
-
Agent skill links now keep the same target across installs when a package version is installed with several sets of peer dependencies. An install no longer fails with
ERR_PNPM_AGENT_SKILL_PATH_OCCUPIEDon a skill link that pnpm created for another of those copies #17319. -
pnpm now fails at once, without retrying, when a downloaded package archive contains a rejected entry path #17205.
-
pnpm now builds a git-hosted dependency in every new location it links the dependency into, such as a new global virtual store slot or a fresh
node_modules. When the side-effects cache holds the build output, pnpm restores it from there. Before, only the install that fetched the dependency built it. This also fixes dependencies that are already in the store #17224. -
Changes a
preResolutionhook makes to the lockfiles now take effect. The hook now also runs when pnpm installs from an up-to-date lockfile. An error thrown by the hook now fails the install. -
A custom fetcher in the pnpmfile can now call
fetchers.directoryto fetch a package from a local directory. The call rejects withERR_PNPM_TARBALL_INTEGRITYif the lockfile pins an archive integrity for the package. Previously, callingfetchers.directoryfailed with aTypeError. -
packageImportMethod: copynow also applies to packages that have lifecycle scripts or patches. On a filesystem that supports block cloning, such as ReFS or a Dev Drive on Windows, these packages were cloned #17195. -
With
nodeLinker: hoistedandautoInstallPeers: false, a package that workspace projects use with different peer versions is now installed once per peer variant, so each project's copy resolves that project's peer #17395. -
Under
nodeLinker: hoisted,pnpm installnow leaves a patched package that is already in place alone. The same holds for a package whose peers include a patched package, such as every package peering on a patchedreact-native#17188. -
With
nodeLinker: hoisted, an install that stops partway no longer leaves the next install trusting the packages it was replacing. Before, reverting the change that started the stopped install, such as a patch edit, kept the half-replaced package innode_modules.
Repeat installs and changed settings
-
A repeat
pnpm installnow acts on a change to these settings. Before, it reported "Already up to date".storeDirorvirtualStoreDir: it recreatesnode_modules.globalVirtualStoreDir: it links the packages from the new directory.virtualStoreDirMaxLength: it renames the virtual store directories.dangerouslyAllowAllBuildsturned on: it runs the builds it skipped.trustLockfileturned off: it verifies the lockfile.
-
pnpm add,pnpm remove, andpnpm updatenow fail ifnode_modulesdoes not match the current settings. Runpnpm installto recreate it. Previously, these commands removed and reinstallednode_moduleswithout asking. They report:ERR_PNPM_UNEXPECTED_STOREifnode_moduleswas linked from a different store.ERR_PNPM_UNEXPECTED_VIRTUAL_STOREif it was linked from a different virtual store directory.ERR_PNPM_MODULES_BREAKING_CHANGEif it was installed by an incompatible pnpm version, or ifnode_modules/.modules.yamlcannot be read.ERR_PNPM_NODE_LINKER_DIFFif it was installed with a differentnodeLinker.
pnpm updatenow also fails withERR_PNPM_HOIST_PATTERN_DIFFand the other errorspnpm addreports for a changed hoist pattern orvirtualStoreDirMaxLength. -
An install filtered to some workspace projects now fails if
node_moduleswas installed with a differentnodeLinker,hoistPattern,publicHoistPattern, store, or virtual store. Before, it recreatednode_modulesand left the projects it skipped without their dependencies. Apnpm installof the whole workspace still recreatesnode_modulespnpm/tasks#217. -
pnpm installand theverifyDepsBeforeRuncheck now notice an edited patch file whensharedWorkspaceLockfileisfalse. -
pnpm installnow takes its fast path in a workspace withpatchedDependencieswhen the previous install ran with another major version of pnpm. -
pnpm installnow relinks the bins of a workspace package when itsbinfield changes, including with--frozen-lockfileand an unchanged lockfile. A filtered install relinks them in the selected projects, and the next install that covers the remaining projects relinks them there #17324. -
pnpm installnow removes command shims fromnode_modules/.binwhen a workspace dependency renames or removes an entry from itsbinfield pnpm/tasks#211.
Resolving dependencies and the lockfile
-
A package that a plain range and a
<prefix>:specifier both resolve from one registry is now recorded once inpnpm-lock.yamland installed once. The lockfile key names the registry a package resolved from. When that registry declares a prefix, throughprefixinregistriesor throughnamedRegistries, the package is keyed<name>@<prefix>:<version>however its dependency was written: with the prefix, as a plain range its scope routes to that registry, or through the default registry. Before, the plain range was keyed<name>@<version>, so one tarball was installed twice andinstanceofchecks and module singletons broke between the two copies. The same name and version served by two registries still get separate entries. When several prefixes declare one registry, its packages are keyed by the lexicographically smallest prefix.If a scope you route reaches a registry that also declares a prefix, your next
pnpm installre-keys those entries, which shows up as a lockfile diff. Commit it.pnpm install --frozen-lockfileinstalls the lockfile as recorded. -
pnpm installnow resolves a scope's packages again when the scope is routed to another registry inregistries. Before, the lockfile kept the versions and integrity resolved from the old registry, so the install failed when the new registry did not serve a locked version or served different content. pnpm records in the workspace state which registry each scope routed to, so a fresh clone without that record installs the lockfile as recorded. Changing the default registry resolves nothing again, andpnpm install --frozen-lockfileis unchanged. -
When
autoDedupeis off, a workspace project's dependencies now resolve from its own ranges, like they do withsharedWorkspaceLockfile: false. Previously, another project's narrower range could hold them back. For example,ajv: "*"now resolves to the latest Ajv even if another project depends onajv@^6. Packages that a project lists inpeerDependenciesstill follow the versions other projects use.pnpm dedupeand installs withautoDedupe: truestill pick one version that satisfies every range. SetautoDedupe: trueif CI runspnpm dedupe --check#17235. -
An optional dependency written with a named-registry prefix, such as
work:^1.0.0, now fails the install when it cannot be resolved but the lockfile already has a matching entry from that registry....