turborepo by antfu/skills
npx skills add https://github.com/antfu/skills --skill 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"
}
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
根任务 (//#taskname) 仅适用于确实无法存在于包中的任务(这种情况很少见)。
turbo run 与 turbo当命令写入代码时,始终使用 turbo run:
// package.json - 始终使用 "turbo run"
{
"scripts": {
"build": "turbo run build"
}
}
# CI 工作流 - 始终使用 "turbo run"
- run: turbo run build --affected
简写形式 turbo <tasks> 仅适用于 由人类或代理直接输入的一次性终端命令。切勿将 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
├─ 包特定配置 → 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 设置?
├─ 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 <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
根目录 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 不会触发。修复方法是:
"@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 仅运行在列为依赖项的包中的构建。没有依赖项声明 = 没有自动的构建顺序。
globalDependenciesglobalDependencies 会影响所有包中的所有任务。要具体明确。
// 错误 - 影响过大,影响所有哈希值
{
"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
}
}
}
何时使用全局配置与任务级配置:
globalEnv / globalDependencies - 影响所有任务,用于真正共享的配置env / inputs - 仅当特定任务需要时使用env 数组大型 env 数组(即使有 50 多个变量)不是问题。这通常意味着用户彻底声明了其构建的环境依赖项。不要将此标记为问题。
--parallel 标志--parallel 标志会绕过 Turborepo 的依赖图。如果任务需要并行执行,应正确配置 dependsOn。
# 错误 - 绕过依赖图
turbo run lint --parallel
# 正确 - 配置任务以允许并行执行
# 在 turbo.json 中,适当设置 dependsOn(或使用 transit nodes)
turbo run lint
当多个包需要不同的任务配置时,应使用包配置(每个包中的 turbo.json),而不是在根 turbo.json 中堆砌 package#task 覆盖。
// 错误 - 根 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_EXTENDS$ 配合使用以继承和扩展数组何时在根配置中使用 package#task:
"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")// 错误:构建产生文件但未被缓存
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
// 正确:构建输出被缓存
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
各框架的常见输出:
[".next/**", "!.next/cache/**"]["dist/**"]["dist/**"] 或自定义的 outDirTypeScript --noEmit 仍可能产生缓存文件:
当 tsconfig.json 中设置了 incremental: true 时,即使不生成 JS,tsc --noEmit 也会写入 .tsbuildinfo 文件。在假设没有输出之前,请检查 tsconfig:
// 如果 tsconfig 有 incremental: true,tsc --noEmit 会产生缓存文件
{
"tasks": {
"typecheck": {
"outputs": ["node_modules/.cache/tsbuildinfo.json"] // 或 tsBuildInfoFile 指向的位置
}
}
}
确定 TypeScript 任务的正确输出:
incremental 或 compositetsBuildInfoFile 以获取自定义缓存位置(默认:与 outDir 一起或在项目根目录)tsc --noEmit 不产生文件^build 与 build 混淆{
"tasks": {
// ^build = 首先在依赖项中运行构建(此包导入的其他包)
"build": {
"dependsOn": ["^build"]
},
// build (无 ^) = 首先在同一包中运行构建
"test": {
"dependsOn": ["build"]
},
// pkg#task = 特定包的任务
"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 的问题:
如果必须共享变量,请使用 globalEnv 来明确共享的内容,并记录原因。
默认情况下,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://turborepo.dev/schema.v2.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
如果你有需要并行执行且需要缓存失效的任务,请添加一个 transit 任务(见下文)。
^dev 模式的开发任务(用于 turbo watch)根 turbo.json 中带有 dependsOn: ["^dev"] 和 persistent: false 的 dev 任务可能看起来不寻常,但对于 turbo watch 工作流来说是正确的:
// 根 turbo.json
{
"tasks": {
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": false // 包有一次性开发脚本
}
}
}
// 包 turbo.json (apps/web/turbo.json)
{
"extends": ["//"],
"tasks": {
"dev": {
"persistent": true // 应用运行长期运行的开发服务器
}
}
}
为什么这样可行:
@acme/db, @acme/validators)有 "dev": "tsc" —— 一次性的类型生成,快速完成persistent: true 覆盖以运行实际的开发服务器(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: ["^taskname"] 的问题:
dependsOn: [](无依赖项)的问题:
Transit Nodes 解决这两个问题:
{
"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/RULE.md | 通用 CI 原则 |
| ci/github-actions.md | 完整的 GitHub Actions 设置 |
| ci/vercel.md | Vercel 部署,turbo-ignore |
| ci/patterns.md | --affected, 缓存策略 |
| 文件 | 用途 |
|---|---|
| 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 与编译,导出 |
| best-practices/dependencies.md | 依赖管理,安装,版本同步 |
| 文件 | 用途 |
|---|---|
| watch/RULE.md | turbo watch, interruptible tasks, dev workflows |
| 文件 | 用途 |
|---|---|
| boundaries/RULE.md | 强制包隔离,基于标签的依赖规则 |
此技能基于 Turborepo 官方文档:
docs/site/content/docs/每周安装量
3.4K
仓库
GitHub 星标数
3.9K
首次出现
2026年1月28日
安全审计
已安装于
opencode2.6K
codex2.6K
gemini-cli2.5K
github-copilot2.4K
cursor2.4K
claude-code2.2K
Build system for JavaScript/TypeScript monorepos. Turborepo caches task outputs and runs tasks in parallel based on dependency graph.
DO NOT create Root Tasks. ALWAYS create package tasks.
When creating tasks/scripts/pipelines, you MUST:
package.jsonturbo.jsonpackage.json only delegates via turbo run <task>DO NOT put task logic in root package.json. This defeats Turborepo's parallelization.
// DO THIS: Scripts in each 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 - register tasks
{
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
"lint": {},
"test": { "dependsOn": ["build"] }
}
}
// Root package.json - ONLY delegates, no task logic
{
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
}
}
// DO NOT DO THIS - defeats parallelization
// Root package.json
{
"scripts": {
"build": "cd apps/web && next build && cd ../api && tsc",
"lint": "eslint apps/ packages/",
"test": "vitest"
}
}
Root Tasks (//#taskname) are ONLY for tasks that truly cannot exist in packages (rare).
turbo run vs turboAlways useturbo run when the command is written into code:
// package.json - ALWAYS "turbo run"
{
"scripts": {
"build": "turbo run build"
}
}
# CI workflows - ALWAYS "turbo run"
- run: turbo run build --affected
The shorthandturbo <tasks> is ONLY for one-off terminal commands typed directly by humans or agents. Never write turbo build into package.json, CI, or scripts.
Configure a task?
├─ Define task dependencies → references/configuration/tasks.md
├─ Lint/check-types (parallel + caching) → Use Transit Nodes pattern (see below)
├─ Specify build outputs → references/configuration/tasks.md#outputs
├─ Handle environment variables → references/environment/RULE.md
├─ Set up dev/watch tasks → references/configuration/tasks.md#persistent
├─ Package-specific config → references/configuration/RULE.md#package-configurations
└─ Global settings (cacheDir, daemon) → references/configuration/global-options.md
Cache problems?
├─ Tasks run but outputs not restored → Missing `outputs` key
├─ Cache misses unexpectedly → references/caching/gotchas.md
├─ Need to debug hash inputs → Use --summarize or --dry
├─ Want to skip cache entirely → Use --force or cache: false
├─ Remote cache not working → references/caching/remote-cache.md
└─ Environment causing misses → references/environment/gotchas.md
Run only what changed?
├─ Changed packages + dependents (RECOMMENDED) → turbo run build --affected
├─ Custom base branch → --affected --affected-base=origin/develop
├─ Manual git comparison → --filter=...[origin/main]
└─ See all filter options → references/filtering/RULE.md
--affected is the primary way to run only changed packages. It automatically compares against the default branch and includes dependents.
Filter packages?
├─ Only changed packages → --affected (see above)
├─ By package name → --filter=web
├─ By directory → --filter=./apps/*
├─ Package + dependencies → --filter=web...
├─ Package + dependents → --filter=...web
└─ Complex combinations → references/filtering/patterns.md
Environment issues?
├─ Vars not available at runtime → Strict mode filtering (default)
├─ Cache hits with wrong env → Var not in `env` key
├─ .env changes not causing rebuilds → .env not in `inputs`
├─ CI variables missing → references/environment/gotchas.md
└─ Framework vars (NEXT_PUBLIC_*) → Auto-included via inference
CI setup?
├─ GitHub Actions → references/ci/github-actions.md
├─ Vercel deployment → references/ci/vercel.md
├─ Remote cache in CI → references/caching/remote-cache.md
├─ Only build changed packages → --affected flag
├─ Skip unnecessary builds → turbo-ignore (references/cli/commands.md)
└─ Skip container setup when no changes → turbo-ignore
Watch mode?
├─ Re-run tasks on change → turbo watch (references/watch/RULE.md)
├─ Dev servers with dependencies → Use `with` key (references/configuration/tasks.md#with)
├─ Restart dev server on dep change → Use `interruptible: true`
└─ Persistent dev tasks → Use `persistent: true`
Package creation/structure?
├─ Create an internal package → references/best-practices/packages.md
├─ Repository structure → references/best-practices/structure.md
├─ Dependency management → references/best-practices/dependencies.md
├─ Best practices overview → references/best-practices/RULE.md
├─ JIT vs Compiled packages → references/best-practices/packages.md#compilation-strategies
└─ Sharing code between apps → references/best-practices/RULE.md#package-types
Monorepo structure?
├─ Standard layout (apps/, packages/) → references/best-practices/RULE.md
├─ Package types (apps vs libraries) → references/best-practices/RULE.md#package-types
├─ Creating internal packages → references/best-practices/packages.md
├─ TypeScript configuration → references/best-practices/structure.md#typescript-configuration
├─ ESLint configuration → references/best-practices/structure.md#eslint-configuration
├─ Dependency management → references/best-practices/dependencies.md
└─ Enforce package boundaries → references/boundaries/RULE.md
Enforce boundaries?
├─ Check for violations → turbo boundaries
├─ Tag packages → references/boundaries/RULE.md#tags
├─ Restrict which packages can import others → references/boundaries/RULE.md#rule-types
└─ Prevent cross-package file imports → references/boundaries/RULE.md
turbo Shorthand in Codeturbo run is recommended in package.json scripts and CI pipelines. The shorthand turbo <task> is intended for interactive terminal use.
// WRONG - using shorthand in package.json
{
"scripts": {
"build": "turbo build",
"dev": "turbo dev"
}
}
// CORRECT
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
# WRONG - using shorthand in CI
- run: turbo build --affected
# CORRECT
- run: turbo run build --affected
Root package.json scripts MUST delegate to turbo run, not run tasks directly.
// WRONG - bypasses turbo entirely
{
"scripts": {
"build": "bun build",
"dev": "bun dev"
}
}
// CORRECT - delegates to turbo
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
&& to Chain Turbo TasksDon't chain turbo tasks with &&. Let turbo orchestrate.
// WRONG - turbo task not using turbo run
{
"scripts": {
"changeset:publish": "bun build && changeset publish"
}
}
// CORRECT
{
"scripts": {
"changeset:publish": "turbo run build && changeset publish"
}
}
prebuild Scripts That Manually Build DependenciesScripts like prebuild that manually build other packages bypass Turborepo's dependency graph.
// WRONG - manually building dependencies
{
"scripts": {
"prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build",
"build": "next build"
}
}
However, the fix depends on whether workspace dependencies are declared:
If dependencies ARE declared (e.g., "@repo/types": "workspace:*" in package.json), remove the prebuild script. Turbo's dependsOn: ["^build"] handles this automatically.
If dependencies are NOT declared , the prebuild exists because ^build won't trigger without a dependency relationship. The fix is to:
"@repo/types": "workspace:*"prebuild script// CORRECT - declare dependency, let turbo handle build order
// package.json
{
"dependencies": {
"@repo/types": "workspace:*",
"@repo/utils": "workspace:*"
},
"scripts": {
"build": "next build"
}
}
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
Key insight: ^build only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering.
globalDependenciesglobalDependencies affects ALL tasks in ALL packages. Be specific.
// WRONG - heavy hammer, affects all hashes
{
"globalDependencies": ["**/.env.*local"]
}
// BETTER - move to task-level inputs
{
"globalDependencies": [".env"],
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": ["dist/**"]
}
}
}
Look for repeated configuration across tasks that can be collapsed. Turborepo supports shared configuration patterns.
// WRONG - repetitive env and inputs across tasks
{
"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
}
}
}
// BETTER - use globalEnv and globalDependencies for shared config
{
"globalEnv": ["API_URL", "DATABASE_URL"],
"globalDependencies": [".env*"],
"tasks": {
"build": {},
"test": {},
"dev": {
"cache": false,
"persistent": true
}
}
}
When to use global vs task-level:
globalEnv / globalDependencies - affects ALL tasks, use for truly shared configenv / inputs - use when only specific tasks need itenv ArraysA large env array (even 50+ variables) is not a problem. It usually means the user was thorough about declaring their build's environment dependencies. Do not flag this as an issue.
--parallel FlagThe --parallel flag bypasses Turborepo's dependency graph. If tasks need parallel execution, configure dependsOn correctly instead.
# WRONG - bypasses dependency graph
turbo run lint --parallel
# CORRECT - configure tasks to allow parallel execution
# In turbo.json, set dependsOn appropriately (or use transit nodes)
turbo run lint
When multiple packages need different task configurations, use Package Configurations (turbo.json in each package) instead of cluttering root turbo.json with package#task overrides.
// WRONG - root turbo.json with many package-specific overrides
{
"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": [] }
}
}
// CORRECT - use Package Configurations
// Root turbo.json - base config only
{
"tasks": {
"test": { "dependsOn": ["build"] }
}
}
// packages/web/turbo.json - package-specific override
{
"extends": ["//"],
"tasks": {
"test": { "outputs": ["coverage/**"] }
}
}
// packages/api/turbo.json
{
"extends": ["//"],
"tasks": {
"test": { "outputs": ["coverage/**"] }
}
}
Benefits of Package Configurations:
$TURBO_EXTENDS$ to inherit + extend arraysWhen to usepackage#task in root:
"deploy": { "dependsOn": ["web#build"] })See references/configuration/RULE.md#package-configurations for full details.
../ to Traverse Out of Package in inputsDon't use relative paths like ../ to reference files outside the package. Use $TURBO_ROOT$ instead.
// WRONG - traversing out of package
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]
}
}
}
// CORRECT - use $TURBO_ROOT$ for repo root
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]
}
}
}
outputs for File-Producing TasksBefore flagging missingoutputs, check what the task actually produces:
"build": "tsc", "test": "vitest")// WRONG: build produces files but they're not cached
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
// CORRECT: build outputs are cached
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
Common outputs by framework:
[".next/**", "!.next/cache/**"]["dist/**"]["dist/**"] or custom outDirTypeScript--noEmit can still produce cache files:
When incremental: true in tsconfig.json, tsc --noEmit writes .tsbuildinfo files even without emitting JS. Check the tsconfig before assuming no outputs:
// If tsconfig has incremental: true, tsc --noEmit produces cache files
{
"tasks": {
"typecheck": {
"outputs": ["node_modules/.cache/tsbuildinfo.json"] // or wherever tsBuildInfoFile points
}
}
}
To determine correct outputs for TypeScript tasks:
incremental or composite is enabled in tsconfigtsBuildInfoFile for custom cache location (default: alongside outDir or in project root)tsc --noEmit produces no files^build vs build Confusion{
"tasks": {
// ^build = run build in DEPENDENCIES first (other packages this one imports)
"build": {
"dependsOn": ["^build"]
},
// build (no ^) = run build in SAME PACKAGE first
"test": {
"dependsOn": ["build"]
},
// pkg#task = specific package's task
"deploy": {
"dependsOn": ["web#build"]
}
}
}
// WRONG: API_URL changes won't cause rebuilds
{
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}
// CORRECT: API_URL changes invalidate cache
{
"tasks": {
"build": {
"outputs": ["dist/**"],
"env": ["API_URL", "API_KEY"]
}
}
}
.env Files Not in InputsTurbo does NOT load .env files - your framework does. But Turbo needs to know about changes:
// WRONG: .env changes don't invalidate cache
{
"tasks": {
"build": {
"env": ["API_URL"]
}
}
}
// CORRECT: .env file changes invalidate cache
{
"tasks": {
"build": {
"env": ["API_URL"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"]
}
}
}
.env File in MonorepoA .env file at the repo root is an anti-pattern — even for small monorepos or starter templates. It creates implicit coupling between packages and makes it unclear which packages depend on which variables.
// WRONG - root .env affects all packages implicitly
my-monorepo/
├── .env # Which packages use this?
├── apps/
│ ├── web/
│ └── api/
└── packages/
// CORRECT - .env files in packages that need them
my-monorepo/
├── apps/
│ ├── web/
│ │ └── .env # Clear: web needs DATABASE_URL
│ └── api/
│ └── .env # Clear: api needs API_KEY
└── packages/
Problems with root.env:
If you must share variables , use globalEnv to be explicit about what's shared, and document why.
By default, Turborepo filters environment variables to only those in env/globalEnv. CI variables may be missing:
// If CI scripts need GITHUB_TOKEN but it's not in env:
{
"globalPassThroughEnv": ["GITHUB_TOKEN", "CI"],
"tasks": { ... }
}
Or use --env-mode=loose (not recommended for production).
// WRONG: Shared code inside an app
apps/
web/
shared/ # This breaks monorepo principles!
utils.ts
// CORRECT: Extract to a package
packages/
utils/
src/utils.ts
// WRONG: Reaching into another package's internals
import { Button } from "../../packages/ui/src/button";
// CORRECT: Install and import properly
import { Button } from "@repo/ui/button";
// WRONG: App dependencies in root
{
"dependencies": {
"react": "^18",
"next": "^14"
}
}
// CORRECT: Only repo tools in root
{
"devDependencies": {
"turbo": "latest"
}
}
{
"$schema": "https://turborepo.dev/schema.v2.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Add a transit task if you have tasks that need parallel execution with cache invalidation (see below).
^dev Pattern (for turbo watch)A dev task with dependsOn: ["^dev"] and persistent: false in root turbo.json may look unusual but is correct forturbo watch workflows:
// Root turbo.json
{
"tasks": {
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": false // Packages have one-shot dev scripts
}
}
}
// Package turbo.json (apps/web/turbo.json)
{
"extends": ["//"],
"tasks": {
"dev": {
"persistent": true // Apps run long-running dev servers
}
}
}
Why this works:
@acme/db, @acme/validators) have "dev": "tsc" — one-shot type generation that completes quicklypersistent: true for actual dev servers (Next.js, etc.)turbo watch re-runs the one-shot package dev scripts when source files change, keeping types in syncIntended usage: Run turbo watch dev (not turbo run dev). Watch mode re-executes one-shot tasks on file changes while keeping persistent tasks running.
Alternative pattern: Use a separate task name like prepare or generate for one-shot dependency builds to make the intent clearer:
{
"tasks": {
"prepare": {
"dependsOn": ["^prepare"],
"outputs": ["dist/**"]
},
"dev": {
"dependsOn": ["prepare"],
"cache": false,
"persistent": true
}
}
}
Some tasks can run in parallel (don't need built output from dependencies) but must invalidate cache when dependency source code changes.
The problem withdependsOn: ["^taskname"]:
The problem withdependsOn: [] (no dependencies):
Transit Nodes solve both:
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"my-task": { "dependsOn": ["transit"] }
}
}
The transit task creates dependency relationships without matching any actual script, so tasks run in parallel with correct cache invalidation.
How to identify tasks that need this pattern: Look for tasks that read source files from dependencies but don't need their build outputs.
{
"globalEnv": ["NODE_ENV"],
"globalDependencies": [".env"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"env": ["API_URL", "DATABASE_URL"]
}
}
}
| File | Purpose |
|---|---|
| configuration/RULE.md | turbo.json overview, Package Configurations |
| configuration/tasks.md | dependsOn, outputs, inputs, env, cache, persistent |
| configuration/global-options.md | globalEnv, globalDependencies, cacheDir, daemon, envMode |
| configuration/gotchas.md | Common configuration mistakes |
| File | Purpose |
|---|---|
| caching/RULE.md | How caching works, hash inputs |
| caching/remote-cache.md | Vercel Remote Cache, self-hosted, login/link |
| caching/gotchas.md | Debugging cache misses, --summarize, --dry |
| File | Purpose |
|---|---|
| environment/RULE.md | env, globalEnv, passThroughEnv |
| environment/modes.md | Strict vs Loose mode, framework inference |
| environment/gotchas.md | .env files, CI issues |
| File | Purpose |
|---|---|
| filtering/RULE.md | --filter syntax overview |
| filtering/patterns.md | Common filter patterns |
| File | Purpose |
|---|---|
| ci/RULE.md | General CI principles |
| ci/github-actions.md | Complete GitHub Actions setup |
| ci/vercel.md | Vercel deployment, turbo-ignore |
| ci/patterns.md | --affected, caching strategies |
| File | Purpose |
|---|---|
| cli/RULE.md | turbo run basics |
| cli/commands.md | turbo run flags, turbo-ignore, other commands |
| File | Purpose |
|---|---|
| best-practices/RULE.md | Monorepo best practices overview |
| best-practices/structure.md | Repository structure, workspace config, TypeScript/ESLint setup |
| best-practices/packages.md | Creating internal packages, JIT vs Compiled, exports |
| best-practices/dependencies.md | Dependency management, installing, version sync |
| File | Purpose |
|---|---|
| watch/RULE.md | turbo watch, interruptible tasks, dev workflows |
| File | Purpose |
|---|---|
| boundaries/RULE.md | Enforce package isolation, tag-based dependency rules |
This skill is based on the official Turborepo documentation at:
docs/site/content/docs/ in the Turborepo repositoryWeekly Installs
3.4K
Repository
GitHub Stars
3.9K
First Seen
Jan 28, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode2.6K
codex2.6K
gemini-cli2.5K
github-copilot2.4K
cursor2.4K
claude-code2.2K
99,500 周安装