pnpm Peer Dependencies 完全指南:以 airi 仓库为例掌握自动安装与解析规则
pnpm Peer Dependencies 完全指南以 airi 仓库为例掌握自动安装与解析规则【免费下载链接】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-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi导读Peer dependencies对等依赖是库工程化中最容易踩坑、也最能体现包管理器功力的设计——库声明它需要的宿主依赖却不替消费者决定装什么。本文以 .agents/skills/pnpm/references/features-peer-deps.md 为核心脉络结合 pnpm 管理的巨型 monorepo——自托管 AI 角色 airi 项目的真实配置与源码系统讲解 pnpm 的 peer 依赖自动安装、严格模式、解析与去重规则以及peerDependencyRules、packageExtensions等治理工具。读完你将能够在自己的仓库中精确控制 peer 依赖的告警、版本对齐与 workspace 场景下的去重行为。先认识 airi 仓库的 pnpm 规模airi 是一个以 pnpm 11 为包管理器的全栈 monorepo见根 package.json 中的packageManager: pnpm11.24.0在 pnpm-workspace.yaml 中通过 glob 声明了packages/**、apps/**、plugins/**、integrations/**、services/**、engines/**、docs/**、server/**等 9 组工作区并大量使用catalog:版本目录、overrides、patchedDependencies与packageExtensions。在这种同一份vue、vite、electron、three被十几个包同时依赖的场景下peer dependencies 的解析是否正确直接决定了“组件库只声明、宿主应用才决定版本”的架构能否成立。Peer Dependencies 为何需要“严格处理”与普通依赖不同peer dependencies 表示**“请宿主环境消费方提供此依赖”**库作者知道代码里会import vue或require(electron)但这些包往往体积大、版本敏感且可能出现“同一份代码里混入两份 Vue”的双实例 bug。因此库不在自己的dependencies里固定它们而是用peerDependencies声明版本范围交由消费者的解析器去对齐。在本仓库中这些典型的“库角色”包都在声明 peer 依赖packages/electron-vueuse/package.jsonelectron: 39 44、vue: 3packages/cap-vite/package.jsoncapacitor/cli: ^8.0.0、vite: ^7.0.0 || ^8.0.0-beta.0此外packages/audio、packages/electron-eventa、packages/electron-screen-capture、packages/unocss-preset-fonts的package.json中也声明了 peer 依赖。pnpm 对 peer 依赖的默认策略比其他包管理器更“严格”它会把 peer 依赖真实地链接到宿主包的node_modules中而不是静默跳过从而避免因“该装的没装”而引发的运行时Cannot find module。官方技能文档 .agents/skills/pnpm/SKILL.md 将其总结为 “enforces strict dependency resolution by default, preventing phantom dependencies”默认强制执行严格依赖解析、杜绝幽灵依赖。配置位置pnpm-workspace.yaml 取代 package.json#pnpm管理 peer 依赖的全部开关统一放在pnpm-workspace.yaml中一律使用camelCase键名autoInstallPeers: true strictPeerDependencies: false resolvePeersFromWorkspaceRoot: true dedupePeerDependents: true dedupePeers: false需要注意自 pnpm 10/11 起package.json中的pnpm字段不再被读取.npmrc也仅用于认证/registry 凭据。以 airi 仓库为例根 package.json 里确实没有任何pnpm配置字段全部工程化设置都集中在 pnpm-workspace.yaml以及全局config.yaml中。若你的仓库还在用package.json#pnpm需要迁移到根级pnpm-workspace.yaml。四大行为开关逐个拆解autoInstallPeers默认自动安装缺失的 peer 依赖自 pnpm v8 起默认开启等于显式写入autoInstallPeers: trueautoInstallPeers: true它会把缺失的非 optional peer 依赖自动安装进对应依赖方组件库的目录下。airi 的 pnpm-workspace.yaml 未显式声明该键即处于默认开启状态这也是为什么仓库里众多声明vue: *、vite: *peer 关系的依赖能直接安装成功的前提。关键边界当不同依赖对同一 peer 提出冲突范围时例如依赖 A 要求react^16、依赖 B 要求react^17pnpm 不会强行安装而是什么都不装并打印警告需要你手动介入解决——例如用下文allowedVersions明确放行或自行提升一个统一版本。strictPeerDependencies把“缺失”升级为“失败”strictPeerDependencies: true # default false默认false时树上出现缺失或不合法的 peer 依赖只会给出 warning置为true后install/build 等命令会直接失败。这适合对依赖卫生要求极高、希望 CI 第一时间暴露问题的团队——但通常需要搭配完善的peerDependencyRules白名单否则会被误报频繁打断。resolvePeersFromWorkspaceRoot从 workspace 根解析resolvePeersFromWorkspaceRoot: true # default在 monorepo 里若某依赖如react、vue在多个子包中都存在pnpm 会优先在 workspace 根只安装一份让各子包共享同一实例避免“每个子包各装一个 Vue”。该键默认即为true。dedupePeerDependents 与 dedupePeers控制实例数量dedupePeerDependents: true # default当 peer 匹配时跨工程共享包实例 dedupePeers: false # v10.33仅以“nameversion”后缀区分 peer减少实例dedupePeerDependents: true默认当两个依赖方的 peer 依赖版本可互相满足时让它们共享同一个包实例从根上规避“重复实例导致instanceof失效、hooks 状态错乱”这类经典问题dedupePeersv10.33 引入默认false开启后 peer 依赖只用nameversion作为目录后缀参与解析会减少虚拟 store 中产生的实例数量代价是可能容忍更粗粒度的版本差异。peerDependencyRules细粒度“豁免”的治理工具箱完全无视告警或全局关掉strictPeerDependencies都不是好做法。pnpm 提供peerDependencyRules让仓库按“谁可以缺、谁可以用什么版本、谁可以任意版本”三种维度精确治理peerDependencyRules: ignoreMissing: - babel/* - eslint allowedVersions: react: 17 || 18 allowAny: - types/*ignoreMissing压制“缺 peer”告警声明哪些 peer 缺失可以不用管。模式支持三种写法精确包名eslint、scopebabel/*匹配整个 scope、以及*不建议等于全盘豁免peerDependencyRules: ignoreMissing: - babel/* - eslint - webpack典型场景eslint-config/ 各类eslint-plugin会把eslint声明为 peer但某些仅做语法层 lint 的包其实用不到——在 airi 这类同时引入antfu/eslint-config、moeru/eslint-config、unocss/eslint-plugin等十几个 lint 相关依赖的仓库里正是用它来屏蔽不必要告警的地方。需要提醒豁免的每一项都应在代码注释或文档中说明“为什么安全”见文末最佳实践。allowedVersions放行指定版本当某依赖方声明了过窄的 peer 范围、而仓库实际需要另一个合法版本时使用。可以用parentpeer语法把豁免精确到“特定父包的 peer”避免全局放宽peerDependencyRules: allowedVersions: react: 17 button2react: 17 # 仅当 react 是 button2 的 peer 时生效airi 的 pnpm-workspace.yaml 中虽然没有直接声明allowedVersions但使用了同思路的overrides对依赖做强制对齐例如将hono固定为4.13.3、把axios通过npm:feaxios^0.0.23别名替换、eslint-plugin-sonarjstypescript用parentchild语法钉死 typescript 版本说明该仓库非常习惯用“上级包限定下级依赖”的精确表达——peerDependencyRules.allowedVersions正是同一思路在 peer 领域的对应物。allowAny无视声明范围、取任意匹配版本peerDependencyRules: allowAny: - types/* - eslint语义是“只要树上存在该包的任何版本就满足”。最常用于types/*类型包之间基本不存在运行时兼容性冲突这类场景。packageExtensions不写 JS 也能“补” peer 依赖当第三方包漏声明了它实际需要的 peer 依赖时这是大量“装完跑不起来”的根源可以用packageExtensions声明式补全pnpm 会在解析时把补丁合并进该包的原生 manifestpackageExtensions: problematic-package: peerDependencies: react: *airi 仓库在 pnpm-workspace.yaml 中给出了大量真实范本这里摘录几例为formkit/auto-animate补上vue: *peer它是一个依赖 Vue 响应式上下文的动画库但此前未正确声明为intlify/unplugin-vue-i18n补上vite: *为vitepress同时补上vite: *与vue: *为vue-sonner补上更精确的vue: ^3.2.0为pixiv/three-vrm-core、pixiv/three-vrm-animation、tresjs/core、pmndrs/pointer-events补上各自的three/types/threepeer确保 three.js 生态的这些库与宿主three仓库统一为catalog:管理的^0.185.1保持同实例。可以看到一个大型 monorepo 里第三方包 peer 声明不全几乎是一种常态packageExtensions的价值就是把这些“打补丁”逻辑集中、声明化、可 code review。需要条件逻辑时换 hookspackageExtensions只能做静态声明。若需要根据平台、版本或环境变量动态改写 manifest应改用.pnpmfile.mjs中的readPackagehook——可参考 .agents/skills/pnpm/references/features-hooks.md。Peer Dependencies in Workspacesworkspace 包之间如何满足workspace 内的其他包可以“扮演”peer 依赖的提供方。文档给出的抽象模型如下// packages/app/package.json { dependencies: { react: ^18.2.0, myorg/components: workspace:^ } } // packages/components/package.json { peerDependencies: { react: ^17.0.0 || ^18.0.0 } }app 提供了react^18.2.0恰好落在 components 声明的^17 || ^18范围内因此 pnpm 会把 app 的 react 链接给 components 使用而不会为 components 额外安装第二份 react——这正是上节resolvePeersFromWorkspaceRoot/dedupePeerDependents在 workspace 中的协同效果。把该模型映射到 airi 仓库即是提供方apps/stage-tamagotchi/package.json 这类应用包其dependencies里直接消费proj-airi/stage-ui、proj-airi/ui等十几个workspace:^内部包并同时自带vue、pinia、three等宿主运行时声明方packages/electron-vueuse/package.json 声明electron: 39 44、vue: 3packages/cap-vite/package.json 声明capacitor/cli: ^8.0.0与vite: ^7 || ^8-beta兜底这些库自身的devDependencies中通常会再放一份用于构建/类型检查的版本如 electron-vueuse 的devDependencies.vue: catalog:从而做到“发布声明、开发自足”。只要宿主应用提供的版本落入库声明的宽范围这也是库作者应把 peer 范围写得足够宽的原因整棵依赖树就只存在一份宿主实例。常见场景速查场景一Monorepo 共享单一运行时配合 catalog如果仓库使用版本目录catalog统一版本库与应用配合的效果如下——airi 对vue、vite、three、eslint等的catalog:管理方式与此同构# pnpm-workspace.yaml catalog: react: ^18.2.0 react-dom: ^18.2.0// packages/ui/package.json —— 库只声明、不装死 { peerDependencies: { react: ^18.0.0, react-dom: ^18.0.0 } } // apps/web/package.json —— 应用从 catalog 提供宿主版本 { dependencies: { react: catalog:, react-dom: catalog:, myorg/ui: workspace:^ } }场景二压制 ESLint 插件的“缺 peer”告警peerDependencyRules: ignoreMissing: - eslint - typescript-eslint/parser场景三放行多主版本共存peerDependencyRules: allowedVersions: webpack: 4 || 5 postcss: 7 || 8调试与 CI把 peer 问题挡在合并前pnpm 提供从 lockfile 层面直查 peer 状态的命令# 直接从 lockfile 报告未满足/缺失的 peer 依赖v11 提供即 pnpm peers 子命令 pnpm peers check # 查看某个包为何被安装来源链路 pnpm why package # 查看整棵依赖树 pnpm list --depthInfinitypnpm peers check非常适合写进 CI仓库 CI 中安装本就应使用带锁文件的 .agents/skills/pnpm/SKILL.md 中强调的pnpm install --frozen-lockfile等价于pnpm ci在此基础上再追加一次pnpm peers check即可在依赖升级合并前捕获 peer 回归避免“本地能跑、CI 一装新依赖就炸”。最佳实践清单保持autoInstallPeers开启v8 默认即如此享受自动补齐的便利用peerDependencyRules精确治理而不是全局关掉严格模式或批量ignoreMissing文档化每一处被豁免的告警写明“为什么它是安全的”——这是仓库可维护性的关键库的 peer 范围要写得宽例如react: ^17 || ^18给消费者留出对齐空间airi 内部包如 electron-vueuse 的electron: 39 44、vue: 3即遵循此风格CI 中运行pnpm peers check在 peer 回归扩散前拦截若仓库正在从 v10 迁移到 v11务必确认所有 peer 相关配置都已迁入pnpm-workspace.yamlcamelCase因为package.json#pnpm已不再被读取。掌握以上自动安装、严格模式、规则豁免与 workspace 对齐四层机制后你既能解释“为什么同一个vue在仓库里只存在一份”也能在引入新依赖或升级大版本时把 peer 相关告警从“玄学报错”变成“可读、可治理、可进 CI 的清单”。如需深入了解本仓库中与 peer 治理联动的其他机制可继续阅读 core-config配置全貌、features-catalogs版本目录、features-overrides强制版本/parentchild语法以及 features-hooksreadPackage条件逻辑。【免费下载链接】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-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考