pnpm 11.28.12
pnpm 11.28.12 fixes many peer dependency, pnpm deploy, frozen-lockfile, and nodeLinker: hoisted bugs. It also includes a security fix. A ${VAR} placeholder in a repository's registry URL now fails the command, so pnpm no longer sends those requests to another registry. The bundled node-gyp no longer ships the undici version that security scanners flag, and remote resolution needs pnpr 0.1.0-alpha.20 or newer.
Patch Changes#
Upgrading and security#
-
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. -
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. -
Remote resolution and shared build caches 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. Upgrade the pnpr server before upgrading pnpm.
Installs that failed#
-
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 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. -
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 fetchno longer fails withENOENTwhennodeLinkerishoistedand the project configurespatchedDependenciesorallowBuilds.pnpm fetchalso preserves an existing hoistednode_modulesdirectory #18037. -
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 no longer fails to remove a dependency whose
package.jsonis malformed. It removes the dependency and keeps its bins pnpm/tasks#295. -
Fixed
pnpm deprecate,pnpm undeprecate, andpnpm unpublishfailing with revision conflicts when a registry cache serves stale package metadata. -
When a package injected through a path, such as
file:../libor../lib, gives one of its dependencies acatalog:specifier,pnpm installnow fails the way a fresh resolution does, andpnpm install --frozen-lockfilereports the lockfile as outdated. Before, both could accept the existing lockfile #17722.
Peer dependencies#
-
Repeated installs no longer change the peer dependency providers that the previous install chose. This covers adding a dependency, removing a shared package from one workspace project, and projects that use different ranges of one dependency pnpm/tasks#221.
An unchanged install keeps a valid locked peer provider even when a direct dependency provides another compatible version. Explicit
pnpm addandpnpm updaterequests take precedence over it. Required dependencies no longer remain marked optional when another workspace project removes a shared package. -
Workspace projects that select different versions of a peer provider no longer share one copy of the package that depends on it. This affected peers reached through an aliased dependency or a peer dependency cycle.
-
New workspace projects now prefer peer versions already pinned in the lockfile. Dependencies that are also optional peers no longer cause an unrelated project to select an older peer version.
-
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. -
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. -
Fixed installation and peer dependency reporting for packages named
constructorortoString. Optional peers with these names are also recognized when declared only inpeerDependenciesMeta.
Resolving and linking dependencies#
-
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. -
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. -
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. -
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. -
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. -
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. -
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. -
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: 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. -
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. This also applies withnodeLinker: hoisted.pnpm installreports each removedlink:dependency once pnpm/tasks#293. -
A repeat
pnpm installwithnodeLinker: pnpnow applies dependency and configuration changes. Before, it printed "Already up to date" and left the lockfile unchanged. -
Config dependencies use the declared registry
serverTypewhen recording and installing packages pnpm/tasks#236.
pnpm deploy#
-
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 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 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. -
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. -
pnpm deploynow binds optional peers declared only inpeerDependenciesMetafor linked workspace packages. The deployed manifest and lockfile also retain these peers when filtering dependency groups.
Builds, bins, and scripts#
-
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. This includes git dependencies that an older pnpm marked in a shared store as not requiring a build pnpm/tasks#189.
-
Fixed
pnpm store statusreporting approved builds of git dependencies as modified when the store contains legacy build metadata. -
pnpm store prunenow stops if a dependency link cannot be read. This keeps installed packages from being removed after an incomplete scan. -
When
pnpm installadds placeholder entries toallowBuildsinpnpm-workspace.yaml, it no longer copiesallowBuildsentries from other config sources into the file. -
Bin shims in the global virtual store no longer include the hoisted modules directory of the project that installed them #18035.
-
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. -
Installing no longer replaces the POSIX bin shims that a newer pnpm wrote in
node_modules/.bin. -
Linking bins is faster for packages that ship large native executables as bins. pnpm now reads only the first 512 bytes of a bin file to find its shebang #17361.
-
pnpm runandpnpm setupno longer clear the visible terminal on Windows when a project path contains non-ASCII characters. -
pnpm stops scripts and their descendants when it is killed during script startup pnpm/tasks#313.
Engines and runtimes#
-
pnpm installnow fails on a workspace project's incompatibleengines.pnpm, or on its incompatibleengines.nodewithengineStrictenabled, even when the project'sos,cpu, orlibcexcludes the current platform pnpm/tasks#281. -
pnpm installnow checks the project'senginesfield when the project is already up to date. A repeat install of a project outside a workspace warns about an incompatible Node.js version, or fails withengineStrictenabled #17751. -
A workspace project that downloads its own Node.js through
devEngines.runtimenow has itsengines.nodechecked against that Node.js. Before, pnpm checked it against the shared Node.js version, soengineStrictcould reject a project whose scripts run on a compatible runtime pnpm/tasks#300. -
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. -
Fixed missing warnings when a required dependency does not support the current Node.js version.
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 loginnow prints the registry'snpm-noticemessage before the web login URL. Some registries use it to show a code that you type in the browser. -
pnpm self-updatenow updates the project'sengines.pnpmrange along with its pnpm pin, keeping the^or~operator #17732. -
pnpm setupand the install script now support PowerShell as the login shell on Linux and macOS. They addPNPM_HOMEtoPATHinprofile.ps1under$XDG_CONFIG_HOME/powershellor~/.config/powershell#18026. -
pnpm setuprejects aPNPM_HOMEvalue that would split the savedPATHpnpm/tasks#315. -
Relative directory selectors set on
config.filterorconfig.filterProdby anupdateConfighook now resolve from the workspace root even when--diris passed orpnpmruns from a subdirectory #17906. -
pnpm now reads the boot document of
pnpm-lock.yamlwhen it is written in lockfile format 13. pnpm then switches to the pnpm version such a project pins. -
Programmatic install mutations that omit workspace project manifests now resolve locally when a pnpr server is configured pnpm/tasks#59.
-
pnpm now recognizes Capawesome Cloud and RWX as CI environments.
-
The warning about unrecognized settings now marks
projectScopedOptionalPeersas a setting of a newer pnpm version.