
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"
}
}
Root 任务(//#taskname)仅适用于确实无法放置在具体 package 中的任务,例如 Vitest Projects 的 //#test、全仓发布脚本,或无需调用 turbo 本身的辅助工具。
次要原则:turbo run 与 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
├─ 代码检查/类型检查(并行 + 缓存) → 使用 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 依赖关系:
-
若已声明依赖(例如在 package.json 中配置了
"@repo/types": "workspace:*"),直接删除prebuild脚本即可。Turbo 的dependsOn: ["^build"]会自动处理构建顺序。 -
若未声明依赖,则说明是因为没有依赖关系导致
^build无法触发才写了prebuild。修复步骤为:- 在 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 会通过**全局哈希(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+ 个变量),也不属于反模式。这通常意味着开发者很严谨地声明了构建所需的全部环境变量依赖。不要将其判定为问题。





