feat: Complete fleet — 94 skills across 10+ domains

Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-16 16:27:42 -06:00
parent 861b28b965
commit f5792c40be
1262 changed files with 212048 additions and 61 deletions

View File

@@ -0,0 +1,241 @@
# Monorepo Best Practices
Essential patterns for structuring and maintaining a healthy Turborepo monorepo.
## Repository Structure
### Standard Layout
```
my-monorepo/
├── apps/ # Application packages (deployable)
│ ├── web/
│ ├── docs/
│ └── api/
├── packages/ # Library packages (shared code)
│ ├── ui/
│ ├── utils/
│ └── config-*/ # Shared configs (eslint, typescript, etc.)
├── package.json # Root package.json (minimal deps)
├── turbo.json # Turborepo configuration
├── pnpm-workspace.yaml # (pnpm) or workspaces in package.json
└── pnpm-lock.yaml # Lockfile (required)
```
### Key Principles
1. **`apps/` for deployables**: Next.js sites, APIs, CLIs - things that get deployed
2. **`packages/` for libraries**: Shared code consumed by apps or other packages
3. **One purpose per package**: Each package should do one thing well
4. **No nested packages**: Don't put packages inside packages
## Package Types
### Application Packages (`apps/`)
- **Deployable**: These are the "endpoints" of your package graph
- **Not installed by other packages**: Apps shouldn't be dependencies of other packages
- **No shared code**: If code needs sharing, extract to `packages/`
```json
// apps/web/package.json
{
"name": "web",
"private": true,
"dependencies": {
"@repo/ui": "workspace:*",
"next": "latest"
}
}
```
### Library Packages (`packages/`)
- **Shared code**: Utilities, components, configs
- **Namespaced names**: Use `@repo/` or `@yourorg/` prefix
- **Clear exports**: Define what the package exposes
```json
// packages/ui/package.json
{
"name": "@repo/ui",
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx"
}
}
```
## Package Compilation Strategies
### Just-in-Time (Simplest)
Export TypeScript directly; let the app's bundler compile it.
```json
{
"name": "@repo/ui",
"exports": {
"./button": "./src/button.tsx"
}
}
```
**Pros**: Zero build config, instant changes
**Cons**: Can't cache builds, requires app bundler support
### Compiled (Recommended for Libraries)
Package compiles itself with `tsc` or bundler.
```json
{
"name": "@repo/ui",
"exports": {
"./button": {
"types": "./src/button.tsx",
"default": "./dist/button.js"
}
},
"scripts": {
"build": "tsc"
}
}
```
**Pros**: Cacheable by Turborepo, works everywhere
**Cons**: More configuration
## Dependency Management
### Install Where Used
Install dependencies in the package that uses them, not the root.
```bash
# Good: Install in the package that needs it
pnpm add lodash --filter=@repo/utils
# Avoid: Installing everything at root
pnpm add lodash -w # Only for repo-level tools
```
### Root Dependencies
Only these belong in root `package.json`:
- `turbo` - The build system
- `husky`, `lint-staged` - Git hooks
- Repository-level tooling
### Internal Dependencies
Use workspace protocol for internal packages:
```json
// pnpm/bun
{ "@repo/ui": "workspace:*" }
// npm/yarn
{ "@repo/ui": "*" }
```
## Exports Best Practices
### Use `exports` Field (Not `main`)
```json
{
"exports": {
".": "./src/index.ts",
"./button": "./src/button.tsx",
"./utils": "./src/utils.ts"
}
}
```
### Avoid Barrel Files
Don't create `index.ts` files that re-export everything:
```typescript
// BAD: packages/ui/src/index.ts
export * from './button';
export * from './card';
export * from './modal';
// ... imports everything even if you need one thing
// GOOD: Direct exports in package.json
{
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx"
}
}
```
### Namespace Your Packages
```json
// Good
{ "name": "@repo/ui" }
{ "name": "@acme/utils" }
// Avoid (conflicts with npm registry)
{ "name": "ui" }
{ "name": "utils" }
```
## Common Anti-Patterns
### Accessing Files Across Package Boundaries
```typescript
// BAD: Reaching into another package
import { Button } from '../../packages/ui/src/button';
// GOOD: Install and import properly
import { Button } from '@repo/ui/button';
```
### Shared Code in Apps
```
// BAD
apps/
web/
shared/ # This should be a package!
utils.ts
// GOOD
packages/
utils/ # Proper shared package
src/utils.ts
```
### Too Many Root Dependencies
```json
// BAD: Root has app dependencies
{
"dependencies": {
"react": "^18",
"next": "^14",
"lodash": "^4"
}
}
// GOOD: Root only has repo tools
{
"devDependencies": {
"turbo": "latest",
"husky": "latest"
}
}
```
## See Also
- [structure.md](./structure.md) - Detailed repository structure patterns
- [packages.md](./packages.md) - Creating and managing internal packages
- [dependencies.md](./dependencies.md) - Dependency management strategies

View File

@@ -0,0 +1,246 @@
# Dependency Management
Best practices for managing dependencies in a Turborepo monorepo.
## Core Principle: Install Where Used
Dependencies belong in the package that uses them, not the root.
```bash
# Good: Install in specific package
pnpm add react --filter=@repo/ui
pnpm add next --filter=web
# Avoid: Installing in root
pnpm add react -w # Only for repo-level tools!
```
## Benefits of Local Installation
### 1. Clarity
Each package's `package.json` lists exactly what it needs:
```json
// packages/ui/package.json
{
"dependencies": {
"react": "^18.0.0",
"class-variance-authority": "^0.7.0"
}
}
```
### 2. Flexibility
Different packages can use different versions when needed:
```json
// packages/legacy-ui/package.json
{ "dependencies": { "react": "^17.0.0" } }
// packages/ui/package.json
{ "dependencies": { "react": "^18.0.0" } }
```
### 3. Better Caching
Installing in root changes workspace lockfile, invalidating all caches.
### 4. Pruning Support
`turbo prune` can remove unused dependencies for Docker images.
## What Belongs in Root
Only repository-level tools:
```json
// Root package.json
{
"devDependencies": {
"turbo": "latest",
"husky": "^8.0.0",
"lint-staged": "^15.0.0"
}
}
```
**NOT** application dependencies:
- react, next, express
- lodash, axios, zod
- Testing libraries (unless truly repo-wide)
## Installing Dependencies
### Single Package
```bash
# pnpm
pnpm add lodash --filter=@repo/utils
# npm
npm install lodash --workspace=@repo/utils
# yarn
yarn workspace @repo/utils add lodash
# bun
cd packages/utils && bun add lodash
```
### Multiple Packages
```bash
# pnpm
pnpm add jest --save-dev --filter=web --filter=@repo/ui
# npm
npm install jest --save-dev --workspace=web --workspace=@repo/ui
# yarn (v2+)
yarn workspaces foreach -R --from '{web,@repo/ui}' add jest --dev
```
### Internal Packages
```bash
# pnpm
pnpm add @repo/ui --filter=web
# This updates package.json:
{
"dependencies": {
"@repo/ui": "workspace:*"
}
}
```
## Keeping Versions in Sync
### Option 1: Tooling
```bash
# syncpack - Check and fix version mismatches
npx syncpack list-mismatches
npx syncpack fix-mismatches
# manypkg - Similar functionality
npx @manypkg/cli check
npx @manypkg/cli fix
# sherif - Rust-based, very fast
npx sherif
```
### Option 2: Package Manager Commands
```bash
# pnpm - Update everywhere
pnpm up --recursive typescript@latest
# npm - Update in all workspaces
npm install typescript@latest --workspaces
```
### Option 3: pnpm Catalogs (pnpm 9.5+)
```yaml
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
catalog:
react: ^18.2.0
typescript: ^5.3.0
```
```json
// Any package.json
{
"dependencies": {
"react": "catalog:" // Uses version from catalog
}
}
```
## Internal vs External Dependencies
### Internal (Workspace)
```json
// pnpm/bun
{ "@repo/ui": "workspace:*" }
// npm/yarn
{ "@repo/ui": "*" }
```
Turborepo understands these relationships and orders builds accordingly.
### External (npm Registry)
```json
{ "lodash": "^4.17.21" }
```
Standard semver versioning from npm.
## Peer Dependencies
For library packages that expect the consumer to provide dependencies:
```json
// packages/ui/package.json
{
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"react": "^18.0.0", // For development/testing
"react-dom": "^18.0.0"
}
}
```
## Common Issues
### "Module not found"
1. Check the dependency is installed in the right package
2. Run `pnpm install` / `npm install` to update lockfile
3. Check exports are defined in the package
### Version Conflicts
Packages can use different versions - this is a feature, not a bug. But if you need consistency:
1. Use tooling (syncpack, manypkg)
2. Use pnpm catalogs
3. Create a lint rule
### Hoisting Issues
Some tools expect dependencies in specific locations. Use package manager config:
```yaml
# .npmrc (pnpm)
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
```
## Lockfile
**Required** for:
- Reproducible builds
- Turborepo dependency analysis
- Cache correctness
```bash
# Commit your lockfile!
git add pnpm-lock.yaml # or package-lock.json, yarn.lock
```

View File

