Skip to main content

pnpm 11.7

· 9 min read
Zoltan Kochan
Lead maintainer of pnpm

pnpm 11.7 增加了一个用于针对只读包存储安装的 frozenStore 设置,一个用于一次请求发布整个工作区的 --batch 标志,范围特定的认证令牌,以及委托给 pacquet 的完整解析安装。它还强化了锁文件别名的处理,使若干安装路径具有确定性,并包含一些发布和 Windows 修复。

🌐 pnpm 11.7 adds a frozenStore setting for installing against a read-only package store, a --batch flag for publishing a whole workspace in one request, scope-specific auth tokens, and full resolving installs delegated to pacquet. It also hardens lockfile alias handling, makes several install paths deterministic, and ships a number of publish and Windows fixes.

小幅更改

🌐 Minor Changes

新的 frozenStore 设置

🌐 New frozenStore setting

新的frozenStore 设置(--frozen-store)允许 pnpm install只读文件系统 上的包存储运行——例如 Nix 存储、只读绑定挂载或 OCI 镜像层。

🌐 The new frozenStore setting (--frozen-store) lets pnpm install run against a package store that lives on a read-only filesystem — a Nix store, a read-only bind mount, or an OCI image layer.

启用时,pnpm 会以不可变模式打开存储的 SQLite index.db —— 跳过无法在只读目录中创建的 WAL/-shm 辅助文件 —— 并抑制所有会向存储写入的代码路径。将其与 --offline --frozen-lockfile 配合使用,用于完整填充的存储:

🌐 When enabled, pnpm opens the store's SQLite index.db in immutable mode — bypassing the WAL/-shm sidecar files that otherwise can't be created on a read-only directory — and suppresses every code path that would write to the store. Pair it with --offline --frozen-lockfile against a fully-populated store:

pnpm install --frozen-store --offline --frozen-lockfile

商店必须已经包含安装所需的一切,包括任何生命周期脚本被批准的(或有补丁的)包的构建输出。如果在 global virtual store 下缺少必需的构建,安装会在一开始就以 ERR_PNPM_FROZEN_STORE_NEEDS_BUILD 失败,而不是在只读写操作中途构建时崩溃——所以先准备好这些构建。

🌐 The store must already contain everything the install needs, including the build output of any package whose lifecycle scripts are approved (or that has a patch). If a required build is missing under the global virtual store, the install fails up front with ERR_PNPM_FROZEN_STORE_NEEDS_BUILD instead of crashing mid-build on a read-only write — so seed those builds first.

frozenStore--force 以及配置的 pnpr 服务器不兼容(两者都会写入存储),且副作用缓存不会被写入。只读存储的打开需要 Node.js >=22.15.0、>=23.11.0 或 >=24.0.0;在较旧的运行时环境中,--frozen-store 会以明确的 ERR_PNPM_FROZEN_STORE_UNSUPPORTED_NODE 错误失败。

pnpm publish --recursive --batch

用于 pnpm publish --recursive 的新可选 --batch 标志会将所有选定的包一次性发送到注册表,而不是每个包发送一个请求。

🌐 The new opt-in --batch flag for pnpm publish --recursive sends all selected packages to the registry in a single PUT /-/pnpm/v1/publish request instead of one request per package.

目标注册表必须实现批量发布端点(pnpr 已实现);未实现的注册表会明确报告 ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED 错误。批量处理采用全有或全无的方式:如果批次中的任何包验证失败,则没有任何包被发布。

🌐 The target registry has to implement the batch publish endpoint (pnpr does); registries that don't are reported with a clear ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED error. The batch is processed all-or-nothing: if any package in the batch fails validation, none of the packages are published.

特定范围的认证令牌

🌐 Scope-specific auth tokens

pnpm 现在可以为不同的包作用域使用不同的认证令牌,即使这些作用域使用相同的注册表 URL。以前认证仅根据注册表 URL 选择,因此共享注册表的两个作用域(例如 GitHub Packages)必须共享一个令牌。

🌐 pnpm can now use different auth tokens for different package scopes, even when those scopes use the same registry URL. Previously auth was selected only by registry URL, so two scopes sharing a registry (such as GitHub Packages) had to share a token.

通过在 auth 键中的注册表 URL 之后添加包作用域来配置特定作用域的令牌:

🌐 Configure a scope-specific token by adding the package scope after the registry URL in the auth key:

@org-a:registry=https://npm.pkg.github.com/
@org-b:registry=https://npm.pkg.github.com/

//npm.pkg.github.com/:@org-a:_authToken=ORG_A_TOKEN
//npm.pkg.github.com/:@org-b:_authToken=ORG_B_TOKEN

//npm.pkg.github.com/:_authToken=FALLBACK_TOKEN

