AIRI Monorepo 中的 pnpm 配置体系:以 pnpm-workspace.yaml 为核心的设置分层与实战
本篇以 AIRI 仓库内置的 pnpm 技能文档 core-config.md 为主体,系统讲解 pnpm 当前的配置分层模型:所有安装与解析设置收敛到 pnpm-workspace.yaml 与全局 config.yaml(YAML、camelCase 键名),.npmrc 只保留认证与 registry 凭据。结合 AIRI 这个 pnpm monorepo 的真实配置,你可以掌握如何在大型工作区中集中管理依赖版本(catalog)、强制版本(overrides)、构建脚本审批(allowBuilds)与包管理器/运行时锁定(packageManager / devEngines)。
一、配置二分法:设置与凭据严格分离
当前 pnpm 最重要的配置概念是:设置(settings)与凭据(credentials)分属两类文件。文档将其归纳为一张表:
| 类别 | 存放位置 | 格式 |
|---|---|---|
全部 pnpm/安装设置(nodeLinker、hoistPattern、autoInstallPeers、overrides、catalog 等) |
pnpm-workspace.yaml(项目级)与 config.yaml(全局) |
YAML,camelCase 键名 |
认证与 registry 凭据(_authToken、cert、key 等) |
.npmrc(项目级,gitignore)与全局 rc |
INI |
这里有三条必须牢记的行为变化:
- pnpm 不再读取
package.json中的pnpm字段,该字段被彻底弃用; .npmrc现在仅用于认证/registry 凭据,其他一切设置都归pnpm-workspace.yaml;- YAML 中的键名是 camelCase(如
nodeLinker),而不是旧.npmrc时代沿用的 kebab-case。
AIRI 仓库本身就是这一模型的完整示范:根目录不存在 .npmrc(凭据不落库),全部解析行为都由 pnpm-workspace.yaml 声明,SKILL.md 中的技能说明也强调"在 pnpm 项目中检查 pnpm-workspace.yaml 获取设置与工作区结构,只在认证场景看 .npmrc"。
二、pnpm-workspace.yaml:主配置文件
该文件放在 workspace/项目根目录,即使是单包项目也用这个文件存放 pnpm 设置。文档给出的完整示例覆盖了近期的核心设置项:
# Workspace packages (omit for a single-package repo)
packages:
- 'packages/*'
- 'apps/*'
- '!**/test/**'
# Common install settings (camelCase)
nodeLinker: isolated # isolated (default) | hoisted | pnp
autoInstallPeers: true
strictPeerDependencies: false
savePrefix: '^'
saveExact: false
hoistPattern:
- '*eslint*'
- '*babel*'
publicHoistPattern: []
shamefullyHoist: false
dedupeDirectDeps: false
resolutionMode: highest # highest | time-based | lowest-direct
# Centralized version management
catalog:
react: ^18.2.0
# Force dependency versions (root only)
overrides:
lodash: ^4.17.21
'foo@^1.0.0>bar': ^2.0.0
# Extend/patch broken package manifests
packageExtensions:
react-redux:
peerDependencies:
react-dom: '*'
# Peer dependency rules
peerDependencyRules:
ignoreMissing:
- '@babel/*'
allowedVersions:
react: '17 || 18'
逐段解读各设置组的语义:
- 安装行为设置:
nodeLinker决定 node_modules 的链接形态(isolated为默认,另有hoisted与pnp);autoInstallPeers控制是否自动安装 peer 依赖;savePrefix/saveExact控制pnpm add写入版本范围时使用的前缀(^、~或精确);hoistPattern/publicHoistPattern/shamefullyHoist控制虚拟 store 中的提升行为;resolutionMode在多个可用版本中的取舍策略为highest、time-based、lowest-direct三选一。 overrides:强制依赖版本的唯一入口,且只在根项目生效。键可以是包名,也可以是直接依赖>传递依赖的嵌套路径,用于把某个特定上游引入的间接依赖钉死到指定版本。packageExtensions:在不改源码的前提下"修补"第三方包的 manifest,典型用途是为缺少 peer 声明的包补上peerDependencies。peerDependencyRules:对 peer 依赖的告警/报错做规则化处理,如忽略缺失(ignoreMissing)或放宽版本要求(allowedVersions)。
AIRI 的真实 pnpm-workspace.yaml:一个生产级范本
AIRI 根目录的 pnpm-workspace.yaml 展示了上述机制在大型 monorepo 中的组合用法,几个值得注意的点:
catalogMode: prefer
minimumReleaseAge: 4320
minimumReleaseAgeExclude:
- '@moeru/*'
- '@proj-airi/*'
# ... 其他内部/自有 scope
shellEmulator: true
packages:
- packages/**
- plugins/**
- integrations/**
- services/**
- examples/**
- docs/**
- engines/**
- apps/**
- server/**
- '!**/dist/**'
overrides:
'@types/hast': 'catalog:'
axios: npm:feaxios@^0.0.23
hono: 4.13.3
# ... npm: 别名替换若干上游包
patchedDependencies:
mineflayer-pathfinder: patches/mineflayer-pathfinder.patch
pixi-live2d-display: patches/pixi-live2d-display.patch
uiohook-napi@1.5.5: patches/uiohook-napi@1.5.5.patch
packages使用**通配并配!**/dist/**负向排除:说明工作区 glob 支持任意深度匹配与排除规则,AIRI 用它把packages/、apps/、server/等九个顶层目录全部纳入同一 workspace 并共用一个 lockfile。overrides中直接写'catalog:':表示"该包以 catalog 中登记的版本为准",让强制版本与集中版本管理打通——@types/hast: 'catalog:'就是把全仓所有@types/hast解析结果钉到 catalog 值。npm:别名 + overrides 组合:axios: npm:feaxios@^0.0.23表示全仓任何位置引入的axios都会被替换为feaxios这个别名包,这是一种典型的"依赖换皮"手段;patchedDependencies则把 patches/ 目录下的补丁文件(如 patches/uiohook-napi@1.5.5.patch)应用到了指定版本之上,两者都属于"不修改上游源码即可修正依赖行为"的官方机制。- 供应链安全设置:
minimumReleaseAge: 4320(仅接受发布满指定小时数的版本)配合minimumReleaseAgeExclude对自有 scope 豁免,这正是新版"供应链接近安全"配置项的实际落地;技能文档目录中的 features-supply-chain-security.md 对该主题有更完整的论述。
三、catalog:集中化版本管理的仓库级应用
文档示例中的 catalog 只有单条目,AIRI 的 pnpm-workspace.yaml 则维护了 400 余条目的 catalog,覆盖 vue、vite、typescript、electron、mineflayer 全家桶等全部关键依赖。其使用方式分三层:
-
子包声明版本为
catalog:。以 packages/plugin-sdk/package.json 为例:"dependencies": { "@moeru/eventa": "catalog:", "@moeru/std": "catalog:", "@proj-airi/plugin-protocol": "workspace:*", "nanoid": "catalog:" }子包不再写具体版本,只引用 catalog 键;
workspace:*则用于指向同 workspace 内的本地包。全仓apps/、packages/、integrations/下的大量package.json都是这一模式。 -
命名 catalog(
catalogs)为特定依赖组建独立版本集。AIRI 在根配置中声明了vitest与xsai两个命名 catalog(pnpm-workspace.yaml):catalogs: vitest: '@vitest/browser-playwright': ^4.1.11 '@vitest/coverage-v8': ^4.1.11 vitest: ^4.1.11 xsai: unspeech: ^0.1.16子包中即可引用
catalog:vitest取该命名空间下的版本,例如根 package.json 里的"@vitest/browser-playwright": "catalog:vitest"、"vitest": "catalog:vitest"。这样 vitest 生态可以整体升级而互不干扰。 -
catalogMode: prefer让 catalog 优先作为版本来源参与解析。解析结果最终沉淀进 pnpm-lock.yaml 的catalogs段,每个包记录specifier与实际version,供审计与pnpm ci冻结安装使用。
这套机制的价值在于:升级 vue 只需改一行 catalog 条目,而不是逐个扫描 40 多个 package.json;AIRI 根 package.json 中的 up 脚本(taze -w -r -I && pnpm prune && pnpm dedupe)则用 taze 工具批量刷新版本后配合 prune/dedupe 收尾,构成完整的升级工作流。
四、全局配置:config.yaml 的位置
用户级(非认证)设置存放在全局 YAML 文件 config.yaml,按平台的查找顺序为:
$XDG_CONFIG_HOME/pnpm/config.yaml(若设置了该环境变量)- Linux:
~/.config/pnpm/config.yaml - macOS:
~/Library/Preferences/pnpm/config.yaml - Windows:
~/AppData/Local/pnpm/config/config.yaml
同目录下还有一个名为 rc 的伴生全局文件,只承载 registry/认证设置。这条分层与项目级形成对照:项目行为看 pnpm-workspace.yaml,机器级默认看 config.yaml,密钥永远只进 .npmrc/rc。
五、workspace 内按包设置:packageConfigs
新版 pnpm 取消了子包各自的 .npmrc,改为在根 pnpm-workspace.yaml 中用 packageConfigs 为单个包覆写设置。文档给出两种形态:
packageConfigs:
# Map form: keyed by package name
project-1:
saveExact: true
project-2:
savePrefix: '~'
# Array form: pattern-matched rules
# - match: ['project-1', 'project-2']
# modulesDir: node_modules
# saveExact: true
- Map 形态以包名为键,精确匹配某个 workspace 包;
- 数组形态以
match做模式匹配,可对一组包批量下发规则(如统一saveExact、modulesDir等)。
对多子包 monorepo 来说,这取代了过去在子目录散落 .npmrc 的做法,让"每个包的行为差异"也在根文件内一处可查、一处可改。
六、.npmrc:只装认证,且按优先级读取
项目级认证文件保持在 .npmrc,但要加入 .gitignore 防止 token 入库。认证文件的读取优先级从高到低:
<workspace root>/.npmrc(项目级,gitignored)<pnpm config>/auth.ini(由pnpm login写入)~/.npmrc(兼容 npm 的兜底)
文档给出的标准 INI 示例(引用环境变量注入 token,而非明文):
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
@myorg:registry=https://npm.myorg.com/
//npm.myorg.com/:_authToken=${MYORG_TOKEN}
注意 registry 的非机密配置(默认 registry、scope 映射、命名 registry 别名)应写在 pnpm-workspace.yaml 而不是 .npmrc:
registries:
default: https://registry.npmjs.org/
'@my-org': https://private.example.com/
# Named registry aliases usable as a prefix, e.g. `pnpm add work:@corp/lib`
namedRegistries:
work: https://npm.work.example.com/
namedRegistries 允许把私有 registry 起别名后作为前缀使用(pnpm add work:@corp/lib),使安装命令不必再携带完整 URL。
安全要点:自 v11 起,项目级
.npmrc中 registry/proxy URL 与凭据键的环境变量展开被禁用,目的是防止恶意仓库通过伪造.npmrc窃取已注入的环境变量密钥。动态 token 行应放入用户级认证文件(第 2 优先级的auth.ini)。AIRI 仓库根目录没有提交任何.npmrc,与"凭据不进库、认证走本地/CI secret"的约定一致。
七、pnpm config 命令:读取与写入
pnpm config 子命令是操作上述配置的入口,行为同样遵循"设置进 YAML、认证进 rc"的分离:
# Writes to global config.yaml / rc by default
pnpm config set nodeVersion 22.0.0
pnpm config set --location=project nodeVersion 22.0.0 # writes pnpm-workspace.yaml
# JSON values create arrays/objects
pnpm config set --location=project --json allowBuilds '{"react": true}'
# get/list print JSON (no longer INI) since v11
pnpm config get nodeLinker
pnpm config get 'allowBuilds.react'
pnpm config list
行为要点:
- 不带
--location时写入全局config.yaml/rc;加--location=project才写入pnpm-workspace.yaml; --json把值按 JSON 解析,从而能写入数组/对象(如allowBuilds这种映射型设置);- v11 起
get/list的输出统一为 JSON(不再是 INI),方便脚本消费;get支持allowBuilds.react这样的点路径取值。
八、环境变量:只认 pnpm_config_*
环境变量注入设置的命名空间也发生了变化:使用 pnpm_config_*(或大写 PNPM_CONFIG_*),不再读取 npm_config_*。
pnpm_config_save_exact=true pnpm add foo
这对 CI 场景意味着:沿用 npm 时代 npm_config_* 的注入脚本会静默失效,需要按新前缀改写。
九、改名/移除的设置项对照表
从旧版 .npmrc 时代迁移时,以下设置项已更名或删除,文档给出了完整对照:
| 旧名(已移除) | 替代项 | 说明 |
|---|---|---|
onlyBuiltDependencies、neverBuiltDependencies、ignoredBuiltDependencies、onlyBuiltDependenciesFile |
allowBuilds: { name: true|false } |
单一映射表统一控制构建脚本审批 |
managePackageManagerVersions、packageManagerStrict、packageManagerStrictVersion、COREPACK_ENABLE_STRICT |
pmOnFail: download|ignore|warn|error |
实际运行的 pnpm 版本与声明版本不一致时的行为 |
useNodeVersion |
devEngines.runtime(写在 package.json) |
运行时锁定 |
auditConfig.ignoreCves |
auditConfig.ignoreGhsas |
改用 GHSA 编号 |
allowNonAppliedPatches |
allowUnusedPatches |
ignorePatchFailures 已删除(补丁失败现在总是抛错) |
package.json#pnpm 字段 |
pnpm-workspace.yaml |
完全不再读取 |
AIRI 的根 pnpm-workspace.yaml 中的 allowBuilds 就是新命名下的完整实例:逐包列出 true/false 审批结果,例如 esbuild: true、sharp: true、better-sqlite3: false,并带注释说明 simple-git-hooks: false 是为 shellEmulator: true 场景下的已知问题所做的工作区规避(workaround)。旧的四个"构建依赖白名单"设置由此收敛为一张表。
十、package.json 中的包管理器与运行时锁定
package.json 在新模型中只保留两件事:packageManager 字段与 devEngines 声明。文档示例:
{
"packageManager": "pnpm@10.0.0",
"devEngines": {
"packageManager": { "name": "pnpm", "version": ">=11.0.0 <12.0.0", "onFail": "download" },
"runtime": { "name": "node", "version": "22.x", "onFail": "download" }
}
}
语义差异:
packageManager要求精确版本(如pnpm@10.0.0);devEngines.packageManager支持版本范围,解析出的实际版本会写入 lockfile;- 两者的
onFail都可以通过设置项pmOnFail/runtimeOnFail在不改 manifest 的情况下整体覆盖。
AIRI 的根 package.json 声明了 "packageManager": "pnpm@11.24.0",即仓库锁定的 pnpm 主版本正是 11.x——上文所有 v11 行为变化(config 输出 JSON、pnpm 字段弃用、项目级 .npmrc 禁用环境变量展开等)都是该仓库的实际运行前提。技能文档 SKILL.md 也注明本技能"基于 pnpm 10.x 生成,同时覆盖 v11 的行为变化(config 拆分、isolated global packages、allowBuilds、pmOnFail、global virtual store)",与本仓库 pnpm@11.24.0 的声明相互印证。
十一、关键要点小结
- 所有 pnpm 设置都放
pnpm-workspace.yaml(camelCase)或全局config.yaml;.npmrc只保留认证/registry 凭据,并应 gitignore。 package.json#pnpm字段与npm_config_*环境变量都不再被读取,环境变量前缀为pnpm_config_*/PNPM_CONFIG_*。- 工作区内按包差异化设置用
packageConfigs(Map 或模式匹配数组两种形态),子包级.npmrc已取消。 - 构建脚本审批统一为一张
allowBuilds映射表(AIRI 根配置中可看到 30 余条真实审批记录);包管理器严格性统一为pmOnFail一个设置。 pnpm config get/list自 v11 起输出 JSON;--location=project把set的落点从全局切到pnpm-workspace.yaml。- 版本集中管理走
catalog/ 命名catalogs(AIRI 使用catalog:与catalog:vitest引用),配合overrides、patchedDependencies与供应链设置(minimumReleaseAge等)构成完整的依赖治理面。
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