pnpm 12.8
pnpm 12.8 warns when pnpm pack or pnpm publish would ship a .env file,
installs sharedWorkspaceLockfile: false workspaces concurrently, applies every
setting passed as --config.<name>=<value>, and no longer leaves the Windows
terminal stuck after Ctrl+C in a script. This release also carries a long list
of bug fixes.
Minor Changes#
Warning about .env files in packages#
pnpm pack and pnpm publish now warn when the tarball includes a .env or
.env.* file that the files field of package.json does not list
(#7826). Templates such as
.env.example are not reported. List the file in files to publish it on
purpose, or exclude it in .npmignore or .gitignore.
Concurrent installs without a shared lockfile#
In a workspace with
sharedWorkspaceLockfile: false,
pnpm install now installs projects concurrently, up to
workspaceConcurrency at a time
(#14480). A project is resolved,
fetched, and written to its virtual store without waiting for the workspace
projects it depends on. It waits for them only before it links its dependencies
and runs its lifecycle scripts, so its scripts still run after theirs. A project
with a preinstall or pnpm:devPreinstall script, or with an injected or
file: workspace dependency, waits for its workspace dependencies before it
starts.
The installs also share package metadata, lockfile verification, and store caches, so they use less CPU and memory when several projects depend on the same packages.
Related fixes for such workspaces:
- With
enableGlobalVirtualStore, each project keeps its current lockfile and its hidden hoisted dependencies in its ownnode_modules/.pnpm, so a repeat install no longer relinks the other projects' packages. pnpm rebuild,pnpm approve-builds, andpnpm ignored-buildswork on the current project'snode_modules(#9402).- An injected workspace package that has lifecycle scripts is hard linked into the projects that depend on it, so later edits reach them (#9828).
Every setting accepted as --config.<name>#
Every setting pnpm supports can now be set with --config.<name>=<value> on the
command line, not only the ones whose command also carries a matching flag
(#16276). Before,
pnpm install --config.frozen-lockfile=true dropped the setting and rewrote
pnpm-lock.yaml as though the install had not been frozen.
Settings given on the command line, such as --registry and --store-dir, now
also take precedence over the values a pnpmfile updateConfig hook sets
(#14063).
Ctrl+C on Windows#
Interrupting a script with Ctrl+C on Windows no longer leaves the terminal stuck
(#14860). A script that runs
through a batch shim, as vite dev does through vite.CMD, made cmd.exe wait
forever on its "Terminate batch job (Y/N)?" answer, and every following
keystroke went to that prompt. pnpm now ends a cmd.exe script shell once it has
sat for a second after the interrupt with nothing running under it. A script
that takes longer to shut down is still waited for, and a second Ctrl+C ends
the script's shell at once.
Other changes#
pnpm packhonors--silent,--reporter=silent, and--loglevel=silentto hide the tarball contents and summary (#10297).- An in-place edit to the source of an injected workspace package now shows up
in its injected copy, unless a build writes to that package or
packageImportMethodis set (#4410). Scripts listed insyncInjectedDepsAfterScriptsnow update injected dependencies while they run, so a dev server watching the injected package sees each change before the script exits. pnpm update --globalreinstalls the global packages that pnpm 10 installed into<global-dir>/5, so their commands are linked again andpnpm list --globallists them. Once every package is migrated, pnpm deletes the previous directory (#11528).pnpm installruns the install hooks of a config dependency plugin's pnpmfile, includingreadPackage,afterAllResolved, and custom resolvers. Before, only itsupdateConfighook ran.- Scripts see the
npm_commandenvironment variable (#16265) and annpm_config_node_gypthat points at the bundlednode-gyp(#16270).
Patch Changes#
Installing#
pnpm install --devandpnpm fetch --devinstall the optional dependencies of devDependencies, such as the platform binaries of Biome and oxlint (#9678).pnpm install --offlineandpnpm add --offlineresolve a range to the newest matching version whose tarball is already in the store (#10715).- An offline install that fails on a metadata cache from before pnpm 12.4
explains that one online install repopulates it, with the
ERR_PNPM_NO_OFFLINE_METAcode (#15656). - Installing a git-hosted dependency that has to be built no longer fails on unapproved build scripts of its own dependencies (#9764), and a git-hosted pnpm workspace without a committed lockfile is detected as a pnpm project (#14011).
pnpm installrefreshes dependencies when a localfile:directory changes its dependencies (#4623).- An optional dependency whose install script fails is removed from
node_modules(#8756). - With
nodeLinker: hoisted,pnpm installrestores a deleted workspace project'snode_modules, and clears orphaned package directories that an interrupted install left behind (#13676). - Installing through a
pnprserver records the pnpmfile checksum in the lockfile (#14460) and links a workspace project at itspublishConfig.directory.
Resolving and linking#
pnpm installno longer aborts on a huge allocation when peer ranges combine overlapping||alternatives (#15867).- A registry package with a
file:dependency on a directory inside itself, such as"@types/css-tree": "file:./typings/css-tree", installs as it does with npm and Yarn (#9141). - An
npm:alias written byoverridesstays in place when pnpm re-resolves the aliased dependency (#16309). - A peer dependency no longer resolves to two different versions for one package (#12098), and an optional peer is no longer resolved from another workspace project's package when that causes bogus unmet peer errors (#13989).
pnpm dedupeno longer changes the lockfile on every run when a nested peer is provided through an npm alias (#15709).- With
resolutionMode: time-basedandminimumReleaseAgeboth set, a subdependency is no longer reported as too new when only the time-based cutoff excludes it (#13569, #16298). - A transient metadata fetch failure is retried and no longer reported as
TRUST_DOWNGRADEorMINIMUM_RELEASE_AGE_VIOLATION(#12031). - pnpm's built-in package compatibility database no longer applies to a
project's own manifest, so a project named like
vue-loaderno longer gains dependencies (#11700). - Packages in an external
virtualStoreDircan resolve the project's hoisted direct dependencies. Runpnpm install --forceto repair an existing installation (#5652). - The bins of auto-installed peer dependencies are linked into the workspace
root's
node_modules/.bin(#8511).
Lockfiles#
pnpm install --frozen-lockfileworks on a detached HEAD withgitBranchLockfile(#7672), and accepts a lockfile without an importer entry for a workspace package that has no dependencies (#15875).pnpm installfails withERR_PNPM_LOCKFILE_MISSING_DEPENDENCYwhen an importer references a dependency version with no snapshot entry (#14764).- On CI, an explicit
preferFrozenLockfile: trueno longer lets the install update an outdated lockfile (#9072).
Workspaces and injected packages#
pnpm installno longer creates anode_modulessymlink inside thepublishConfig.directoryof a package linked withlinkDirectory, where a build tool cleaning its output could delete dependency files (#16226).- An injected workspace dependency that publishes from a
publishConfig.directorybuilt by its ownpreparescript installs correctly (#7811). injectWorkspacePackagestreats a dependency declared with a relative path, such asworkspace:../foo, the same way as aworkspace:*one (#10446).- Workspace discovery skips dot-prefixed directories, so
**no longer matches projects inside.cache(#16250). pnpm importkeeps the versions pinned by ayarn.lockinside a workspace project (#4385).
Store and caches#
- Files imported from the store follow the umask of the install (#3807), and files already in a shared store keep their owner, group, and mode (#12765).
- Repairing a store file modified through a hard link keeps its inode on Linux and macOS, so other projects are healed at the same time (#3445).
- pnpm warns when it cannot hard link from the store in the pnpm home and falls back to a store on the project's filesystem (#14505).
- The side-effects cache restores symlinks that a build script creates. After upgrading, every package with a build script is built once more (#12859).
- Concurrent installs that share a global virtual store build a package in its slot one at a time (#15568).
- A warm install reuses on-disk metadata for five minutes when the registry
sends no ETag (#13976), and
honors
Cache-Controlfor registry metadata (#13487) and tarball URL dependencies (#15648).
Patched dependencies#
pnpm installrepairs a lockfile whosepatch_hashpaths disagree withpatchedDependencies, and--frozen-lockfilefails on it withERR_PNPM_INCONSISTENT_PATCH_HASH(#15336).- A missing patch file fails the install with
ERR_PNPM_PATCH_NOT_FOUND(#5268). - With
nodeLinker: hoisted, a shared copy of a patched dependency is patched only once (#7565). engineStrictchecks the patchedpackage.json(#9603).pnpm patchapplies the existing patch to a git-hosted dependency (#9699).
Adding and updating#
pnpm add <dir>warns when the directory declares peer dependencies (#5523).pnpm add --save-typesskips deprecated@types/*stubs such as@types/typescript(#15636).pnpm version,pnpm add, andpnpm pkg setkeep JSON5 style inpackage.json5(#15717).
Running scripts#
pnpm runandpnpm execno longer auto-install when the rootpackage.jsonstill keepsoverridesor similar settings in itspnpmfield. They fail and ask to move the settings topnpm-workspace.yaml(#16278).- When
verifyDepsBeforeRuninstalls before a filtered command, it installs only the selected projects (#11865). pnpm -r run /regexp/honorstasksdependsOn(#15596).pnpm runexits with the code of a script that handles Ctrl+C (#9945), and no longer hangs when a background process keeps a finished script's output open (#5730).scriptShellis used even whenshellEmulatoris enabled (#14719).- With
enableGlobalVirtualStore, dependency build scripts see the workspace root'snode_modules/.bin(#15652). - Install scripts find the bundled node-gyp when pnpm runs through a symlink (#15694).
- Commands run through a dependency's own
node_modules/.binno longer fail withMODULE_NOT_FOUND(#10189). pnpx --versionandpnpm dlx --versionprint the pnpm version (#16259).
Publishing and deploying#
pnpm packandpnpm publishship a file thatfilesnames even when another entry excludes its directory (#16213), and prune directories that afilesexclusion names (#15738).pnpm publishwaits at least 5 minutes for the registry to answer, fixing "409 Conflict - Failed to save packument" errors (#11454).pnpm deploy --prodworks with adevEngines.runtimethat usesonFail: download(#15703).pnpm deploycopies workspace dependencies instead of hard linking them to their sources (#12176), andpnpm deploy --legacyno longer leaves broken links (#9575).
Configuration#
pnpm config set --location=projectinside a workspace package writes to the workspace root'spnpm-workspace.yaml(#13757).PNPM_CONFIG_WORKSPACE_DIRis read like other settings (#16275).- An invalid base64
_passwordfails withERR_PNPM_AUTH_INVALID_BASE64(#16273). proxy=falseturns proxying off even when proxy environment variables are set.- A pnpmfile
fetchershook runs once per package (#15584, #15025), a custom resolver's package is re-fetched when itsintegritychanges (#15670), and invalidreadPackageresults are rejected (#15730, #15705).
Global packages and pnpm versions#
- Signals such as
SIGTERMreach the pnpm version pnpm switches to and the onepnpm withruns (#9948). - On arm64 musl Linux, switching to a pinned pnpm older than 12 runs the JavaScript package (#10443).
- Global shims work when pnpm runs through a relative symlink, as with Homebrew (#15691).
pnpm env remove --globaldeletes Node.js versions in pnpm's store (#8357).pnpm self-updateno longer suggests a downgrade whenminimumReleaseAgeholds backlatest(#12006).
Windows#
pnpm runpasses arguments to the script as typed, without%VAR%expansion or doubled backslashes (#16257).pnpm.exeruns without the Visual C++ Redistributable (#15723)..cmdshims keep a%in the project path (#15716) and run tools with non-ASCII paths (#6999, #16217). Bin shims run from Cygwin again (#12845).- Installing pnpm with npm writes
node_modules/.binshims that runpnpm.exe(#15688). - Wildcard workspace patterns work when the workspace is on a different drive than the pnpm cache (#16239).
pnpm setupno longer writespn.ps1,pnpx.ps1, andpnx.ps1, which failed under restrictive execution policies (#8444), no longer panics on non-ASCII environment variable names (#15684), and expands nested%VAR%references inPNPM_HOME(#13236).
Inspecting dependencies and output#
pnpm auditfails on unresolvable dependency references (#13638).pnpm licenses listreports actual on-disk locations with hoisting (#8589).pnpm rootprints the configuredmodulesDir(#9113).- With
--loglevel warnorerror, the full output of a failed install script is printed. - Resolution errors show their cause, such as
invalid peer certificate: UnknownIssuer(#9556), and SSHPermission denied (publickey)errors suggestssh-add -l(#13743). - Lockfile verification errors carry specific codes and suggest relaxing a policy only when that could help (#14411).
See the v12.8.0 release notes for the complete list of changes.