mcpField
S
shared-monorepo-pnpm-workspaces

shared-monorepo-pnpm-workspaces

github.com/agents-inc开发2669 次安装2 天前更新
代码仓库

关于 shared-monorepo-pnpm-workspaces

面向 JavaScript 和 TypeScript monorepo 的 pnpm 工作区管理指南,涵盖包链接、依赖目录、定向命令、共享配置、发布与 CI 实践。

主要功能

  1. 01使用 pnpm-workspace.yaml 配置工作区
  2. 02通过 workspace:* 链接内部包
  3. 03用 catalog: 统一依赖版本
  4. 04使用 --filter 定向运行命令
  5. 05发布与 CI 配置指南

适用场景

  1. 01搭建 pnpm monorepo
  2. 02在工作区包之间共享依赖版本
  3. 03仅对变更的包运行命令
  4. 04从一个仓库发布相关包

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill shared-monorepo-pnpm-workspaces

安装后在 Claude Code 中输入/shared-monorepo-pnpm-workspaces确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
ci.md
core.md
packages.md
publishing.md
scripts.md

详细介绍

用于 Monorepo 管理的 pnpm 工作区

**快速指南:**使用 pnpm 10.x 工作区管理 monorepo。pnpm-workspace.yaml 用于定义工作区包。使用 workspace:* 协议链接内部包。使用 catalog: 协议同步依赖版本。使用 --filter 执行定向命令。在各个包之间共享 tsconfig 和工具配置。使用 Changesets 管理版本和发布。默认严格隔离依赖。


<critical_requirements>

重要:使用此技能前

所有代码都必须遵循 CLAUDE.md 中的项目规范(kebab-case、命名导出、导入顺序、import type、命名常量)

(所有内部包依赖都必须使用 workspace:* 协议,绝不能硬编码版本)

(CI 中必须使用 --frozen-lockfile,pnpm 会在 CI 环境中默认启用此选项)

(必须在仓库根目录的 pnpm-workspace.yaml 中定义工作区包)

(pnpm 专属设置必须放在 pnpm-workspace.yaml 中,不能放在 .npmrc 中(pnpm v10 的变更))

(在 3 个或更多包之间共享依赖版本时,必须使用 catalog: 协议)

(仅有特定包发生更改时,必须使用 --filter 执行定向命令,而不是使用 pnpm -r)

</critical_requirements>


**自动检测:**pnpm-workspace.yaml、pnpm 工作区、工作区协议、workspace:*、catalog:、pnpm filter、pnpm recursive、pnpm monorepo、pnpm-lock.yaml、.npmrc pnpm、pnpm catalogs、pnpm publish

适用场景:

  • 使用 pnpm 工作区搭建 monorepo
  • 配置 pnpm-workspace.yaml 以发现工作区包
  • 使用 workspace:* 协议链接内部包
  • 使用 catalog: 协议同步依赖版本
  • 使用 --filter 或 -r 在工作区中运行脚本
  • 从 pnpm 工作区发布包
  • 使用 pnpm 缓存设置 CI/CD 流水线
  • 在工作区包之间共享 TypeScript、ESLint 或 Prettier 配置
  • 从 npm/yarn 工作区迁移到 pnpm

不适用场景:

  • 没有共享代码的单包项目
  • 使用 Bun 或 Yarn 作为包管理器的项目
  • 仅需要兼容 npm 的项目(例如 npm 工作区)
  • 任务编排逻辑(请在 pnpm 之上使用专用任务运行器)

涵盖的关键模式:

  • pnpm-workspace.yaml 配置和工作区包发现
  • 工作区协议(workspace:*、workspace:^、workspace:~)
  • 用于同步依赖版本的目录
  • 过滤命令(--filter、-F、glob 模式、依赖选择器)
  • 在工作区中运行脚本(-r、--parallel、--workspace-concurrency)
  • pnpm-workspace.yaml 中的设置(v10:设置从 .npmrc 移至此文件)
  • 使用 publishConfig 和 changesets 发布
  • 使用 GitHub Actions、缓存和 --frozen-lockfile 的 CI/CD
  • 共享 TypeScript 配置模式
  • Monorepo 目录结构规范

