turborepo

turborepo

热门

Turborepo monorepo 构建系统开发指南。触发条件包括:turbo.json、任务流水线(task pipelines)、dependsOn、缓存机制、远程缓存、"turbo" CLI、--filter、--affected、CI 优化、环境变量、内部包(internal packages)、monorepo 架构/最佳实践以及边界控制(boundaries)。 适用场景:用户配置任务/工作流/流水线、创建 package、搭建 monorepo、在应用间共享代码、运行受影响/变更的 package、排查缓存问题,或项目中包含 apps/packages 目录结构时。

5514Star
0Fork
更新于 2026/7/11
SKILL.md
只读
名称
turborepo
描述

Turborepo monorepo 构建系统开发指南。触发条件包括:turbo.json、任务流水线(task pipelines)、dependsOn、缓存机制、远程缓存、"turbo" CLI、--filter、--affected、CI 优化、环境变量、内部包(internal packages)、monorepo 架构/最佳实践以及边界控制(boundaries)。 适用场景:用户配置任务/工作流/流水线、创建 package、搭建 monorepo、在应用间共享代码、运行受影响/变更的 package、排查缓存问题,或项目中包含 apps/packages 目录结构时。

Turborepo Skill

针对 JavaScript/TypeScript monorepo 的构建系统指南。Turborepo 能够缓存任务输出,并基于依赖图谱并行执行任务。

重要原则:优先使用 Package 任务,而非 Root 任务

优先选择 Package 级别的任务,而非根目录(Root)任务。

在创建任务/脚本/流水线时,你必须默认采用 Package 任务:

  1. 将脚本添加至各个具体 package 的 package.json
  2. 在根目录的 turbo.json 中注册该任务
  3. 根目录的 package.json 仅通过 turbo run <task> 进行任务分发

当任务逻辑可以存在于 package 内时,切勿将其直接写在根目录的 package.json 中。这会破坏 Turborepo 的并行构建能力。

// 推荐做法:在各个 package 中定义脚本
// apps/web/package.json
{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }

// apps/api/package.json
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }

// packages/ui/package.json
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }
// turbo.json - 注册任务
{
  "tasks": {
    "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
    "lint": {},
    "test": { "dependsOn": ["build"] }
  }
}
// 根目录 package.json - 仅做分发,不含任务逻辑
{
  "scripts": {
    "build": "turbo run build",
    "lint": "turbo run lint",
    "test": "turbo run test"
  }
}
// 切勿这样写 - 会破坏并行执行
// 根目录 package.json
{
  "scripts": {
    "build": "cd apps/web && next build && cd ../api && tsc",
    "lint": "eslint apps/ packages/",
    "test": "vitest"
  }
}

