PNpM CLI
Minor Changes
-
pnpm loginandpnpm addusernow record the granted token in the globalconfig.yaml, under the_authsetting, with--scope's scope routed to that registry underregistries.pnpm logoutremoves it from there, and still from anauth.inian earlier version wrote. Tokens already inauth.inikeep working. -
A
scopeset in a project'spnpm-workspace.yamlis now ignored, with a warning naming where to set it instead.pnpm loginrecords the scope as a@scope:registryroute in the machine-globalauth.ini, which outranks~/.npmrcin every project — so a repository-committed file could redirect a scope such as@acmefor all of a user's other projects after one routine login. Use--scope, thePNPM_CONFIG_SCOPEenvironment variable, or the global config file instead #13557. -
Verified remote build artifacts are persisted in the shared store with their signed origin metadata. Later installs reverify the artifact against current trust, policy, platform, and source before reuse, while invalid remote variants are quarantined per channel (pnpm/pnpm#13771).
-
Persist completed recursive tasks so
--resume-fromskips exactly the work that passed during a matching interrupted or failedpnpm -r run/pnpm -r execinvocation. When no compatible state exists, pnpm retains its graph-based resume behavior. -
Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group.
-
Added per-task concurrency limits to workspace task orchestration. Set
tasks.<name>.concurrencyinpnpm-workspace.yamlto limit how many instances of that task may run across workspace projects at once:tasks: build: concurrency: 2 -
sideEffectsCachenow declares the whole of how a package's build output is reused — whether one is restored, whether one is saved, and the remote tier that shares it between machines:sideEffectsCache: read: true write: true remote: org: acme packages: ['native-addon']sideEffectsCache: true,sideEffectsCacheReadonly,remoteSideEffectsCache, and itsorganizationfield all keep working. Where a field is set under both spellings the one above wins; where it is set under only one, it is kept.Two behaviors change, both bringing this CLI in line with what the Rust one already did:
sideEffectsCacheReadonly: truenow blocks writing to the cache, and setting it alongsidesideEffectsCache: falsegives a read-only view rather than switching the cache off entirely. A cache can also be declared write-only now, to populate one the run does not read. -
Workspace task orchestration (pnpm/rfcs#23).
pnpm -r runandpnpm -r execnow schedule per task instead of in topological chunks: a task starts as soon as the tasks it depends on have finished, so a project no longer waits for unrelated projects that happen to share its chunk.A new
taskssection inpnpm-workspace.yamldeclares what a task depends on, using the^convention:tasks: build: dependsOn: ['^build'] test: dependsOn: ['build'] lint: {}^namemeans the named task in each of the project's workspace dependencies; a barenamemeans the task in the same project; an entry with nodependsOndeclares an empty dependency list. A task with no entry behaves asdependsOn: ['^<its own name>'], which is exactly what the previous chunked ordering implied — an unconfigured workspace gets the scheduler improvement and nothing else changes meaning. A project without the script is reported skipped and passes its edges through to its own dependencies, so a scriptless package does not sever a chain.Also part of this change:
- A dependency cycle among the tasks of a run is now an error naming the participating tasks (
ERR_PNPM_TASK_CYCLE) instead of silently running in an arbitrary order. SettingignoreWorkspaceCycles: truedowngrades the error to a warning: the cycle's tasks run in an arbitrary order relative to each other. --resume-fromnow skips exactly the transitive dependencies of the anchor package; work unrelated to the anchor still runs.- Under
--no-bail, tasks whose dependencies failed are reported as skipped, not failed, and do not add to the exit code. - With
--bail(the default), the first failure still ends the run at once and nothing new is dispatched — including scripts already queued behind the concurrency limit. pnpm -r run --dry-run <script>prints the task graph that would execute without running anything (including skipping theverifyDepsBeforeRuncheck);--jsonemits the tasks and their resolved dependency edges.- Output is inherited rather than piped only when at most one script can ever be in flight (
--workspace-concurrency=1, or the graph forces the scripts to run one after another).
- A dependency cycle among the tasks of a run is now an error naming the participating tasks (
-
Added macOS and Windows x64 and arm64 support to remote shared build artifacts pnpm/pnpm#13771.
-
Generalized the experimental shared-artifact protocol so candidates and signed payloads identify a discriminated subject. Dependency side effects use package and source-integrity subjects, while workspace tasks use project and task subjects.
This changes shared-artifact request bodies and signed payloads. A pnpr server and its clients have to be on matching versions.
Patch Changes
-
An
_authentry in the global config file no longer decides which registry packages come from when something else says. Aregistryorregistriesdeclared inpnpm-workspace.yamlor the global config now wins over the route inferred from a stored credential, which still applies where nothing else declares one. Thepnpm_config__authenvironment variable is unchanged: it stays the way to point a CI runner at a mandated proxy, and still overrides what a repository declares. -
Fixed
pnpm deploy --legacyto exclude dependencies that are only reachable from unselected workspace projects afterpnpm fetch. -
Fixed dependency-verification install logs corrupting
pnpm execoutput and ignoring--silentpnpm/pnpm#14197. -
pnpm clean/pnpm purgerun from a workspace subdirectory now remove each project's ownnode_modulesinstead of emptying the workspace root's for every project #14239. A custommodulesDiris resolved against each project directory too. -
pnpm dlx <pkg>@catalog:now resolves the specifier through the calling workspace's catalogs instead of failing withERR_PNPM_CATALOG_ENTRY_NOT_FOUND_FOR_SPEC#14294. -
Fixed
pnpm doctorreporting a version that does not matchpnpm --versionpnpm/pnpm#14225. -
Pacquet now strips exactly one leading path component from
./-prefixed tarball entries, matching pnpm and npm's tar extraction semantics and keeping shared store keys consistent. -
Installs whose lockfile carries platform or engine constraints are up to ~150 ms faster when resolution runs: the
node --versionprobe behind the installability checks now starts before the lockfile is parsed and finishes while dependencies resolve, instead of running afterwards. -
Treat empty scripts selected by a regular expression as missing before running dependent tasks.
-
Filter hidden scripts matched by a regular expression during recursive runs when a visible script also matches.
-
Fixed
.mjspnpmfile hooks failing to load on Windows, including hooks supplied by config dependencies pnpm/pnpm#14301. -
Fixed automatically switched pnpm versions forcing all descendant pnpm processes to use the same version pnpm/pnpm#14309.
-
Fixed
pnpm deploy --prodfailing when an excluded dev dependency was also declared as an optional peer dependency pnpm/pnpm#14302. -
Fixed
pnpm packto respect thefilesfield when deciding whether to include root-level changelog, history, and notice files. -
pnpm update -gno longer downgrades a global package.--latestresolves thelatestdist-tag, which can point at an older release than the one installed — afterpnpm add -g <pkg>@next, for instance #14270.pnpm update -galso no longer changes the pnpm version. pnpm's own global install belongs topnpm self-update#14270. -
When multiple versions of the same package expose the same binary, pnpm now links the binary from the highest version #14249.
-
pnpm updateno longer replaces the specifier a project declares for a dependency that is also listed inoverrides. Acatalog:reference stays acatalog:reference, and a declared range stays as written, instead of being rewritten to the version the override resolved to #12115. -
pnpm updateno longer moves the range a project declares for a dependency thatoverridesalso lists, even when the override repeats that range verbatim. Previously the updatedpackage.jsondisagreed with the lockfile, so the nextpnpm install --frozen-lockfilefailed with a specifier mismatch #14224. -
Allowed pnpm's shared-artifact client to connect to an artifact-only pnpr tier.
-
Rebuilding
node_modulesfrom an up-to-date lockfile is up to ~200 ms faster: thenode --versionprobe that installability checks and store keying need now runs concurrently with the store's warm-cache reads instead of before them. -
Remove the duplicate colon from the one-time password prompt.
-
Print errors as JSON on stdout when
--jsonis passed topnpm viewor its aliases (info,show, andv). -
Installs complete faster on workspaces with many projects: each project's
node_modulesis now linked concurrently. -
Fixed
patchedDependenciesmatching for git-hosted dependencies during fresh and frozen installs pnpm/pnpm#14273. -
pnpm pm <command>works again: thepmprefix, which forces pnpm's built-in command over apackage.jsonscript of the same name, is recognized instead of failing withERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL/Command "pm" not found.pnpm pm cleanandpnpm pm purgenow removenode_moduleseven when the project (or the workspace root) declares aclean/purgescript #14226. -
The settings that pnpm accepts as command-line flags are recognized again:
--package-import-method,--hoist-pattern,--public-hoist-pattern,--no-hoist,--global-dir,--virtual-store-dir,--modules-dir,--child-concurrency,--no-lockfile,--strict-peer-dependencies,--side-effects-cache,--side-effects-cache-readonly,--trust-policy,--trust-policy-exclude,--trust-policy-ignore-after, and--optimistic-repeat-install. Each is accepted anywhere on the command line, spelled either--setting=valueor--setting value, and overrides the same setting read frompnpm-workspace.yamlor.npmrc#14281. -
pnpm add,pnpm update, andpnpm removenow savepackage.jsonbefore failing withERR_PNPM_IGNORED_BUILDS. The dependency they were asked to change is already materialized by that point, so the manifest has to record it — otherwise the next install removes the packages again. -
The progress output no longer overwrites the lines above it once it grows taller than the terminal window #14270.
-
Restoring a dependency's build from the remote side-effects cache no longer downloads files the store already holds.
-
Recognize
pnpm install --fix-lockfile, including filtered installs, and regenerate broken lockfile metadata while preserving compatible locked versions pnpm/pnpm#14250. -
Fixed intermittent
Access is deniedfailures when concurrent global commands hand off the global bin lock on Windows. -
Fixed the
--shamefully-hoistCLI option being rejected pnpm/pnpm#14235. -
The environment variables for the remote side-effects cache are named for the setting they configure:
PNPM_SIDE_EFFECTS_CACHE_REMOTE_KEY_ID,..._BUILDER_ID,..._IMAGE_DIGEST,..._ARCHITECTURE_BASELINE,..._PRIVATE_KEY,..._BUILD_ENV,..._TRUSTED_KEYSand..._PUBLISH. ThePNPM_REMOTE_SIDE_EFFECTS_CACHE_*names keep working, and the new one wins when both are set. -
Installs that run no build scripts finish faster, especially in workspaces with many projects.
-
A
devEngines.packageManagerrange pin on pnpm is now recorded inpnpm-lock.yaml'spackageManagerDependencieswhen the running pnpm already satisfies it, using the running version and keeping the range as the recorded specifier. Previously only an exact pin — or a range resolved on the way through a version switch — reached the lockfile, so a range pin written by hand (or by any tool other thanpnpm add/pnpm self-update) left the project without the shared resolution the pin exists to provide. -
Workspace installs are substantially faster (~0.7 s on a 60-project workspace): after hoisting, pnpm now shims only the bins of publicly hoisted workspace packages instead of re-walking every project's
node_modulesto rediscover bins that were already linked. -
Fixed a large install-time regression on macOS for installs that rebuild
node_modulesfrom a warm store #14231. APFS serializes file-cloning and hard-linking syscalls volume-wide, so importing packages one file at a time from many threads was bounded by a per-volume ceiling and got slower the more CPU cores the machine had. On macOS,pnpm installnow materializes each package once into the store'slinksdirectory (the same canonical slotsenableGlobalVirtualStoreuses) and copies it intonode_modules/.pnpmwith a single copy-on-write directory clone per package, replacing tens of thousands of per-file syscalls with one per package. Applies with the defaultnodeLinker: isolatedwhenenableGlobalVirtualStoreis off andpackageImportMethodisauto,clone, orclone-or-copy; hoisted, global-virtual-store, and explicithardlink/copyinstalls are unchanged. -
Stop in-flight recursive
runandexeccommands when bailing after the first failure. -
Warm installs that rebuild
node_moduleson macOS are about 10% faster: creating each package's virtual-store directory now issues fewer filesystem calls. -
An
_authcredential in an.npmrcnow authenticates even when its base64 is written without the trailing=padding (or with extra padding, or with whitespace inside it), instead of failing with a 401. An_auththat is not valid base64, or that carries no:between the username and the password, now fails withERR_PNPM_AUTH_INVALID_BASE64/ERR_PNPM_AUTH_MISSING_SEPARATOR#14257. -
Colored output is no longer printed as raw escape sequences in the Windows Command Prompt #14292. Commands such as
pnpm listnow style their output there.
<!-- sponsors -->
Platinum Sponsors
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> <td align="center" valign="middle"> <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" /> <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/notion.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/notion_light.svg" /> <img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion" /> </picture> </a> </td> </tr> </tbody> </table>
Gold Sponsors
<table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a> </td> </tr> </tbody> </table>
<!-- sponsors end -->