@@ -0,0 +1,335 @@
# Creating Internal Packages
How to create and structure internal packages in your monorepo.
## Package Creation Checklist
1. Create directory in `packages/`
2. Add `package.json` with name and exports
3. Add source code in `src/`
4. Add `tsconfig.json` if using TypeScript
5. Install as dependency in consuming packages
6. Run package manager install to update lockfile
## Package Compilation Strategies
### Just-in-Time (JIT)
Export TypeScript directly. The consuming app's bundler compiles it.
```json
// packages/ui/package.json
{
"name": "@repo/ui",
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx"
},
"scripts": {
"lint": "eslint .",
"check-types": "tsc --noEmit"
}
}
```
**When to use:**
- Apps use modern bundlers (Turbopack, webpack, Vite)
- You want minimal configuration
- Build times are acceptable without caching
**Limitations:**
- No Turborepo cache for the package itself
- Consumer must support TypeScript compilation
- Can't use TypeScript `paths` (use Node.js subpath imports instead)
### Compiled
Package handles its own compilation.
```json
// packages/ui/package.json
{
"name": "@repo/ui",
"exports": {
"./button": {
"types": "./src/button.tsx",
"default": "./dist/button.js"
}
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch"
}
}
```
```json
// packages/ui/tsconfig.json
{
"extends": "@repo/typescript-config/library.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
```
**When to use:**
- You want Turborepo to cache builds
- Package will be used by non-bundler tools
- You need maximum compatibility
**Remember:** Add `dist/**` to turbo.json outputs!
## Defining Exports
### Multiple Entrypoints
```json
{
"exports": {
".": "./src/index.ts", // @repo/ui
"./button": "./src/button.tsx", // @repo/ui/button
"./card": "./src/card.tsx", // @repo/ui/card
"./hooks": "./src/hooks/index.ts" // @repo/ui/hooks
}
}
```
### Conditional Exports (Compiled)
```json
{
"exports": {
"./button": {
"types": "./src/button.tsx",
"import": "./dist/button.mjs",
"require": "./dist/button.cjs",
"default": "./dist/button.js"
}
}
}
```
## Installing Internal Packages
### Add to Consuming Package
```json
// apps/web/package.json
{
"dependencies": {
"@repo/ui": "workspace:*" // pnpm/bun
// "@repo/ui": "*" // npm/yarn
}
}
```
### Run Install
```bash
pnpm install # Updates lockfile with new dependency
```
### Import and Use
```typescript
// apps/web/src/page.tsx
import { Button } from '@repo/ui/button';
export default function Page() {
return <Button>Click me</Button>;
}
```
## One Purpose Per Package
### Good Examples
```
packages/
├── ui/ # Shared UI components
├── utils/ # General utilities
├── auth/ # Authentication logic
├── database/ # Database client/schemas
├── eslint-config/ # ESLint configuration
├── typescript-config/ # TypeScript configuration
└── api-client/ # Generated API client
```
### Avoid Mega-Packages
```
// BAD: One package for everything
packages/
└── shared/
├── components/
├── utils/
├── hooks/
├── types/
└── api/
// GOOD: Separate by purpose
packages/
├── ui/ # Components
├── utils/ # Utilities
├── hooks/ # React hooks
├── types/ # Shared TypeScript types
└── api-client/ # API utilities
```
## Config Packages
### TypeScript Config
```json
// packages/typescript-config/package.json
{
"name": "@repo/typescript-config",
"exports": {
"./base.json": "./base.json",
"./nextjs.json": "./nextjs.json",
"./library.json": "./library.json"
}
}
```
### ESLint Config
```json
// packages/eslint-config/package.json
{
"name": "@repo/eslint-config",
"exports": {
"./base": "./base.js",
"./next": "./next.js"
},
"dependencies": {
"eslint": "^8.0.0",
"eslint-config-next": "latest"
}
}
```
## Common Mistakes
### Forgetting to Export
```json
// BAD: No exports defined
{
"name": "@repo/ui"
}
// GOOD: Clear exports
{
"name": "@repo/ui",
"exports": {
"./button": "./src/button.tsx"
}
}
```
### Wrong Workspace Syntax
```json
// pnpm/bun
{ "@repo/ui": "workspace:*" } // Correct
// npm/yarn
{ "@repo/ui": "*" } // Correct
{ "@repo/ui": "workspace:*" } // Wrong for npm/yarn!
```
### Missing from turbo.json Outputs
```json
// Package builds to dist/, but turbo.json doesn't know
{
"tasks": {
"build": {
"outputs": [".next/**"] // Missing dist/**!
}
}
}
// Correct
{
"tasks": {
"build": {
"outputs": [".next/**", "dist/**"]
}
}
}
```
## TypeScript Best Practices
### Use Node.js Subpath Imports (Not `paths`)
TypeScript `compilerOptions.paths` breaks with JIT packages. Use Node.js subpath imports instead (TypeScript 5.4+).
**JIT Package:**
```json
// packages/ui/package.json
{
"imports": {
"#*": "./src/*"
}
}
```
```typescript
// packages/ui/button.tsx
import { MY_STRING } from "#utils.ts"; // Uses .ts extension
```
**Compiled Package:**
```json
// packages/ui/package.json
{
"imports": {
"#*": "./dist/*"
}
}
```
```typescript
// packages/ui/button.tsx
import { MY_STRING } from "#utils.js"; // Uses .js extension
```
### Use `tsc` for Internal Packages
For internal packages, prefer `tsc` over bundlers. Bundlers can mangle code before it reaches your app's bundler, causing hard-to-debug issues.
### Enable Go-to-Definition
For Compiled Packages, enable declaration maps:
```json
// tsconfig.json
{
"compilerOptions": {
"declaration": true,
"declarationMap": true
}
}
```
This creates `.d.ts` and `.d.ts.map` files for IDE navigation.
### No Root tsconfig.json Needed
Each package should have its own `tsconfig.json`. A root one causes all tasks to miss cache when changed. Only use root `tsconfig.json` for non-package scripts.
### Avoid TypeScript Project References
They add complexity and another caching layer. Turborepo handles dependencies better.

View File

@@ -0,0 +1,269 @@
# Repository Structure
Detailed guidance on structuring a Turborepo monorepo.
## Workspace Configuration
### pnpm (Recommended)
```yaml
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
```
### npm/yarn/bun
```json
// package.json
{
"workspaces": ["apps/*", "packages/*"]
}
```
## Root package.json
```json
{
"name": "my-monorepo",
"private": true,
"packageManager": "pnpm@9.0.0",
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}
```
Key points:
- `private: true` - Prevents accidental publishing
- `packageManager` - Enforces consistent package manager version
- **Scripts only delegate to `turbo run`** - No actual build logic here!
- Minimal devDependencies (just turbo and repo tools)
## Always Prefer Package Tasks
**Always use package tasks. Only use Root Tasks if you cannot succeed with package tasks.**
```json
// packages/web/package.json
{
"scripts": {
"build": "next build",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
// packages/api/package.json
{
"scripts": {
"build": "tsc",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
```
Package tasks enable Turborepo to:
1. **Parallelize** - Run `web#lint` and `api#lint` simultaneously
2. **Cache individually** - Each package's task output is cached separately
3. **Filter precisely** - Run `turbo run test --filter=web` for just one package
**Root Tasks are a fallback** for tasks that truly cannot run per-package:
```json
// AVOID unless necessary - sequential, not parallelized, can't filter
{
"scripts": {
"lint": "eslint apps/web && eslint apps/api && eslint packages/ui"
}
}
```
## Root turbo.json
```json
{
"$schema": "https://turborepo.dev/schema.v2.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"lint": {},
"test": {
"dependsOn": ["build"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
```
## Directory Organization
### Grouping Packages
You can group packages by adding more workspace paths:
```yaml
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
- "packages/config/*" # Grouped configs
- "packages/features/*" # Feature packages
```
This allows:
```
packages/
├── ui/
├── utils/
├── config/
│ ├── eslint/
│ ├── typescript/
│ └── tailwind/
└── features/
├── auth/
└── payments/
```
### What NOT to Do
```yaml
# BAD: Nested wildcards cause ambiguous behavior
packages:
- "packages/**" # Don't do this!
```
## Package Anatomy
### Minimum Required Files
```
packages/ui/
├── package.json # Required: Makes it a package
├── src/ # Source code
│ └── button.tsx
└── tsconfig.json # TypeScript config (if using TS)
```
### package.json Requirements
```json
{
"name": "@repo/ui", // Unique, namespaced name
"version": "0.0.0", // Version (can be 0.0.0 for internal)
"private": true, // Prevents accidental publishing
"exports": { // Entry points
"./button": "./src/button.tsx"
}
}
```
## TypeScript Configuration
### Shared Base Config
Create a shared TypeScript config package:
```
packages/
└── typescript-config/
├── package.json
├── base.json
├── nextjs.json
└── library.json
```
```json
// packages/typescript-config/base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "bundler",
"module": "ESNext",
"target": "ES2022"
}
}
```
### Extending in Packages
```json
// packages/ui/tsconfig.json
{
"extends": "@repo/typescript-config/library.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
```
### No Root tsconfig.json
You likely don't need a `tsconfig.json` in the workspace root. Each package should have its own config extending from the shared config package.
## ESLint Configuration
### Shared Config Package
```
packages/
└── eslint-config/
├── package.json
├── base.js
├── next.js
└── library.js
```
```json
// packages/eslint-config/package.json
{
"name": "@repo/eslint-config",
"exports": {
"./base": "./base.js",
"./next": "./next.js",
"./library": "./library.js"
}
}
```
### Using in Packages
```js
// apps/web/.eslintrc.js
module.exports = {
extends: ["@repo/eslint-config/next"],
};
```
## Lockfile
A lockfile is **required** for:
- Reproducible builds
- Turborepo to understand package dependencies
- Cache correctness
Without a lockfile, you'll see unpredictable behavior.

View File