根任务(//#taskname仅适用于确实无法在子 package 中存在的任务,例如 Vitest Projects 的 //#test、全仓库范围的发布脚本,或者不直接调用 turbo 的工具脚本。

次要规则:turbo run vs turbo

在代码或脚本配置文件中编写命令时,务必使用 turbo run

// package.json - 始终使用 "turbo run"
{
  "scripts": {
    "build": "turbo run build"
  }
}
# CI 工作流 - 始终使用 "turbo run"
- run: turbo run build --affected

简写形式 turbo <tasks> 仅适用于开发者或 Agent 在终端中手动输入的单次一次性命令。绝不要将 turbo build 直接写入 package.json、CI 配置或各类脚本中。

快速决策树

"我需要配置一个任务"

配置任务?
├─ 定义任务依赖关系 → references/configuration/tasks.md
├─ Lint/类型检查(并行 + 缓存) → 使用 Transit Nodes 模式(见下文)
├─ 指定构建产物输出路径 → references/configuration/tasks.md#outputs
├─ 处理环境变量 → references/environment/RULE.md
├─ 配置开发/监听任务 → references/configuration/tasks.md#persistent
├─ Package 专属配置 → references/configuration/RULE.md#package-configurations
└─ 全局设置(cacheDir、daemon 等) → references/configuration/global-options.md

"我的缓存没有生效"

缓存问题?
├─ 任务运行了但输出未恢复 → 遗漏了 `outputs` 配置项
├─ 意外出现缓存未命中 → references/caching/gotchas.md
├─ 需要排查哈希输入(Hash Inputs) → 使用 --summarize 或 --dry
├─ 完全跳过缓存 → 使用 --force 或设置 cache: false
├─ 远程缓存未生效 → references/caching/remote-cache.md
└─ 环境变量导致缓存未命中 → references/environment/gotchas.md

"我只想运行发生变更的 package"

只运行变更部分?
├─ 变更的 package + 依赖它的项(推荐) → turbo run build --affected
├─ 自定义基准分支 → --affected --affected-base=origin/develop
├─ 手动 git 对比 → --filter=...[origin/main]
└─ 查看所有过滤选项 → references/filtering/RULE.md

--affected 是只运行变更 package 的首选方式。 它会自动与默认分支对比,并包含所有受影响的依赖项。

"我想过滤指定的 package"

过滤 package?
├─ 仅变更的 package → --affected(见上文)
├─ 按 package 名称过滤 → --filter=web
├─ 按目录过滤 → --filter=./apps/*
├─ Package 及其依赖项 → --filter=web...
├─ Package 及其被依赖项 → --filter=...web
└─ 复杂过滤组合 → references/filtering/patterns.md

"环境变量没有生效"

环境变量问题?
├─ 运行时获取不到变量 → 严格模式过滤(默认策略)
├─ 环境变量不同却误中缓存 → 变量未声明在 `env` 键中
├─ 修改 .env 未触发重新构建 → .env 未声明在 `inputs` 中
├─ CI 环境变量缺失 → references/environment/gotchas.md
└─ 框架内置变量(NEXT_PUBLIC_*) → 自动推导包含

"我需要配置 CI"

配置 CI?
├─ GitHub Actions → references/ci/github-actions.md
├─ Vercel 部署 → references/ci/vercel.md
├─ CI 中的远程缓存 → references/caching/remote-cache.md
├─ 仅构建发生变更的 package → 使用 --affected 标记
├─ 跳过不必要的构建 → turbo-ignore (references/cli/commands.md)
└─ 无变更时跳过容器构建 → turbo-ignore

"我想在开发时监听文件变更(Watch 模式)"

监听模式?
├─ 变更时重新运行任务 → turbo watch (references/watch/RULE.md)
├─ 带依赖关系的开发服务器 → 使用 `with` 字段 (references/configuration/tasks.md#with)
├─ 依赖变更时重启开发服务器 → 使用 `interruptible: true`
└─ 常驻开发任务 → 使用 `persistent: true`

"我需要创建/组织 package 结构"

Package 创建与结构?
├─ 创建内部 package → references/best-practices/packages.md
├─ 目录架构组织 → references/best-practices/structure.md
├─ 依赖项管理 → references/best-practices/dependencies.md
├─ 最佳实践概览 → references/best-practices/RULE.md
├─ JIT 与 预编译 package 策略 → references/best-practices/packages.md#compilation-strategies
└─ 在应用间共享代码 → references/best-practices/RULE.md#package-types

"应该如何组织 monorepo 项目架构?"

Monorepo 项目架构?
├─ 标准目录布局(apps/、packages/) → references/best-practices/RULE.md
├─ Package 类型划分(应用 vs 库) → references/best-practices/RULE.md#package-types
├─ 创建内部 package → references/best-practices/packages.md
├─ TypeScript 配置 → references/best-practices/structure.md#typescript-configuration
├─ ESLint 配置 → references/best-practices/structure.md#eslint-configuration
├─ 依赖项管理 → references/best-practices/dependencies.md
└─ 强制约束 package 边界 → references/boundaries/RULE.md

"我想强制约束架构边界"

约束边界?
├─ 检查越界行为 → turbo boundaries
├─ 为 package 打标签 → references/boundaries/RULE.md#tags
├─ 限制 package 间的导入关系 → references/boundaries/RULE.md#rule-types
└─ 禁止跨 package 文件直接引用 → references/boundaries/RULE.md

关键反模式(Anti-Patterns)

在代码中误用 turbo 简写命令

推荐在 package.json 脚本和 CI 流水线中使用 turbo run 简写形式 turbo <task> 仅用于终端交互式操作。

// 错误写法 - 在 package.json 中使用简写
{
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev"
  }
}

// 正确写法
{
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev"
  }
}
# 错误写法 - 在 CI 中使用简写
- run: turbo build --affected

# 正确写法
- run: turbo run build --affected

根目录脚本绕过 Turbo

根目录 package.json 中的脚本必须通过 turbo run 进行分发,而不是直接运行具体的构建指令。

// 错误写法 - 完全绕过了 turbo
{
  "scripts": {
    "build": "bun build",
    "dev": "bun dev"
  }
}

// 正确写法 - 转发给 turbo
{
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev"
  }
}

使用 && 串联 Turbo 任务

不要使用 && 手动串联 turbo 任务,应交由 turbo 统一调度管理。

// 错误写法 - turbo 任务内部未规范使用 turbo run
{
  "scripts": {
    "changeset:publish": "bun build && changeset publish"
  }
}

// 正确写法
{
  "scripts": {
    "changeset:publish": "turbo run build && changeset publish"
  }
}

prebuild 脚本中手动构建依赖包

形如 prebuild 这样手动构建其他 package 的脚本,会直接绕过 Turborepo 的依赖图谱。

// 错误写法 - 手动构建依赖项
{
  "scripts": {
    "prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build",
    "build": "next build"
  }
}

不过,具体的修复方案取决于是否显式声明了 Workspace 依赖:

  1. 如果已声明依赖(例如在 package.json 中配置了 "@repo/types": "workspace:*"),直接删除 prebuild 脚本即可。Turbo 的 dependsOn: ["^build"] 会自动完成构建调度。

  2. 如果未声明依赖,之所以会有 prebuild 脚本,是因为在没有依赖关系时 ^build 不会被触发。此时的修复步骤为:

    • 在 package.json 中追加依赖关系:"@repo/types": "workspace:*"
    • 随后移除 prebuild 脚本
// 正确写法 - 声明依赖,交由 turbo 自动控制构建顺序
// package.json
{
  "dependencies": {
    "@repo/types": "workspace:*",
    "@repo/utils": "workspace:*"
  },
  "scripts": {
    "build": "next build"
  }
}

// turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"]
    }
  }
}

核心要点: ^build 仅会运行已声明为依赖项的 package 中的构建任务。缺少依赖声明 = 无法自动编排构建顺序。

globalDependencies 配置过于宽泛

globalDependencies 会通过全局哈希影响所有 package 中的所有任务——哪怕在 task 的 inputs 中使用取反匹配(negation globs),也无法为特定文件豁免。请务必精确配置。

// 错误写法 - 太过粗暴,影响所有哈希计算
{
  "globalDependencies": ["**/.env.*local"]
}

