monorepo-management

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Monorepo Management

Monorepo 管理

Monorepo tooling and workspace architecture expert for JavaScript and TypeScript projects. Covers tool selection, task pipeline configuration, dependency graph management, CI optimization, and versioning strategy.
面向JavaScript和TypeScript项目的Monorepo工具与工作区架构专家指南,涵盖工具选型、任务流水线配置、依赖图管理、CI优化及版本控制策略。

When to Use

适用场景

Use for:
  • Choosing between Turborepo, Nx, Lerna, and Rush for a new or migrating repository
  • Configuring
    turbo.json
    task pipelines, remote caching, and Docker pruning
  • Structuring workspace packages: apps, packages, shared configs, internal libraries
  • Setting up pnpm or npm workspaces with correct hoisting rules
  • Eliminating circular dependencies between workspace packages
  • Configuring path-based CI with affected package detection
  • Managing package versioning and changelogs with Changesets
NOT for:
  • Git submodules or polyrepo coordination → discuss trade-offs separately
  • Bazel, Pants, or Buck for non-JavaScript monorepos
  • Single-package repository setup → use project-level tooling skills
  • Container orchestration of services within the monorepo → use docker-containerization

以下场景可使用本指南:
  • 为新仓库或迁移中的仓库选择Turborepo、Nx、Lerna或Rush
  • 配置
    turbo.json
    任务流水线、远程缓存及Docker清理规则
  • 规划工作区包结构:应用、工具包、共享配置、内部库
  • 基于正确的提升规则搭建pnpm或npm工作区
  • 消除工作区包之间的循环依赖
  • 配置基于路径的CI,实现受影响包的精准检测
  • 使用Changesets管理包版本与变更日志
以下场景请勿使用:
  • Git子模块或多仓库协同 → 请单独讨论权衡方案
  • 非JavaScript类Monorepo的Bazel、Pants或Buck工具
  • 单包仓库搭建 → 使用项目级工具技能
  • Monorepo内服务的容器编排 → 使用docker-containerization技能

Tool Selection Decision Tree

工具选型决策树