@@ -0,0 +1,126 @@
# Boundaries
**Experimental feature** - See [RFC](https://github.com/vercel/turborepo/discussions/9435)
Full docs: https://turborepo.dev/docs/reference/boundaries
Boundaries enforce package isolation by detecting:
1. Imports of files outside the package's directory
2. Imports of packages not declared in `package.json` dependencies
## Usage
```bash
turbo boundaries
```
Run this to check for workspace violations across your monorepo.
## Tags
Tags allow you to create rules for which packages can depend on each other.
### Adding Tags to a Package
```json
// packages/ui/turbo.json
{
"tags": ["internal"]
}
```
### Configuring Tag Rules
Rules go in root `turbo.json`:
```json
// turbo.json
{
"boundaries": {
"tags": {
"public": {
"dependencies": {
"deny": ["internal"]
}
}
}
}
}
```
This prevents `public`-tagged packages from importing `internal`-tagged packages.
### Rule Types
**Allow-list approach** (only allow specific tags):
```json
{
"boundaries": {
"tags": {
"public": {
"dependencies": {
"allow": ["public"]
}
}
}
}
}
```
**Deny-list approach** (block specific tags):
```json
{
"boundaries": {
"tags": {
"public": {
"dependencies": {
"deny": ["internal"]
}
}
}
}
}
```
**Restrict dependents** (who can import this package):
```json
{
"boundaries": {
"tags": {
"private": {
"dependents": {
"deny": ["public"]
}
}
}
}
}
```
### Using Package Names
Package names work in place of tags:
```json
{
"boundaries": {
"tags": {
"private": {
"dependents": {
"deny": ["@repo/my-pkg"]
}
}
}
}
}
```
## Key Points
- Rules apply transitively (dependencies of dependencies)
- Helps enforce architectural boundaries at scale
- Catches violations before runtime/build errors

View File

@@ -0,0 +1,107 @@
# How Turborepo Caching Works
Turborepo's core principle: **never do the same work twice**.
## The Cache Equation
```
fingerprint(inputs) → stored outputs
```
If inputs haven't changed, restore outputs from cache instead of re-running the task.
## What Determines the Cache Key
### Global Hash Inputs
These affect ALL tasks in the repo:
- `package-lock.json` / `yarn.lock` / `pnpm-lock.yaml`
- Files listed in `globalDependencies`
- Environment variables in `globalEnv`
- `turbo.json` configuration
```json
{
"globalDependencies": [".env", "tsconfig.base.json"],
"globalEnv": ["CI", "NODE_ENV"]
}
```
### Task Hash Inputs
These affect specific tasks:
- All files in the package (unless filtered by `inputs`)
- `package.json` contents
- Environment variables in task's `env` key
- Task configuration (command, outputs, dependencies)
- Hashes of dependent tasks (`dependsOn`)
```json
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["src/**", "package.json", "tsconfig.json"],
"env": ["API_URL"]
}
}
}
```
## What Gets Cached
1. **File outputs** - files/directories specified in `outputs`
2. **Task logs** - stdout/stderr for replay on cache hit
```json
{
"tasks": {
"build": {
"outputs": ["dist/**", ".next/**"]
}
}
}
```
## Local Cache Location
```
.turbo/cache/
├── <hash1>.tar.zst # compressed outputs
├── <hash2>.tar.zst
└── ...
```
Add `.turbo` to `.gitignore`.
## Cache Restoration
On cache hit, Turborepo:
1. Extracts archived outputs to their original locations
2. Replays the logged stdout/stderr
3. Reports the task as cached (shows `FULL TURBO` in output)
## Example Flow
```bash
# First run - executes build, caches result
turbo build
# → packages/ui: cache miss, executing...
# → packages/web: cache miss, executing...
# Second run - same inputs, restores from cache
turbo build
# → packages/ui: cache hit, replaying output
# → packages/web: cache hit, replaying output
# → FULL TURBO
```
## Key Points
- Cache is content-addressed (based on input hash, not timestamps)
- Empty `outputs` array means task runs but nothing is cached
- Tasks without `outputs` key cache nothing (use `"outputs": []` to be explicit)
- Cache is invalidated when ANY input changes

View File

@@ -0,0 +1,169 @@
# Debugging Cache Issues
## Diagnostic Tools
### `--summarize`
Generates a JSON file with all hash inputs. Compare two runs to find differences.
```bash
turbo build --summarize
# Creates .turbo/runs/<run-id>.json
```
The summary includes:
- Global hash and its inputs
- Per-task hashes and their inputs
- Environment variables that affected the hash
**Comparing runs:**
```bash
# Run twice, compare the summaries
diff .turbo/runs/<first-run>.json .turbo/runs/<second-run>.json
```
### `--dry` / `--dry=json`
See what would run without executing anything:
```bash
turbo build --dry
turbo build --dry=json # machine-readable output
```
Shows cache status for each task without running them.
### `--force`
Skip reading cache, re-execute all tasks:
```bash
turbo build --force
```
Useful to verify tasks actually work (not just cached results).
## Unexpected Cache Misses
**Symptom:** Task runs when you expected a cache hit.
### Environment Variable Changed
Check if an env var in the `env` key changed:
```json
{
"tasks": {
"build": {
"env": ["API_URL", "NODE_ENV"]
}
}
}
```
Different `API_URL` between runs = cache miss.
### .env File Changed
`.env` files aren't tracked by default. Add to `inputs`:
```json
{
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.local"]
}
}
}
```
Or use `globalDependencies` for repo-wide env files:
```json
{
"globalDependencies": [".env"]
}
```
### Lockfile Changed
Installing/updating packages changes the global hash.
### Source Files Changed
Any file in the package (or in `inputs`) triggers a miss.
### turbo.json Changed
Config changes invalidate the global hash.
## Incorrect Cache Hits
**Symptom:** Cached output is stale/wrong.
### Missing Environment Variable
Task uses an env var not listed in `env`:
```javascript
// build.js
const apiUrl = process.env.API_URL; // not tracked!
```
Fix: add to task config:
```json
{
"tasks": {
"build": {
"env": ["API_URL"]
}
}
}
```
### Missing File in Inputs
Task reads a file outside default inputs:
```json
{
"tasks": {
"build": {
"inputs": [
"$TURBO_DEFAULT$",
"../../shared-config.json" // file outside package
]
}
}
}
```
## Useful Flags
```bash
# Only show output for cache misses
turbo build --output-logs=new-only
# Show output for everything (debugging)
turbo build --output-logs=full
# See why tasks are running
turbo build --verbosity=2
```
## Quick Checklist
Cache miss when expected hit:
1. Run with `--summarize`, compare with previous run
2. Check env vars with `--dry=json`
3. Look for lockfile/config changes in git
Cache hit when expected miss:
1. Verify env var is in `env` array
2. Verify file is in `inputs` array
3. Check if file is outside package directory

View File

@@ -0,0 +1,127 @@
# Remote Caching
Share cache artifacts across your team and CI pipelines.
## Benefits
- Team members get cache hits from each other's work
- CI gets cache hits from local development (and vice versa)
- Dramatically faster CI runs after first build
- No more "works on my machine" rebuilds
## Vercel Remote Cache
Free, zero-config when deploying on Vercel. For local dev and other CI:
### Local Development Setup
```bash
# Authenticate with Vercel
npx turbo login
# Link repo to your Vercel team
npx turbo link
```
This creates `.turbo/config.json` with your team info (gitignored by default).
### CI Setup
Set these environment variables:
```bash
TURBO_TOKEN=<your-token>
TURBO_TEAM=<your-team-slug>
```
Get your token from Vercel dashboard → Settings → Tokens.
**GitHub Actions example:**
```yaml
- name: Build
run: npx turbo build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```
## Configuration in turbo.json
```json
{
"remoteCache": {
"enabled": true,
"signature": false
}
}
```
Options:
- `enabled`: toggle remote cache (default: true when authenticated)
- `signature`: require artifact signing (default: false)
## Artifact Signing
Verify cache artifacts haven't been tampered with:
```bash
# Set a secret key (use same key across all environments)
export TURBO_REMOTE_CACHE_SIGNATURE_KEY="your-secret-key"
```
Enable in config:
```json
{
"remoteCache": {
"signature": true
}
}
```
Signed artifacts can only be restored if the signature matches.
## Self-Hosted Options
Community implementations for running your own cache server:
- **turbo-remote-cache** (Node.js) - supports S3, GCS, Azure
- **turborepo-remote-cache** (Go) - lightweight, S3-compatible
- **ducktape** (Rust) - high-performance option
Configure with environment variables:
```bash
TURBO_API=https://your-cache-server.com
TURBO_TOKEN=your-auth-token
TURBO_TEAM=your-team
```
## Cache Behavior Control
```bash
# Disable remote cache for a run
turbo build --remote-cache-read-only # read but don't write
turbo build --no-cache # skip cache entirely
# Environment variable alternative
TURBO_REMOTE_ONLY=true # only use remote, skip local
```
## Debugging Remote Cache
```bash
# Verbose output shows cache operations
turbo build --verbosity=2
# Check if remote cache is configured
turbo config
```
Look for:
- "Remote caching enabled" in output
- Upload/download messages during runs
- "cache hit, replaying output" with remote cache indicator

View File

@@ -0,0 +1,79 @@
# CI/CD with Turborepo
General principles for running Turborepo in continuous integration environments.
## Core Principles
### Always Use `turbo run` in CI
**Never use the `turbo <tasks>` shorthand in CI or scripts.** Always use `turbo run`:
```bash
# CORRECT - Always use in CI, package.json, scripts
turbo run build test lint
# WRONG - Shorthand is only for one-off terminal commands
turbo build test lint
```
The shorthand `turbo <tasks>` is only for one-off invocations typed directly in terminal by humans or agents. Anywhere the command is written into code (CI, package.json, scripts), use `turbo run`.
### Enable Remote Caching
Remote caching dramatically speeds up CI by sharing cached artifacts across runs.
Required environment variables:
```bash
TURBO_TOKEN=your_vercel_token
TURBO_TEAM=your_team_slug
```
### Use --affected for PR Builds
The `--affected` flag only runs tasks for packages changed since the base branch:
```bash
turbo run build test --affected
```
This requires Git history to compute what changed.
## Git History Requirements
### Fetch Depth
`--affected` needs access to the merge base. Shallow clones break this.
```yaml
# GitHub Actions
- uses: actions/checkout@v4
with:
fetch-depth: 2 # Minimum for --affected
# Use 0 for full history if merge base is far
```
### Why Shallow Clones Break --affected
Turborepo compares the current HEAD to the merge base with `main`. If that commit isn't fetched, `--affected` falls back to running everything.
For PRs with many commits, consider:
```yaml
fetch-depth: 0 # Full history
```
## Environment Variables Reference
| Variable | Purpose |
| ------------------- | ------------------------------------ |
| `TURBO_TOKEN` | Vercel access token for remote cache |
| `TURBO_TEAM` | Your Vercel team slug |
| `TURBO_REMOTE_ONLY` | Skip local cache, use remote only |
| `TURBO_LOG_ORDER` | Set to `grouped` for cleaner CI logs |
## See Also
- [github-actions.md](./github-actions.md) - GitHub Actions setup
- [vercel.md](./vercel.md) - Vercel deployment
- [patterns.md](./patterns.md) - CI optimization patterns

View File

@@ -0,0 +1,162 @@
# GitHub Actions
Complete setup guide for Turborepo with GitHub Actions.
## Basic Workflow Structure
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Build and Test
run: turbo run build test lint
```
## Package Manager Setup
### pnpm
```yaml
- uses: pnpm/action-setup@v3
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
```
### Yarn
```yaml
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'yarn'
- run: yarn install --frozen-lockfile
```
### Bun
```yaml
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: bun install --frozen-lockfile
```
## Remote Cache Setup
### 1. Create Vercel Access Token
1. Go to [Vercel Dashboard](https://vercel.com/account/tokens)
2. Create a new token with appropriate scope
3. Copy the token value
### 2. Add Secrets and Variables
In your GitHub repository settings:
**Secrets** (Settings > Secrets and variables > Actions > Secrets):
- `TURBO_TOKEN`: Your Vercel access token
**Variables** (Settings > Secrets and variables > Actions > Variables):
- `TURBO_TEAM`: Your Vercel team slug
### 3. Add to Workflow
```yaml
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```
## Alternative: actions/cache
If you can't use remote cache, cache Turborepo's local cache directory:
```yaml
- uses: actions/cache@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ hashFiles('**/turbo.json', '**/package-lock.json') }}
restore-keys: |
turbo-${{ runner.os }}-
```
Note: This is less effective than remote cache since it's per-branch.
## Complete Example
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: pnpm/action-setup@v3
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: turbo run build --affected
- name: Test
run: turbo run test --affected
- name: Lint
run: turbo run lint --affected
```