详细资源:


## 设计理念

pnpm 工作区通过内容寻址存储实现严格、高效的单体仓库管理。与 npm/yarn 不同,pnpm 创建的 node_modules 并非扁平结构,包只能访问自己声明的依赖项——这种严格性能够及早发现缺失的依赖声明。工作区协议(workspace:*)可确保内部包始终在本地链接,而目录(catalog:)则集中管理版本,避免版本不一致。

核心原则:

  • 默认严格——包无法访问未声明的依赖项
  • 节省磁盘空间——内容寻址存储可在各项目间共享相同文件
  • 安全优先——v10 默认阻止生命周期脚本,以防范供应链攻击
  • 单一锁文件——所有包共用工作区根目录中的一个 pnpm-lock.yaml

适用场景:

  • 包含多个应用和共享包的单体仓库
  • 需要严格隔离依赖项的项目
  • 希望节省磁盘空间来存储依赖项的团队
  • 在一个仓库中发布多个相关 npm 包

不适用场景:

  • 单包项目(没有工作区带来的好处)
  • 深度依赖 Yarn PnP 或 Berry 特性的项目
  • 仅提供 npm 的环境(例如某些 CI/CD 环境或受限的企业环境)

核心模式

模式 1:工作区配置(pnpm-workspace.yaml)

仓库根目录中的 pnpm-workspace.yaml 文件用于定义包含工作区包的目录。每个 pnpm 工作区都必须包含此文件。

# pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"

在 pnpm v10 中,设置项从 .npmrc 移到了此文件中:

# pnpm-workspace.yaml (v10+)
packages:
  - "apps/*"
  - "packages/*"

linkWorkspacePackages: true
saveWorkspaceProtocol: rolling
disallowWorkspaceCycles: true

优点: 工作区定义和设置的单一事实来源

完整的工作区设置、目录结构和所有设置项,请参阅 examples/core.md。


模式 2:工作区协议(workspace:*)

工作区协议可确保内部包始终解析为本地工作区版本,而不会从注册表获取。

协议开发期间执行 pnpm publish 后
workspace:*链接到本地包替换为精确版本(例如 1.5.0)
workspace:^链接到本地包替换为插入符范围(例如 ^1.5.0)
workspace:~链接到本地包替换为波浪号范围(例如 ~1.5.0)
{
  "dependencies": {
    "@repo/ui": "workspace:*",
    "@repo/types": "workspace:*"
  }
}

优点: 确保本地链接,pnpm 会拒绝从外部解析;发布时进行版本转换,以确保符合正确的 semver 版本格式

{
  "dependencies": {
    "@repo/ui": "^1.0.0"
  }
}

缺点: 硬编码版本可能会从 npm 注册表安装,而不是使用本地工作区;不同包之间的版本可能不匹配

有关协议变体、别名和内部包设置,请参阅 examples/packages.md。


模式 3:使用目录同步版本

目录在 pnpm-workspace.yaml 中定义一次依赖项版本,并通过 catalog: 在所有包中引用。

# pnpm-workspace.yaml
catalog:
  react: ^19.0.0
  react-dom: ^19.0.0
  typescript: ^5.7.0
{
  "dependencies": {
    "react": "catalog:",
    "react-dom": "catalog:"
  }
}

优点: 版本的单一事实来源,只需更新一行即可更新所有包,并避免合并冲突

命名目录支持版本迁移:

catalogs:
  react18:
    react: ^18.3.1
  react19:
    react: ^19.0.0

有关命名目录、严格强制执行和完整示例,请参阅 examples/packages.md。


模式 4:筛选命令

--filter(或 -F)可将命令限制在指定包中运行,而不是在整个工作区中运行。

# Exact package
pnpm --filter @repo/web-app build

# Package and ALL its dependencies
pnpm --filter "web-app..." build

# Changed packages since main
pnpm --filter "...[origin/main]" test

# Exclude a package
pnpm --filter "!@repo/docs" build

优点: 针对性执行可节省 CI 时间,基于依赖关系的筛选可确保正确的构建顺序

# BAD: Running everything when only one package changed
pnpm -r build

