pnpm 12.15.0 在 2026-10-21 正式发布(GitHub Release 时间为 UTC 11:16,对应北京时间当天 19:16)。这次次版本里,对日常锁文件影响最直接的是 lockfile.integrityTarget: tar:锁文件改记未压缩 tar 的哈希,注册表只是重压或改传 .tgz 时,lockfile 不必跟着飘。本机用同一份 is-odd@3.0.1 分别装默认模式和 tar 模式,两边的 resolution.integrity 整段都不一样。
官方说明见 pnpm 12.15.0 发布说明 与 GitHub Release。同次还带了 include 拆配置、SOURCE_DATE_EPOCH 约束部署时间戳、以及元数据包名对不上就报 ERR_PNPM_REGISTRY_NAME_MISMATCH。下面按本机实测写。
环境
本机装的是 pnpm 12.15.0,Node.js 20。演示目录放在 ~/demo 下,一个小 workspace 里放前端包,旁边留了空的 Spring Boot 模块目录,模拟常见「Java 后端 + pnpm 管前端」的仓库布局。包管理器本身跟 Spring Boot 无关,只是多数 Spring 仓库现在都会顺手带一份前端 workspace。
$ pnpm --version
12.15.0
integrityTarget: tar,同包哈希对不上默认模式
默认情况下,pnpm 把 registry 下发的压缩包(常见是 .tgz)的哈希写进 pnpm-lock.yaml。注册表如果对同一版本重新压缩,或改成直接提供未压缩 tar,锁文件里的 integrity 就可能变,CI 的 frozen lockfile 也会跟着炸。12.15.0 加了设置:
# pnpm-workspace.yaml
lockfile:
integrityTarget: tar
打开之后,锁文件记录的是未压缩 tar 内容的哈希。官方原话是:registry 重压或改用 tar 分发时,lockfile 不再跟着变;pnpm 会从本地 store 或下载一次里学到该版本的 tar 哈希。
本机建了两个几乎一样的小项目,都只依赖 is-odd@3.0.1。一边不设 integrityTarget,一边写成 tar,分别 pnpm install。
默认模式的锁文件片段:
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
packages:
is-odd@3.0.1:
resolution: {integrity: sha512-CQpnWPrDwmP1+SMHXZhtLtJv90yiyVfluGsX5iNCVkrhQtU3TQHsUWPG9wkdk9Lgd5yNpAg9jQEo90CBaXgWMA==}
engines: {node: '>=4'}
tar 模式:
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
integrityTarget: tar
packages:
is-odd@3.0.1:
resolution: {integrity: sha512-SElt+mvaeh56ryoEOFNnkjUJgi+DDqsHkkF5zlEr9h2setH9b+s3zKscWsa1ppKEgeUeSSs14f1421Z+K/doCQ==}
engines: {node: '>=4'}
版本号完全一样,integrity 字符串却整段不同。settings 里也会多出一行 integrityTarget: tar,方便一眼看出这份锁文件按哪套口径算的。依赖图没变,is-number@6.0.0 作为传递依赖同样换了一套哈希。
安装过程本身很平常,tar 模式还能复用已有 store:
dependencies:
+ is-odd 3.0.1
Done in 39ms using pnpm v12.15.0
如果仓库已经按默认口径锁过一批包,切到 tar 等于换哈希体系,需要重新生成 lockfile,不能指望旧 integrity 继续对上。
include:配置拆到多个文件,越权就报错
同一版还加了 include。根 pnpm-workspace.yaml 声明每个附属 YAML 能管哪些设置,每个设置只能有一个归属方。本地 include 文件路径必须以 ./ 或 ../ 开头。文档在 Splitting Settings Across Files。
本机 workspace 这样写:
# pnpm-workspace.yaml
packages:
- 'packages/*'
include:
./settings/lockfile.yaml:
- lockfile
./settings/catalog.yaml:
- catalog
# settings/lockfile.yaml
lockfile:
integrityTarget: tar
# settings/catalog.yaml
catalog:
lodash: ^4.17.21
pnpm install 正常完成,生成的锁文件 settings 段带上了 integrityTarget: tar,catalog 也生效,workspace 里的 @demo/web 能跑通:
$ pnpm -C packages/web run build
web build ok { a: 1 }
故意在只委托了 catalog 的文件里塞 minimumReleaseAge,立刻失败:
Error: ERR_PNPM_SETTING_NOT_DELEGATED
× load configuration
╰─▶ "minimumReleaseAge" is defined in ./settings/catalog.yaml, which is not
delegated that setting
help: Add "minimumReleaseAge" to the settings "include" delegates to ./
settings/catalog.yaml in pnpm-workspace.yaml, or move it to the source
that owns it.
这不是警告,是加载配置阶段就停。适合跟 CODEOWNERS 一起用:安全相关 settings 单独文件、单独审批,日常改 catalog 的人碰不到。
SOURCE_DATE_EPOCH 会压住 modules 时间戳
发布说明写明:pnpm deploy 和 pnpm install 会把环境变量 SOURCE_DATE_EPOCH 当作 node_modules/.modules.yaml 与 .pnpm-workspace-state-v1.json 里时间戳的上限。本机在 tar 模式项目里设 SOURCE_DATE_EPOCH=1700000000 再装一遍:
# node_modules/.modules.yaml
"prunedAt": "Tue, 14 Nov 2023 22:13:20 GMT"
# node_modules/.pnpm-workspace-state-v1.json
"lastValidatedTimestamp": 1700000000000
1700000000 秒对应的就是 2023-11-14 22:13:20 UTC。不做可复现发布的话感知不强;要做镜像层或制品比对时,这行环境变量现在真会进 pnpm 写出来的状态文件。
其余值得知道、但本机没单独搭环境的
同次还有:能装只提供未压缩 tar、并用 zstd/gzip 内容编码的 registry;元数据里的包名和请求名不一致时直接 ERR_PNPM_REGISTRY_NAME_MISMATCH(以前会按元数据里的名字装,导致 allowBuilds 规则对不上);大 workspace 安装和 macOS 链接有性能优化。这些需要自建 registry 或大仓库才好量化,文中不展开成实测。
升级方式官方仍是常规渠道。本机用的是 get.pnpm.io 安装脚本指定 12.15.0;已有环境也可以 pnpm self-update 或让项目 packageManager 字段钉住版本。
一起交流
分享你的思考,让讨论更进一步。