View File

@@ -0,0 +1,145 @@
# CI Optimization Patterns
Strategies for efficient CI/CD with Turborepo.
## PR vs Main Branch Builds
### PR Builds: Only Affected
Test only what changed in the PR:
```yaml
- name: Test (PR)
if: github.event_name == 'pull_request'
run: turbo run build test --affected
```
### Main Branch: Full Build
Ensure complete validation on merge:
```yaml
- name: Test (Main)
if: github.ref == 'refs/heads/main'
run: turbo run build test
```
## Custom Git Ranges with --filter
For advanced scenarios, use `--filter` with git refs:
```bash
# Changes since specific commit
turbo run test --filter="...[abc123]"
# Changes between refs
turbo run test --filter="...[main...HEAD]"
# Changes in last 3 commits
turbo run test --filter="...[HEAD~3]"
```
## Caching Strategies
### Remote Cache (Recommended)
Best performance - shared across all CI runs and developers:
```yaml
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```
### actions/cache Fallback
When remote cache isn't available:
```yaml
- uses: actions/cache@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ github.ref }}-
turbo-${{ runner.os }}-
```
Limitations:
- Cache is branch-scoped
- PRs restore from base branch cache
- Less efficient than remote cache
## Matrix Builds
Test across Node versions:
```yaml
strategy:
matrix:
node: [18, 20, 22]
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: turbo run test
```
## Parallelizing Across Jobs
Split tasks into separate jobs:
```yaml
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: turbo run lint --affected
test:
runs-on: ubuntu-latest
steps:
- run: turbo run test --affected
build:
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- run: turbo run build
```
### Cache Considerations
When parallelizing:
- Each job has separate cache writes
- Remote cache handles this automatically
- With actions/cache, use unique keys per job to avoid conflicts
```yaml
- uses: actions/cache@v4
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ github.job }}-${{ github.sha }}
```
## Conditional Tasks
Skip expensive tasks on draft PRs:
```yaml
- name: E2E Tests
if: github.event.pull_request.draft == false
run: turbo run test:e2e --affected
```
Or require label for full test:
```yaml
- name: Full Test Suite
if: contains(github.event.pull_request.labels.*.name, 'full-test')
run: turbo run test
```

View File

@@ -0,0 +1,103 @@
# Vercel Deployment
Turborepo integrates seamlessly with Vercel for monorepo deployments.
## Remote Cache
Remote caching is **automatically enabled** when deploying to Vercel. No configuration needed - Vercel detects Turborepo and enables caching.
This means:
- No `TURBO_TOKEN` or `TURBO_TEAM` setup required on Vercel
- Cache is shared across all deployments
- Preview and production builds benefit from cache
## turbo-ignore
Skip unnecessary builds when a package hasn't changed using `turbo-ignore`.
### Installation
```bash
npx turbo-ignore
```
Or install globally in your project:
```bash
pnpm add -D turbo-ignore
```
### Setup in Vercel
1. Go to your project in Vercel Dashboard
2. Navigate to Settings > Git > Ignored Build Step
3. Select "Custom" and enter:
```bash
npx turbo-ignore
```
### How It Works
`turbo-ignore` checks if the current package (or its dependencies) changed since the last successful deployment:
1. Compares current commit to last deployed commit
2. Uses Turborepo's dependency graph
3. Returns exit code 0 (skip) if no changes
4. Returns exit code 1 (build) if changes detected
### Options
```bash
# Check specific package
npx turbo-ignore web
# Use specific comparison ref
npx turbo-ignore --fallback=HEAD~1
# Verbose output
npx turbo-ignore --verbose
```
## Environment Variables
Set environment variables in Vercel Dashboard:
1. Go to Project Settings > Environment Variables
2. Add variables for each environment (Production, Preview, Development)
Common variables:
- `DATABASE_URL`
- `API_KEY`
- Package-specific config
## Monorepo Root Directory
For monorepos, set the root directory in Vercel:
1. Project Settings > General > Root Directory
2. Set to the package path (e.g., `apps/web`)
Vercel automatically:
- Installs dependencies from monorepo root
- Runs build from the package directory
- Detects framework settings
## Build Command
Vercel auto-detects `turbo run build` when `turbo.json` exists at root.
Override if needed:
```bash
turbo run build --filter=web
```
Or for production-only optimizations:
```bash
turbo run build --filter=web --env-mode=strict
```

View File

@@ -0,0 +1,100 @@
# turbo run
The primary command for executing tasks across your monorepo.
## Basic Usage
```bash
# Full form (use in CI, package.json, scripts)
turbo run <tasks>
# Shorthand (only for one-off terminal invocations)
turbo <tasks>
```
## When to Use `turbo run` vs `turbo`
**Always use `turbo run` when the command is written into code:**
- `package.json` scripts
- CI/CD workflows (GitHub Actions, etc.)
- Shell scripts
- Documentation
- Any static/committed configuration
**Only use `turbo` (shorthand) for:**
- One-off commands typed directly in terminal
- Ad-hoc invocations by humans or agents
```json
// package.json - ALWAYS use "turbo run"
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test"
}
}
```
```yaml
# CI workflow - ALWAYS use "turbo run"
- run: turbo run build --affected
- run: turbo run test --affected
```
```bash
# Terminal one-off - shorthand OK
turbo build --filter=web
```
## Running Tasks
Tasks must be defined in `turbo.json` before running.
```bash
# Single task
turbo build
# Multiple tasks
turbo run build lint test
# See available tasks (run without arguments)
turbo run
```
## Passing Arguments to Scripts
Use `--` to pass arguments through to the underlying package scripts:
```bash
turbo run build -- --sourcemap
turbo test -- --watch
turbo lint -- --fix
```
Everything after `--` goes directly to the task's script.
## Package Selection
By default, turbo runs tasks in all packages. Use `--filter` to narrow scope:
```bash
turbo build --filter=web
turbo test --filter=./apps/*
```
See `filtering/` for complete filter syntax.
## Quick Reference
| Goal | Command |
| ------------------- | -------------------------- |
| Build everything | `turbo build` |
| Build one package | `turbo build --filter=web` |
| Multiple tasks | `turbo build lint test` |
| Pass args to script | `turbo build -- --arg` |
| Preview run | `turbo build --dry` |
| Force rebuild | `turbo build --force` |

View File

