Reorganize: Move all skills to skills/ folder
- Created skills/ directory - Moved 272 skills to skills/ subfolder - Kept agents/ at root level - Kept installation scripts and docs at root level Repository structure: - skills/ - All 272 skills from skills.sh - agents/ - Agent definitions - *.sh, *.ps1 - Installation scripts - README.md, etc. - Documentation Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
912
skills/turborepo/skill.md
Normal file
912
skills/turborepo/skill.md
Normal file
@@ -0,0 +1,912 @@
|
||||
---
|
||||
name: turborepo
|
||||
description: |
|
||||
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines,
|
||||
dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment
|
||||
variables, internal packages, monorepo structure/best practices, and boundaries.
|
||||
|
||||
Use when user: configures tasks/workflows/pipelines, creates packages, sets up
|
||||
monorepo, shares code between apps, runs changed/affected packages, debugs cache,
|
||||
or has apps/packages directories.
|
||||
---
|
||||
|
||||
# Turborepo Skill
|
||||
|
||||
Build system for JavaScript/TypeScript monorepos. Turborepo caches task outputs and runs tasks in parallel based on dependency graph.
|
||||
|
||||
## IMPORTANT: Package Tasks, Not Root Tasks
|
||||
|
||||
**DO NOT create Root Tasks. ALWAYS create package tasks.**
|
||||
|
||||
When creating tasks/scripts/pipelines, you MUST:
|
||||
|
||||
1. Add the script to each relevant package's `package.json`
|
||||
2. Register the task in root `turbo.json`
|
||||
3. Root `package.json` only delegates via `turbo run <task>`
|
||||
|
||||
**DO NOT** put task logic in root `package.json`. 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 (`//#taskname`) are ONLY for tasks that truly cannot exist in packages (rare).
|
||||
|
||||
## Secondary Rule: `turbo run` vs `turbo`
|
||||
|
||||
**Always use `turbo run` when the command is written into code:**
|
||||
|
||||
```json
|
||||
// package.json - ALWAYS "turbo run"
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo run build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
# CI workflows - ALWAYS "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.
|
||||
|
||||
## 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/README.md
|
||||
├─ Set up dev/watch tasks → references/configuration/tasks.md#persistent
|
||||
├─ Package-specific config → references/configuration/README.md#package-configurations
|
||||
└─ Global settings (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
|
||||
```
|
||||
|
||||
### "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/README.md
|
||||
```
|
||||
|
||||
**`--affected` is the primary way to run only changed packages.** It automatically compares against the default branch and includes dependents.
|
||||
|
||||
### "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
|
||||
```
|
||||
|
||||
### "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
|
||||
```
|
||||
|
||||
### "I need to set up 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
|
||||
```
|
||||
|
||||
### "I want to watch for changes during development"
|
||||
|
||||
```
|
||||
Watch mode?
|
||||
├─ Re-run tasks on change → turbo watch (references/watch/README.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`
|
||||
```
|
||||
|
||||
### "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/README.md
|
||||
├─ JIT vs Compiled packages → references/best-practices/packages.md#compilation-strategies
|
||||
└─ Sharing code between apps → references/best-practices/README.md#package-types
|
||||
```
|
||||
|
||||
### "How should I structure my monorepo?"
|
||||
|
||||
```
|
||||
Monorepo structure?
|
||||
├─ Standard layout (apps/, packages/) → references/best-practices/README.md
|
||||
├─ Package types (apps vs libraries) → references/best-practices/README.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/README.md
|
||||
```
|
||||
|
||||
### "I want to enforce architectural boundaries"
|
||||
|
||||
```
|
||||
Enforce boundaries?
|
||||
├─ Check for violations → turbo boundaries
|
||||
├─ Tag packages → references/boundaries/README.md#tags
|
||||
├─ Restrict which packages can import others → references/boundaries/README.md#rule-types
|
||||
└─ Prevent cross-package file imports → references/boundaries/README.md
|
||||
```
|
||||
|
||||
## Critical Anti-Patterns
|
||||
|
||||
### Using `turbo` Shorthand in Code
|
||||
|
||||
**`turbo run` is recommended in package.json scripts and CI pipelines.** The shorthand `turbo <task>` 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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
# WRONG - using shorthand in CI
|
||||
- run: turbo build --affected
|
||||
|
||||
# CORRECT
|
||||
- run: turbo run build --affected
|
||||
```
|
||||
|
||||
### Root Scripts Bypassing Turbo
|
||||
|
||||
Root `package.json` scripts MUST delegate to `turbo run`, 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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `prebuild` Scripts That Manually Build Dependencies
|
||||
|
||||
Scripts like `prebuild` 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:**
|
||||
|
||||
1. **If dependencies ARE declared** (e.g., `"@repo/types": "workspace:*"` in package.json), remove the `prebuild` script. Turbo's `dependsOn: ["^build"]` handles this automatically.
|
||||
|
||||
2. **If dependencies are NOT declared**, the `prebuild` exists because `^build` won't trigger without a dependency relationship. The fix is to:
|
||||
- Add the dependency to package.json: `"@repo/types": "workspace:*"`
|
||||
- Then remove the `prebuild` 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:** `^build` only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering.
|
||||
|
||||
### Overly Broad `globalDependencies`
|
||||
|
||||
`globalDependencies` 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/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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:**
|
||||
|
||||
- `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config
|
||||
- Task-level `env` / `inputs` - use when only specific tasks need it
|
||||
|
||||
### NOT an Anti-Pattern: Large `env` Arrays
|
||||
|
||||
A 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.
|
||||
|
||||
### Using `--parallel` Flag
|
||||
|
||||
The `--parallel` flag bypasses Turborepo's dependency graph. If tasks need parallel execution, configure `dependsOn` correctly instead.
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
### Package-Specific Task Overrides in Root turbo.json
|
||||
|
||||
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.
|
||||
|
||||
```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 `$TURBO_EXTENDS$` to inherit + extend arrays
|
||||
|
||||
**When to use `package#task` in root:**
|
||||
|
||||
- Single package needs a unique dependency (e.g., `"deploy": { "dependsOn": ["web#build"] }`)
|
||||
- Temporary override while migrating
|
||||
|
||||
See `references/configuration/README.md#package-configurations` for full details.
|
||||
|
||||
### Using `../` to Traverse Out of Package in `inputs`
|
||||
|
||||
Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` 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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Missing `outputs` for File-Producing Tasks
|
||||
|
||||
**Before flagging missing `outputs`, check what the task actually produces:**
|
||||
|
||||
1. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`)
|
||||
2. Determine if it writes files to disk or only outputs to stdout
|
||||
3. 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: `["dist/**"]`
|
||||
- tsc: `["dist/**"]` or custom `outDir`
|
||||
|
||||
**TypeScript `--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:
|
||||
|
||||
```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:
|
||||
|
||||
1. Check if `incremental` or `composite` is enabled in tsconfig
|
||||
2. Check `tsBuildInfoFile` for custom cache location (default: alongside `outDir` or in project root)
|
||||
3. If no incremental mode, `tsc --noEmit` produces no files
|
||||
|
||||
### `^build` vs `build` Confusion
|
||||
|
||||
```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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `.env` Files Not in Inputs
|
||||
|
||||
Turbo does NOT load `.env` 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.*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Root `.env` File in Monorepo
|
||||
|
||||
A `.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`:**
|
||||
|
||||
- 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 `globalEnv` to be explicit about what's shared, and document why.
|
||||
|
||||
### Strict Mode Filtering CI Variables
|
||||
|
||||
By default, Turborepo filters environment variables to only those in `env`/`globalEnv`. 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 `--env-mode=loose` (not recommended for production).
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 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';
|
||||
```
|
||||
|
||||
### Too Many Root Dependencies
|
||||
|
||||
```json
|
||||
// WRONG: App dependencies in root
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "^18",
|
||||
"next": "^14"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT: Only repo tools in root
|
||||
{
|
||||
"devDependencies": {
|
||||
"turbo": "latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 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 `transit` task if you have tasks that need parallel execution with cache invalidation (see below).
|
||||
|
||||
### Dev Task with `^dev` Pattern (for `turbo watch`)
|
||||
|
||||
A `dev` task with `dependsOn: ["^dev"]` and `persistent: false` in root turbo.json may look unusual but is **correct for `turbo watch` 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., `@acme/db`, `@acme/validators`) have `"dev": "tsc"` — one-shot type generation that completes quickly
|
||||
- **Apps** override with `persistent: 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 sync
|
||||
|
||||
**Intended 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:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"prepare": {
|
||||
"dependsOn": ["^prepare"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"dev": {
|
||||
"dependsOn": ["prepare"],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
**The problem with `dependsOn: ["^taskname"]`:**
|
||||
|
||||
- Forces sequential execution (slow)
|
||||
|
||||
**The problem with `dependsOn: []` (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 `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.
|
||||
|
||||
### With Environment Variables
|
||||
|
||||
```json
|
||||
{
|
||||
"globalEnv": ["NODE_ENV"],
|
||||
"globalDependencies": [".env"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"],
|
||||
"env": ["API_URL", "DATABASE_URL"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Reference Index
|
||||
|
||||
### Configuration
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| [configuration/README.md](./references/configuration/README.md) | turbo.json overview, Package Configurations |
|
||||
| [configuration/tasks.md](./references/configuration/tasks.md) | dependsOn, outputs, inputs, env, cache, persistent |
|
||||
| [configuration/global-options.md](./references/configuration/global-options.md) | globalEnv, globalDependencies, cacheDir, daemon, envMode |
|
||||
| [configuration/gotchas.md](./references/configuration/gotchas.md) | Common configuration mistakes |
|
||||
|
||||
### Caching
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------------------- | -------------------------------------------- |
|
||||
| [caching/README.md](./references/caching/README.md) | How caching works, hash inputs |
|
||||
| [caching/remote-cache.md](./references/caching/remote-cache.md) | Vercel Remote Cache, self-hosted, login/link |
|
||||
| [caching/gotchas.md](./references/caching/gotchas.md) | Debugging cache misses, --summarize, --dry |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------------------------------------------- | ----------------------------------------- |
|
||||
| [environment/README.md](./references/environment/README.md) | env, globalEnv, passThroughEnv |
|
||||
| [environment/modes.md](./references/environment/modes.md) | Strict vs Loose mode, framework inference |
|
||||
| [environment/gotchas.md](./references/environment/gotchas.md) | .env files, CI issues |
|
||||
|
||||
### Filtering
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------------------- | ------------------------ |
|
||||
| [filtering/README.md](./references/filtering/README.md) | --filter syntax overview |
|
||||
| [filtering/patterns.md](./references/filtering/patterns.md) | Common filter patterns |
|
||||
|
||||
### CI/CD
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------------- | ------------------------------- |
|
||||
| [ci/README.md](./references/ci/README.md) | General CI principles |
|
||||
| [ci/github-actions.md](./references/ci/github-actions.md) | Complete GitHub Actions setup |
|
||||
| [ci/vercel.md](./references/ci/vercel.md) | Vercel deployment, turbo-ignore |
|
||||
| [ci/patterns.md](./references/ci/patterns.md) | --affected, caching strategies |
|
||||
|
||||
### CLI
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------- | --------------------------------------------- |
|
||||
| [cli/README.md](./references/cli/README.md) | turbo run basics |
|
||||
| [cli/commands.md](./references/cli/commands.md) | turbo run flags, turbo-ignore, other commands |
|
||||
|
||||
### Best Practices
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| [best-practices/README.md](./references/best-practices/README.md) | Monorepo best practices overview |
|
||||
| [best-practices/structure.md](./references/best-practices/structure.md) | Repository structure, workspace config, TypeScript/ESLint setup |
|
||||
| [best-practices/packages.md](./references/best-practices/packages.md) | Creating internal packages, JIT vs Compiled, exports |
|
||||
| [best-practices/dependencies.md](./references/best-practices/dependencies.md) | Dependency management, installing, version sync |
|
||||
|
||||
### Watch Mode
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------- | ----------------------------------------------- |
|
||||
| [watch/README.md](./references/watch/README.md) | turbo watch, interruptible tasks, dev workflows |
|
||||
|
||||
### Boundaries (Experimental)
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------- |
|
||||
| [boundaries/README.md](./references/boundaries/README.md) | Enforce package isolation, tag-based dependency rules |
|
||||
|
||||
## Source Documentation
|
||||
|
||||
This skill is based on the official Turborepo documentation at:
|
||||
|
||||
- Source: `docs/site/content/docs/` in the Turborepo repository
|
||||
- Live: https://turborepo.dev/docs
|
||||
Reference in New Issue
Block a user