mermaid
flowchart TD
    A[Monorepo tool needed] --> B{Team size and complexity?}
    B -->|Small team, apps-first| C{Need plugin ecosystem?}
    B -->|Large org, many teams| D[Rush or Nx]
    C -->|No — just fast builds| E[Turborepo]
    C -->|Yes — code gen, generators| F[Nx]
    D --> G{Microsoft/enterprise patterns?}
    G -->|Yes| H[Rush]
    G -->|No| I[Nx]
    E --> J{Package manager preference?}
    J -->|pnpm — recommended| K[pnpm + Turborepo]
    J -->|npm or yarn| L[npm/yarn workspaces + Turborepo]
    I --> M[Nx Cloud for remote caching]
    H --> N[Rush's own cache]
    K --> O[Vercel Remote Cache\nor self-hosted]
mermaid
flowchart TD
    A[Monorepo tool needed] --> B{Team size and complexity?}
    B -->|Small team, apps-first| C{Need plugin ecosystem?}
    B -->|Large org, many teams| D[Rush or Nx]
    C -->|No — just fast builds| E[Turborepo]
    C -->|Yes — code gen, generators| F[Nx]
    D --> G{Microsoft/enterprise patterns?}
    G -->|Yes| H[Rush]
    G -->|No| I[Nx]
    E --> J{Package manager preference?}
    J -->|pnpm — recommended| K[pnpm + Turborepo]
    J -->|npm or yarn| L[npm/yarn workspaces + Turborepo]
    I --> M[Nx Cloud for remote caching]
    H --> N[Rush's own cache]
    K --> O[Vercel Remote Cache\nor self-hosted]

Tool Comparison Summary

工具对比总结

ToolBest ForRemote CacheLearning Curve
TurborepoApps-first, fast builds, simple configVercel / self-hostedLow
NxLibrary-heavy, code generation, plugin ecosystemNx Cloud / self-hostedMedium
RushEnterprise, Microsoft stack, strict isolationCustomHigh
LernaLegacy — migrating fromNone (use with Turborepo)Low
Recommendation for new projects: Start with pnpm workspaces + Turborepo. Migrate to Nx if you need advanced code generation or project graph visualization.

工具最佳适用场景远程缓存支持学习曲线
Turborepo以应用为核心、快速构建、配置简单Vercel / 自托管
Nx以库为核心、代码生成、插件生态丰富Nx Cloud / 自托管
Rush企业级、微软技术栈、严格隔离自定义
Lerna遗留项目迁移无(需搭配Turborepo使用)
新项目推荐:从pnpm workspaces + Turborepo开始。若需要高级代码生成或项目图可视化功能,再迁移至Nx。

Workspace Architecture Decision Tree

工作区架构决策树

mermaid
flowchart TD
    A[New package in monorepo?] --> B{Who consumes it?}
    B -->|Internal only, not published| C[Internal package:\nno versioning, workspace: protocol]
    B -->|Published to npm| D[Published package:\nChangesets, semver, CHANGELOG]
    B -->|Shared config only| E[Config package:\neslint-config-*, tsconfig-*]
    C --> F{What type?}
    F -->|UI components| G[packages/ui]
    F -->|Business logic / utilities| H[packages/utils or packages/core]
    F -->|Shared types| I[packages/types]
    F -->|API client| J[packages/api-client]
    D --> K[packages/publishable-name]
    A --> L{Is it an application?}
    L -->|Yes| M[apps/ directory:\nnext-app, api, docs-site]

mermaid
flowchart TD
    A[New package in monorepo?] --> B{Who consumes it?}
    B -->|Internal only, not published| C[Internal package:\nno versioning, workspace: protocol]
    B -->|Published to npm| D[Published package:\nChangesets, semver, CHANGELOG]
    B -->|Shared config only| E[Config package:\neslint-config-*, tsconfig-*]
    C --> F{What type?}
    F -->|UI components| G[packages/ui]
    F -->|Business logic / utilities| H[packages/utils or packages/core]
    F -->|Shared types| I[packages/types]
    F -->|API client| J[packages/api-client]
    D --> K[packages/publishable-name]
    A --> L{Is it an application?}
    L -->|Yes| M[apps/ directory:\nnext-app, api, docs-site]

Dependency Graph and Build Pipeline

依赖图与构建流水线

mermaid
graph LR
    A[apps/web] --> B[packages/ui]
    A --> C[packages/utils]
    A --> D[packages/types]
    E[apps/api] --> C
    E --> D
    B --> D
    F[packages/ui-icons] --> D
    B --> F

    style A fill:#4a9d9e,color:#fff
    style E fill:#4a9d9e,color:#fff
    style B fill:#6b7280,color:#fff
    style C fill:#6b7280,color:#fff
    style D fill:#9ca3af
    style F fill:#9ca3af
Reading the graph: Build order flows from dependencies to dependents.
packages/types
(no deps) builds first.
packages/ui
builds after
types
.
apps/web
builds last. Turborepo and Nx compute this graph automatically — you define task dependencies, they order execution.

mermaid
graph LR
    A[apps/web] --> B[packages/ui]
    A --> C[packages/utils]
    A --> D[packages/types]
    E[apps/api] --> C
    E --> D
    B --> D
    F[packages/ui-icons] --> D
    B --> F

    style A fill:#4a9d9e,color:#fff
    style E fill:#4a9d9e,color:#fff
    style B fill:#6b7280,color:#fff
    style C fill:#6b7280,color:#fff
    style D fill:#9ca3af
    style F fill:#9ca3af
依赖图解读:构建顺序从依赖项流向被依赖项。
packages/types
(无依赖)最先构建,
packages/ui
types
之后构建,
apps/web
最后构建。Turborepo和Nx会自动计算该依赖图——你只需定义任务依赖,它们会自动排序执行。

Turborepo Configuration

Turborepo 配置

turbo.json — Task Pipeline

turbo.json — 任务流水线

json
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "package.json", "tsconfig.json"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["^build"],
      "inputs": ["src/**/*.ts", "src/**/*.tsx", "**/*.test.ts", "**/*.test.tsx"],
      "outputs": ["coverage/**"]
    },
    "lint": {
      "inputs": ["src/**/*.ts", "src/**/*.tsx", ".eslintrc*"]
    },
    "typecheck": {
      "dependsOn": ["^build"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
Key concepts:
  • ^build
    means "build all dependencies first before building this package"
  • inputs
    determines cache keys — changes outside inputs don't invalidate the cache
  • outputs
    are stored in cache and restored on cache hit
  • cache: false
    for long-running tasks (dev servers, watchers)
  • persistent: true
    for tasks that don't exit
json
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["src/**/*.tsx", "src/**/*.ts", "package.json", "tsconfig.json"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["^build"],
      "inputs": ["src/**/*.ts", "src/**/*.tsx", "**/*.test.ts", "**/*.test.tsx"],
      "outputs": ["coverage/**"]
    },
    "lint": {
      "inputs": ["src/**/*.ts", "src/**/*.tsx", ".eslintrc*"]
    },
    "typecheck": {
      "dependsOn": ["^build"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
核心概念:
  • ^build
    表示「在构建当前包之前,先构建所有依赖包的build任务」
  • inputs
    用于确定缓存键——输入内容之外的变更不会使缓存失效
  • outputs
    是会被存入缓存的内容,命中缓存时会自动恢复
  • cache: false
    适用于长时间运行的任务(如开发服务器、监听器)
  • persistent: true
    适用于不会自动退出的任务

Running Tasks

任务执行命令

bash
undefined
bash
undefined

Run build for all packages

为所有包执行build任务

turbo build
turbo build

Run only for packages affected by changes since main branch

仅为与main分支相比有变更的包执行build任务

turbo build --filter=...[origin/main]
turbo build --filter=...[origin/main]

Run for a specific app and its dependencies

为指定应用及其依赖包执行build任务

turbo build --filter=web...
turbo build --filter=web...

Run in parallel across packages

并行执行lint和typecheck任务

turbo lint typecheck --parallel
turbo lint typecheck --parallel

Dry run to see what would execute

预演将要执行的任务(干跑)

turbo build --dry-run
undefined
turbo build --dry-run
undefined

Remote Caching

远程缓存配置

bash
undefined
bash
undefined

Login to Vercel remote cache (free for open source)

登录Vercel远程缓存(开源项目免费)

npx turbo login
npx turbo login

Link to team/project

关联到团队/项目

npx turbo link
npx turbo link

CI: pass token via environment

CI环境:通过环境变量传递令牌

TURBO_TOKEN=$TURBO_TOKEN turbo build

Self-hosted alternative: `turbo-remote-cache` package or Turborepo's built-in HTTP cache server in Turborepo 2.x.

**Consult** `references/turborepo-patterns.md` for Docker pruning for deployment, scoped filtering, and advanced pipeline patterns.

---
TURBO_TOKEN=$TURBO_TOKEN turbo build

自托管替代方案:使用`turbo-remote-cache`包,或Turborepo 2.x内置的HTTP缓存服务器。

**进阶参考**:`references/turborepo-patterns.md` 包含部署时的Docker清理、范围过滤及高级流水线模式。

---

pnpm Workspaces

pnpm 工作区

pnpm-workspace.yaml

pnpm-workspace.yaml

yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'tools/*'
yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - 'tools/*'

package.json workspace dependencies

package.json 工作区依赖

json
{
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:^1.0.0"
  }
}
Use
workspace:*
for internal packages that should always match the local version. Use
workspace:^
only for internal packages that are also published and where you want semver range resolution.
json
{
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:^1.0.0"
  }
}
对于始终应与本地版本保持一致的内部包,使用
workspace:*
。对于需要发布且遵循语义化版本范围解析的内部包,才使用
workspace:^

Hoisting Control

依赖提升控制

undefined
undefined

.npmrc — control hoisting behavior

.npmrc — 控制依赖提升行为

hoist=true public-hoist-pattern[]=eslint public-hoist-pattern[]=prettier shamefully-hoist=false # never — breaks encapsulation

`shamefully-hoist=true` is a trap: it makes all packages available everywhere but breaks encapsulation. If your tools require it, fix the tool dependency instead.

---
hoist=true public-hoist-pattern[]=eslint public-hoist-pattern[]=prettier shamefully-hoist=false # 绝不启用——会破坏隔离性

`shamefully-hoist=true`是陷阱:它会让所有包在全局可用,但会破坏依赖隔离。如果你的工具要求启用该选项,请修复工具的依赖问题而非启用它。

---

Changesets for Versioning

使用Changesets进行版本管理

bash
undefined
bash
undefined

Initialize in your monorepo

在Monorepo中初始化Changesets

npx changeset init
npx changeset init

Create a changeset when making a change

提交变更时创建变更集

npx changeset
npx changeset

Prompts: which packages changed, major/minor/patch, description

交互提示:哪些包有变更、版本变更类型(主版本/次版本/修订版本)、变更描述

Preview what versions will be bumped

预览即将升级的版本

npx changeset status
npx changeset status

Bump versions and update changelogs (CI or release branch)

升级版本并更新变更日志(在CI或发布分支执行)

npx changeset version
npx changeset version

Publish to npm

发布到npm

npx changeset publish
undefined
npx changeset publish
undefined

.changeset/config.json

.changeset/config.json

json
{
  "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": ["@myorg/app-web", "@myorg/app-api"]
}
Set
access: "public"
for open-source packages.
ignore
lists apps that should not be published to npm.
Consult
references/workspace-architecture.md
for full CODEOWNERS setup, ESLint config sharing, and shared tsconfig patterns.

json
{
  "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": ["@myorg/app-web", "@myorg/app-api"]
}
开源包需设置
access: "public"
ignore
列表用于配置无需发布到npm的应用。
进阶参考
references/workspace-architecture.md
包含完整的CODEOWNERS配置、ESLint配置共享及TypeScript共享配置模式。

Path-Based CI (Only Test What Changed)

基于路径的CI(仅测试变更内容)

yaml
undefined
yaml
undefined

.github/workflows/ci.yml

.github/workflows/ci.yml

name: CI
on: push: branches: [main] pull_request:
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # required for turbo --filter to work correctly
  - uses: pnpm/action-setup@v4
    with:
      version: 9

  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: 'pnpm'

  - run: pnpm install --frozen-lockfile

  - name: Build affected packages
    run: pnpm turbo build --filter=...[origin/main]
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}

  - name: Test affected packages
    run: pnpm turbo test --filter=...[origin/main]

`--filter=...[origin/main]` means "run for packages whose files changed compared to `main`, plus all packages that depend on them (upstream consumers)."

---
name: CI
on: push: branches: [main] pull_request:
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # 必须配置,否则turbo --filter无法正常工作
  - uses: pnpm/action-setup@v4
    with:
      version: 9

  - uses: actions/setup-node@v4
    with:
      node-version: 20
      cache: 'pnpm'

  - run: pnpm install --frozen-lockfile

  - name: Build affected packages
    run: pnpm turbo build --filter=...[origin/main]
    env:
      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
      TURBO_TEAM: ${{ vars.TURBO_TEAM }}

  - name: Test affected packages
    run: pnpm turbo test --filter=...[origin/main]

`--filter=...[origin/main]`表示「对与`main`相比有文件变更的包,以及所有依赖这些包的上游消费者执行任务」。

---

Anti-Patterns

反模式

Anti-Pattern: No Task Caching (Rebuilding Everything)

反模式:无任务缓存(全量重建)

Novice: "We run
turbo build
and it always rebuilds all 40 packages, even when only one changed."
Expert: Turborepo caches by computing a hash of inputs (source files + env + turbo config). If inputs haven't changed, the output is restored from cache in milliseconds. The most common cause of cache misses is missing
inputs
declarations — Turborepo falls back to hashing the entire package directory, including files like
.DS_Store
,
node_modules
, and IDE configs that change constantly.
Detection: Run
turbo build --verbosity=2
and look for "MISS" next to packages. Check whether
.turbo
cache entries exist and if the hash matches between runs.
Fix: Explicitly declare
inputs
in
turbo.json
to include only files that affect build output. Explicitly declare
outputs
so the cache knows what to store. Add non-source files to
.gitignore
and
.turboignore
.
LLM mistake: Tutorials often omit
inputs
and
outputs
because a working demo doesn't need them. Production repos require explicit declarations or cache hit rates stay near 0%.

新手误区:「我们执行
turbo build
时,总是会重建所有40个包,哪怕只修改了一个文件。」
专家解读:Turborepo通过计算输入内容(源文件+环境+turbo配置)的哈希值来实现缓存。如果输入内容未变更,会从缓存中恢复输出,耗时仅需毫秒级。缓存未命中最常见的原因是缺少
inputs
声明——此时Turborepo会回退为计算整个包目录的哈希值,包括
.DS_Store
node_modules
、IDE配置等频繁变更的文件。
检测方法:执行
turbo build --verbosity=2
,查看包旁边的「MISS」标记。检查
.turbo
缓存条目是否存在,以及多次运行的哈希值是否一致。
修复方案:在
turbo.json
中显式声明
inputs
,仅包含影响构建输出的文件。显式声明
outputs
,让缓存明确需要存储的内容。将非源文件添加到
.gitignore
.turboignore
中。
LLM常见错误:教程通常会省略
inputs
outputs
,因为演示环境不需要,但生产环境仓库必须显式声明,否则缓存命中率会接近0%。

Anti-Pattern: Circular Dependencies Between Workspace Packages

反模式:工作区包之间存在循环依赖

Novice: "
packages/auth
imports from
packages/api-client
and
packages/api-client
imports from
packages/auth
— is that a problem?"
Expert: Yes. Circular dependencies make build order impossible to determine. Turborepo will error on cycles. More importantly, circular deps indicate a design flaw: two packages whose concerns are entangled. The fix is to extract the shared types or utilities to a third package that both can import without creating a cycle.
Detection:
bash
undefined
新手误区:「
packages/auth
导入
packages/api-client
,而
packages/api-client
又导入
packages/auth
——这有问题吗?」
专家解读:有问题。循环依赖会导致无法确定构建顺序,Turborepo会直接报错。更重要的是,循环依赖表明设计缺陷:两个包的职责纠缠在一起。修复方案是将共享类型或工具提取到第三个包中,让两个包都可以导入该包而不形成循环。
检测方法:
bash
undefined

Turborepo detects cycles and refuses to run

Turborepo会检测到循环依赖并拒绝执行

turbo build # "Error: Package graph cycle detected"
turbo build # 报错:"Error: Package graph cycle detected"

Manual detection with madge

使用madge手动检测

npx madge --circular --extensions ts packages/

**Fix**:
Before: packages/auth → packages/api-client → packages/auth (cycle!)
After: packages/types (new: shared auth types, no dependencies) packages/api-client → packages/types packages/auth → packages/types packages/auth → packages/api-client (one-way, no cycle)

**Timeline**: This is not a new problem — circular deps have been a JavaScript packaging issue since npm v1 (2010). The reason it appears in monorepos specifically is that workspace packages make it easy to import across package boundaries without thinking about dependency direction.

---
npx madge --circular --extensions ts packages/

**修复方案**:
修复前: packages/auth → packages/api-client → packages/auth(循环依赖!)
修复后: packages/types(新增:共享认证类型,无依赖) packages/api-client → packages/types packages/auth → packages/types packages/auth → packages/api-client(单向依赖,无循环)

**历史背景**:这并非新问题——自npm v1(2010年)以来,循环依赖一直是JavaScript打包的痛点。它在Monorepo中频繁出现的原因是,工作区包之间的跨包导入过于便捷,容易忽略依赖方向。

---

Anti-Pattern: Using
shamefully-hoist=true
in pnpm

参考文档

Novice: "My CLI tool can't find its peer dependency. I'll add
shamefully-hoist=true
to
.npmrc
to fix it."
Expert:
shamefully-hoist
makes pnpm behave like npm/yarn classic, putting all packages in a flat
node_modules
. This "fixes" the immediate issue but breaks pnpm's strict isolation, which is the entire reason to use pnpm. The right fix is to add the missing peer dependency to the package that needs it, or use
public-hoist-pattern
to hoist only the specific package that requires it.
Detection: Any
.npmrc
with
shamefully-hoist=true
in a pnpm workspace.

  • references/turborepo-patterns.md
    — 配置turbo.json、搭建远程缓存、部署时的Docker清理、调试缓存未命中时参考。
  • references/workspace-architecture.md
    — 设计包边界、共享ESLint/TypeScript配置、设置CODEOWNERS、规划从单仓库到Monorepo的迁移时参考。

References

  • references/turborepo-patterns.md
    — Consult when configuring turbo.json, setting up remote caching, using Docker pruning for deployment, or debugging cache misses.
  • references/workspace-architecture.md
    — Consult when designing package boundaries, sharing ESLint/TypeScript configs, setting up CODEOWNERS, or planning a migration from single-repo to monorepo.