@@ -0,0 +1,297 @@
# turbo run Flags Reference
Full docs: https://turborepo.dev/docs/reference/run
## Package Selection
### `--filter` / `-F`
Select specific packages to run tasks in.
```bash
turbo build --filter=web
turbo build -F=@repo/ui -F=@repo/utils
turbo test --filter=./apps/*
```
See `filtering/` for complete syntax (globs, dependencies, git ranges).
### Task Identifier Syntax (v2.2.4+)
Run specific package tasks directly:
```bash
turbo run web#build # Build web package
turbo run web#build docs#lint # Multiple specific tasks
```
### `--affected`
Run only in packages changed since the base branch.
```bash
turbo build --affected
turbo test --affected --filter=./apps/* # combine with filter
```
**How it works:**
- Default: compares `main...HEAD`
- In GitHub Actions: auto-detects `GITHUB_BASE_REF`
- Override base: `TURBO_SCM_BASE=development turbo build --affected`
- Override head: `TURBO_SCM_HEAD=your-branch turbo build --affected`
**Requires git history** - shallow clones may fall back to running all tasks.
## Execution Control
### `--dry` / `--dry=json`
Preview what would run without executing.
```bash
turbo build --dry # human-readable
turbo build --dry=json # machine-readable
```
### `--force`
Ignore all cached artifacts, re-run everything.
```bash
turbo build --force
```
### `--concurrency`
Limit parallel task execution.
```bash
turbo build --concurrency=4 # max 4 tasks
turbo build --concurrency=50% # 50% of CPU cores
```
### `--continue`
Keep running other tasks when one fails.
```bash
turbo build test --continue
```
### `--only`
Run only the specified task, skip its dependencies.
```bash
turbo build --only # skip running dependsOn tasks
```
### `--parallel` (Discouraged)
Ignores task graph dependencies, runs all tasks simultaneously. **Avoid using this flag**—if tasks need to run in parallel, configure `dependsOn` correctly instead. Using `--parallel` bypasses Turborepo's dependency graph, which can cause race conditions and incorrect builds.
## Cache Control
### `--cache`
Fine-grained cache behavior control.
```bash
# Default: read/write both local and remote
turbo build --cache=local:rw,remote:rw
# Read-only local, no remote
turbo build --cache=local:r,remote:
# Disable local, read-only remote
turbo build --cache=local:,remote:r
# Disable all caching
turbo build --cache=local:,remote:
```
## Output & Debugging
### `--graph`
Generate task graph visualization.
```bash
turbo build --graph # opens in browser
turbo build --graph=graph.svg # SVG file
turbo build --graph=graph.png # PNG file
turbo build --graph=graph.json # JSON data
turbo build --graph=graph.mermaid # Mermaid diagram
```
### `--summarize`
Generate JSON run summary for debugging.
```bash
turbo build --summarize
# creates .turbo/runs/<run-id>.json
```
### `--output-logs`
Control log output verbosity.
```bash
turbo build --output-logs=full # all logs (default)
turbo build --output-logs=new-only # only cache misses
turbo build --output-logs=errors-only # only failures
turbo build --output-logs=none # silent
```
### `--profile`
Generate Chrome tracing profile for performance analysis.
```bash
turbo build --profile=profile.json
# open chrome://tracing and load the file
```
### `--verbosity` / `-v`
Control turbo's own log level.
```bash
turbo build -v # verbose
turbo build -vv # more verbose
turbo build -vvv # maximum verbosity
```
## Environment
### `--env-mode`
Control environment variable handling.
```bash
turbo build --env-mode=strict # only declared env vars (default)
turbo build --env-mode=loose # include all env vars in hash
```
## UI
### `--ui`
Select output interface.
```bash
turbo build --ui=tui # interactive terminal UI (default in TTY)
turbo build --ui=stream # streaming logs (default in CI)
```
---
# turbo-ignore
Full docs: https://turborepo.dev/docs/reference/turbo-ignore
Skip CI work when nothing relevant changed. Useful for skipping container setup.
## Basic Usage
```bash
# Check if build is needed for current package (uses Automatic Package Scoping)
npx turbo-ignore
# Check specific package
npx turbo-ignore web
# Check specific task
npx turbo-ignore --task=test
```
## Exit Codes
- `0`: No changes detected - skip CI work
- `1`: Changes detected - proceed with CI
## CI Integration Example
```yaml
# GitHub Actions
- name: Check for changes
id: turbo-ignore
run: npx turbo-ignore web
continue-on-error: true
- name: Build
if: steps.turbo-ignore.outcome == 'failure' # changes detected
run: pnpm build
```
## Comparison Depth
Default: compares to parent commit (`HEAD^1`).
```bash
# Compare to specific commit
npx turbo-ignore --fallback=abc123
# Compare to branch
npx turbo-ignore --fallback=main
```
---
# Other Commands
## turbo boundaries
Check workspace violations (experimental).
```bash
turbo boundaries
```
See `references/boundaries/` for configuration.
## turbo watch
Re-run tasks on file changes.
```bash
turbo watch build test
```
See `references/watch/` for details.
## turbo prune
Create sparse checkout for Docker.
```bash
turbo prune web --docker
```
## turbo link / unlink
Connect/disconnect Remote Cache.
```bash
turbo link # connect to Vercel Remote Cache
turbo unlink # disconnect
```
## turbo login / logout
Authenticate with Remote Cache provider.
```bash
turbo login # authenticate
turbo logout # log out
```
## turbo generate
Scaffold new packages.
```bash
turbo generate
```

View File

@@ -0,0 +1,211 @@
# turbo.json Configuration Overview
Configuration reference for Turborepo. Full docs: https://turborepo.dev/docs/reference/configuration
## File Location
Root `turbo.json` lives at repo root, sibling to root `package.json`:
```
my-monorepo/
├── turbo.json # Root configuration
├── package.json
└── packages/
└── web/
├── turbo.json # Package Configuration (optional)
└── package.json
```
## Always Prefer Package Tasks Over Root Tasks
**Always use package tasks. Only use Root Tasks if you cannot succeed with package tasks.**
Package tasks enable parallelization, individual caching, and filtering. Define scripts in each package's `package.json`:
```json
// packages/web/package.json
{
"scripts": {
"build": "next build",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
// packages/api/package.json
{
"scripts": {
"build": "tsc",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
```
```json
// Root package.json - delegates to turbo
{
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck"
}
}
```
When you run `turbo run lint`, Turborepo finds all packages with a `lint` script and runs them **in parallel**.
**Root Tasks are a fallback**, not the default. Only use them for tasks that truly cannot run per-package (e.g., repo-level CI scripts, workspace-wide config generation).
```json
// AVOID: Task logic in root defeats parallelization
{
"scripts": {
"lint": "eslint apps/web && eslint apps/api && eslint packages/ui"
}
}
```
## Basic Structure
```json
{
"$schema": "https://turborepo.dev/schema.v2.json",
"globalEnv": ["CI"],
"globalDependencies": ["tsconfig.json"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
```
The `$schema` key enables IDE autocompletion and validation.
## Configuration Sections
**Global options** - Settings affecting all tasks:
- `globalEnv`, `globalDependencies`, `globalPassThroughEnv`
- `cacheDir`, `daemon`, `envMode`, `ui`, `remoteCache`
**Task definitions** - Per-task settings in `tasks` object:
- `dependsOn`, `outputs`, `inputs`, `env`
- `cache`, `persistent`, `interactive`, `outputLogs`
## Package Configurations
Use `turbo.json` in individual packages to override root settings:
```json
// packages/web/turbo.json
{
"extends": ["//"],
"tasks": {
"build": {
"outputs": [".next/**", "!.next/cache/**"]
}
}
}
```
The `"extends": ["//"]` is required - it references the root configuration.
**When to use Package Configurations:**
- Framework-specific outputs (Next.js, Vite, etc.)
- Package-specific env vars
- Different caching rules for specific packages
- Keeping framework config close to the framework code
### Extending from Other Packages
You can extend from config packages instead of just root:
```json
// packages/web/turbo.json
{
"extends": ["//", "@repo/turbo-config"]
}
```
### Adding to Inherited Arrays with `$TURBO_EXTENDS$`
By default, array fields in Package Configurations **replace** root values. Use `$TURBO_EXTENDS$` to **append** instead:
```json
// Root turbo.json
{
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}
```
```json
// packages/web/turbo.json
{
"extends": ["//"],
"tasks": {
"build": {
// Inherits "dist/**" from root, adds ".next/**"
"outputs": ["$TURBO_EXTENDS$", ".next/**", "!.next/cache/**"]
}
}
}
```
Without `$TURBO_EXTENDS$`, outputs would only be `[".next/**", "!.next/cache/**"]`.
**Works with:**
- `dependsOn`
- `env`
- `inputs`
- `outputs`
- `passThroughEnv`
- `with`
### Excluding Tasks from Packages
Use `extends: false` to exclude a task from a package:
```json
// packages/ui/turbo.json
{
"extends": ["//"],
"tasks": {
"e2e": {
"extends": false // UI package doesn't have e2e tests
}
}
}
```
## `turbo.jsonc` for Comments
Use `turbo.jsonc` extension to add comments with IDE support:
```jsonc
// turbo.jsonc
{
"tasks": {
"build": {
// Next.js outputs
"outputs": [".next/**", "!.next/cache/**"]
}
}
}
```

View File

