pnpm 11 升级笔记:allowBuilds、配置搬家,以及会打断 CI 的默认值
官方材料两份,先放着:
- 迁移指南:https://pnpm.io/zh/migration
- 11.0 release 博客:https://pnpm.io/zh/blog/releases/11.0
运行环境门槛
pnpm 11 要求 Node.js 22+,18/19/20/21 都不再支持。pnpm 自身改成纯 ESM 分发,独立可执行文件要求 glibc 2.27+。CI 镜像和 Dockerfile 里的 Node 基础镜像需要先对齐,否则升级后装不起来不是配置问题,是运行时问题。
供应链默认值变严
这几个默认值不改也能跑,但 monorepo 的首次安装和锁文件更新行为会变:
minimumReleaseAge默认1440(分钟,即 1 天)。新发布的包至少要过一天才能被解析。急着上新版本时设0退出该限制。minimumReleaseAgeStrict默认false。blockExoticSubdeps默认true。strictDepBuilds默认true。optimisticRepeatInstall默认true。verifyDepsBeforeRun默认install。
allowBuilds 取代五个旧设置
v11 移除了 onlyBuiltDependencies、onlyBuiltDependenciesFile、neverBuiltDependencies、ignoredBuiltDependencies、ignoreDepScripts,全部合成一个映射:包名匹配器 → true / false。
规则是:没写进 allowBuilds 的包,默认不允许执行构建脚本。当 strictDepBuilds 为默认的 true 时,这会按错误处理(ERR_PNPM_IGNORED_BUILDS);设成 false 则只警告。
安装时被忽略构建的依赖会被自动写进 pnpm-workspace.yaml,值是占位用的,需要你确认后再改。确认入口有两个:pnpm approve-builds,或者安装时加 --allow-build。
支持版本限定,例如:
allowBuilds:
"nx@21.6.4 || 21.6.5": true
键里带空格和 ||,YAML 里的引号写法建议按你所在 pnpm 版本实测验证一遍。
配置来源整体搬家
v11 不再读 package.json 里的 pnpm 字段。配置统一放 pnpm-workspace.yaml,全局配置放 config.yaml。
.npmrc 只保留认证与注册表相关项,例如:
registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
hoist-pattern、node-linker、save-exact 这类要搬到 pnpm-workspace.yaml,并改成驼峰键。子项目的 .npmrc 放到 packageConfigs["."] 下。
前后对照大致是这样:
# 升级前:.npmrc + package.json#pnpm
# .npmrc
hoist-pattern[]=*
node-linker=hoisted
save-exact=true
onlyBuiltDependencies[]=esbuild
onlyBuiltDependencies[]=sharp
ignoredBuiltDependencies[]=fsevents
# 升级后:pnpm-workspace.yaml
hoistPattern: "*"
nodeLinker: hoisted
saveExact: true
allowBuilds:
esbuild: true
sharp: true
fsevents: false
hoist-pattern 在 .npmrc 里是数组写法,搬到 YAML 后是字符串还是数组,按项目实际取值自行验证。
环境变量前缀改名
不再读 npm_config_*,改成 pnpm_config_*。要排查的地方比想象中多:CI 流水线变量、shell profile、Dockerfile 的 ENV、以及脚本里 cross-env npm_config_xxx=... 这种内联赋值。
其它破坏性改动
ignorePatchFailures移除,补丁失败一律抛异常。allowNonAppliedPatches改名为allowUnusedPatches。auditConfig.ignoreCves改名为ignoreGhsas。值也要换:从CVE-YYYY-NNNNN手工改成GHSA-xxxx-xxxx-xxxx,对应 ID 在pnpm audit输出的「更多信息」列里。pnpm link不带参数不再从全局 store 解析,要传相对或绝对路径。pnpm install -g不带参数不再支持,用pnpm add -g。pnpm server移除,无替代。useNodeVersion改成根package.json的devEngines.runtime。- 子包
package.json#pnpm里的executionEnv.nodeVersion移除。
脚本名会遮蔽内置命令
如果 package.json 里定义了 clean、setup、deploy、rebuild,pnpm 现在跑的是你的脚本,不再是内置命令。要强制走内置命令,用 pnpm pm 。
存储与全局安装
store 换成 v11:每个包的 JSON 索引改成单个 SQLite 数据库,打包 manifest 和 hex 摘要,减少 syscall。
全局安装改成隔离模式:每次 pnpm add -g 有自己的目录、package.json、node_modules 和 lockfile,不再共享同一套依赖树。
发布不再走 npm CLI
publish、login、logout、view、deprecate、unpublish、dist-tag、version 都是原生实现。发布链路上如果有依赖 npm CLI 输出格式或行为的脚本,需要重新对一遍。
迁移步骤
机械改动交给 codemod:
pnpx codemod run pnpm-v10-to-v11
它会处理:package.json#pnpm → pnpm-workspace.yaml、拆分 .npmrc、合并构建设置、重命名各类键、升级 packageManager 版本。
装完 v11 后再跑一次,更新 shell 里的 bin 路径:
pnpm setup
codemod 覆盖不到的,是上面那些默认值和语义变化:minimumReleaseAge 的一天窗口、allowBuilds 白名单需要人工确认、ignoreGhsas 的 ID 换算、以及被脚本名遮住的内置命令。这些按项目实际情况逐条过。