缺点: 重建所有包会浪费 CI 时间,且无法检测变更

有关所有筛选变体、针对 CI 优化的模式和依赖关系图探索,请参阅 examples/scripts.md。


模式 5:跨工作区运行脚本
# Topological order (respects dependency graph) -- use for build
pnpm -r build

# Parallel (ignores dependency graph) -- use for test, lint
pnpm -r --parallel test

# Controlled concurrency
pnpm -r --workspace-concurrency 4 build
# BAD: Building in parallel when packages depend on each other
pnpm -r --parallel build

**不好的原因:**依赖项尚未完成时,软件包可能已开始构建,导致模块缺失错误

有关依赖项管理和添加依赖项,请参阅 examples/scripts.md。


模式 6:共享 TypeScript 配置

使用配置包在所有工作区软件包之间共享 TypeScript 编译器选项。

{
  "name": "@repo/config-typescript",
  "private": true,
  "exports": {
    "./base": "./tsconfig.base.json",
    "./react": "./tsconfig.react.json",
    "./node": "./tsconfig.node.json"
  }
}

使用者用法:

{
  "extends": "@repo/config-typescript/react",
  "include": ["src/**/*.ts", "src/**/*.tsx"]
}

**好的原因:**TypeScript 设置只有一个真实来源,变更会传播到所有软件包

有关完整的 base/react/node 配置和 ESLint 共享,请参阅 examples/packages.md。


模式 7:从工作区发布

使用 publishConfig 控制发布内容,并使用 changesets 管理版本。

{
  "name": "@repo/ui",
  "main": "./src/index.ts",
  "publishConfig": {
    "main": "./dist/index.js",
    "types": "./dist/index.d.ts",
    "access": "public"
  }
}

**好的原因:**开发期间使用源代码(HMR 更快),发布时使用构建输出

pnpm changeset          # Create changeset
pnpm changeset version  # Bump versions + changelogs
pnpm publish -r         # Publish to npm

有关 changesets 配置、Docker 部署和迁移,请参阅 examples/publishing.md。


模式 8:使用 GitHub Actions 实现 CI/CD
- uses: pnpm/action-setup@v4
  with:
    version: 10
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: "pnpm"
- run: pnpm install
- run: pnpm --filter "...[origin/main]" build
- run: pnpm --filter "...[origin/main]" test

好的原因:pnpm/action-setup@v4 负责安装,cache: "pnpm" 缓存存储目录,CI 中会自动启用 --frozen-lockfile,基于变更的筛选只会构建受影响的软件包

有关完整工作流(包括使用 changesets 自动发布),请参阅 examples/ci.md。


性能优化

安装性能:

  • pnpm 使用内容寻址存储——相同文件在磁盘上只存储一次
  • 通过硬链接,热安装速度最多比 npm/yarn 快 2 倍
  • --frozen-lockfile(CI 中的默认选项)会跳过解析,实现最快安装

工作区性能:

  • 使用 --filter 仅在受影响的软件包上运行命令
  • 对没有跨软件包依赖的任务(test、lint)使用 --parallel
  • 在资源受限的 CI 中使用 --workspace-concurrency 控制并行度
  • 基于变更的筛选(--filter "...[origin/main]")会跳过未变更的软件包

节省磁盘空间:

  • 全局存储会在多个项目之间去重(pnpm store path)
  • 通常比 npm 节省 50-70% 的磁盘空间

CI 缓存:

- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: "pnpm"

这会在多次运行之间缓存 pnpm 内容寻址存储,使后续安装几乎瞬间完成。


<decision_framework>

决策框架

何时使用 Catalogs,何时直接指定版本
Does 3+ packages use this dependency?
  YES -> Use catalog: protocol
  NO  -> Direct version is fine

Will this dependency version be updated frequently?
  YES -> Use catalog: (one-line update)
  NO  -> Direct version is acceptable
workspace:* 与 workspace:^ 和 workspace:~
Are you publishing packages to npm?
  NO  -> Use workspace:* (exact local linking, version irrelevant)
  YES -> Do consumers need flexible version ranges?
    YES -> workspace:^ (caret range on publish)
    NO  -> workspace:* (exact version on publish)
