airi 仓库 pnpm 配置指南:用 Config Dependencies 跨仓库共享 Hooks、Catalogs、Overrides 与补丁
本篇技术指南围绕 pnpm 的 Config Dependencies(配置依赖)展开:它是让多个仓库复用同一套 pnpm 钩子、设置、catalog 版本表、补丁与 overrides 的机制。读完本文,你将掌握 configDependencies 的声明方式、约束规则、自动加载插件的命名约定,以及如何借助 updateConfig 钩子把 catalog 与设置"推送"到消费方——并以 airi 这个大型 pnpm 单仓库中的真实配置(catalog、patchedDependencies、overrides、allowBuilds 等)作为落地参照,理解这类中心化配置在超大型工作区中的实际形态。
什么是 Config Dependencies
Config dependencies 是一批 npm 包,pnpm 会在安装所有常规依赖之前先行安装它们,使它们有机会向当前项目提供钩子(hooks)、设置(settings)、补丁(patches)、catalogs 和 overrides。核心意图是:你只需维护一个共享的 "pnpm 配置包",就能在所有仓库中消费它,避免把同一套 pnpmfile 钩子逻辑、同一份版本目录、同一组补丁文件复制到每个仓库。
声明 Config Dependencies
声明位置是工作区根目录的 pnpm-workspace.yaml 中的 configDependencies 字段;它们的完整性(integrity)会记录在 pnpm-lock.yaml 内的一个专属 env-lockfile 文档中,与常规依赖的 lockfile 部分隔离。
configDependencies:
my-configs: "1.0.0"
用 --config 标志添加(pnpm 会把它写入 configDependencies 而不是普通的 dependencies):
pnpm add --config my-configs
pnpm add --config @myorg/pnpm-plugin-my-catalogs
需要注意的前提:pnpm 的绝大多数设置现统一放在 pnpm-workspace.yaml(camelCase 键),.npmrc 仅用于认证/registry 凭据,package.json 中的 pnpm 字段已不再被读取。airi 仓库锁定的包管理器版本为 pnpm@11.24.0(见 package.json 的 packageManager 字段),Config Dependencies 即在该版本线(pnpm 10.x/11.x)下可用。
约束:Config Dependencies 能声明什么、不能声明什么
这是使用 Config Dependencies 前必须牢记的三条硬性限制:
- 不允许普通
dependencies。可以声明optionalDependencies,但只能有一层深度(one level deep)。 - 不允许生命周期脚本(
preinstall、postinstall等)。这既限制了攻击面,也保证了"配置包只做配置"的纯度。 optionalDependencies必须使用精确版本(exact versions)。由于常用于平台相关二进制(esbuild 风格的条件安装),版本范围或 dist-tag 会被拒绝,以确保安装结果可复现。
自动加载插件:命名约定触发 pnpmfile 自动装载
一个满足以下任一命名模式的 config dependency:
pnpm-plugin-*@*/pnpm-plugin-*@pnpm/plugin-*
其包根目录下的 pnpmfile.mjs(或 .cjs)会被 pnpm 自动加载,无需在消费方仓库中手工引入。这意味着"插件即配置":发布一个 @myorg/pnpm-plugin-my-catalogs,任何仓库只要 pnpm add --config 它,就会自动获得其中定义的钩子。
用例一:从共享包导入钩子逻辑
由于 config deps 先于 .pnpmfile.mjs 加载完成,工作区根目录的 pnpmfile 可以直接 import 它们的导出:
import { readPackage } from '.pnpm-config/my-hooks'
export const hooks = { readPackage }
readPackage 钩子在解析前可以修改依赖的 package.json(补 peer 依赖、固定传递版本、替换已弃用依赖等)。共享包 .pnpm-config/my-hooks 承载这段逻辑,多个仓库各自维护的 .pnpmfile.mjs 只需一行 import,逻辑升级集中在配置包发版,而不是逐仓库改代码。
用例二:用 updateConfig 共享设置与 Catalogs
updateConfig 钩子在安装前修改 pnpm 自身配置,是 config dependency 里威力最大的钩子。一个 catalog 插件可以这样向消费方注入版本目录:
export const hooks = {
updateConfig(config) {
config.catalogs.default ??= {}
config.catalogs.default['is-odd'] = '1.0.0'
return config
}
}
安装为 config dependency 后,消费方即可像本地定义 catalog 一样使用它:
pnpm add is-odd@catalog: # 实际安装 is-odd@1.0.0,package.json 写入 "is-odd": "catalog:"
这里与 features-catalogs.md 的机制衔接:catalog: 是 catalog:default 的简写,catalog: 协议可用于 dependencies/devDependencies/peerDependencies/optionalDependencies 以及 pnpm-workspace.yaml 的 overrides 中。airi 的 pnpm-workspace.yaml 正是 catalog 体系的典型重度使用场景——它维护了 400 余条 catalog: 版本表(从 @vitest/* 到 xsai 的命名 catalog)、catalogMode: prefer 模式、overrides(如 axios: npm:feaxios@^0.0.23)、packageExtensions 与供应链相关的 allowBuilds、minimumReleaseAge: 4320 等设置。从源码结构看,这正是 Config Dependencies 设计要解决的问题的反面教材:当一个团队维护多个结构相似的仓库时,把这些数百行配置逐仓复制并手工同步,极易漂移;而 updateConfig + configDependencies 把"版本的唯一事实来源"收敛到一个可发版的 npm 包里。
用例三:共享补丁文件
补丁文件可以存放在 config dependency 内部,patchedDependencies 通过 node_modules/.pnpm-config/ 下的相对路径引用:
configDependencies:
my-patches: "1.0.0"
patchedDependencies:
react: "node_modules/.pnpm-config/my-patches/react.patch"
对照 airi 仓库的本地补丁用法可以看到两者形态一致、只是存放位置不同:pnpm-workspace.yaml 中的 patchedDependencies 把 uiohook-napi@1.5.5 映射到 patches/uiohook-napi@1.5.5.patch,同样的写法还有 mineflayer-pathfinder、pixi-live2d-display、sponsorkit@17.1.0 等。若这类"通用修复补丁"需要在组织内多个仓库生效,把它们收进一个 my-patches 配置包、各仓库 configDependencies 一行引用,就是文档给出的中心化方案;补丁的创建与格式细节可参考 features-patches.md 中的 pnpm patch / pnpm patch-commit 流程。
关键要点
- 在一个包里集中管理 hooks、settings、catalogs、overrides 与 patches,被多个仓库消费。
- 通过
pnpm-workspace.yaml的configDependencies声明;它们先于常规依赖安装,integrity 记录在 lockfile 内的专属 env-lockfile 文档中。 - 约束:无普通
dependencies、无生命周期脚本;optionalDependencies必须精确版本,保证安装可复现。 pnpm-plugin-*/@*/pnpm-plugin-*/@pnpm/plugin-*命名的包自动加载其pnpmfile.mjs/.cjs。- 与
updateConfig钩子搭配,把设置与 catalog 注入消费方项目;与.pnpmfile.mjs的 import 能力搭配,把readPackage等钩子逻辑外置到共享包。
与 airi 仓库现状的关系
需要说明:在当前 airi 仓库中检索不到 configDependencies 声明——它目前是单仓库内的自包含配置,共享逻辑(catalog、overrides、补丁、packageExtensions、allowBuilds)都集中在根目录的 pnpm-workspace.yaml 中,由 pnpm-lock.yaml 的 catalogs 段落锁定 specifier 与解析后的实际版本。上述 Config Dependencies 方案适用于 airi 团队若要把这套配置复用到组织内其他 pnpm 仓库的场景:把 pnpm-workspace.yaml 中可参数化的部分(catalog 版本表、补丁文件、readPackage 钩子)抽成 @moeru/* 或 @proj-airi/* scope 下的配置包发布,各仓库一行 pnpm add --config 即可同步。pnpmfile 各钩子的完整参考见 features-hooks.md,工作区与过滤机制见 core-workspaces.md。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00