pnpm 11.7
pnpm 11.7 引入了 frozenStore 设置,支持针对只读包存储进行安装;新增了 --batch 标志,允许在单个请求中发布整个工作区;支持针对特定作用域的身份验证令牌;并将完整的解析与安装过程委托给 pacquet 处理。 此外,该版本还增强了对锁文件别名的处理,让多个安装路径确定,并修复了若干与发布及 Windows 平台相关的问题。
次要更改
新增 frozenStore 设置
新的 frozenStore 设置(--frozen-store)允许 pnpm 在位于只读文件系统(如 Nix 存储、只读绑定挂载或 OCI 镜像层)上的包存储中执行安装操作。
启用后,pnpm 会以不可变模式打开存储中的 SQLite 索引文件 index.db——从而避免创建在只读目录下无法生成的 WAL/-shm 辅助文件——并禁用所有会向存储写入数据的代码路径。 配合 --offline --frozen-lockfile 标志针对全部包含的存储:
pnpm install --frozen-store --offline --frozen-lockfile
存储中必须已包含安装所需的所有内容,包括任何已批准执行生命周期脚本(或包含补丁)的包的构建产物。 如果全局虚拟存储中缺少必要的构建产物,安装过程会立即失败并抛出 ERR_PNPM_FROZEN_STORE_NEEDS_BUILD 错误,而不是在尝试向只读存储写入数据时中途崩溃——因此请务必预先生成这些构建产物。
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 标志,允许将所有选定包通过单个 PUT /-/pnpm/v1/publish 请求发送到注册源,而不是为每个包单独发送请求。
目标注册源必须实现批量发布端点(pnpr 已实现该功能);对于不支持批量发布的注册源,pnpm 会明确报错 ERR_PNPM_BATCH_PUBLISH_UNSUPPORTED。 该批次的处理遵循“全有或全无”原则:如果批次中的任何包未通过验证,则所有包都不会被发布。
特定作用域的认证令牌
pnpm 现在支持为不同的包作用域使用不同的认证令牌,即使这些作用域使用相同的注册源 URL。 此前,认证方式仅根据注册源 URL 确定,因此共用同一注册源(例如 GitHub Packages)的两个作用域必须共用同一个令牌。
通过在认证配置项的注册源 URL 之后添加包作用域,来配置特定作用域的令牌:
@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 时,也会写入同样的特定作用域配置项。 详见 认证设置。
完整的解析安装委托给 pacquet
当在 configDependencies 中声明了 pacquet (pnpm 的 Rust 实现版本)时,只要安装的 pacquet 版本足够新(>= 0.11.7),pnpm 现在不仅将物化委托给它,还将依赖解析过程也一并委托给它。
此前,pacquet 仅在“冻结安装”模式下运行:即 pnpm 负责解析依赖图,然后将生成的 锁文件交给 pacquet 进行获取、导入或链接操作。 现在,非冻结模式的 pnpm install(默认使用隔离模式的 nodeLinker,即普通安装)将端到端地委托给 pacquet 一次性完成——它负责解析清单、写入锁文件并生成 node_modules。 旧版本的 pacquet 仍保持“先解析后生成”的分离模式,而 add、update 和 remove 操作中的解析过程仍由 pnpm 处理。 这依然属于 Rust 安装引擎的一项可选预览功能 (#11723)。
补丁更改
- 安全:拒绝处理来自锁文件而非新解析的清单的路径遍历路径及保留的依赖别名(例如
../../../escape、.bin、.pnpm或node_modules)。 否则,精心构造的锁文件别名被直接合并到提升的node_modules目录下,允许软件包文件被写入安装根目录之外或覆盖 pnpm 管理的目录结构。 锁文件验证门现在运行一个始终开启的、与策略无关的检查,在任何获取或文件系统工作之前,拒绝任何无效的导入器或快照别名,对每个节点链接器一次性执行此检查。 - 防止
pnpm patch-remove移除配置的补丁目录之外的文件。 - 修复了向使用自签名证书的注册源发布包时,
pnpm publish忽略strictSsl: false配置的问题。 该选项现在会像pnpm install一样传递给libnpmpublish/npm-registry-fetch(#12012)。 - 修复了在 Windows 上于工作区之外运行
pnpm add <pkg>时出现的Cannot destructure property 'manifest' of 'manifestsByPath[rootDir]'递归问题(引入于 11.6.0 版本) (#12379)。 - 指向仓库子目录的 Git 依赖(格式为
repo#commit&path:/sub/dir)现在会在锁文件中保留其路径信息。 若缺少路径信息,后续基于该锁文件的安装操作会静默解压整个仓库根目录,而非仅解压子目录 (#12304)。 - 确保当通过多个上下文访问同一个包时,共享包的子依赖解析结果具有确定性 (#12358)。
- 修复了锁文件输出不确定的问题:当某个没有自身子依赖的包锁定了特定的对等提供者时,该问题会导致 CI 环境中的
pnpm dedupe --check偶尔失败。 - 修复了执行
pnpm update -i和pnpm audit --fix -i后摘要行显示乱码的问题。 现在,摘要仅列出选定的包名(或漏洞键),而不再显示每个选项对应的完整表格行。 - 在执行生命周期脚本期间,用户定义的
npm_config_*环境变量现在会被保留。 此前,所有以npm_为前缀的环境变量都会被移除,导致用户设置的变量(如npm_config_platform_arch)丢失 (#12399)。 - 安装独立可执行文件时,
pnpm setup不再提示确认@pnpm/exe的构建脚本 (#12377)。 - 通过将锁文件验证的获取及链接并行,加快了使用冻结锁文件时
pnpm install的速度,避免阻塞整个安装流程。 依赖项生命周期脚本仍会推迟执行,直到验证成功为止,因此不会针对未经验证的锁文件运行任何脚本。 - 当注册源返回 “304 Not Modified”响应时,现在会更新缓存元数据文件的修改时间,从而确保
minimumReleaseAge新鲜度检查机制能继续利用缓存中的解析结果,而无需反复进行重新验证。 - 修复了仅在 Windows 上出现的程序挂起问题:此前,失败的命令可能需要 20 到 46 秒才能退出,这是由缓慢的
wmic/PowerShell 子进程查找操作引起的;现已为该操作设置了较短的超时限制。 - 更新了依赖项版本范围,主要包括:
msgpackr从 1.11.8 升级至 2.0.4(存储索引文件保持双向字节兼容)、open从 ^7.4.2 升级至 ^11.0.0,以及@zkochan/cmd-shim升级至 v9.0.6。