何时使用 --filter,何时使用 -r
Running a command in CI?
  YES -> Use --filter "...[origin/main]" (only affected packages)
  NO  -> Running locally?
    ALL packages -> pnpm -r <cmd>
    ONE package  -> pnpm --filter <name> <cmd>

Is the command order-dependent (build)?
  YES -> Use -r (topological order) or --filter with ...
  NO  -> Use --parallel for speed (lint, test)
pnpm、npm 与 Yarn Workspaces
Need strict dependency isolation?
  YES -> pnpm (non-flat node_modules by default)
  NO  -> Any works

Need disk efficiency?
  YES -> pnpm (content-addressable store)
  NO  -> Any works

Need zero-config PnP (no node_modules)?
  YES -> Yarn Berry with PnP
  NO  -> pnpm or npm

</decision_framework>


<red_flags>

危险信号

高优先级问题:

  • 内部包使用硬编码版本,而不是 workspace:*(会破坏本地链接,并从注册表安装)
  • 将本应放在 pnpm-workspace.yaml 中的设置写入 .npmrc(在 pnpm v10 中会被静默忽略)
  • 仓库根目录缺少 pnpm-workspace.yaml(pnpm 将无法识别工作区包)
  • 在 CI 中运行 pnpm install 时未使用 --frozen-lockfile(锁文件可能被静默修改)
  • 在包之间存在依赖关系时,使用 --parallel 运行构建命令(会导致竞态条件)

中优先级问题:

  • 当 3 个或更多包共享相同依赖版本时,未使用 catalog:(会导致版本漂移)
  • 在 CI 中运行 pnpm -r build,而不是 --filter "...[origin/main]"(浪费时间)
  • 内部包缺少 private: true(存在意外发布到 npm 的风险)
  • 未配置 allowBuilds(或旧版 onlyBuiltDependencies)允许列表(会在 v10 中阻止所有安装脚本)

常见错误:

  • 混用包管理器(在 pnpm 工作区中运行 npm install 会破坏锁文件)
  • 将 shamefullyHoist: true 当作快速修复,而不是正确声明缺失的依赖项
  • 忘记在 GitHub Actions checkout 中设置 fetch-depth: 0(会破坏基于 Git 的变更检测)
  • 本地与 CI 使用不同的 pnpm 版本(会导致锁文件格式不兼容)

注意事项与边缘情况:

  • pnpm publish 会将 workspace:* 替换为实际版本——这是预期行为,不是 bug
  • catalog: 条目必须与依赖项名称完全一致——拼写错误会被静默忽略
  • --filter "...[origin/main]" 需要 Git 历史记录——请使用 fetch-depth: 0,或至少使用 fetch-depth: 2
  • pnpm v10 默认会阻止所有生命周期脚本——使用 pnpm approve-builds 将需要运行 postinstall 的包(如 esbuild、sharp)加入允许列表,或在 pnpm-workspace.yaml 中配置 allowBuilds
  • pnpm deploy 必须设置 injectWorkspacePackages: true(或使用 pnpm deploy --legacy 绕过)
  • 循环工作区依赖会导致脚本执行顺序不可预测——请使用 disallowWorkspaceCycles: true
  • saveWorkspaceProtocol: rolling(默认值)意味着 pnpm add 会自动使用 workspace: 保存依赖——这是正确行为

</red_flags>


<critical_reminders>

重要提醒

所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、import type、命名常量)

(所有内部包依赖都必须使用 workspace:* 协议——绝不要硬编码版本)

(CI 中必须使用 --frozen-lockfile——pnpm 在 CI 环境中默认启用此选项)

(必须在仓库根目录的 pnpm-workspace.yaml 中定义工作区包)

(pnpm 专属设置必须放在 pnpm-workspace.yaml 中——不要放在 .npmrc 中(pnpm v10 的变更))

(当 3 个或更多包共享依赖版本时,必须使用 catalog: 协议)

(仅有特定包发生变更时,必须使用 --filter 运行针对性命令,而不是使用 pnpm -r)

不遵循这些规则会导致依赖解析失败、版本漂移、CI 缓存失效以及安全漏洞。

</critical_reminders>

评论

支持 Markdown