Turborepo Skill
Turborepo 使用指南
Build system for JavaScript/TypeScript monorepos. Turborepo caches task outputs and runs tasks in parallel based on dependency graph.
面向JavaScript/TypeScript单体仓库(monorepo)的构建系统。Turborepo会缓存任务输出,并基于依赖图并行运行任务。
IMPORTANT: Package Tasks, Not Root Tasks
重要提示:使用包级任务,而非根级任务
DO NOT create Root Tasks. ALWAYS create package tasks.
When creating tasks/scripts/pipelines, you MUST:
- Add the script to each relevant package's
- Register the task in root
- Root only delegates via
DO NOT put task logic in root
. This defeats Turborepo's parallelization.
json
// 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" } }
json
// turbo.json - register tasks
{
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
"lint": {},
"test": { "dependsOn": ["build"] }
}
}
json
// Root package.json - ONLY delegates, no task logic
{
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
}
}
json
// 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 (
) are ONLY for tasks that truly cannot exist in packages (rare).
请勿创建根级任务。始终创建包级任务。
在创建任务/脚本/流水线时,你必须:
- 将脚本添加到每个相关包的中
- 在根目录的中注册该任务
- 根目录仅通过进行代理
请勿在根目录
中编写任务逻辑。这会破坏Turborepo的并行化能力。
json
// 正确做法:在每个包中添加脚本
// 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" } }
json
// turbo.json - 注册任务
{
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
"lint": {},
"test": { "dependsOn": ["build"] }
}
}
json
// 根目录package.json - 仅做代理,无任务逻辑
{
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
}
}
json
// 错误做法:破坏并行化能力
// 根目录package.json
{
"scripts": {
"build": "cd apps/web && next build && cd ../api && tsc",
"lint": "eslint apps/ packages/",
"test": "vitest"
}
}
根级任务(
)仅适用于确实无法在包中存在的任务(这种情况很少见)。
Always use when the command is written into code:
json
// package.json - ALWAYS "turbo run"
{
"scripts": {
"build": "turbo run build"
}
}
json
// package.json - 务必使用"turbo run"
{
"scripts": {
"build": "turbo run build"
}
}
CI workflows - ALWAYS "turbo run"
CI工作流 - 务必使用"turbo run"
- run: turbo run build --affected
**The shorthand `turbo <tasks>` is ONLY for one-off terminal commands** typed directly by humans or agents. Never write `turbo build` into package.json, CI, or scripts.
- run: turbo run build --affected
**简写形式`turbo <tasks>`仅适用于人类或Agent直接输入的一次性终端命令。切勿在package.json、CI或脚本中写入`turbo build`。**
Quick Decision Trees
快速决策树
"I need to configure a task"
"我需要配置一个任务"
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
配置任务?
├─ 定义任务依赖 → 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
"My cache isn't working"
"我的缓存不工作"
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
缓存问题?
├─ 任务运行但输出未恢复 → 缺少`outputs`键
├─ 缓存意外未命中 → references/caching/gotchas.md
├─ 需要调试哈希输入 → 使用--summarize或--dry
├─ 想要完全跳过缓存 → 使用--force或cache: false
├─ 远程缓存不工作 → references/caching/remote-cache.md
└─ 环境导致缓存未命中 → references/environment/gotchas.md
"I want to run only changed packages"
"我只想运行已变更的包"
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
is the primary way to run only changed packages. It automatically compares against the default branch and includes dependents.
只运行已变更的内容?
├─ 已变更包及其依赖包(推荐)→ turbo run build --affected
├─ 自定义基准分支 → --affected --affected-base=origin/develop
├─ 手动Git对比 → --filter=...[origin/main]
└─ 查看所有过滤选项 → references/filtering/RULE.md
**
是仅运行已变更包的主要方式。**它会自动与默认分支对比,并包含依赖包。
"I want to filter packages"
"我想过滤包"
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
过滤包?
├─ 仅已变更包 → --affected(见上文)
├─ 按包名 → --filter=web
├─ 按目录 → --filter=./apps/*
├─ 包及其依赖 → --filter=web...
├─ 包及其依赖者 → --filter=...web
└─ 复杂组合 → references/filtering/patterns.md
"Environment variables aren't working"
"环境变量不工作"
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
环境问题?
├─ 运行时无法获取变量 → 严格模式过滤(默认)
├─ 环境错误但缓存命中 → 变量未在`env`键中
├─ .env变更未触发重建 → .env未在`inputs`中
├─ CI变量缺失 → references/environment/gotchas.md
└─ 框架变量(NEXT_PUBLIC_*)→ 通过自动推断包含
"I need to set up CI"
"我需要搭建CI"
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
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
"I want to watch for changes during development"
"我想在开发期间监听变更"
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`
监听模式?
├─ 变更时重新运行任务 → turbo watch(references/watch/RULE.md)
├─ 带依赖的开发服务器 → 使用`with`键(references/configuration/tasks.md#with)
├─ 依赖变更时重启开发服务器 → 使用`interruptible: true`
└─ 持久化开发任务 → 使用`persistent: true`
"I need to create/structure a package"
"我需要创建/结构化包"
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
包创建/结构化?
├─ 创建内部包 → 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
"How should I structure my monorepo?"
"我应该如何构建我的单体仓库结构?"
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
单体仓库结构?
├─ 标准布局(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
"I want to enforce architectural boundaries"
"我想强制架构边界"
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 boundaries
├─ 标记包 → references/boundaries/RULE.md#tags
├─ 限制包的导入权限 → references/boundaries/RULE.md#rule-types
└─ 禁止跨包文件导入 → references/boundaries/RULE.md
Critical Anti-Patterns
关键反模式
is recommended in package.json scripts and CI pipelines. The shorthand
is intended for interactive terminal use.
json
// WRONG - using shorthand in package.json
{
"scripts": {
"build": "turbo build",
"dev": "turbo dev"
}
}
// CORRECT
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
**在package.json脚本和CI流水线中推荐使用
。**简写形式
仅用于交互式终端操作。
json
// 错误 - 在package.json中使用简写
{
"scripts": {
"build": "turbo build",
"dev": "turbo dev"
}
}
// 正确
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
yaml
// 错误 - 在CI中使用简写
- run: turbo build --affected
// 正确
- run: turbo run build --affected
WRONG - using shorthand in CI
根目录脚本绕过Turbo
- run: turbo build --affected
json
// 错误 - 完全绕过turbo
{
"scripts": {
"build": "bun build",
"dev": "bun dev"
}
}
// 正确 - 代理到turbo
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
- run: turbo run build --affected
不要使用
链式调用Turbo任务。让Turbo来协调任务。
json
// 错误 - Turbo任务未使用turbo run
{
"scripts": {
"changeset:publish": "bun build && changeset publish"
}
}
// 正确
{
"scripts": {
"changeset:publish": "turbo run build && changeset publish"
}
}
Root Scripts Bypassing Turbo
Root
scripts MUST delegate to
, not run tasks directly.
json
// WRONG - bypasses turbo entirely
{
"scripts": {
"build": "bun build",
"dev": "bun dev"
}
}
// CORRECT - delegates to turbo
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
像
这样手动构建其他包的脚本会绕过Turborepo的依赖图。
json
// 错误 - 手动构建依赖
{
"scripts": {
"prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build",
"build": "next build"
}
}
不过,修复方式取决于是否声明了工作区依赖:
-
如果已声明依赖(例如package.json中的
"@repo/types": "workspace:*"
),移除
脚本。Turbo的
会自动处理这一点。
-
如果未声明依赖,
的存在是因为没有依赖关系的话
不会触发。修复方法是:
- 在package.json中添加依赖:
"@repo/types": "workspace:*"
- 然后移除脚本
json
// 正确 - 声明依赖,让Turbo处理构建顺序
// package.json
{
"dependencies": {
"@repo/types": "workspace:*",
"@repo/utils": "workspace:*"
},
"scripts": {
"build": "next build"
}
}
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
关键要点:仅在列为依赖的包中运行构建。未声明依赖 = 无自动构建顺序。
Using to Chain Turbo Tasks
Don't chain turbo tasks with
. Let turbo orchestrate.
json
// WRONG - turbo task not using turbo run
{
"scripts": {
"changeset:publish": "bun build && changeset publish"
}
}
// CORRECT
{
"scripts": {
"changeset:publish": "turbo run build && changeset publish"
}
}
json
// 错误 - 影响所有哈希的重型操作
{
"globalDependencies": ["**/.env.*local"]
}
// 更好 - 移至任务级inputs
{
"globalDependencies": [".env"],
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": ["dist/**"]
}
}
}
Scripts That Manually Build Dependencies
重复的任务配置
Scripts like
that manually build other packages bypass Turborepo's dependency graph.
json
// 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
script. Turbo's
handles this automatically.
-
If dependencies are NOT declared, the
exists because
won't trigger without a dependency relationship. The fix is to:
- Add the dependency to package.json:
"@repo/types": "workspace:*"
- Then remove the script
json
// 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: only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering.
寻找可以合并的跨任务重复配置。Turborepo支持共享配置模式。
json
// 错误 - 跨任务重复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 任务级配置:
- / - 影响所有任务,用于真正共享的配置
- 任务级 / - 仅当特定任务需要时使用
affects ALL tasks in ALL packages. Be specific.
json
// 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/**"]
}
}
}
大型
数组(甚至50+个变量)
不是问题。这通常意味着用户彻底声明了构建的环境依赖。不要将此标记为问题。
Repetitive Task Configuration
Look for repeated configuration across tasks that can be collapsed. Turborepo supports shared configuration patterns.
json
// 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:
- / - affects ALL tasks, use for truly shared config
- Task-level / - use when only specific tasks need it
标志会绕过Turborepo的依赖图。如果任务需要并行执行,请正确配置
。
bash
// 错误 - 绕过依赖图
turbo run lint --parallel
// 正确 - 配置任务以允许并行执行
// 在turbo.json中,正确设置dependsOn(或使用中转节点)
turbo run lint
NOT an Anti-Pattern: Large Arrays
根目录turbo.json中的包专属任务覆盖
A large
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.
当多个包需要不同的任务配置时,使用
包配置(每个包中的
),而非在根目录
中使用
覆盖来造成混乱。
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保持简洁,专注于基础模式
- 更容易理解每个包的特殊之处
- 支持来继承并扩展数组
- 单个包需要独特的依赖(例如
"deploy": { "dependsOn": ["web#build"] }
)
- 迁移期间的临时覆盖
详见
references/configuration/RULE.md#package-configurations
获取完整详情。
The
flag bypasses Turborepo's dependency graph. If tasks need parallel execution, configure
correctly instead.
不要使用
这样的相对路径引用包外的文件。请使用
替代。
json
// 错误 - 跳出包目录
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]
}
}
}
// 正确 - 使用$TURBO_ROOT$指向仓库根目录
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]
}
}
}
WRONG - bypasses dependency graph
turbo run lint --parallel
- 查看包的脚本(例如, )
- 确定它是将文件写入磁盘还是仅输出到stdout
- 仅当任务生成应缓存的文件时才标记
json
// 错误:构建生成文件但未缓存
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
// 正确:构建输出被缓存
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
各框架的常见输出:
- Next.js:
[".next/**", "!.next/cache/**"]
- Vite/Rollup:
- tsc: 或自定义
当tsconfig.json中
时,
即使不生成JS也会写入
文件。在假设无输出之前检查tsconfig:
json
// 如果tsconfig中incremental: true,tsc --noEmit会生成缓存文件
{
"tasks": {
"typecheck": {
"outputs": ["node_modules/.cache/tsbuildinfo.json"] // 或tsBuildInfoFile指向的位置
}
}
}
确定TypeScript任务的正确输出:
- 检查tsconfig中是否启用了或
- 检查的自定义缓存位置(默认:与同目录或项目根目录)
- 如果未启用增量模式,不生成文件
CORRECT - configure tasks to allow parallel execution
In turbo.json, set dependsOn appropriately (or use transit nodes)
json
{
"tasks": {
// ^build = 先在依赖包中运行build(当前包导入的其他包)
"build": {
"dependsOn": ["^build"]
},
// build(无^)= 先在当前包中运行build
"test": {
"dependsOn": ["build"]
},
// pkg#task = 指定包的任务
"deploy": {
"dependsOn": ["web#build"]
}
}
}
Package-Specific Task Overrides in Root turbo.json
环境变量未被哈希
When multiple packages need different task configurations, use
Package Configurations (
in each package) instead of cluttering root
with
overrides.
json
// 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:
- Keeps configuration close to the code it affects
- Root turbo.json stays clean and focused on base patterns
- Easier to understand what's special about each package
- Works with to inherit + extend arrays
- Single package needs a unique dependency (e.g.,
"deploy": { "dependsOn": ["web#build"] }
)
- Temporary override while migrating
See
references/configuration/RULE.md#package-configurations
for full details.
json
// 错误:API_URL变更不会触发重建
{
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}
// 正确:API_URL变更会使缓存失效
{
"tasks": {
"build": {
"outputs": ["dist/**"],
"env": ["API_URL", "API_KEY"]
}
}
}
Using to Traverse Out of Package in
Don't use relative paths like
to reference files outside the package. Use
instead.
json
// 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"]
}
}
}
Turbo不会加载
文件 - 你的框架会加载。但Turbo需要知道变更:
json
// 错误:.env变更不会使缓存失效
{
"tasks": {
"build": {
"env": ["API_URL"]
}
}
}
// 正确:.env文件变更会使缓存失效
{
"tasks": {
"build": {
"env": ["API_URL"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"]
}
}
}
Missing for File-Producing Tasks
Before flagging missing , check what the task actually produces:
- Read the package's script (e.g., , )
- Determine if it writes files to disk or only outputs to stdout
- Only flag if the task produces files that should be cached
json
// 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.js:
[".next/**", "!.next/cache/**"]
- Vite/Rollup:
- tsc: or custom
TypeScript can still produce cache files:
When
in tsconfig.json,
writes
files even without emitting JS. Check the tsconfig before assuming no outputs:
json
// 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:
- Check if or is enabled in tsconfig
- Check for custom cache location (default: alongside or in project root)
- If no incremental mode, produces no files
仓库根目录的
文件是反模式 - 即使是小型单体仓库或启动模板也不应该使用。它会在包之间创建隐式耦合,并且不清楚哪些包依赖哪些变量。
// 错误 - 根目录.env隐式影响所有包
my-monorepo/
├── .env # 哪些包使用这个?
├── apps/
│ ├── web/
│ └── api/
└── packages/
// 正确 - .env文件放在需要它们的包中
my-monorepo/
├── apps/
│ ├── web/
│ │ └── .env # 明确:web需要DATABASE_URL
│ └── api/
│ └── .env # 明确:api需要API_KEY
└── packages/
- 不清楚哪些包使用哪些变量
- 所有包都会获取所有变量(即使它们不需要)
- 缓存失效粒度粗(根目录.env变更会使所有内容失效)
- 安全风险:包可能意外访问为其他包准备的敏感变量
- 坏习惯从小事开始 - 启动模板应该示范正确的模式
如果必须共享变量,使用
明确说明共享的内容,并记录原因。
json
{
"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"]
}
}
}
默认情况下,Turbo会过滤环境变量,仅保留
/
中的变量。CI变量可能缺失:
json
// 如果CI脚本需要GITHUB_TOKEN但它不在env中:
{
"globalPassThroughEnv": ["GITHUB_TOKEN", "CI"],
"tasks": { ... }
}
Environment Variables Not Hashed
应用中的共享代码(应转为包)
json
// 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"]
}
}
}
// 错误:共享代码在应用内部
apps/
web/
shared/ # 这违反了单体仓库原则!
utils.ts
// 正确:提取为包
packages/
utils/
src/utils.ts
Turbo does NOT load
files - your framework does. But Turbo needs to know about changes:
json
// 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.*"]
}
}
}
typescript
// 错误:访问另一个包的内部实现
import { Button } from "../../packages/ui/src/button";
// 正确:正确安装并导入
import { Button } from "@repo/ui/button";
A
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/
- Unclear which packages consume which variables
- All packages get all variables (even ones they don't need)
- Cache invalidation is coarse-grained (root .env change invalidates everything)
- Security risk: packages may accidentally access sensitive vars meant for others
- Bad habits start small — starter templates should model correct patterns
If you must share variables, use
to be explicit about what's shared, and document why.
json
// 错误:应用依赖在根目录
{
"dependencies": {
"react": "^18",
"next": "^14"
}
}
// 正确:根目录仅包含仓库工具
{
"devDependencies": {
"turbo": "latest"
}
}
Strict Mode Filtering CI Variables
常见任务配置
By default, Turborepo filters environment variables to only those in
/
. CI variables may be missing:
json
// If CI scripts need GITHUB_TOKEN but it's not in env:
{
"globalPassThroughEnv": ["GITHUB_TOKEN", "CI"],
"tasks": { ... }
}
Or use
(not recommended for production).
json
{
"$schema": "https://turborepo.dev/schema.v2.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
如果有需要并行执行且缓存失效的任务,添加
任务(见下文)。
Shared Code in Apps (Should Be a Package)
// 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
根目录turbo.json中
且
的
任务可能看起来不寻常,但对于
工作流是
正确的:
json
// 根目录turbo.json
{
"tasks": {
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": false // 包使用一次性开发脚本
}
}
}
// 包turbo.json (apps/web/turbo.json)
{
"extends": ["//"],
"tasks": {
"dev": {
"persistent": true // 应用运行长期的开发服务器
}
}
}
为什么这样有效:
- 包(例如, )有 — 快速完成的一次性类型生成
- 应用覆盖为以运行实际的开发服务器(Next.js等)
- ****在源文件变更时重新运行包的一次性脚本,保持类型同步
**预期用法:**运行
(而非
)。监听模式会在文件变更时重新执行一次性任务,同时保持持久化任务运行。
**替代模式:**使用单独的任务名称如
或
用于一次性依赖构建,使意图更清晰:
json
{
"tasks": {
"prepare": {
"dependsOn": ["^prepare"],
"outputs": ["dist/**"]
},
"dev": {
"dependsOn": ["prepare"],
"cache": false,
"persistent": true
}
}
}
Accessing Files Across Package Boundaries
用于并行任务且缓存失效的中转节点
typescript
// 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";
有些任务可以并行执行(不需要依赖的构建输出)但必须在依赖源代码变更时使缓存失效。
- 允许并行执行(快)
- 但缓存不正确 - 依赖源代码变更不会使缓存失效
中转节点解决了这两个问题:
json
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"my-task": { "dependsOn": ["transit"] }
}
}
任务创建了依赖关系但不匹配任何实际脚本,因此任务可以并行运行且缓存失效正确。
**如何识别需要此模式的任务:**寻找读取依赖源代码但不需要其构建输出的任务。
Too Many Root Dependencies
带环境变量的配置
json
// WRONG: App dependencies in root
{
"dependencies": {
"react": "^18",
"next": "^14"
}
}
// CORRECT: Only repo tools in root
{
"devDependencies": {
"turbo": "latest"
}
}
json
{
"globalEnv": ["NODE_ENV"],
"globalDependencies": [".env"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"env": ["API_URL", "DATABASE_URL"]
}
}
}
Common Task Configurations
参考索引
Standard Build Pipeline
配置
json
{
"$schema": "https://turborepo.dev/schema.v2.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Add a
task if you have tasks that need parallel execution with cache invalidation (see below).
| 文件 | 用途 |
|---|
| 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 | 常见配置错误 |
Dev Task with Pattern (for )
缓存
A
task with
and
in root turbo.json may look unusual but is
correct for workflows:
json
// 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:
- Packages (e.g., , ) have — one-shot type generation that completes quickly
- Apps override with for actual dev servers (Next.js, etc.)
- re-runs the one-shot package scripts when source files change, keeping types in sync
Intended usage: Run
(not
). Watch mode re-executes one-shot tasks on file changes while keeping persistent tasks running.
Alternative pattern: Use a separate task name like
or
for one-shot dependency builds to make the intent clearer:
json
{
"tasks": {
"prepare": {
"dependsOn": ["^prepare"],
"outputs": ["dist/**"]
},
"dev": {
"dependsOn": ["prepare"],
"cache": false,
"persistent": true
}
}
}
| File | 用途 |
|---|
| caching/RULE.md | 缓存工作原理、哈希输入 |
| caching/remote-cache.md | Vercel远程缓存、自托管、登录/链接 |
| caching/gotchas.md | 调试缓存未命中、--summarize、--dry |
Transit Nodes for Parallel Tasks with Cache Invalidation
环境变量
Some tasks can run in parallel (don't need built output from dependencies) but must invalidate cache when dependency source code changes.
- Forces sequential execution (slow)
The problem with (no dependencies):
- Allows parallel execution (fast)
- But cache is INCORRECT - changing dependency source won't invalidate cache
Transit Nodes solve both:
json
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"my-task": { "dependsOn": ["transit"] }
}
}
The
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.
| File | 用途 |
|---|
| environment/RULE.md | env、globalEnv、passThroughEnv |
| environment/modes.md | 严格模式 vs 宽松模式、框架自动推断 |
| environment/gotchas.md | .env文件、CI问题 |
With Environment Variables
过滤
json
{
"globalEnv": ["NODE_ENV"],
"globalDependencies": [".env"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"env": ["API_URL", "DATABASE_URL"]
}
}
}
| File | 用途 |
|---|
| filtering/RULE.md | --filter语法概述 |
| filtering/patterns.md | 常见过滤模式 |
| 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 | 用途 |
|---|
| ci/RULE.md | 通用CI原则 |
| ci/github-actions.md | 完整GitHub Actions设置 |
| ci/vercel.md | Vercel部署、turbo-ignore |
| ci/patterns.md | --affected、缓存策略 |
| 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 | 用途 |
|---|
| cli/RULE.md | turbo run基础用法 |
| cli/commands.md | turbo run标志、turbo-ignore、其他命令 |
Environment Variables
最佳实践
| 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 | 用途 |
|---|
| best-practices/RULE.md | 单体仓库最佳实践概述 |
| best-practices/structure.md | 仓库结构、工作区配置、TypeScript/ESLint设置 |
| best-practices/packages.md | 创建内部包、JIT vs 编译型、导出 |
| best-practices/dependencies.md | 依赖管理、安装、版本同步 |
| File | Purpose |
|---|
| filtering/RULE.md | --filter syntax overview |
| filtering/patterns.md | Common filter patterns |
| File | 用途 |
|---|
| watch/RULE.md | turbo watch、可中断任务、开发工作流 |
| 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 | 用途 |
|---|
| boundaries/RULE.md | 强制包隔离、基于标签的依赖规则 |
| 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 |
Boundaries (Experimental)
| File | Purpose |
|---|
| boundaries/RULE.md | Enforce package isolation, tag-based dependency rules |
This skill is based on the official Turborepo documentation at:
- Source: in the Turborepo repository
- Live: https://turborepo.dev/docs