pnpm 11.28.0 adds the forceIgnoresPlatform setting and pnpm update --peer, and fixes many bugs in pnpm deploy, --filter, nodeLinker: hoisted, and custom modulesDir setups. This release also carries security fixes for shell completion, bin shims on Nix, lifecycle scripts inside a custom modulesDir, and userAgent placeholders in pnpm-workspace.yaml.
Minor Changes
- Added the
forceIgnoresPlatformsetting. When it isfalse,pnpm install --forceskips optional dependencies whoseos,cpuorlibcdo not match the host instead of installing all of them. The default staystrue#6133.
Patch Changes
Security
-
pnpm no longer expands environment variables in a
userAgentset in a project'spnpm-workspace.yaml. AuserAgentwith a placeholder in that file is now ignored. Before this fix, pnpm sent the variable's value to the configured registry #15415. -
pnpm no longer treats packages inside a custom
modulesDiras workspace projects, including one thatpackageConfigssets for a project. Before, with amodulesDirsuch asvendorand apackagespattern such as**, a repeat install ran the lifecycle scripts of dependencies thatallowBuildshad not approved #15412. -
On Nix, a dependency's bin named like a system utility such as
sedcan no longer redirect a POSIX bin shim or thepnpm,pn,pnpx, andpnxlaunchers. The shims and launchers now ignorenode_modulesand relativePATHentries while they locate their own files. Installing again replaces the shims already innode_modules#14883. -
Shell completion now omits candidates containing control or invisible formatting characters, and fish completion omits names containing backslashes. Package and script names can no longer inject extra completion records or terminal escape sequences.
-
Commands that run pnpm again, such as
pnpm runtime setandpnpm env use, no longer re-run a script that only looks like pnpm. A script namedpnpmorpnthat another package installed was run as though it were pnpm. -
pnpm store prunenow leaves adlxcache root that is a symlink or Windows junction untouched. Cleanup no longer removes directories through that link.
Installing packages
-
pnpm installnow fails at once when a registry or tarball server presents a TLS certificate that fails verification, such as a self-signed or expired one. The error names the certificate problem. Such requests were retried for more than a minute #9134. -
pnpm installno longer appears to hang when a git dependency is fetched over SSH and ssh asks for a key passphrase or a host key confirmation. pnpm now runs ssh in batch mode, so the install fails right away with the ssh error, and a key that needs a passphrase has to be loaded into an SSH agent first. An ssh command selected throughGIT_SSH_COMMAND,GIT_SSH, or thecore.sshCommandgit setting is kept as is #2227. -
pnpm no longer crashes on startup when the temporary directory set by
TMPDIR,TEMP, orTMPdoes not exist #4960. -
pnpm install --silentno longer fails when the install is delegated to pacquet. pnpm also stops passing-s,--logleveland the other reporting flags to pacquet #11936. -
Fixed
pnpm installfailing withEEXISTwhen a concurrent install cleared the file or directory that was occupying a symlink path. On Windows, a symlink another process is still holding is no longer moved aside and recreated. -
pnpm installno longer fails when writing the workspace state file encounters an error. Failures to update the state file now emit a warning instead of aborting the install #14550. -
Installing or adding dependencies no longer fails when a previously installed local tarball file was deleted from disk #8367.
-
pnpm installnow reads the same local tarball it installs when a dependency's absolutefile:path contains... Such a path could install a different tarball than the one it read, failing withERR_PNPM_TARBALL_INTEGRITY, or fail to resolve at all. -
pnpm install --frozen-lockfilenow rejects changed local tarballs, even when the previous archive contents are in the store #1889. -
pnpm addandpnpm installnow support installing bzip2 compressed tarballs #6761. -
pnpm installnow fetches committed submodules of git dependencies #1470. -
Interrupting
pnpm installwith Ctrl+C or SIGTERM no longer leaves a temporary lockfile (.pnpm-lock.yaml.*.tmp) behind in the project #1418. -
pnpm installandpnpm runnow reinstall a single project that was moved or renamed together with itsnode_modules. Before, they reported "Already up to date" while links such as Windows junctions still pointed at the old location #9512. -
pnpm installnow relinks a direct dependency whose link innode_modulespoints to a missing target. Before, it reported "Already up to date" and left the broken link #9758. -
pnpm installandpnpm addno longer skip optional dependencies that the Node.js version resolved for adevEngines.runtimerange supports, when the range usesonFail: download. An explicitly setnodeVersionstill takes priority #14628. -
pnpm installnow uses the running Node.js whendevEngines.runtimedeclares a range withoutonFail: download. Optional dependencies supported by the active Node.js are no longer skipped #15230. -
pnpm install --engine-strictnow respectsenginesrelaxed byreadPackagehooks in.pnpmfile.cjs#15482. -
pnpm installnow applies changes to or removal of a globalreadPackagehook when an existing lockfile is present #15136. -
The project's
.pnpmfile.mjsor.pnpmfile.cjsnow runs after the pnpmfiles of config dependency plugins #9891. -
A
readPackagehook that sets a dependency range to a value other than a string, such asundefined, now fails the install with an error that names the dependency, the package, and the pnpmfile. Delete the property to remove a dependency #5517. -
pnpm install --prodand other installs that skipdevDependenciesno longer run thepnpm:devPreinstallscript #7065. They skippreparelifecycle scripts too, as doespnpm installgiven package arguments.pnpm deploydoes not run thepreparescripts of the deployed project #7282. -
The root project's
preinstallscript now runs before dependencies are resolved and linked. A guard such asnpx only-allow yarncan stop the install before pnpm populatesnode_modules#3760. -
pnpm install --prod,pnpm fetch --prodandpnpm deploy --prodno longer install a devDependency that is only there to satisfy an optional peer dependency of a production dependency.pnpm list,pnpm why,pnpm licenses,pnpm sbomandpnpm auditleave it out of--prodresults too. The same applies to--dev. A peer that is not optional is still installed and audited #15344. -
pnpm prune --prodand production installs now prune excluded development dependencies even when lockfile generation is disabled. -
pnpm fetchnow also installs the pnpm version thatpnpm-lock.yamlpins, when it differs from the running pnpm. A laterpnpm install --offlinethat switches to the pinned version no longer fails because that version is missing from the store #11808. -
A dependency that ships a
binding.gypand setsgypfile: falseno longer gets thenode-gyp rebuildinstall script pnpm synthesizes for it. Such a dependency needs noallowBuildsentry and is no longer listed under "Ignored build scripts". -
pnpm installno longer addsallowBuildsplaceholder entries topnpm-workspace.yamlwhen it runs in CI or without a terminal. Interactive installs still add them #11574. -
Installing through a pnpr server now installs a project's peer dependencies when
autoInstallPeersis enabled. A project that declared only peer dependencies failed withERR_PNPM_OUTDATED_LOCKFILEor skipped its peers #14833.
Resolving and linking dependencies
-
pnpm now installs a dependency that a package also declares as an optional peer dependency, for example
lightningcssin some vite builds. The dependency was missing fromnode_modules, so the package failed to import it #8912. -
Removal overrides such as
"parent>peer": "-"now prevent optional peers from being installed from another workspace package #15008. -
Removing an entry from
overridesnow re-resolves the packages it targeted. A version the override had locked is no longer kept just because the declared range still accepts it #4587. -
packageExtensionsandoverridesentries with a ranged selector (such as@<Xor@*) no longer match a dependency that has nopackage.json, such as a local directory dependency #15007. -
Trim leading and trailing whitespace from dependency override selectors in
pnpm.overrides#6356. -
With
trustPolicy: no-downgrade, pnpm now resolves the newest matching version that is not a trust downgrade. Previously a dependency failed withERR_PNPM_TRUST_DOWNGRADEeven when an older version satisfied its range.pnpm self-updatepicks its target version the same way. A request for an exact version still fails #14176. -
pnpm installandpnpm updatenow resolve a dependency range to the newest matching version that is not deprecated. A version already recorded in the lockfile is still used #15128. -
pnpm addandpnpm removeno longer move unrelated transitive dependencies to other versions. Adding a package and then removing it now leavespnpm-lock.yamlunchanged. Before, the dependencies of auto-installed peers andnpm:aliased subdependencies could move to a newer version that was already in the lockfile #11859. -
pnpm dedupenow moves transitive dependencies to the version acatalog:dependency pins, as it already did for versions written directly inpackage.json. -
pnpm install --ignore-pnpmfileno longer removespnpmfileChecksumfrom an up-to-datepnpm-lock.yaml.pnpm install --frozen-lockfile --ignore-pnpmfileno longer fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHwhen the lockfile records apnpmfileChecksum. A command that resolves dependencies with the pnpmfile ignored still writes the lockfile without it #10944. -
pnpm install --frozen-lockfilenow succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile. Previously, frozen installs failed withERR_PNPM_OUTDATED_LOCKFILE. The notice states that the dependency could not be resolved and names the requested range #3960. -
pnpm no longer rewrites
packageManagerDependenciesinpnpm-lock.yamlwhen that block pins@pnpm/exebesidepnpm. The rewrite ran on every command, sopnpm listleft a clean working tree dirty, andpnpm versionthen refused to run #14926. -
pnpm importand fresh resolutions now recordintegrityfor git-hosted tarballs, such ascodeload.github.comURLs, even when the tarball is already in the store #13338. -
Merging lockfiles now preserves recorded configuration fields such as
overrides,neverBuiltDependencies,patchedDependencies,packageExtensionsChecksum,settings, andcatalogs#8366. -
A lockfile entry whose resolution is unchanged now keeps its recorded
deprecatedmessage #5772. -
pnpm no longer writes a package's legacy array-form
engines, such as["node >= 0.8"], to the lockfile. It was recorded as an object keyed by index, such as{'0': node >= 0.8}#4518. -
With
nodeLinker: hoisted,hoistWorkspacePackagesnow links each workspace project thathoistPatternorpublicHoistPatternselects into the rootnode_modules, unless a hoisted package or a root dependency already uses its name. The project's bins are linked into the rootnode_modules/.bin#7553. -
Workspace projects that
hoistPatternorpublicHoistPatternselects are now hoisted on every install. A project added to the workspace was not hoisted untilnode_moduleswas deleted and reinstalled. A workspace that installs nothing from a registry hoisted none of its projects at all #3642. -
With
nodeLinker: hoisted,pnpm installnow removes the commands of the packages it removes fromnode_modules/.bin, such as a nested copy deduped into the rootnode_modules#7568. -
pnpm installnow links a dependency's bin even when the bin's file does not exist yet, such as a workspace package's bin that a build script creates after install. Previously pnpm printed aFailed to create binwarning. The command then stayed missing untilnode_moduleswas removed #10007, #10216. -
Executable linking now makes a bin executable for every user. A target with only some execute permission bits set was left non-executable for other users #3699. Bin linking leaves workspace and linked dependency files outside
node_modulesunchanged. -
Dependencies and executable binaries are now correctly linked and accessible for workspace packages using
publishConfig.directoryandpublishConfig.linkDirectory#8338.
Workspaces and filtering
-
Fixed
pnpm installfor workspace projects reached through a symlink, such as apackagesdirectory that links to a folder outside the workspace. pnpm now installs their dependencies, and the links in theirnode_modulesresolve #1044. -
A dependency declared with
catalog:now counts as a workspace dependency when its catalog entry points at a workspace project, for exampleworkspace:*#15587. WithlinkWorkspacePackagesenabled, so does annpm:alias of a workspace project, such as"math-alias": "npm:math@^1.0.0".pnpm -r runruns that project first.--filter <pkg>...selects it. -
A
workspace:dependency now resolves to a workspace project whose version is not valid semver, such as1or1.0.workspace:*,workspace:^, andworkspace:~match it. A range identical to the version also matches it #4567. -
A
workspace:dependency with an exact version now resolves to a workspace project whose version carries SemVer build metadata. For example,workspace:0.5.6-next.3matches a project at0.5.6-next.3+f60facc#6483. A workspace project with build metadata is no longer skipped when it matches the requested range and has the same version precedence as the registry package #2812. -
Secondary dependencies now prefer the version resolved by the local project's direct dependencies over versions from sibling workspace projects #7191.
-
pnpm installnow re-resolves a workspace project's auto-installed peer dependency when another workspace project changes its specifier for that package to one that excludes the locked version but still overlaps the peer range. The peer then resolves to the version a fresh install would pick #11800. -
pnpm install --frozen-lockfilenow fails withERR_PNPM_OUTDATED_LOCKFILEwhenpnpm-lock.yamllists a workspace project whose directory or manifest file is missing. The install used to report success without installing that project's dependencies #7667. -
pnpm install --frozen-lockfilenow fails when a workspace package's version no longer satisfies the range that a dependent workspace project declares for it. This includes injected workspace dependencies #7823. -
pnpm install -rnow installs every workspace project whenrecursiveInstallis set tofalseinpnpm-workspace.yaml#7504. -
pnpm installwith--filternow installs only the dependencies of the selected projects when usingnodeLinker: hoisted#8882. -
pnpm installnow updates an injected workspace dependency after that package's own dependencies change, whenshared-workspace-lockfileisfalse#7209. -
syncInjectedDepsAfterScriptsnow copies files into injected dependencies whennode_modulesis on another filesystem than the package sources. The sync previously failed with a cross-device link error and made the script run exit with an error #14703. -
Fixed injected workspace dependency synchronization failing with
EPERMon Windows when removing nested directories. -
Workspace discovery now returns one project per directory when multiple manifest formats are present. It selects
package.json, thenpackage.json5, thenpackage.yaml#3027. -
Wildcards in negated
packagespatterns ofpnpm-workspace.yamlnow match directories whose names start with a dot. For example,!packages/**now also excludespackages/.dev/toolwhen another pattern includes.devexplicitly. -
pnpm now warns when a workspace install covers a project that has its own
pnpm-workspace.yaml. The nested file's settings, such aspatchedDependencies, do not apply when the outer workspace installs that project. pnpm reads settings only from thepnpm-workspace.yamlat the workspace root #11724. -
pnpm now warns when
shared-workspace-lockfileis passed on the command line outside a workspace #1617. -
The
[<since>]filter selector now compares against the commit where the current branch forked from<since>. Projects changed only by newer commits on<since>are no longer selected. Uncommitted changes are still included. In a shallow clone without that commit, pnpm compares against<since>directly, as before #9907. -
--filter "[<since>]"now selects workspace packages when dependency versions change in a catalog inpnpm-workspace.yaml#8718. It also selects projects that files were moved out of when git detects the move as a rename #15481. -
--filternow evaluates selectors in order, so later inclusion filters can re-include packages that an earlier exclusion filter excluded #9354. -
Directory filters such as
--filter=./packages/*now select projects when the current directory was entered with a lowercase drive letter on Windows, likec:\repo#5500.
Custom modulesDir
-
pnpm run,pnpm exec,pnpm versionhooks, and the lifecycle scripts a project runs during install now find commands in a custommodulesDir, and bin shims resolve dependency paths there #3604. Project.hooksscripts are read from the configured modules directory. Installs resolved by pnpr now preserve configured modules and executable directories.pnpm binnow reports the configured executable directory. In a workspace whose projects keep their own lockfiles, apackageConfigsentry that gives one project its ownmodulesDiris followed too. -
Tools installed in a custom
modulesDircan load CommonJS plugins installed there, the same way they would fromnode_modules. When executables are symlinks, as withpreferSymlinkedExecutablesor the hoisted linker, this works forpnpm run,pnpm exec,pnpm versionhooks, and the lifecycle scripts a project runs during install. A symlinked tool started directly from a shell does not get it. Paths containing the platform path-list separator do not receive this fallback.extendNodePath: falsedisables this fallback #3604. -
A repeat
pnpm installin a workspace with a custommodulesDirnow takes the up-to-date fast path. Before, pnpm looked for each workspace project's dependencies innode_modulesand ran a full install every time.
Adding, updating, and removing dependencies
-
pnpm addnow keeps the specifier areadPackagehook provides when the hook rewrites the requested one. When the hook removes the dependency instead, the add skips it and reports why. Previously the add wrote the request either way, and the hook undid it on the next read, sopnpm install --frozen-lockfilefailed #15156. -
pnpm addnow saves changes topackage.jsonbefore running lifecycle scripts, so a postinstall script failure leaves the added dependency inpackage.json#8627. -
pnpm addnow saves the requested exact version when adding a dependency, even when the manifest already contains a version range #6040. -
pnpm addandpnpm installkeep an emptypeerDependencies,dependencies,devDependencies, oroptionalDependenciesfield that was already inpackage.json. pnpm still drops such a field when it removes the last entry itself, aspnpm removedoes #5096. -
Added a
--peerflag topnpm updateto update ranges inpeerDependencies#8081. -
pnpm updatenow keeps a version range whose shape has no save prefix, such as<= 3.0.0or>=1.0.0 <2.0.0, when the updated version still satisfies it. Before,<= 3.0.0became^3.0.0#6714. -
pnpm update <name>now updates a peer dependency that pnpm installed automatically #10486. -
pnpm update <pkg>now moves a package off a locked version the registry no longer serves, such as an unpublished release. The lockfile check for supply-chain policies such asminimumReleaseAgeused to reject that version before the update could replace it #9953. -
pnpm update --prodno longer installs devDependencies when run in a project installed with--prod#8038. -
pnpm update --interactive --workspacenow allows external dependencies to be updated. -
pnpm outdatedandpnpm updatenow applyminimumReleaseAgeto GitHub Actions.minimumReleaseAgeExcludeentries match action names such asactions/checkout#13923. -
pnpm removenow runs the project's ownpreuninstall,uninstall, andpostuninstallscripts.preuninstallanduninstallrun before dependencies are unlinked. A failure in either stage aborts the removal.postuninstallruns after unlinking completes. TheignoreScriptssetting and--lockfile-onlyskip all three stages #3276. -
pnpm remove -rnow fails if any requested dependency is absent from all selected workspace projects. Validation respects--save-prod,--save-dev, and--save-optionaland completes before modifying project manifests #2319. -
pnpm removenow accepts--trust-lockfileand--no-trust-lockfileto control supply-chain policy checks while removing a package #14406. -
pnpm unlinknow removes thelink:dependency thatpnpm link <dir>added topackage.json. The linked package is removed fromnode_modulesand the lockfile. Alink:dependency to another directory is kept #4219. -
minimumReleaseAgeExcludePruneandtrustPolicyExcludePrunenow work in workspaces withshared-workspace-lockfile=false. Once every project has been installed, pnpm drops an entry only if no project lockfile records it. UndecidedallowBuildsentries are pruned the same way #14612. -
pnpm importnow converts dependencies that use Yarn'spatch:protocol. The dependency keeps the version it patches, and the patch file is added topatchedDependenciesinpnpm-workspace.yaml. If the patch file is missing, pnpm prints a warning and imports the dependency without the patch #10278. -
pnpm importin a workspace now keeps the versions pinned by the rootyarn.lock,package-lock.json, ornpm-shrinkwrap.jsonwhen another workspace project's range allows a newer version. Before, the root project got the newest version in its range #4385. -
pnpm patch-commitnow resolves default patch directory locations when passed a package name or package specifier (such aspnpm patch-commit <pkg>orpnpm patch-commit <pkg>@<version>). -
pnpm patch-commitnow updates the lockfile snapshot and prunes removed dependencies when the patch modifiespackage.json#6866. -
pnpm patch-commitnow falls back to copying package files when hard linking fails. -
make-dedicated-lockfileno longer removes fields such asmainandtypesfrom thepublishConfigof the project'spackage.json. It now restorespackage.jsonwhen it cannot move the originalnode_modulesback to its place. The error then names.tmp_node_modules, where the originalnode_moduleswas left. The command refuses to run while that directory exists, so a retry cannot overwrite it.
Running scripts and commands
-
A script that pnpm runs without a terminal now ends when pnpm itself is killed. Killing pnpm's process group, as Playwright's
webServerdoes to stop the command it started, used to leave the script running and holding the caller's output pipes open #15555. -
pnpm --filter <project> <command>andpnpm -r <command>now run a command installed in the selected projects' dependencies when none of them has a script by that name. This matchespnpm <command>in a single project.pnpm runwith--filteror-rstill reports the missing script #10151. -
pnpm execandpnpm dlxnow setnpm_execpath,INIT_CWD,npm_node_execpath, andNODEfor child processes #7037. Scripts thatpnpxandpnxrun now get pnpm itself asnpm_execpath. A script that ran$npm_execpath installthere ranpnpm dlx install. -
pnpm execnow sets thePWDenvironment variable to the directory the command runs in. Shells and tools that readPWDnow report the logical path of a workspace package reached through a symlink #1550. -
A script that runs
pnpm runno longer adds duplicatenode_modules/.binandnode-gyp-binentries toPATH#5352. -
Concurrent
pnpm runandpnpm execcommands now serialize their dependency installs #14551. -
pnpm run --recursiveno longer reports interrupted scripts as lifecycle failures afterCtrl+C. -
pnpm restartnow runs the "stop" and "start" scripts when the package has no "restart" script. Previously it ran "stop" and then failed with "Missing script: restart" #4750. -
pnpm install-testnow accepts--no-bailwhen executed directly and in recursive runs #3777. -
pnpm dlxnow keeps a separate cache entry for each Node.js major version. A package built under one Node.js major version, such as a native addon, is no longer reused under another #8611. -
A
runtime:version range that contains||or a space, such as adevEngines.runtimeversion of^22.18.0 || ^24.0.0, now installs the requested runtime. pnpm used to install the npm package with the same name, such asnode#14817. -
When the configured
scriptShelldoes not exist, running a script now fails with an error that names the shell. Previously pnpm printed only an exit code or the package directory #7562. -
If a script is killed by a signal that pnpm survives, such as SIGPIPE, the error now names the signal:
Command failed with signal SIGPIPE.#9821.
Publishing, packing, and deploying
-
pnpm publishnow resolvesworkspace:dependencies from workspace manifests whennode_modulesis not installed. Previously, publishing withoutnode_modulesfailed withERR_PNPM_CANNOT_RESOLVE_WORKSPACE_PROTOCOL#6567. -
pnpm publishnow honorspublishConfig["@scope:registry"]for a package in that scope. It takes precedence over the registry set for the same scope in.npmrcand overpublishConfig.registry#12071. -
pnpm packandpnpm publishnow include bundled dependencies when using the isolated linker. This covers workspace packages and the dependencies of each bundled package. Bundled dependencies are also included whenpublishConfig.directoryselects a build directory #1643. -
pnpm pack,pnpm deploy, and installs of local directory dependencies now keep symlinks that point to files or directories included in the package.pnpm packleaves out symlinks that point outside the package #8208. -
pnpm packnow honors thefilesfield ofpackage.yamlandpackage.json5manifests. Git-hosted and injected local dependencies that use these manifests now honor it too #7906. The archive includes exactly onepackage.jsonwhen the project uses an alternative manifest format, even when.npmignoreorfilesexcludes the source file. -
pnpm packnow preserves file executable permissions in the packed tarball when source files are executable on disk. -
pnpm publishandpnpm packnow report a missingversionornamefield on a workspace dependency. Previously, pnpm reported that the dependency was not installed #4164. -
pnpm publishandpnpm packnow report an error when a bin script has a shebang line ending with CRLF #7311. -
pnpm deploynow copies thepackageManageranddevEngines.packageManagerfields of the workspace rootpackage.jsoninto the deployedpackage.json, unless the deployed project pins a package manager itself #9079. -
pnpm deploynow puts the virtual store atvirtualStoreDir, resolved against the deploy directory. A shared-lockfile deploy recordsvirtualStoreDirin the deployedpnpm-workspace.yaml. With the global virtual store enabled or an absolutevirtualStoreDir, the deploy still usesnode_modules/.pnpm#8787. -
pnpm deploynow respects--package-import-methodpassed on the command line and reports the package import method correctly #7593. -
pnpm deployno longer triggers an install when running scripts in a read-only deployed filesystem #11617. -
A legacy
pnpm deploywithnode-linker=hoistednow puts the deployed project's direct dependencies at the top of the deployednode_modules#9671. -
pnpm deploy --legacyno longer rewritesnode_modules/.pnpm-workspace-state-v1.jsonin the source workspace. The nextverifyDepsBeforeRuncheck there reported the workspace as out of date #15352.
Manifests and configuration files
-
Fixed
pnpm versionfailing on projects using apackage.yamlmanifest.Fixed
pnpm initcreating an extrapackage.jsonwhenpackage.yamlis already present. -
pnpm now preserves CRLF line endings when it modifies project manifests.
-
pnpm versionnow applies pending bumps to private workspace packages. A private package's changelog is written to its committedCHANGELOG.md, also whenversioning.changelog.storageisregistry#13736, #13519. -
pnpm change checknow validates the pending change intents in.changeset/. It fails when an intent names a package that is not in the workspace or cannot be released. -
.npmrcfiles now support npm's${VAR?}placeholder. It expands to the value ofVAR, or to an empty string without a warning whenVARis unset #14404. -
pnpm now expands environment variables in
_auth.authTokenvalues loaded from globalconfig.yamlandpnpm_config__auth#12828. -
pnpm now warns when an empty environment variable removes an
.npmrcauthentication token. Authentication environment warnings now name the affected key #4806. -
pnpm now keeps the configured default registry when
_authholds credentials for several registries and some of those registries serve package scopes.Lockfile verification checks a tarball hosted on a scoped registry against that registry's metadata, unless the package's own scope has a registry assigned #15530.
-
pnpm now treats a missing global
config.yaml,auth.ini, or other optional config file as absent in Node.js-compatible runtimes such as StackBlitz WebContainers. Commands such aspnpm --versionfailed there withENOENT#14030. -
pnpm loginnow logs back in to an existing user on registries without web login, such as verdaccio. The classic login request sends the username and password as basic auth, asnpm logindoes #12055. -
pnpm doctornow checks the configured default registry and sends its credentials. It used to always pinghttps://registry.npmjs.org/#15618.
Global packages, pnpm versions, and runtimes
-
Global commands such as
pnpm add --global,pnpm list --global, andpnpm bin --globalnow run with the pnpm you invoked, even in a project that pins another pnpm version. Previously, a pin withonFail: "download"switched them to the pinned pnpm, and a pinned pnpm 10 or older failed because its global bin directory was not inPATH#14531. -
pnpm add -g,pnpm update -g, andpnpm remove -gnow recover a global package group whose entirenode_modulesdirectory was deleted.pnpm remove -gleaves such a group's command shims in the global bin directory #15093. These commands no longer fail withERR_PNPM_PACKAGE_MANIFEST_IO_ERRORwhen another global package's link into the store dangles, for example after the store was pruned. The pnpm install script failed the same way on such a machine. -
pnpm update --globalnow skips a global package installed from afile:path that no longer exists, prints a warning, and updates the remaining global packages. Previously the whole update failed withERR_PNPM_LINKED_PKG_DIR_NOT_FOUND#12533. -
pnpm update -gno longer asks more than once for approval of the same immaturename@versionwhenminimumReleaseAgeStrictis enabled #15091. -
pnpm self-updaterun in a project that pins pnpm throughpackageManagerordevEngines.packageManagernow also updates the global pnpm, as it does outside a project #14747. -
pnpm self-updateno longer leaves the previous pnpm in the global packages when it was installed as@pnpm/exe.pnpm ls -gnow lists a single pnpm #14709. -
pnpm setupno longer deletes aliases and other lines that sit between a# pnpmcomment and the pnpm block in a shell startup file #7067. -
pnpm env removenow cleans up dangling Node.js executables and symlinks. Surviving global commands remain intact. -
Node.js runtime resolution now supports Windows ARM64. Node.js 20 and newer resolve native
win-arm64builds, and older versions fall back towin-x64under emulation #7123.
Windows and WSL
-
pn,pnpx,pnx, andpnpmnow run when Git Bash, MSYS2, or Cygwin launches them through a Windows path such asC:\Users\me\node_modules\pnpm\pn. The aliases used to fail to find the pnpm installed beside them, or hand the call to an unrelated one #14884. -
Fallback
.cmdand.ps1Windows wrappers in@pnpm/exenow propagate the exit status of the invokedpnpmcommand #14826. -
On Windows, bin shims run from Git Bash, MSYS2, or Cygwin now pass
NODE_PATHto Node.js as Windows paths. A project installed from cmd or PowerShell gave its bins aNODE_PATHunder the Git install directory when they ran from Git Bash. Installing again replaces the shims already innode_modules#3360. -
pnpm installin WSL now waits out Windows file locks on a Windows drive such as/mnt/c, as it already does on Windows. Before, an antivirus or indexer scan holding a file open could fail the install withEACCES#6155. -
On Windows, pnpm now retries saving
pnpm-lock.yamlfor up to a minute while another process holds the file open. The save used to fail at once withEPERM,EBUSY, or "Access is denied" #9461. -
On Windows, pnpm now fails within about a second when it cannot move a
node_modulesdirectory installed by another package manager because a file in it is in use. The error names the directory and suggests stopping the process that uses it. pnpm used to retry for a minute and then print a rawEPERMstack trace #7505. -
pnpm now escapes trailing dots and spaces in
node_modules/.pnpmdirectory names. Windows strips these characters, so a dependency such as"parent-pkg": "file:../"created a directory that could not be deleted or failed to install #8101. -
pnpm installnow resolves local tarballs specified with bare UNC paths on Windows #1669. -
pnpm now recognizes local paths with forward slashes on Windows.
-
On Windows,
pnpm addandpnpm updatenow write relativefile:andlink:specifiers with forward slashes topackage.jsonand the lockfile. They used to write backslashes, so the same project produced different files on Windows and on other systems #7497, #9687. -
Fixed scripts failing with errors such as
'an-compile' is not recognizedwhenscriptShellis set tocmd.exeon Windows #7181.
Inspecting dependencies
-
pnpm auditandpnpm audit signaturesnow check only the dependencies of the projects selected by--filter,--filter-prod, or--workspace-root. The filter used to be ignored, so a filtered audit reported the whole workspace #10982. -
pnpm auditnow lists at least one dependency path from every workspace project that depends on a vulnerable package. Before, a project whose dependency was reached through more than 100 paths filled the path list, and other projects that depend on the same package were left out #12200. -
pnpm audit --fix=updatenow fixes vulnerabilities in dependencies declared through an npm alias. A specifier such as"foo": "npm:vulnerable-pkg@1.0.0"moves to the patched version and keeps the alias. Versions pinned with a leading=are fixed as well #15155. -
pnpm audit --fixnow prunes redundant overrides when one vulnerable range is a subset of another for the same package #8577. -
Running
pnpm listinside a workspace package without--recursiveor a filter now lists only the current package #14494.pnpm licenses listdoes the same. Use--recursiveor--filterto list the licenses of other workspace projects #5689. -
pnpm list --only-projectsnow prints every project selected with--filteror--recursive, including a project that has no workspace dependencies #9770. It also lists the workspace projects whensharedWorkspaceLockfileisfalse#7151, and a project that setspublishConfig.directory#10635. It no longer reports packages innode_modulesthat are missing from the lockfile #9528. -
pnpm licenses listfailed or reported nothing in a workspace withsharedWorkspaceLockfile: false. It now reads the lockfile of each selected project #10140. -
With
nodeLinker: hoisted,pnpm licenses listreported paths undernode_modules/.pnpmthat do not exist. It now reports the directory where the hoisted linker placed each package #8589. -
pnpm outdatedandpnpm -r outdatednow fail withERR_PNPM_NO_PACKAGE_IN_DEPENDENCIESwhen a requested package selector does not match any dependency in the inspected projects #2319. -
pnpm -r outdated --jsonnow includes every outdated workspace dependency when multiple projects depend on different versions or dependency types of the same package. Such a package is keyed by its current version and dependency type, for examplevue@2.7.14 (dev)#7693. -
pnpm sbomfiltered to a single workspace project no longer replaces the project's ownlicenseorbugsfield with the workspace root's value when the project's value is blank. The same applies to anauthor,description,license,repository, orbugsfield set tonull#14882. -
pnpm store statusno longer reports packages with build or postinstall scripts as modified in the store. When packages were mutated, it now lists only those packages and no longer suggests runningpnpm install --force#919. -
pnpm peers checkand theERR_PNPM_PEER_DEP_ISSUESerror now group peer dependency issues under the workspace project they were found in #15351. -
The
pnpm:peer-dependency-issueslog event, which--reporter ndjsonprints, no longer lists peers silenced bypeerDependencyRules.ignoreMissingunderconflictsorintersections#8295.
Output and messages
-
The error for an incompatible pnpm-lock.yaml now reports the lockfileVersion the file was generated with and the lockfileVersion the current pnpm supports. The error also warns that recreating the lockfile with
--forcemay break the application and suggests installing the pnpm version that generated the lockfile #848. -
When the registry stops sending data for longer than
fetchTimeout, pnpm now reports that the metadata or tarball request timed out. Previously the error did not mention the timeout #3646. -
Resolution no longer logs an error when a package metadata request fails and resolution succeeds via cached metadata #2522.
-
The ignored build scripts warning and the update notice are printed as plain lines when output is not a terminal, in CI, or with
--reporter append-only. They were drawn inside a box that broke apart in CI logs #9421. -
pnpm runwith--loglevelset towarn,error, orsilent(or the sameloglevelsetting) no longer prints the$ <command>line before a script, nor the summary of the install thatverifyDepsBeforeRunruns first. Both are info-level output #8944. -
pnpm addnow warns when replacing an existing dependency with a specifier pointing to a different source #14869. -
pnpm updateno longer warns "Skip adding ... to the default catalog" for a dependency that already usescatalog:#13715. -
pnpm remove --helpno longer shows a[@<version>]suffix in its usage line. The command accepts package names only #7751. -
Bash completion now completes script names that contain a colon, such as
pnpm run test:utopnpm run test:unit#5482.
Platinum Sponsors
|
|
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
|
|
|
|
|