🚀 快速安装
复制以下命令并运行,立即安装此 Skill:
npx @anthropic-ai/skills install vercel/turborepo/turborepo
💡 提示:需要 Node.js 和 NPM
Turborepo 技能
适用于 JavaScript/TypeScript 单体仓库的构建系统。Turborepo 会根据依赖关系图缓存任务输出并并行运行任务。
重要提示:包任务,而非根任务
不要创建根任务。始终创建包任务。
在创建任务/脚本/流水线时,您必须:
- 将脚本添加到每个相关包的
package.json中 - 在根目录的
turbo.json中注册该任务 - 根目录的
package.json仅通过turbo run <任务名>进行委托
不要将任务逻辑放在根目录的 package.json 中。这会破坏 Turborepo 的并行化能力。
// 正确做法:脚本放在每个包中
// 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"
}
}
根任务 (//#任务名) 仅用于那些确实无法存在于包中的任务(这种情况很少见)。
次要规则:turbo run 与 turbo
当命令被写入代码时,始终使用 turbo run:
// package.json - 始终使用 "turbo run"
{
"scripts": {
"build": "turbo run build"
}
}
# CI 工作流 - 始终使用 "turbo run"
- run: turbo run build --affected
简写 turbo <任务> 仅适用于人类或代理直接键入的一次性终端命令。切勿将 turbo build 写入 package.json、CI 或脚本中。
快速决策树
“我需要配置一个任务”
配置任务?
├─ 定义任务依赖 → references/configuration/tasks.md
├─ 代码检查/类型检查(并行 + 缓存) → 使用中转节点模式(见下文)
├─ 指定构建输出 → references/configuration/tasks.md#outputs
├─ 处理环境变量 → references/environment/RULE.md
├─ 设置开发/监视任务 → references/configuration/tasks.md#persistent
├─ 包特定配置 → references/configuration/RULE.md#package-configurations
└─ 全局设置 (cacheDir, daemon) → references/configuration/global-options.md
“我的缓存不工作”
缓存问题?
├─ 任务运行但输出未恢复 → 缺少 `outputs` 键
├─ 缓存意外未命中 → references/caching/gotchas.md
├─ 需要调试哈希输入 → 使用 --summarize 或 --dry
├─ 想要完全跳过缓存 → 使用 --force 或 cache: false
├─ 远程缓存不工作 → references/caching/remote-cache.md
└─ 环境导致缓存未命中 → references/environment/gotchas.md
“我只想运行有变更的包”
只运行有变更的内容?
├─ 变更的包 + 依赖者(推荐)→ turbo run build --affected
├─ 自定义基础分支 → --affected --affected-base=origin/develop
├─ 手动 git 比较 → --filter=...[origin/main]
└─ 查看所有过滤选项 → references/filtering/RULE.md
--affected 是运行仅变更包的主要方式。它会自动与默认分支进行比较,并包含依赖者。
“我想过滤包”
过滤包?
├─ 仅变更的包 → --affected(见上文)
├─ 按包名 → --filter=web
├─ 按目录 → --filter=./apps/*
├─ 包 + 依赖项 → --filter=web...
├─ 包 + 依赖者 → --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
├─ 仅构建变更的包 → --affected 标志
├─ 跳过不必要的构建 → turbo-ignore (references/cli/commands.md)
└─ 无变更时跳过容器设置 → turbo-ignore
“我想在开发期间监视变更”
监视模式?
├─ 变更时重新运行任务 → turbo watch (references/watch/RULE.md)
├─ 具有依赖项的开发服务器 → 使用 `with` 键 (references/configuration/tasks.md#with)
├─ 依赖变更时重启开发服务器 → 使用 `interruptible: true`
└─ 持久性开发任务 → 使用 `persistent: true`
“我需要创建/构建一个包”
包的创建/结构?
├─ 创建内部包 → references/best-practices/packages.md
├─ 仓库结构 → references/best-practices/structure.md
├─ 依赖管理 → references/best-practices/dependencies.md
├─ 最佳实践概述 → references/best-practices/RULE.md
├─ JIT 与 编译包 → references/best-practices/packages.md#compilation-strategies
└─ 在应用间共享代码 → references/best-practices/RULE.md#package-types
“我应该如何构建我的单体仓库?”
单体仓库结构?
├─ 标准布局 (apps/, packages/) → references/best-practices/RULE.md
├─ 包类型 (应用 vs 库) → references/best-practices/RULE.md#package-types
├─ 创建内部包 → 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
└─ 强制执行包边界 → references/boundaries/RULE.md
“我想强制执行架构边界”
强制执行边界?
├─ 检查违规 → turbo boundaries
├─ 标记包 → references/boundaries/RULE.md#tags
├─ 限制哪些包可以导入其他包 → references/boundaries/RULE.md#rule-types
└─ 防止跨包的文件导入 → references/boundaries/RULE.md
关键反模式
在代码中使用 turbo 简写
在 package.json 脚本和 CI 管道中推荐使用 turbo run。简写 turbo <任务> 旨在用于交互式终端。
// 错误 - 在 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 这样手动构建其他包的脚本绕过了 Turborepo 的依赖图。
// 错误 - 手动构建依赖项
{
"scripts": {
"prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build",
"build": "next build"
}
}
然而,修复方法取决于是否声明了工作区依赖项:
- 如果已声明依赖项(例如,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 仅在列为依赖项的包中运行构建。没有依赖声明 = 没有自动构建顺序。
过于宽泛的 globalDependencies
globalDependencies 影响所有包中的所有任务。请具体指定。
// 错误 - 大锤,影响所有哈希
{
"globalDependencies": ["**/.env.*local"]
}
// 更好 - 移到任务级别的 inputs
{
"globalDependencies": [".env"],
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": ["dist/**"]
}
}
}
重复的任务配置
寻找跨任务的重复配置,这些配置可以被合并。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+ 个变量)不是问题。这通常意味着用户对他们构建的环境依赖项声明得很彻底。不要将此标记为问题。
使用 --parallel 标志
--parallel 标志绕过了 Turborepo 的依赖图。如果任务需要并行执行,应正确配置 dependsOn 而不是使用此标志。
# 错误 - 绕过依赖图
turbo run lint --parallel
# 正确 - 配置任务以允许并行执行
# 在 turbo.json 中,适当设置 dependsOn(或使用中转节点)
turbo run lint
在根 turbo.json 中覆盖包特定任务
当多个包需要不同的任务配置时,应使用包配置(每个包中的 turbo.json),而不是在根 turbo.json 中用 包#任务 覆盖来堆砌。
// 错误 - 根 turbo.json 中包含许多包特定的覆盖
{
"tasks": {
"test": { "dependsOn": ["build"] },
"@repo/web#test": { "outputs": ["coverage/**"] },
"@repo/api#test": { "outputs": ["coverage/**"] },
"@repo/utils#test": { "outputs": [] },
"@repo/cli#test": { "outputs": [] },
"@repo/core#test": { "outputs": [] }
}
}
// 正确 - 使用包配置
// 根 turbo.json - 仅基础配置
{
"tasks": {
"test": { "dependsOn": ["build"] }
}
}
// packages/web/turbo.json - 包特定覆盖
{
"extends": ["//"],
"tasks": {
"test": { "outputs": ["coverage/**"] }
}
}
// packages/api/turbo.json
{
"extends": ["//"],
"tasks": {
"test": { "outputs": ["coverage/**"] }
}
}
包配置的好处:
- 使配置靠近受其影响的代码
- 根 turbo.json 保持简洁,专注于基础模式
- 更容易理解每个包的特殊之处
- 与
$TURBO_EXTENDS$配合使用,可以继承并扩展数组
何时在根中使用 包#任务:
- 单个包需要唯一的依赖关系(例如,
"deploy": { "dependsOn": ["web#build"] }) - 迁移期间的临时覆盖
详见 references/configuration/RULE.md#package-configurations。
在 inputs 中使用 ../ 跳出包目录
不要使用像 ../ 这样的相对路径来引用包外部的文件。应使用 $TURBO_ROOT$。
// 错误 - 跳出包目录
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]
}
}
}
// 正确 - 使用 $TURBO_ROOT$ 指向仓库根
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]
}
}
}
为生成文件的任务缺少 outputs
在标记缺少 outputs 之前,检查任务实际产生什么:
- 读取包的脚本(例如,
"build": "tsc","test": "vitest") - 确定它是将文件写入磁盘,还是仅输出到 stdout
- 仅当任务生成应被缓存的文件时才标记
// 错误:构建生成了文件,但它们未被缓存
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
// 正确:构建输出被缓存
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
按框架划分的常见输出:
- Next.js:
[".next/**", "!.next/cache/**"] - Vite/Rollup:
["dist/**"] - tsc:
["dist/**"]或自定义outDir
TypeScript --noEmit 仍可能产生缓存文件:
当 tsconfig.json 中启用了 incremental: true 时,tsc --noEmit 即使不输出 JS 文件,也会写入 .tsbuildinfo 文件。在假设没有输出之前,检查 tsconfig:
// 如果 tsconfig 有 incremental: true, tsc --noEmit 会产生缓存文件
{
"tasks": {
"typecheck": {
"outputs": ["node_modules/.cache/tsbuildinfo.json"] // 或 tsBuildInfoFile 指向的位置
}
}
}
为 TypeScript 任务确定正确输出的方法:
- 检查 tsconfig 中是否启用了
incremental或composite - 检查
tsBuildInfoFile以获取自定义缓存位置(默认在outDir旁边或项目根目录) - 如果没有增量模式,
tsc --noEmit不会产生文件
^build 与 build 的混淆
{
"tasks": {
// ^build = 先在依赖项中运行 build(此包导入的其他包)
"build": {
"dependsOn": ["^build"]
},
// build (无 ^) = 先在同一个包中运行 build
"test": {
"dependsOn": ["build"]
},
// 包#任务 = 特定包的任务
"deploy": {
"dependsOn": ["web#build"]
}
}
}
环境变量未被哈希
// 错误:API_URL 的更改不会触发重建
{
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}
// 正确:API_URL 的更改会使缓存失效
{
"tasks": {
"build": {
"outputs": ["dist/**"],
"env": ["API_URL", "API_KEY"]
}
}
}
.env 文件未包含在 inputs 中
Turbo 不会加载 .env 文件 —— 这是你的框架做的。但 Turbo 需要知道文件的变化:
// 错误:.env 的更改不会使缓存失效
{
"tasks": {
"build": {
"env": ["API_URL"]
}
}
}
// 正确:.env 文件的更改会使缓存失效
{
"tasks": {
"build": {
"env": ["API_URL"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"]
}
}
}
单体仓库根目录的 .env 文件
仓库根目录的 .env 文件是一种反模式——即使对于小型单体仓库或起始模板也是如此。它会在包之间创建隐式耦合,并使弄清楚哪些包依赖于哪些变量变得困难。
// 错误 - 根目录 .env 隐式影响所有包
my-monorepo/
├── .env # 哪些包用到了这个?
├── apps/
│ ├── web/
│ └── api/
└── packages/
// 正确 - .env 文件放在需要它们的包里
my-monorepo/
├── apps/
│ ├── web/
│ │ └── .env # 明确:web 需要 DATABASE_URL
│ └── api/
│ └── .env # 明确:api 需要 API_KEY
└── packages/
根目录 .env 的问题:
- 不清楚哪些包使用了哪些变量
- 所有包都获得所有变量(即使它们不需要)
- 缓存失效是粗粒度的(根 .env 的更改会使所有内容失效)
- 安全风险:包可能意外访问本应保密的变量
- 坏习惯从小处开始 —— 起始模板应模拟正确的模式
如果你必须共享变量,使用 globalEnv 来明确共享内容,并记录原因。
严格模式过滤 CI 变量
默认情况下,Turborepo 会将环境变量过滤到只在 env/globalEnv 中声明的那些。CI 变量可能会缺失:
// 如果 CI 脚本需要 GITHUB_TOKEN 但它不在 env 中:
{
"globalPassThroughEnv": ["GITHUB_TOKEN", "CI"],
"tasks": { ... }
}
或者使用 --env-mode=loose(生产环境不推荐)。
应用中的共享代码(应作为一个包)
// 错误:应用内部的共享代码
apps/
web/
shared/ # 这违背了单体仓库的原则!
utils.ts
// 正确:提取到一个包
packages/
utils/
src/utils.ts
跨包边界访问文件
// 错误:触及另一个包的内部
import { Button } from "../../packages/ui/src/button";
// 正确:安装并正确导入
import { Button } from "@repo/ui/button";
根依赖过多
// 错误:应用依赖放在根
{
"dependencies": {
"react": "^18",
"next": "^14"
}
}
// 正确:只有仓库工具在根
{
"devDependencies": {
"turbo": "latest"
}
}
常见任务配置
标准构建流水线
{
"$schema": "https://v2-8-17-canary-4.turborepo.dev/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
如果你有需要并行执行且需要缓存失效的任务,可以添加一个 transit 任务(见下文)。
带 ^dev 模式的 Dev 任务(用于 turbo watch)
一个带有 dependsOn: ["^dev"] 和 persistent: false 的 dev 任务在根 turbo.json 中可能看起来不寻常,但对于 turbo watch 工作流是正确的:
// 根 turbo.json
{
"tasks": {
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": false // 包有一劳永逸的 dev 脚本
}
}
}
// 包 turbo.json (apps/web/turbo.json)
{
"extends": ["//"],
"tasks": {
"dev": {
"persistent": true // 应用运行长久的 dev 服务器
}
}
}
为什么这能工作:
- 包(例如
@acme/db,@acme/validators)有"dev": "tsc"—— 一次性类型生成,很快完成 - 应用用
persistent: true覆盖,用于实际的 dev 服务器(Next.js 等) turbo watch会在源文件更改时重新运行包的一次性dev脚本,使类型保持同步
预期用途: 运行 turbo watch dev(而不是 turbo run dev)。监视模式在文件更改时重新执行一次性任务,同时保持持久性任务运行。
替代模式: 使用像 prepare 或 generate 这样的单独任务名来表示一次性依赖项构建,以使意图更清晰:
{
"tasks": {
"prepare": {
"dependsOn": ["^prepare"],
"outputs": ["dist/**"]
},
"dev": {
"dependsOn": ["prepare"],
"cache": false,
"persistent": true
}
}
}
用于并行任务和缓存失效的中转节点
某些任务可以并行运行(不需要依赖项的构建输出),但必须在依赖项源代码更改时使缓存失效。
dependsOn: ["^任务名"] 的问题:
- 强制顺序执行(慢)
dependsOn: [](无依赖项)的问题:
- 允许并行执行(快)
- 但缓存是错误的——更改依赖项源代码不会使缓存失效
中转节点同时解决了这两个问题:
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"my-task": { "dependsOn": ["transit"] }
}
}
transit 任务创建了依赖关系,而不匹配任何实际的脚本,因此任务可以并行运行,同时具有正确的缓存失效。
如何识别需要此模式的任务: 查找那些从依赖项读取源代码但不需要其构建输出的任务。
带环境变量的任务
{
"globalEnv": ["NODE_ENV"],
"globalDependencies": [".env"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"env": ["API_URL", "DATABASE_URL"]
}
}
}
参考索引
配置
| 文件 | 用途 |
|---|---|
| configuration/RULE.md | turbo.json 概述,包配置 |
| configuration/tasks.md | dependsOn, outputs, inputs, env, cache, persistent |
| configuration/global-options.md | globalEnv, globalDependencies, cacheDir, daemon, envMode |
| configuration/gotchas.md | 常见的配置错误 |
缓存
| 文件 | 用途 |
|---|---|
| caching/RULE.md | 缓存工作原理,哈希输入 |
| caching/remote-cache.md | Vercel 远程缓存,自托管,登录/链接 |
| caching/gotchas.md | 调试缓存未命中,–summarize, –dry |
环境变量
| 文件 | 用途 |
|---|---|
| environment/RULE.md | env, globalEnv, passThroughEnv |
| environment/modes.md | 严格模式与宽松模式,框架推断 |
| environment/gotchas.md | .env 文件,CI 问题 |
过滤
| 文件 | 用途 |
|---|---|
| filtering/RULE.md | –filter 语法概述 |
| filtering/patterns.md | 常见的过滤模式 |
CI/CD
| 文件 | 用途 |
|---|---|
| ci/RULE.md | 通用 CI 原则 |
| ci/github-actions.md | 完整的 GitHub Actions 设置 |
| ci/vercel.md | Vercel 部署,turbo-ignore |
| ci/patterns.md | –affected,缓存策略 |
CLI
| 文件 | 用途 |
|---|---|
| cli/RULE.md | turbo run 基础 |
| cli/commands.md | turbo run 标志,turbo-ignore,其他命令 |
最佳实践
| 文件 | 用途 |
|---|---|
| best-practices/RULE.md | 单体仓库最佳实践概述 |
| best-practices/structure.md | 仓库结构,工作区配置,TypeScript/ESLint 设置 |
| best-practices/packages.md | 创建内部包,JIT 与 编译,exports |
| best-practices/dependencies.md | 依赖管理,安装,版本同步 |
监视模式
| 文件 | 用途 |
|---|---|
| watch/RULE.md | turbo watch,可中断任务,开发工作流 |
边界(实验性)
| 文件 | 用途 |
|---|---|
| boundaries/RULE.md | 强制执行包隔离,基于标签的依赖规则 |
源文档
本技能基于 Turborepo 官方文档:
- 源:Turborepo 仓库中的
apps/docs/content/docs/ - 在线:https://turborepo.dev/docs
📄 原始文档
完整文档(英文):
https://skills.sh/vercel/turborepo/turborepo
💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。

评论(0)