@@ -0,0 +1,191 @@
# Global Options Reference
Options that affect all tasks. Full docs: https://turborepo.dev/docs/reference/configuration
## globalEnv
Environment variables affecting all task hashes.
```json
{
"globalEnv": ["CI", "NODE_ENV", "VERCEL_*"]
}
```
Use for variables that should invalidate all caches when changed.
## globalDependencies
Files that affect all task hashes.
```json
{
"globalDependencies": ["tsconfig.json", ".env", "pnpm-lock.yaml"]
}
```
Lockfile is included by default. Add shared configs here.
## globalPassThroughEnv
Variables available to tasks but not included in hash.
```json
{
"globalPassThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
}
```
Use for credentials that shouldn't affect cache keys.
## cacheDir
Custom cache location. Default: `node_modules/.cache/turbo`.
```json
{
"cacheDir": ".turbo/cache"
}
```
## daemon
Background process for faster subsequent runs. Default: `true`.
```json
{
"daemon": false
}
```
Disable in CI or when debugging.
## envMode
How unspecified env vars are handled. Default: `"strict"`.
```json
{
"envMode": "strict" // Only specified vars available
// or
"envMode": "loose" // All vars pass through
}
```
Strict mode catches missing env declarations.
## ui
Terminal UI mode. Default: `"stream"`.
```json
{
"ui": "tui" // Interactive terminal UI
// or
"ui": "stream" // Traditional streaming logs
}
```
TUI provides better UX for parallel tasks.
## remoteCache
Configure remote caching.
```json
{
"remoteCache": {
"enabled": true,
"signature": true,
"timeout": 30,
"uploadTimeout": 60
}
}
```
| Option | Default | Description |
| --------------- | ---------------------- | ------------------------------------------------------ |
| `enabled` | `true` | Enable/disable remote caching |
| `signature` | `false` | Sign artifacts with `TURBO_REMOTE_CACHE_SIGNATURE_KEY` |
| `preflight` | `false` | Send OPTIONS request before cache requests |
| `timeout` | `30` | Timeout in seconds for cache operations |
| `uploadTimeout` | `60` | Timeout in seconds for uploads |
| `apiUrl` | `"https://vercel.com"` | Remote cache API endpoint |
| `loginUrl` | `"https://vercel.com"` | Login endpoint |
| `teamId` | - | Team ID (must start with `team_`) |
| `teamSlug` | - | Team slug for querystring |
See https://turborepo.dev/docs/core-concepts/remote-caching for setup.
## concurrency
Default: `"10"`
Limit parallel task execution.
```json
{
"concurrency": "4" // Max 4 tasks at once
// or
"concurrency": "50%" // 50% of available CPUs
}
```
## futureFlags
Enable experimental features that will become default in future versions.
```json
{
"futureFlags": {
"errorsOnlyShowHash": true
}
}
```
### `errorsOnlyShowHash`
When using `outputLogs: "errors-only"`, show task hashes on start/completion:
- Cache miss: `cache miss, executing <hash> (only logging errors)`
- Cache hit: `cache hit, replaying logs (no errors) <hash>`
## noUpdateNotifier
Disable update notifications when new turbo versions are available.
```json
{
"noUpdateNotifier": true
}
```
## dangerouslyDisablePackageManagerCheck
Bypass the `packageManager` field requirement. Use for incremental migration.
```json
{
"dangerouslyDisablePackageManagerCheck": true
}
```
**Warning**: Unstable lockfiles can cause unpredictable behavior.
## Git Worktree Cache Sharing
When working in Git worktrees, Turborepo automatically shares local cache between the main worktree and linked worktrees.
**How it works:**
- Detects worktree configuration
- Redirects cache to main worktree's `.turbo/cache`
- Works alongside Remote Cache
**Benefits:**
- Cache hits across branches
- Reduced disk usage
- Faster branch switching
**Disabled by**: Setting explicit `cacheDir` in turbo.json.

View File