在安装或发布 @org-a/* 时,pnpm 使用 ORG_A_TOKEN;对于 @org-b/*,它使用 ORG_B_TOKEN。没有匹配范围的包会回退到全注册表的令牌。pnpm login --registry=https://npm.pkg.github.com --scope=@org-a 写入相同的范围特定密钥。请参阅 身份验证设置

🌐 When installing or publishing @org-a/*, pnpm uses ORG_A_TOKEN; for @org-b/*, it uses ORG_B_TOKEN. Packages without a matching scope fall back to the registry-wide token. pnpm login --registry=https://npm.pkg.github.com --scope=@org-a writes to the same scope-specific key. See Authentication Settings.

完全解析安装委托给 pacquet

🌐 Full resolving installs delegated to pacquet

pacquet(pnpm 的 Rust 移植版)在 configDependencies 中声明时,pnpm 现在也将依赖的解析委托给它 —— 不仅仅是物化 —— 前提是已安装的 pacquet 足够新(>= 0.11.7)。

🌐 When pacquet (the Rust port of pnpm) is declared in configDependencies, pnpm now delegates dependency resolution to it too — not just materialization — provided the installed pacquet is new enough (>= 0.11.7).

以前 pacquet 只能在 frozen-install 模式下运行:pnpm 总是自己解析依赖图,然后将一个完成的锁文件交给 pacquet 来获取 / 导入 / 链接。现在,非冻结的 pnpm install(默认隔离的 nodeLinker,普通安装)在一次操作中端到端地委托给 pacquet —— 它解析清单文件,写入锁文件,并生成 node_modules。较早的 pacquet 版本仍然保持先解析再生成的分离模式,而 add / update / remove 仍然在 pnpm 中解析。这仍然是 Rust 安装引擎的可选预览功能(#11723)。

🌐 Previously pacquet only ran in frozen-install mode: pnpm always resolved the graph itself and handed pacquet a finished lockfile to fetch / import / link. Now a non-frozen pnpm install (default isolated nodeLinker, plain install) is delegated to pacquet end-to-end in a single pass — it resolves the manifests, writes the lockfile, and materializes node_modules. Older pacquet releases keep the resolve-then-materialize split, and add / update / remove still resolve in pnpm. This remains an opt-in preview of the Rust install engine (#11723).

补丁更改

🌐 Patch Changes

  • Security: Reject path-traversal and reserved dependency aliases (such as ../../../escape, .bin, .pnpm, or node_modules) that come from a lockfile rather than a freshly resolved manifest. A crafted lockfile alias could otherwise be joined directly under a hoisted node_modules directory, letting package files be written outside the install root or overwrite pnpm-owned layout. The lockfile verification gate now runs an always-on, policy-independent check that rejects any invalid importer or snapshot alias before any fetch or filesystem work, for every node linker at once.

  • Prevent pnpm patch-remove from removing files outside the configured patches directory.

  • Fixed pnpm publish ignoring strictSsl: false when publishing to registries with self-signed certificates. The option is now forwarded to libnpmpublish / npm-registry-fetch, the same way it is for pnpm install (#12012).

  • Fixed Cannot destructure property 'manifest' of 'manifestsByPath[rootDir]' regression (introduced in 11.6.0) when running pnpm add <pkg> outside a workspace on Windows (#12379).

  • Git dependencies that point to a subdirectory of a repository (repo#commit&path:/sub/dir) keep their path in the lockfile again. Without path, later installs from that lockfile silently unpacked the repository root instead of the subdirectory (#12304).

  • Made shared package child resolution deterministic when the same package is reached through multiple contexts (#12358).

  • Fixed nondeterministic lockfile output that made pnpm dedupe --check fail intermittently in CI when a locked peer provider was pinned for a dependency with no children of its own.

  • Fix garbled summary line after submitting pnpm update -i and pnpm audit --fix -i. The summary now lists only the selected package names (or vulnerability keys) instead of every selected choice's full table row.

  • User-defined npm_config_* environment variables are now preserved during lifecycle script execution. Previously all npm_-prefixed env vars were stripped, losing user-set variables like npm_config_platform_arch (#12399).

  • pnpm setup no longer prompts to approve build scripts for @pnpm/exe when installing the standalone executable (#12377).

  • Sped up pnpm install with a frozen lockfile by running lockfile verification concurrently with fetching and linking instead of blocking the whole install on it. Dependency lifecycle scripts are still held back until verification succeeds, so no script runs on an unverified lockfile.

  • A 304 Not Modified answer from the registry now renews the cached metadata file's mtime, so the minimumReleaseAge freshness shortcut keeps serving resolutions from the cache instead of re-validating forever.

  • Fixed a Windows-only hang where a failed command could take 20–46 seconds to exit, caused by a slow wmic/PowerShell descendant-process lookup that is now bounded by a short timeout.

  • Updated dependency ranges, notably msgpackr 1.11.8 → 2.0.4 (store index files remain byte-compatible in both directions), open ^7.4.2 → ^11.0.0, and @zkochan/cmd-shim to v9.0.6.