
turborepo
热门Turborepo monorepo 构建系统开发指南。触发条件包括:turbo.json、任务流水线(task pipelines)、dependsOn、缓存机制、远程缓存、"turbo" CLI、--filter、--affected、CI 优化、环境变量、内部包(internal packages)、monorepo 架构/最佳实践以及边界控制(boundaries)。 适用场景:用户配置任务/工作流/流水线、创建 package、搭建 monorepo、在应用间共享代码、运行受影响/变更的 package、排查缓存问题,或项目中包含 apps/packages 目录结构时。
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 任务:
- 将脚本添加至各个具体 package 的
package.json中 - 在根目录的
turbo.json中注册该任务 - 根目录的
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 依赖:
-
如果已声明依赖(例如在 package.json 中配置了
"@repo/types": "workspace:*"),直接删除prebuild脚本即可。Turbo 的dependsOn: ["^build"]会自动完成构建调度。 -
如果未声明依赖,之所以会有
prebuild脚本,是因为在没有依赖关系时^build不会被触发。此时的修复步骤为:- 在 package.json 中追加依赖关系:
"@repo/types": "workspace:*" - 随后移除
prebuild脚本
- 在 package.json 中追加依赖关系:
// 正确写法 - 声明依赖,交由 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+ 个),也不属于反模式问题。这通常说明开发者对构建环境的依赖声明非常细致严谨。请勿将其标记为错误或问题。





