turborepo

turborepo

热门

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

3.1万Star
2380Fork
更新于 2026/6/10
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"
  }
}

Root 任务(//#taskname仅适用于确实无法放置在具体 package 中的任务,例如 Vitest Projects 的 //#test、全仓发布脚本,或无需调用 turbo 本身的辅助工具。

次要原则:turbo runturbo

在代码或配置文件中书写命令时,请务必使用 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
├─ 代码检查/类型检查(并行 + 缓存) → 使用 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` 配置项
├─ 缓存未命中(Cache miss)不及预期 → references/caching/gotchas.md
├─ 需要调试哈希输入项 → 使用 --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 mode)?
├─ 文件变更时重新运行任务 → 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

"我想强制执行架构边界(Architectural Boundaries)"

边界约束?
├─ 检查越界引用 → 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. 若未声明依赖,则说明是因为没有依赖关系导致 ^build 无法触发才写了 prebuild。修复步骤为:

    • 在 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 会通过**全局哈希(global hash)**影响所有 package 中的所有任务 —— 即使在 inputs 中使用取反匹配(negation globs),任务也无法排除其中的特定文件。请务必保持精确。

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

// 较好做法 - 下沉至任务级别的 inputs
{
  "globalDependencies": [".env"],
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": ["dist/**"]
    }
  }
}

开启 futureFlags.globalConfiguration 后,这个问题会得到缓解,因为 global.inputs 中的文件会被合并到各个任务的 inputs 中(而不是注入全局哈希),任务也可以显式排除特定文件:

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

重复的任务配置

注意排查跨任务的重复配置并进行合并。Turborepo 支持共享配置模式。

// 错误做法 - 环境变量和 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
    }
  }
}

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

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

不属于反模式的情况:大型 env 数组

即使 env 数组很大(哪怕包含 50+ 个变量),也不属于反模式。这通常意味着开发者很严谨地声明了构建所需的全部环境变量依赖。不要将其判定为问题。