chore(agent): configure AI agents and tools
Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
This commit is contained in:
@@ -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
|
||||
```
|
||||
335
.opencode/skills/turborepo/references/best-practices/packages.md
Normal file
335
.opencode/skills/turborepo/references/best-practices/packages.md
Normal 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.
|
||||
@@ -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.
|
||||
169
.opencode/skills/turborepo/references/caching/gotchas.md
Normal file
169
.opencode/skills/turborepo/references/caching/gotchas.md
Normal 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
|
||||
127
.opencode/skills/turborepo/references/caching/remote-cache.md
Normal file
127
.opencode/skills/turborepo/references/caching/remote-cache.md
Normal 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
|
||||
162
.opencode/skills/turborepo/references/ci/github-actions.md
Normal file
162
.opencode/skills/turborepo/references/ci/github-actions.md
Normal 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
|
||||
```
|
||||
145
.opencode/skills/turborepo/references/ci/patterns.md
Normal file
145
.opencode/skills/turborepo/references/ci/patterns.md
Normal 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
|
||||
```
|
||||
103
.opencode/skills/turborepo/references/ci/vercel.md
Normal file
103
.opencode/skills/turborepo/references/ci/vercel.md
Normal 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
|
||||
```
|
||||
297
.opencode/skills/turborepo/references/cli/commands.md
Normal file
297
.opencode/skills/turborepo/references/cli/commands.md
Normal 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
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
# 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 (Pre-release)
|
||||
|
||||
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.
|
||||
348
.opencode/skills/turborepo/references/configuration/gotchas.md
Normal file
348
.opencode/skills/turborepo/references/configuration/gotchas.md
Normal 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.
|
||||
285
.opencode/skills/turborepo/references/configuration/tasks.md
Normal file
285
.opencode/skills/turborepo/references/configuration/tasks.md
Normal 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 (Pre-release)
|
||||
|
||||
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 |
|
||||
145
.opencode/skills/turborepo/references/environment/gotchas.md
Normal file
145
.opencode/skills/turborepo/references/environment/gotchas.md
Normal 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
|
||||
101
.opencode/skills/turborepo/references/environment/modes.md
Normal file
101
.opencode/skills/turborepo/references/environment/modes.md
Normal 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'
|
||||
```
|
||||
152
.opencode/skills/turborepo/references/filtering/patterns.md
Normal file
152
.opencode/skills/turborepo/references/filtering/patterns.md
Normal 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...
|
||||
```
|
||||
Reference in New Issue
Block a user