@@ -0,0 +1,348 @@
# Configuration Gotchas
Common mistakes and how to fix them.
## #1 Root Scripts Not Using `turbo run`
Root `package.json` scripts for turbo tasks MUST use `turbo run`, not direct commands.
```json
// WRONG - bypasses turbo, no parallelization or caching
{
"scripts": {
"build": "bun build",
"dev": "bun dev"
}
}
// CORRECT - delegates to turbo
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
}
}
```
**Why this matters:** Running `bun build` or `npm run build` at root bypasses Turborepo entirely - no parallelization, no caching, no dependency graph awareness.
## #2 Using `&&` to Chain Turbo Tasks
Don't use `&&` to chain tasks that turbo should orchestrate.
```json
// WRONG - changeset:publish chains turbo task with non-turbo command
{
"scripts": {
"changeset:publish": "bun build && changeset publish"
}
}
// CORRECT - use turbo run, let turbo handle dependencies
{
"scripts": {
"changeset:publish": "turbo run build && changeset publish"
}
}
```
If the second command (`changeset publish`) depends on build outputs, the turbo task should run through turbo to get caching and parallelization benefits.
## #3 Overly Broad globalDependencies
`globalDependencies` affects hash for ALL tasks in ALL packages. Be specific.
```json
// WRONG - affects all hashes
{
"globalDependencies": ["**/.env.*local"]
}
// CORRECT - move to specific tasks that need it
{
"globalDependencies": [".env"],
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": ["dist/**"]
}
}
}
```
**Why this matters:** `**/.env.*local` matches .env files in ALL packages, causing unnecessary cache invalidation. Instead:
- Use `globalDependencies` only for truly global files (root `.env`)
- Use task-level `inputs` for package-specific .env files with `$TURBO_DEFAULT$` to preserve default behavior
## #4 Repetitive Task Configuration
Look for repeated configuration across tasks that can be collapsed.
```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*"]
}
}
}
// BETTER - use globalEnv and globalDependencies
{
"globalEnv": ["API_URL", "DATABASE_URL"],
"globalDependencies": [".env*"],
"tasks": {
"build": {},
"test": {}
}
}
```
**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
## #5 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"]
}
}
}
```
## #6 MOST COMMON MISTAKE: Creating Root Tasks
**DO NOT create Root Tasks. ALWAYS create package tasks.**
When you need to create a task (build, lint, test, typecheck, etc.):
1. Add the script to **each relevant package's** `package.json`
2. Register the task in root `turbo.json`
3. Root `package.json` only contains `turbo run <task>`
```json
// WRONG - DO NOT DO THIS
// Root package.json with task logic
{
"scripts": {
"build": "cd apps/web && next build && cd ../api && tsc",
"lint": "eslint apps/ packages/",
"test": "vitest"
}
}
// CORRECT - DO THIS
// 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" } }
// Root package.json - ONLY delegates
{ "scripts": { "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test" } }
// turbo.json - register tasks
{
"tasks": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
"lint": {},
"test": {}
}
}
```
**Why this matters:**
- Package tasks run in **parallel** across all packages
- Each package's output is cached **individually**
- You can **filter** to specific packages: `turbo run test --filter=web`
Root Tasks (`//#taskname`) defeat all these benefits. Only use them for tasks that truly cannot exist in any package (extremely rare).
## #7 Tasks That Need Parallel Execution + Cache Invalidation
Some tasks can run in parallel (don't need built output from dependencies) but must still invalidate cache when dependency source code changes. Using `dependsOn: ["^taskname"]` forces sequential execution. Using no dependencies breaks cache invalidation.
**Use Transit Nodes for these tasks:**
```json
// WRONG - forces sequential execution (SLOW)
"my-task": {
"dependsOn": ["^my-task"]
}
// ALSO WRONG - no dependency awareness (INCORRECT CACHING)
"my-task": {}
// CORRECT - use Transit Nodes for parallel + correct caching
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"my-task": { "dependsOn": ["transit"] }
}
}
```
**Why Transit Nodes work:**
- `transit` creates dependency relationships without matching any actual script
- Tasks that depend on `transit` gain dependency awareness
- Since `transit` completes instantly (no script), tasks run in parallel
- Cache correctly invalidates when dependency source code changes
**How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs.
## 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
"build": {
"dependsOn": ["^build"]
}
// CORRECT - outputs are cached
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
```
No `outputs` key is fine for stdout-only tasks. For file-producing tasks, missing `outputs` means Turbo has nothing to cache.
## Forgetting ^ in dependsOn
```json
// WRONG - looks for "build" in SAME package (infinite loop or missing)
"build": {
"dependsOn": ["build"]
}
// CORRECT - runs dependencies' build first
"build": {
"dependsOn": ["^build"]
}
```
The `^` means "in dependency packages", not "in this package".
## Missing persistent on Dev Tasks
```json
// WRONG - dependent tasks hang waiting for dev to "finish"
"dev": {
"cache": false
}
// CORRECT
"dev": {
"cache": false,
"persistent": true
}
```
## Package Config Missing extends
```json
// WRONG - packages/web/turbo.json
{
"tasks": {
"build": { "outputs": [".next/**"] }
}
}
// CORRECT
{
"extends": ["//"],
"tasks": {
"build": { "outputs": [".next/**"] }
}
}
```
Without `"extends": ["//"]`, Package Configurations are invalid.
## Root Tasks Need Special Syntax
To run a task defined only in root `package.json`:
```bash
# WRONG
turbo run format
# CORRECT
turbo run //#format
```
And in dependsOn:
```json
"build": {
"dependsOn": ["//#codegen"] // Root package's codegen
}
```
## Overwriting Default Inputs
```json
// WRONG - only watches test files, ignores source changes
"test": {
"inputs": ["tests/**"]
}
// CORRECT - extends defaults, adds test files
"test": {
"inputs": ["$TURBO_DEFAULT$", "tests/**"]
}
```
Without `$TURBO_DEFAULT$`, you replace all default file watching.
## Caching Tasks with Side Effects
```json
// WRONG - deploy might be skipped on cache hit
"deploy": {
"dependsOn": ["build"]
}
// CORRECT
"deploy": {
"dependsOn": ["build"],
"cache": false
}
```
Always disable cache for deploy, publish, or mutation tasks.

View File

@@ -0,0 +1,285 @@
# Task Configuration Reference
Full docs: https://turborepo.dev/docs/reference/configuration#tasks
## dependsOn
Controls task execution order.
```json
{
"tasks": {
"build": {
"dependsOn": [
"^build", // Dependencies' build tasks first
"codegen", // Same package's codegen task first
"shared#build" // Specific package's build task
]
}
}
}
```
| Syntax | Meaning |
| ---------- | ------------------------------------ |
| `^task` | Run `task` in all dependencies first |
| `task` | Run `task` in same package first |
| `pkg#task` | Run specific package's task first |
The `^` prefix is crucial - without it, you're referencing the same package.
### Transit Nodes for Parallel Tasks
For tasks like `lint` and `check-types` that can run in parallel but need dependency-aware caching:
```json
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"lint": { "dependsOn": ["transit"] },
"check-types": { "dependsOn": ["transit"] }
}
}
```
**DO NOT use `dependsOn: ["^lint"]`** - this forces sequential execution.
**DO NOT use `dependsOn: []`** - this breaks cache invalidation.
The `transit` task creates dependency relationships without running anything (no matching script), so tasks run in parallel with correct caching.
## outputs
Glob patterns for files to cache. **If omitted, nothing is cached.**
```json
{
"tasks": {
"build": {
"outputs": ["dist/**", "build/**"]
}
}
}
```
**Framework examples:**
```json
// Next.js
"outputs": [".next/**", "!.next/cache/**"]
// Vite
"outputs": ["dist/**"]
// TypeScript (tsc)
"outputs": ["dist/**", "*.tsbuildinfo"]
// No file outputs (lint, typecheck)
"outputs": []
```
Use `!` prefix to exclude patterns from caching.
## inputs
Files considered when calculating task hash. Defaults to all tracked files in package.
```json
{
"tasks": {
"test": {
"inputs": ["src/**", "tests/**", "vitest.config.ts"]
}
}
}
```
**Special values:**
| Value | Meaning |
| --------------------- | --------------------------------------- |
| `$TURBO_DEFAULT$` | Include default inputs, then add/remove |
| `$TURBO_ROOT$/<path>` | Reference files from repo root |
```json
{
"tasks": {
"build": {
"inputs": [
"$TURBO_DEFAULT$",
"!README.md",
"$TURBO_ROOT$/tsconfig.base.json"
]
}
}
}
```
## env
Environment variables to include in task hash.
```json
{
"tasks": {
"build": {
"env": [
"API_URL",
"NEXT_PUBLIC_*", // Wildcard matching
"!DEBUG" // Exclude from hash
]
}
}
}
```
Variables listed here affect cache hits - changing the value invalidates cache.
## cache
Enable/disable caching for a task. Default: `true`.
```json
{
"tasks": {
"dev": { "cache": false },
"deploy": { "cache": false }
}
}
```
Disable for: dev servers, deploy commands, tasks with side effects.
## persistent
Mark long-running tasks that don't exit. Default: `false`.
```json
{
"tasks": {
"dev": {
"cache": false,
"persistent": true
}
}
}
```
Required for dev servers - without it, dependent tasks wait forever.
## interactive
Allow task to receive stdin input. Default: `false`.
```json
{
"tasks": {
"login": {
"cache": false,
"interactive": true
}
}
}
```
## outputLogs
Control when logs are shown. Options: `full`, `hash-only`, `new-only`, `errors-only`, `none`.
```json
{
"tasks": {
"build": {
"outputLogs": "new-only" // Only show logs on cache miss
}
}
}
```
## with
Run tasks alongside this task. For long-running tasks that need runtime dependencies.
```json
{
"tasks": {
"dev": {
"with": ["api#dev"],
"persistent": true,
"cache": false
}
}
}
```
Unlike `dependsOn`, `with` runs tasks concurrently (not sequentially). Use for dev servers that need other services running.
## interruptible
Allow `turbo watch` to restart the task on changes. Default: `false`.
```json
{
"tasks": {
"dev": {
"persistent": true,
"interruptible": true,
"cache": false
}
}
}
```
Use for dev servers that don't automatically detect dependency changes.
## description
Human-readable description of the task.
```json
{
"tasks": {
"build": {
"description": "Compiles the application for production deployment"
}
}
}
```
For documentation only - doesn't affect execution or caching.
## passThroughEnv
Environment variables available at runtime but NOT included in cache hash.
```json
{
"tasks": {
"build": {
"passThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
}
}
}
```
**Warning**: Changes to these vars won't cause cache misses. Use `env` if changes should invalidate cache.
## extends (Package Configuration only)
Control task inheritance in Package Configurations.
```json
// packages/ui/turbo.json
{
"extends": ["//"],
"tasks": {
"lint": {
"extends": false // Exclude from this package
}
}
}
```
| Value | Behavior |
| ---------------- | -------------------------------------------------------------- |
| `true` (default) | Inherit from root turbo.json |
| `false` | Exclude task from package, or define fresh without inheritance |

View File

@@ -0,0 +1,96 @@
# Environment Variables in Turborepo
Turborepo provides fine-grained control over which environment variables affect task hashing and runtime availability.
## Configuration Keys
### `env` - Task-Specific Variables
Variables that affect a specific task's hash. When these change, only that task rebuilds.
```json
{
"tasks": {
"build": {
"env": ["DATABASE_URL", "API_KEY"]
}
}
}
```
### `globalEnv` - Variables Affecting All Tasks
Variables that affect EVERY task's hash. When these change, all tasks rebuild.
```json
{
"globalEnv": ["CI", "NODE_ENV"]
}
```
### `passThroughEnv` - Runtime-Only Variables (Not Hashed)
Variables available at runtime but NOT included in hash. **Use with caution** - changes won't trigger rebuilds.
```json
{
"tasks": {
"deploy": {
"passThroughEnv": ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
}
}
}
```
### `globalPassThroughEnv` - Global Runtime Variables
Same as `passThroughEnv` but for all tasks.
```json
{
"globalPassThroughEnv": ["GITHUB_TOKEN"]
}
```
## Wildcards and Negation
### Wildcards
Match multiple variables with `*`:
```json
{
"env": ["MY_API_*", "FEATURE_FLAG_*"]
}
```
This matches `MY_API_URL`, `MY_API_KEY`, `FEATURE_FLAG_DARK_MODE`, etc.
### Negation
Exclude variables (useful with framework inference):
```json
{
"env": ["!NEXT_PUBLIC_ANALYTICS_ID"]
}
```
## Complete Example
```json
{
"$schema": "https://turborepo.dev/schema.v2.json",
"globalEnv": ["CI", "NODE_ENV"],
"globalPassThroughEnv": ["GITHUB_TOKEN", "NPM_TOKEN"],
"tasks": {
"build": {
"env": ["DATABASE_URL", "API_*"],
"passThroughEnv": ["SENTRY_AUTH_TOKEN"]
},
"test": {
"env": ["TEST_DATABASE_URL"]
}
}
}
```

View File

@@ -0,0 +1,145 @@
# Environment Variable Gotchas
Common mistakes and how to fix them.
## .env Files Must Be in `inputs`
Turbo does NOT read `.env` files. Your framework (Next.js, Vite, etc.) or `dotenv` loads them. But Turbo needs to know when they change.
**Wrong:**
```json
{
"tasks": {
"build": {
"env": ["DATABASE_URL"]
}
}
}
```
**Right:**
```json
{
"tasks": {
"build": {
"env": ["DATABASE_URL"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.local", ".env.production"]
}
}
}
```
## Strict Mode Filters CI Variables
In strict mode, CI provider variables (GITHUB_TOKEN, GITLAB_CI, etc.) are filtered unless explicitly listed.
**Symptom:** Task fails with "authentication required" or "permission denied" in CI.
**Solution:**
```json
{
"globalPassThroughEnv": ["GITHUB_TOKEN", "GITLAB_CI", "CI"]
}
```
## passThroughEnv Doesn't Affect Hash
Variables in `passThroughEnv` are available at runtime but changes WON'T trigger rebuilds.
**Dangerous example:**
```json
{
"tasks": {
"build": {
"passThroughEnv": ["API_URL"]
}
}
}
```
If `API_URL` changes from staging to production, Turbo may serve a cached build pointing to the wrong API.
**Use passThroughEnv only for:**
- Auth tokens that don't affect output (SENTRY_AUTH_TOKEN)
- CI metadata (GITHUB_RUN_ID)
- Variables consumed after build (deploy credentials)
## Runtime-Created Variables Are Invisible
Turbo captures env vars at startup. Variables created during execution aren't seen.
**Won't work:**
```bash
# In package.json scripts
"build": "export API_URL=$COMPUTED_VALUE && next build"
```
**Solution:** Set vars before invoking turbo:
```bash
API_URL=$COMPUTED_VALUE turbo run build
```
## Different .env Files for Different Environments
If you use `.env.development` and `.env.production`, both should be in inputs.
```json
{
"tasks": {
"build": {
"inputs": [
"$TURBO_DEFAULT$",
".env",
".env.local",
".env.development",
".env.development.local",
".env.production",
".env.production.local"
]
}
}
}
```
## Complete Next.js Example
```json
{
"$schema": "https://turborepo.dev/schema.v2.json",
"globalEnv": ["CI", "NODE_ENV", "VERCEL"],
"globalPassThroughEnv": ["GITHUB_TOKEN", "VERCEL_URL"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"env": [
"DATABASE_URL",
"NEXT_PUBLIC_*",
"!NEXT_PUBLIC_ANALYTICS_ID"
],
"passThroughEnv": ["SENTRY_AUTH_TOKEN"],
"inputs": [
"$TURBO_DEFAULT$",
".env",
".env.local",
".env.production",
".env.production.local"
],
"outputs": [".next/**", "!.next/cache/**"]
}
}
}
```
This config:
- Hashes DATABASE*URL and NEXT_PUBLIC*\* vars (except analytics)
- Passes through SENTRY_AUTH_TOKEN without hashing
- Includes all .env file variants in the hash
- Makes CI tokens available globally

View File

@@ -0,0 +1,101 @@
# Environment Modes
Turborepo supports different modes for handling environment variables during task execution.
## Strict Mode (Default)
Only explicitly configured variables are available to tasks.
**Behavior:**
- Tasks only see vars listed in `env`, `globalEnv`, `passThroughEnv`, or `globalPassThroughEnv`
- Unlisted vars are filtered out
- Tasks fail if they require unlisted variables
**Benefits:**
- Guarantees cache correctness
- Prevents accidental dependencies on system vars
- Reproducible builds across machines
```bash
# Explicit (though it's the default)
turbo run build --env-mode=strict
```
## Loose Mode
All system environment variables are available to tasks.
```bash
turbo run build --env-mode=loose
```
**Behavior:**
- Every system env var is passed through
- Only vars in `env`/`globalEnv` affect the hash
- Other vars are available but NOT hashed
**Risks:**
- Cache may restore incorrect results if unhashed vars changed
- "Works on my machine" bugs
- CI vs local environment mismatches
**Use case:** Migrating legacy projects or debugging strict mode issues.
## Framework Inference (Automatic)
Turborepo automatically detects frameworks and includes their conventional env vars.
### Inferred Variables by Framework
| Framework | Pattern |
| ---------------- | ------------------- |
| Next.js | `NEXT_PUBLIC_*` |
| Vite | `VITE_*` |
| Create React App | `REACT_APP_*` |
| Gatsby | `GATSBY_*` |
| Nuxt | `NUXT_*`, `NITRO_*` |
| Expo | `EXPO_PUBLIC_*` |
| Astro | `PUBLIC_*` |
| SvelteKit | `PUBLIC_*` |
| Remix | `REMIX_*` |
| Redwood | `REDWOOD_ENV_*` |
| Sanity | `SANITY_STUDIO_*` |
| Solid | `VITE_*` |
### Disabling Framework Inference
Globally via CLI:
```bash
turbo run build --framework-inference=false
```
Or exclude specific patterns in config:
```json
{
"tasks": {
"build": {
"env": ["!NEXT_PUBLIC_*"]
}
}
}
```
### Why Disable?
- You want explicit control over all env vars
- Framework vars shouldn't bust the cache (e.g., analytics IDs)
- Debugging unexpected cache misses
## Checking Environment Mode
Use `--dry` to see which vars affect each task:
```bash
turbo run build --dry=json | jq '.tasks[].environmentVariables'
```

View File

@@ -0,0 +1,148 @@
# Turborepo Filter Syntax Reference
## Running Only Changed Packages: `--affected`
**The primary way to run only changed packages is `--affected`:**
```bash
# Run build/test/lint only in changed packages and their dependents
turbo run build test lint --affected
```
This compares your current branch to the default branch (usually `main` or `master`) and runs tasks in:
1. Packages with file changes
2. Packages that depend on changed packages (dependents)
### Why Include Dependents?
If you change `@repo/ui`, packages that import `@repo/ui` (like `apps/web`) need to re-run their tasks to verify they still work with the changes.
### Customizing --affected
```bash
# Use a different base branch
turbo run build --affected --affected-base=origin/develop
# Use a different head (current state)
turbo run build --affected --affected-head=HEAD~5
```
### Common CI Pattern
```yaml
# .github/workflows/ci.yml
- run: turbo run build test lint --affected
```
This is the most efficient CI setup - only run tasks for what actually changed.
---
## Manual Git Comparison with --filter
For more control, use `--filter` with git comparison syntax:
```bash
# Changed packages + dependents (same as --affected)
turbo run build --filter=...[origin/main]
# Only changed packages (no dependents)
turbo run build --filter=[origin/main]
# Changed packages + dependencies (packages they import)
turbo run build --filter=[origin/main]...
# Changed since last commit
turbo run build --filter=...[HEAD^1]
# Changed between two commits
turbo run build --filter=[a1b2c3d...e4f5g6h]
```
### Comparison Syntax
| Syntax | Meaning |
| ------------- | ------------------------------------- |
| `[ref]` | Packages changed since `ref` |
| `...[ref]` | Changed packages + their dependents |
| `[ref]...` | Changed packages + their dependencies |
| `...[ref]...` | Dependencies, changed, AND dependents |
---
## Other Filter Types
Filters select which packages to include in a `turbo run` invocation.
### Basic Syntax
```bash
turbo run build --filter=<package-name>
turbo run build -F <package-name>
```
Multiple filters combine as a union (packages matching ANY filter run).
### By Package Name
```bash
--filter=web # exact match
--filter=@acme/* # scope glob
--filter=*-app # name glob
```
### By Directory
```bash
--filter=./apps/* # all packages in apps/
--filter=./packages/ui # specific directory
```
### By Dependencies/Dependents
| Syntax | Meaning |
| ----------- | -------------------------------------- |
| `pkg...` | Package AND all its dependencies |
| `...pkg` | Package AND all its dependents |
| `...pkg...` | Dependencies, package, AND dependents |
| `^pkg...` | Only dependencies (exclude pkg itself) |
| `...^pkg` | Only dependents (exclude pkg itself) |
### Negation
Exclude packages with `!`:
```bash
--filter=!web # exclude web
--filter=./apps/* --filter=!admin # apps except admin
```
### Task Identifiers
Run a specific task in a specific package:
```bash
turbo run web#build # only web's build task
turbo run web#build api#test # web build + api test
```
### Combining Filters
Multiple `--filter` flags create a union:
```bash
turbo run build --filter=web --filter=api # runs in both
```
---
## Quick Reference: Changed Packages
| Goal | Command |
| ---------------------------------- | ----------------------------------------------------------- |
| Changed + dependents (recommended) | `turbo run build --affected` |
| Custom base branch | `turbo run build --affected --affected-base=origin/develop` |
| Only changed (no dependents) | `turbo run build --filter=[origin/main]` |
| Changed + dependencies | `turbo run build --filter=[origin/main]...` |
| Since last commit | `turbo run build --filter=...[HEAD^1]` |

View File

@@ -0,0 +1,152 @@
# Common Filter Patterns
Practical examples for typical monorepo scenarios.
## Single Package
Run task in one package:
```bash
turbo run build --filter=web
turbo run test --filter=@acme/api
```
## Package with Dependencies
Build a package and everything it depends on:
```bash
turbo run build --filter=web...
```
Useful for: ensuring all dependencies are built before the target.
## Package Dependents
Run in all packages that depend on a library:
```bash
turbo run test --filter=...ui
```
Useful for: testing consumers after changing a shared package.
## Dependents Only (Exclude Target)
Test packages that depend on ui, but not ui itself:
```bash
turbo run test --filter=...^ui
```
## Changed Packages
Run only in packages with file changes since last commit:
```bash
turbo run lint --filter=[HEAD^1]
```
Since a specific branch point:
```bash
turbo run lint --filter=[main...HEAD]
```
## Changed + Dependents (PR Builds)
Run in changed packages AND packages that depend on them:
```bash
turbo run build test --filter=...[HEAD^1]
```
Or use the shortcut:
```bash
turbo run build test --affected
```
## Directory-Based
Run in all apps:
```bash
turbo run build --filter=./apps/*
```
Run in specific directories:
```bash
turbo run build --filter=./apps/web --filter=./apps/api
```
## Scope-Based
Run in all packages under a scope:
```bash
turbo run build --filter=@acme/*
```
## Exclusions
Run in all apps except admin:
```bash
turbo run build --filter=./apps/* --filter=!admin
```
Run everywhere except specific packages:
```bash
turbo run lint --filter=!legacy-app --filter=!deprecated-pkg
```
## Complex Combinations
Apps that changed, plus their dependents:
```bash
turbo run build --filter=...[HEAD^1] --filter=./apps/*
```
All packages except docs, but only if changed:
```bash
turbo run build --filter=[main...HEAD] --filter=!docs
```
## Debugging Filters
Use `--dry` to see what would run without executing:
```bash
turbo run build --filter=web... --dry
```
Use `--dry=json` for machine-readable output:
```bash
turbo run build --filter=...[HEAD^1] --dry=json
```
## CI/CD Patterns
PR validation (most common):
```bash
turbo run build test lint --affected
```
Deploy only changed apps:
```bash
turbo run deploy --filter=./apps/* --filter=[main...HEAD]
```
Full rebuild of specific app and deps:
```bash
turbo run build --filter=production-app...
```

View File

@@ -0,0 +1,99 @@
# turbo watch
Full docs: https://turborepo.dev/docs/reference/watch
Re-run tasks automatically when code changes. Dependency-aware.
```bash
turbo watch [tasks]
```
## Basic Usage
```bash
# Watch and re-run build task when code changes
turbo watch build
# Watch multiple tasks
turbo watch build test lint
```
Tasks re-run in order configured in `turbo.json` when source files change.
## With Persistent Tasks
Persistent tasks (`"persistent": true`) won't exit, so they can't be depended on. They work the same in `turbo watch` as `turbo run`.
### Dependency-Aware Persistent Tasks
If your tool has built-in watching (like `next dev`), use its watcher:
```json
{
"tasks": {
"dev": {
"persistent": true,
"cache": false
}
}
}
```
### Non-Dependency-Aware Tools
For tools that don't detect dependency changes, use `interruptible`:
```json
{
"tasks": {
"dev": {
"persistent": true,
"interruptible": true,
"cache": false
}
}
}
```
`turbo watch` will restart interruptible tasks when dependencies change.
## Limitations
### Caching
Caching is experimental with watch mode:
```bash
turbo watch your-tasks --experimental-write-cache
```
### Task Outputs in Source Control
If tasks write files tracked by git, watch mode may loop infinitely. Watch mode uses file hashes to prevent this but it's not foolproof.
**Recommendation**: Remove task outputs from git.
## vs turbo run
| Feature | `turbo run` | `turbo watch` |
| ----------------- | ----------- | ------------- |
| Runs once | Yes | No |
| Re-runs on change | No | Yes |
| Caching | Full | Experimental |
| Use case | CI, one-off | Development |
## Common Patterns
### Development Workflow
```bash
# Run dev servers and watch for build changes
turbo watch dev build
```
### Type Checking During Development
```bash
# Watch and re-run type checks
turbo watch check-types
```