Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
349 lines
7.9 KiB
Markdown
349 lines
7.9 KiB
Markdown
# 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.
|