// 推荐写法 - 移至 task 级别的 inputs
{
  "globalDependencies": [".env"],
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": ["dist/**"]
    }
  }
}

在开启 futureFlags.globalConfiguration 后,该问题会得到缓解,因为 global.inputs 里的文件会被归入各个 task 自身的 inputs 中(而非计入全局哈希),任务便可以单独排除指定文件:

// 最佳写法 - 使用 global.inputs 并支持单 task 排除
{
  "futureFlags": { "globalConfiguration": true },
  "global": {
    "inputs": [".env"]
  },
  "tasks": {
    "build": { "outputs": ["dist/**"] },
    "lint": {
      "inputs": ["$TURBO_DEFAULT$", "!$TURBO_ROOT$/.env"]
    }
  }
}

重复冗余的任务配置

检查跨任务的重复配置并进行精简合并。Turborepo 支持共享配置模式。

// 错误写法 - 多个任务之间重复配置 env 和 inputs
{
  "tasks": {
    "build": {
      "env": ["API_URL", "DATABASE_URL"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"]
    },
    "test": {
      "env": ["API_URL", "DATABASE_URL"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"]
    },
    "dev": {
      "env": ["API_URL", "DATABASE_URL"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "cache": false,
      "persistent": true
    }
  }
}

// 推荐写法 - 使用 globalEnv 和 globalDependencies 提取公共配置
{
  "globalEnv": ["API_URL", "DATABASE_URL"],
  "globalDependencies": [".env*"],
  "tasks": {
    "build": {},
    "test": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

何时使用全局 vs 任务级别配置:

  • globalEnv / globalDependencies:影响所有任务,仅用于真正全局共享的配置。
  • 任务级别的 env / inputs:仅在特定任务需要时使用。

并非反模式:较大的 env 数组

即使 env 数组包含较多变量(甚至是 50+ 个),也不属于反模式问题。这通常说明开发者对构建环境的依赖声明非常细致严谨。请勿将其标记为错误或问题。