airi 仓库 pnpm 迁移实战:从 npm/Yarn 到 pnpm,再到 v10→v11 配置迁移全解析
【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi
本篇基于 airi 仓库内置的迁移技能文档 best-practices-migration.md 展开,系统讲解如何把现有 npm / Yarn 项目低摩擦地迁移到 pnpm,以及如何完成 pnpm v10 → v11 的配置升级。读完本文,你将掌握pnpm import锁文件导入、workspace 协议改造、v11 codemod 自动化迁移、.npmrc与pnpm-workspace.yaml配置拆分等完整操作,并能以 airi 这个真实大型 monorepo 的 pnpm-workspace.yaml 和 package.json 作为落地参照。
为什么 airi 是一个天然的 pnpm 迁移样本
airi 是一个 LLM 驱动的虚拟角色项目(self-hosted 的 Grok Companion,支持实时语音聊天、Minecraft/Factorio 等玩法),当前以 pnpm 作为唯一的包管理器:
- 根 package.json 中声明了
"packageManager": "pnpm@11.24.0",即项目已运行在 pnpm v11 之上; - 仓库根目录存在完整的 pnpm-workspace.yaml,其中
packages字段覆盖packages/**、plugins/**、integrations/**、services/**、docs/**、engines/**、apps/**、server/**八大目录,并用!/dist/` 排除构建产物; - pnpm-lock.yaml 顶部即
lockfileVersion: '9.0',并在settings中记录了autoInstallPeers: true等安装行为。
这意味着本文所有迁移步骤都能在 airi 中找到"迁移完成后的目标形态",读者可以对照仓库现状验证每一步的效果。
一、pnpm v10 → v11:配置读取方式的变化与 codemod
v11 最大的变化在于配置从.npmrc/package.json#pnpm集中迁移到pnpm-workspace.yaml(全局配置则放在config.yaml),且键名统一为camelCase。这部分迁移大多是机械性的,官方提供了 codemod:
cd /path/to/project pnpx codemod run pnpm-v10-to-v11codemod 会自动完成以下六类改写(引自迁移文档的完整清单):
- 把
package.json#pnpm设置移入pnpm-workspace.yaml——v11 不再读取pnpm字段; - 拆分
.npmrc:只有 auth/registry 凭据留在.npmrc,其余键全部以 camelCase 形式迁入pnpm-workspace.yaml(例如node-linker→nodeLinker);各子项目的.npmrc则变成packageConfigs["<name>"]; - 合并构建设置:
onlyBuiltDependencies、neverBuiltDependencies、ignoredBuiltDependencies、onlyBuiltDependenciesFile统一为一个allowBuilds: { name: true|false }映射; - 替换包管理器版本管控:
managePackageManagerVersions/packageManagerStrict/packageManagerStrictVersion三个键合并为pmOnFail: download|ignore|warn|error; - 重命名:
allowNonAppliedPatches→allowUnusedPatches,auditConfig.ignoreCves→auditConfig.ignoreGhsas; - 转换
useNodeVersion→devEngines.runtime,并同步提升packageManager版本号。
airi 中 v11 配置形态的真实例证
airi 根目录的 pnpm-workspace.yaml 是上述迁移规则的直接产物,几个关键键都可以逐条对上:
catalogMode: prefer minimumReleaseAge: 4320 minimumReleaseAgeExclude: - '@moeru/*' - '@proj-airi/*' shellEmulator: trueshellEmulator: true就是 v10 时代.npmrc中shell-emulator=true的 camelCase 形态;minimumReleaseAge/minimumReleaseAgeExclude属于 v11 的供应链安全配置(限制依赖发布时间的下限,并为本组织自有包豁免)。
再比如 pnpm-workspace.yaml 中的allowBuilds映射,正是第 3 条"构建设置合并"的落地结果——airi 里有大量带原生编译步骤的依赖(canvas、sharp、electron、node-pty、onnxruntime-node等),每一个都被显式声明为true(允许构建)或false(拒绝构建),例如:
allowBuilds: canvas: true electron: true node-pty: true '@prisma/client': false better-sqlite3: false simple-git-hooks: false # [workworkaround] With `shellEmulator: true`, simple-git-hooks install may fail when no .git dir exists.可以看到 v11 的allowBuilds让"哪些依赖允许执行 install 脚本"变成了一份可 diff、可 review 的白名单,这比 v10 时代分散在多个键里的onlyBuiltDependencies等配置清晰得多。
无法自动化的手工跟进项
codemod 覆盖不到的部分,迁移文档列出了完整清单,需逐一人工处理:
- 在
auditConfig.ignoreGhsas中把CVE-…编号改写为GHSA-…格式; ignorePatchFailures已被移除——失败的 patch 现在总是抛错(airi 的 5 个补丁位于 patches/ 目录,由 pnpm-workspace.yaml 的patchedDependencies引用,迁移后任何一个补丁无法应用都会直接让安装失败);- 环境变量
npm_config_*→pnpm_config_*,需检查 CI、shell profile、Docker 配置; pnpm link <name>需改用路径形式pnpm link ./foo;pnpm link --global→pnpm add -g .;- 无参数的
pnpm install -g和pnpm server命令被移除; - 名为
clean/setup/deploy/rebuild的package.json脚本会遮蔽同名的 pnpm 内建命令,需要显式调用内建时改用pnpm pm <name>。
二、快速迁移:从 npm / Yarn 切换
从 npm 迁移
# 删除 npm 锁文件与 node_modules rm -rf node_modules package-lock.json # 用 pnpm 重新安装 pnpm install从 Yarn 迁移
# 删除 yarn 锁文件与 node_modules rm -rf node_modules yarn.lock # 用 pnpm 重新安装 pnpm install导入既有锁文件(pnpm import)
不想从零解析依赖树时,pnpm 支持直接消费旧锁文件:
# 从 npm 或 yarn 锁文件导入 pnpm import # 支持从以下文件生成 pnpm-lock.yaml: # - package-lock.json (npm) # - yarn.lock (yarn) # - npm-shrinkwrap.json (npm)pnpm import的价值在于尽量保留 npm/yarn 时代已经锁定的版本解析结果,降低"迁移后行为漂移"的风险;airi 当前锁文件即为lockfileVersion: '9.0'的 pnpm-lock.yaml,其中还内嵌了 catalog 的 specifier→version 解析明细,说明导入后的版本被完整固化。
三、处理常见问题
幽灵依赖(Phantom Dependencies)
pnpm 默认采用严格依赖解析:如果代码 import 了一个没有写进package.json的包,安装/构建就会失败。
问题示例:
// 在 npm(提升式安装)下能跑,在 pnpm 下会失败 import lodash from 'lodash' // 未声明,只是被另一个包带进来的解决:显式补齐依赖。
pnpm add lodash这条规则解释了为什么许多从 npm 迁过来的项目一开始会"莫名其妙"报模块找不到——不是 pnpm 装丢了,而是它第一次如实暴露了依赖声明缺失。
缺失的 Peer Dependencies
pnpm 默认会报告 peer dependency 问题,有三种处理方式:
方式 1:让 pnpm 自动安装(v8+ 默认开启),在pnpm-workspace.yaml中:
autoInstallPeers: trueairi 的锁文件设置区即启用了该行为(pnpm-lock.yaml 的settings: autoInstallPeers: true)。
方式 2:手动安装缺失的 peer:
pnpm add react react-dom方式 3:在可接受的情况下抑制警告:
peerDependencyRules: ignoreMissing: - react对于 airi 这样多依赖树的大型 monorepo,更常见的做法不是全局忽略,而是用packageExtensions补齐"上游漏声明"的 peer。例如 pnpm-workspace.yaml 中为vitepress、@formkit/auto-animate、@pixiv/three-vrm-core等包补写了peerDependencies,避免 peer 缺失报错同时不污染各子项目的package.json。
符号链接兼容问题(Symlink Issues)
个别工具无法正确跟随 pnpm 的 symlink 结构,可以整体切换到 hoisted 模式:
nodeLinker: hoisted或者只把特定模式的包提升到node_modules顶层:
publicHoistPattern: - '*eslint*' - '*babel*'airi 当前未配置nodeLinker: hoisted,即保持默认的 symlink + 虚拟存储方案(内容寻址 store 跨项目去重、磁盘占用最低);只有当某个工具确实因 symlink 报错时,再按上面的两个开关局部调整。
原生模块重建(Native Module Rebuilds)
原生模块构建失败时的标准处置:
# 重建所有原生模块 pnpm rebuild # 或者彻底重装 rm -rf node_modules pnpm install在 v11 项目里还要先确认目标包在allowBuilds中为true——airi 里uiohook-napi、stockfish、isolated-vm等都被显式放行(pnpm-workspace.yaml),这类"构建审批"机制就是原生模块问题的第一排查点。
四、Monorepo 迁移
从 npm Workspaces 迁移
创建
pnpm-workspace.yaml:packages: - 'packages/*'内部依赖改用 workspace 协议:
{ "dependencies": { "@myorg/utils": "workspace:^" } }重新安装:
rm -rf node_modules packages/*/node_modules package-lock.json pnpm install
airi 的实际情况可作为参照:根 package.json 保留了"workspaces"字段(packages/**、plugins/**等九个目录),同时 pnpm-workspace.yaml 用 glob 列表描述了同一组目录并额外加了'!**/dist/**'排除项。从源码结构看,pnpm 以pnpm-workspace.yaml为准,package.json#workspaces属于兼容保留;两者并存不会冲突,但建议以 pnpm 侧为唯一事实来源维护。
workspace 协议在子项目中的真实用法,例如 packages/core-character/package.json 里的"@proj-airi/stream-kit": "workspace:^";apps/stage-web/package.json 则同时混用了workspace:与catalog:两种内部引用(后者见下文"配置迁移")。
从 Yarn Workspaces 迁移
删除 Yarn 专属文件:
rm yarn.lock .yarnrc.yml rm -rf .yarn按
package.json中的workspaces内容创建pnpm-workspace.yaml:packages: - 'packages/*'视情况从
package.json中移除 Yarn 的 workspace 配置(可选,pnpm 读取的是pnpm-workspace.yaml);转换 workspace 引用:
// 来自 Yarn "@myorg/utils": "*" // 改为 pnpm "@myorg/utils": "workspace:*"
从 Lerna 迁移
对多数使用场景,pnpm 原生能力可以替代 Lerna:
# Lerna:在所有包中运行脚本 lerna run build # pnpm 等价写法 pnpm -r run build # Lerna:在特定包中运行 lerna run build --scope=@myorg/app # pnpm 等价写法 pnpm --filter @myorg/app run build # Lerna:发布 lerna publish # pnpm:改用 changesets 流程 pnpm add -Dw @changesets/cli pnpm changeset pnpm changeset version pnpm publish -rairi 的脚本层就是pnpm -rF/--filter用法的密集样本,根 package.json 中:
"dev": "pnpm -r -F @proj-airi/stage-web dev", "dev:apps": "pnpm -rF=\"./apps/*\" run --parallel dev", "build": "turbo run build -F=\"./packages/*\" -F=\"./apps/*\" -F=\"./server/**\""-F(--filter缩写)既接受包名也接受路径 glob,--parallel控制并行执行——这套组合完全覆盖了 Lernarun的日常能力,而跨包的构建缓存与拓扑排序则交由turbo承担。
五、配置迁移:.npmrc与pnpm-workspace.yaml的职责划分
v11 的核心原则:.npmrc只保留 auth/registry 凭据,其余全部迁入pnpm-workspace.yaml(camelCase)。
# .npmrc(仅 auth,建议 gitignore) //registry.npmjs.org/:_authToken=${NPM_TOKEN} //npm.myorg.com/:_authToken=${MYORG_TOKEN}# pnpm-workspace.yaml registries: default: https://registry.npmjs.org/ '@myorg': https://npm.myorg.com/ autoInstallPeers: true strictPeerDependencies: false值得注意的是 airi 仓库根目录没有.npmrc文件——因为公开仓库没有任何需要入库的凭据,这正是".npmrc只放 auth"原则的极端形态:无 auth 就无文件。
在此之上,airi 的 pnpm-workspace.yaml 还展示了 v11 配置面能承载的更重型的仓库级策略,迁移时若旧项目有对应需求,也应一并归位到这里:
overrides(L25-L37):强制统一版本与别名替换,例如axios: npm:feaxios@^0.0.23、hono: 4.13.3,以及对is-core-module、side-channel等换成@nolyfill/*的实现;catalog/catalogs(L44-L451):集中版本目录,子项目里直接写"vue": "catalog:"、"vitest": "catalog:vitest"(多 catalog 命名空间),配合catalogMode: prefer使用;airi 根 package.json 的 devDependencies 几乎全部是catalog:引用;patchedDependencies(L38-L43):声明式补丁,指向 patches/ 下的 5 个.patch文件(mineflayer、pixi-live2d-display、uiohook-napi等),替代 v10 时代package.json#pnpm.patchedDependencies的位置;allowBuilds(L455-L488):前文所述的构建审批白名单。
脚本迁移(Scripts Migration)
多数脚本无需改动,只需替换包管理器特定写法:
{ "scripts": { // npm:递归脚本 "build:all": "npm run build --workspaces", // pnpm:改用 -r "build:all": "pnpm -r run build", // npm:在指定 workspace 中运行 "dev:app": "npm run dev -w packages/app", // pnpm:改用 --filter "dev:app": "pnpm --filter @myorg/app run dev" } }对照 airi 根 package.json 可看到这套模式的规模化应用:"dev:ui": "pnpm -rF @proj-airi/stage-ui run story:dev"、"typecheck": "pnpm -rF=\"./packages/*\" ... --parallel typecheck"等,均按"包名过滤 + 子脚本透传"书写。
六、CI/CD 迁移
文档给出的通用改法是把npm ci换成 pnpm 的动作:
# Before (npm) - run: npm ci # After (pnpm) - uses: pnpm/action-setup@v4 - run: pnpm install --frozen-lockfile # 或:pnpm ci并在package.json中声明 Corepack 字段:
{ "packageManager": "pnpm@10.0.0" }airi 主工作流 .github/workflows/ci.yml 展示的是等价的新式写法——用pnpm/setup@v2一次性完成运行时 + 包管理器 + 缓存:
- uses: pnpm/setup@v2 with: runtime: node@26.7.0 cache: true install: false # ... - run: pnpm install --frozen-lockfile配合根 package.json 的"packageManager": "pnpm@11.24.0",CI 中的 pnpm 版本由 Corepack 机制从packageManager字段推导,保证与本地一致;--frozen-lockfile则确保锁文件不被 CI 静默改写。工作流中的构建步骤同样全部走pnpm -F <pkg> run build(如pnpm -F @proj-airi/stage-web run build),与本地脚本形成闭环。
七、渐进式迁移与回滚方案
渐进式迁移(Gradual Migration)
大型项目建议分步推进:
- 先切 CI:CI 用 pnpm,本地暂留 npm/yarn;
- 生成 pnpm-lock.yaml:跑
pnpm import从旧锁文件导入; - 充分测试:确认构建、测试在 pnpm 下全部通过;
- 更新文档:README、CONTRIBUTING 等统一为 pnpm 命令;
- 删除旧文件:团队全员切换后移除旧锁文件。
回滚计划(Rollback Plan)
迁移出问题时:
# 删除 pnpm 产物 rm -rf node_modules pnpm-lock.yaml pnpm-workspace.yaml # 恢复 npm npm install # 或恢复 Yarn yarn install关键策略是把旧锁文件保留在 git 历史中,以便随时 checkout 回来,把回滚成本压到最低。
八、小结:迁移检查清单
把全文压缩成一份可执行的核对表:
| 步骤 | 关键操作 | airi 中的对应证据 |
|---|---|---|
| v10→v11 codemod | pnpx codemod run pnpm-v10-to-v11 | pnpm-workspace.yaml 的 camelCase 键与allowBuilds |
| 锁文件切换 | pnpm import/ 删除旧锁文件重装 | pnpm-lock.yaml(lockfileVersion: '9.0') |
| 幽灵依赖 | 显式pnpm add补齐声明 | 严格模式默认行为 |
| peer 依赖 | autoInstallPeers/peerDependencyRules/packageExtensions | 锁文件 settings、pnpm-workspace.yaml |
| Monorepo | pnpm-workspace.yaml+workspace:协议 | package.json、packages/core-character/package.json |
| 配置拆分 | .npmrc只留 auth,其余入pnpm-workspace.yaml | 仓库根目录无.npmrc |
| CI | pnpm/setup+pnpm install --frozen-lockfile+packageManager字段 | .github/workflows/ci.yml、package.json |
这套迁移路径在 airi 上已完整落地:从packageManager声明、pnpm-workspace.yaml的八大工作区 glob,到allowBuilds构建审批与 5 个声明式补丁,都可以作为迁移完成后应当达到的配置形态来参照。
【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考