diff --git a/.codex/skills/turborepo/SKILL.md b/.codex/skills/turborepo/SKILL.md new file mode 100644 index 00000000000..2934300f714 --- /dev/null +++ b/.codex/skills/turborepo/SKILL.md @@ -0,0 +1,914 @@ +--- +name: turborepo +description: | + Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, + dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment + variables, internal packages, monorepo structure/best practices, and boundaries. + + Use when user: configures tasks/workflows/pipelines, creates packages, sets up + monorepo, shares code between apps, runs changed/affected packages, debugs cache, + or has apps/packages directories. +metadata: + version: 2.8.14-canary.8 +--- + +# Turborepo Skill + +Build system for JavaScript/TypeScript monorepos. Turborepo caches task outputs and runs tasks in parallel based on dependency graph. + +## IMPORTANT: Package Tasks, Not Root Tasks + +**DO NOT create Root Tasks. ALWAYS create package tasks.** + +When creating tasks/scripts/pipelines, you MUST: + +1. Add the script to each relevant package's `package.json` +2. Register the task in root `turbo.json` +3. Root `package.json` only delegates via `turbo run ` + +**DO NOT** put task logic in root `package.json`. This defeats Turborepo's parallelization. + +```json +// DO THIS: Scripts in each package +// apps/web/package.json +{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } } + +// apps/api/package.json +{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } } + +// packages/ui/package.json +{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } } +``` + +```json +// turbo.json - register tasks +{ + "tasks": { + "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }, + "lint": {}, + "test": { "dependsOn": ["build"] } + } +} +``` + +```json +// Root package.json - ONLY delegates, no task logic +{ + "scripts": { + "build": "turbo run build", + "lint": "turbo run lint", + "test": "turbo run test" + } +} +``` + +```json +// DO NOT DO THIS - defeats parallelization +// Root package.json +{ + "scripts": { + "build": "cd apps/web && next build && cd ../api && tsc", + "lint": "eslint apps/ packages/", + "test": "vitest" + } +} +``` + +Root Tasks (`//#taskname`) are ONLY for tasks that truly cannot exist in packages (rare). + +## Secondary Rule: `turbo run` vs `turbo` + +**Always use `turbo run` when the command is written into code:** + +```json +// package.json - ALWAYS "turbo run" +{ + "scripts": { + "build": "turbo run build" + } +} +``` + +```yaml +# CI workflows - ALWAYS "turbo run" +- run: turbo run build --affected +``` + +**The shorthand `turbo ` is ONLY for one-off terminal commands** typed directly by humans or agents. Never write `turbo build` into package.json, CI, or scripts. + +## Quick Decision Trees + +### "I need to configure a task" + +``` +Configure a task? +├─ Define task dependencies → references/configuration/tasks.md +├─ Lint/check-types (parallel + caching) → Use Transit Nodes pattern (see below) +├─ Specify build outputs → references/configuration/tasks.md#outputs +├─ Handle environment variables → references/environment/RULE.md +├─ Set up dev/watch tasks → references/configuration/tasks.md#persistent +├─ Package-specific config → references/configuration/RULE.md#package-configurations +└─ Global settings (cacheDir, daemon) → references/configuration/global-options.md +``` + +### "My cache isn't working" + +``` +Cache problems? +├─ Tasks run but outputs not restored → Missing `outputs` key +├─ Cache misses unexpectedly → references/caching/gotchas.md +├─ Need to debug hash inputs → Use --summarize or --dry +├─ Want to skip cache entirely → Use --force or cache: false +├─ Remote cache not working → references/caching/remote-cache.md +└─ Environment causing misses → references/environment/gotchas.md +``` + +### "I want to run only changed packages" + +``` +Run only what changed? +├─ Changed packages + dependents (RECOMMENDED) → turbo run build --affected +├─ Custom base branch → --affected --affected-base=origin/develop +├─ Manual git comparison → --filter=...[origin/main] +└─ See all filter options → references/filtering/RULE.md +``` + +**`--affected` is the primary way to run only changed packages.** It automatically compares against the default branch and includes dependents. + +### "I want to filter packages" + +``` +Filter packages? +├─ Only changed packages → --affected (see above) +├─ By package name → --filter=web +├─ By directory → --filter=./apps/* +├─ Package + dependencies → --filter=web... +├─ Package + dependents → --filter=...web +└─ Complex combinations → references/filtering/patterns.md +``` + +### "Environment variables aren't working" + +``` +Environment issues? +├─ Vars not available at runtime → Strict mode filtering (default) +├─ Cache hits with wrong env → Var not in `env` key +├─ .env changes not causing rebuilds → .env not in `inputs` +├─ CI variables missing → references/environment/gotchas.md +└─ Framework vars (NEXT_PUBLIC_*) → Auto-included via inference +``` + +### "I need to set up CI" + +``` +CI setup? +├─ GitHub Actions → references/ci/github-actions.md +├─ Vercel deployment → references/ci/vercel.md +├─ Remote cache in CI → references/caching/remote-cache.md +├─ Only build changed packages → --affected flag +├─ Skip unnecessary builds → turbo-ignore (references/cli/commands.md) +└─ Skip container setup when no changes → turbo-ignore +``` + +### "I want to watch for changes during development" + +``` +Watch mode? +├─ Re-run tasks on change → turbo watch (references/watch/RULE.md) +├─ Dev servers with dependencies → Use `with` key (references/configuration/tasks.md#with) +├─ Restart dev server on dep change → Use `interruptible: true` +└─ Persistent dev tasks → Use `persistent: true` +``` + +### "I need to create/structure a package" + +``` +Package creation/structure? +├─ Create an internal package → references/best-practices/packages.md +├─ Repository structure → references/best-practices/structure.md +├─ Dependency management → references/best-practices/dependencies.md +├─ Best practices overview → references/best-practices/RULE.md +├─ JIT vs Compiled packages → references/best-practices/packages.md#compilation-strategies +└─ Sharing code between apps → references/best-practices/RULE.md#package-types +``` + +### "How should I structure my monorepo?" + +``` +Monorepo structure? +├─ Standard layout (apps/, packages/) → references/best-practices/RULE.md +├─ Package types (apps vs libraries) → references/best-practices/RULE.md#package-types +├─ Creating internal packages → references/best-practices/packages.md +├─ TypeScript configuration → references/best-practices/structure.md#typescript-configuration +├─ ESLint configuration → references/best-practices/structure.md#eslint-configuration +├─ Dependency management → references/best-practices/dependencies.md +└─ Enforce package boundaries → references/boundaries/RULE.md +``` + +### "I want to enforce architectural boundaries" + +``` +Enforce boundaries? +├─ Check for violations → turbo boundaries +├─ Tag packages → references/boundaries/RULE.md#tags +├─ Restrict which packages can import others → references/boundaries/RULE.md#rule-types +└─ Prevent cross-package file imports → references/boundaries/RULE.md +``` + +## Critical Anti-Patterns + +### Using `turbo` Shorthand in Code + +**`turbo run` is recommended in package.json scripts and CI pipelines.** The shorthand `turbo ` is intended for interactive terminal use. + +```json +// WRONG - using shorthand in package.json +{ + "scripts": { + "build": "turbo build", + "dev": "turbo dev" + } +} + +// CORRECT +{ + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev" + } +} +``` + +```yaml +# WRONG - using shorthand in CI +- run: turbo build --affected + +# CORRECT +- run: turbo run build --affected +``` + +### Root Scripts Bypassing Turbo + +Root `package.json` scripts MUST delegate to `turbo run`, not run tasks directly. + +```json +// WRONG - bypasses turbo entirely +{ + "scripts": { + "build": "bun build", + "dev": "bun dev" + } +} + +// CORRECT - delegates to turbo +{ + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev" + } +} +``` + +### Using `&&` to Chain Turbo Tasks + +Don't chain turbo tasks with `&&`. Let turbo orchestrate. + +```json +// WRONG - turbo task not using turbo run +{ + "scripts": { + "changeset:publish": "bun build && changeset publish" + } +} + +// CORRECT +{ + "scripts": { + "changeset:publish": "turbo run build && changeset publish" + } +} +``` + +### `prebuild` Scripts That Manually Build Dependencies + +Scripts like `prebuild` that manually build other packages bypass Turborepo's dependency graph. + +```json +// WRONG - manually building dependencies +{ + "scripts": { + "prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build", + "build": "next build" + } +} +``` + +**However, the fix depends on whether workspace dependencies are declared:** + +1. **If dependencies ARE declared** (e.g., `"@repo/types": "workspace:*"` in package.json), remove the `prebuild` script. Turbo's `dependsOn: ["^build"]` handles this automatically. + +2. **If dependencies are NOT declared**, the `prebuild` exists because `^build` won't trigger without a dependency relationship. The fix is to: + - Add the dependency to package.json: `"@repo/types": "workspace:*"` + - Then remove the `prebuild` script + +```json +// CORRECT - declare dependency, let turbo handle build order +// package.json +{ + "dependencies": { + "@repo/types": "workspace:*", + "@repo/utils": "workspace:*" + }, + "scripts": { + "build": "next build" + } +} + +// turbo.json +{ + "tasks": { + "build": { + "dependsOn": ["^build"] + } + } +} +``` + +**Key insight:** `^build` only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering. + +### Overly Broad `globalDependencies` + +`globalDependencies` affects ALL tasks in ALL packages. Be specific. + +```json +// WRONG - heavy hammer, affects all hashes +{ + "globalDependencies": ["**/.env.*local"] +} + +// BETTER - move to task-level inputs +{ + "globalDependencies": [".env"], + "tasks": { + "build": { + "inputs": ["$TURBO_DEFAULT$", ".env*"], + "outputs": ["dist/**"] + } + } +} +``` + +### Repetitive Task Configuration + +Look for repeated configuration across tasks that can be collapsed. Turborepo supports shared configuration patterns. + +```json +// WRONG - repetitive env and inputs across tasks +{ + "tasks": { + "build": { + "env": ["API_URL", "DATABASE_URL"], + "inputs": ["$TURBO_DEFAULT$", ".env*"] + }, + "test": { + "env": ["API_URL", "DATABASE_URL"], + "inputs": ["$TURBO_DEFAULT$", ".env*"] + }, + "dev": { + "env": ["API_URL", "DATABASE_URL"], + "inputs": ["$TURBO_DEFAULT$", ".env*"], + "cache": false, + "persistent": true + } + } +} + +// BETTER - use globalEnv and globalDependencies for shared config +{ + "globalEnv": ["API_URL", "DATABASE_URL"], + "globalDependencies": [".env*"], + "tasks": { + "build": {}, + "test": {}, + "dev": { + "cache": false, + "persistent": true + } + } +} +``` + +**When to use global vs task-level:** + +- `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config +- Task-level `env` / `inputs` - use when only specific tasks need it + +### NOT an Anti-Pattern: Large `env` Arrays + +A large `env` array (even 50+ variables) is **not** a problem. It usually means the user was thorough about declaring their build's environment dependencies. Do not flag this as an issue. + +### Using `--parallel` Flag + +The `--parallel` flag bypasses Turborepo's dependency graph. If tasks need parallel execution, configure `dependsOn` correctly instead. + +```bash +# WRONG - bypasses dependency graph +turbo run lint --parallel + +# CORRECT - configure tasks to allow parallel execution +# In turbo.json, set dependsOn appropriately (or use transit nodes) +turbo run lint +``` + +### Package-Specific Task Overrides in Root turbo.json + +When multiple packages need different task configurations, use **Package Configurations** (`turbo.json` in each package) instead of cluttering root `turbo.json` with `package#task` overrides. + +```json +// WRONG - root turbo.json with many package-specific overrides +{ + "tasks": { + "test": { "dependsOn": ["build"] }, + "@repo/web#test": { "outputs": ["coverage/**"] }, + "@repo/api#test": { "outputs": ["coverage/**"] }, + "@repo/utils#test": { "outputs": [] }, + "@repo/cli#test": { "outputs": [] }, + "@repo/core#test": { "outputs": [] } + } +} + +// CORRECT - use Package Configurations +// Root turbo.json - base config only +{ + "tasks": { + "test": { "dependsOn": ["build"] } + } +} + +// packages/web/turbo.json - package-specific override +{ + "extends": ["//"], + "tasks": { + "test": { "outputs": ["coverage/**"] } + } +} + +// packages/api/turbo.json +{ + "extends": ["//"], + "tasks": { + "test": { "outputs": ["coverage/**"] } + } +} +``` + +**Benefits of Package Configurations:** + +- Keeps configuration close to the code it affects +- Root turbo.json stays clean and focused on base patterns +- Easier to understand what's special about each package +- Works with `$TURBO_EXTENDS$` to inherit + extend arrays + +**When to use `package#task` in root:** + +- Single package needs a unique dependency (e.g., `"deploy": { "dependsOn": ["web#build"] }`) +- Temporary override while migrating + +See `references/configuration/RULE.md#package-configurations` for full details. + +### Using `../` to Traverse Out of Package in `inputs` + +Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` instead. + +```json +// WRONG - traversing out of package +{ + "tasks": { + "build": { + "inputs": ["$TURBO_DEFAULT$", "../shared-config.json"] + } + } +} + +// CORRECT - use $TURBO_ROOT$ for repo root +{ + "tasks": { + "build": { + "inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"] + } + } +} +``` + +### Missing `outputs` for File-Producing Tasks + +**Before flagging missing `outputs`, check what the task actually produces:** + +1. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`) +2. Determine if it writes files to disk or only outputs to stdout +3. Only flag if the task produces files that should be cached + +```json +// WRONG: build produces files but they're not cached +{ + "tasks": { + "build": { + "dependsOn": ["^build"] + } + } +} + +// CORRECT: build outputs are cached +{ + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + } + } +} +``` + +Common outputs by framework: + +- Next.js: `[".next/**", "!.next/cache/**"]` +- Vite/Rollup: `["dist/**"]` +- tsc: `["dist/**"]` or custom `outDir` + +**TypeScript `--noEmit` can still produce cache files:** + +When `incremental: true` in tsconfig.json, `tsc --noEmit` writes `.tsbuildinfo` files even without emitting JS. Check the tsconfig before assuming no outputs: + +```json +// If tsconfig has incremental: true, tsc --noEmit produces cache files +{ + "tasks": { + "typecheck": { + "outputs": ["node_modules/.cache/tsbuildinfo.json"] // or wherever tsBuildInfoFile points + } + } +} +``` + +To determine correct outputs for TypeScript tasks: + +1. Check if `incremental` or `composite` is enabled in tsconfig +2. Check `tsBuildInfoFile` for custom cache location (default: alongside `outDir` or in project root) +3. If no incremental mode, `tsc --noEmit` produces no files + +### `^build` vs `build` Confusion + +```json +{ + "tasks": { + // ^build = run build in DEPENDENCIES first (other packages this one imports) + "build": { + "dependsOn": ["^build"] + }, + // build (no ^) = run build in SAME PACKAGE first + "test": { + "dependsOn": ["build"] + }, + // pkg#task = specific package's task + "deploy": { + "dependsOn": ["web#build"] + } + } +} +``` + +### Environment Variables Not Hashed + +```json +// WRONG: API_URL changes won't cause rebuilds +{ + "tasks": { + "build": { + "outputs": ["dist/**"] + } + } +} + +// CORRECT: API_URL changes invalidate cache +{ + "tasks": { + "build": { + "outputs": ["dist/**"], + "env": ["API_URL", "API_KEY"] + } + } +} +``` + +### `.env` Files Not in Inputs + +Turbo does NOT load `.env` files - your framework does. But Turbo needs to know about changes: + +```json +// WRONG: .env changes don't invalidate cache +{ + "tasks": { + "build": { + "env": ["API_URL"] + } + } +} + +// CORRECT: .env file changes invalidate cache +{ + "tasks": { + "build": { + "env": ["API_URL"], + "inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"] + } + } +} +``` + +### Root `.env` File in Monorepo + +A `.env` file at the repo root is an anti-pattern — even for small monorepos or starter templates. It creates implicit coupling between packages and makes it unclear which packages depend on which variables. + +``` +// WRONG - root .env affects all packages implicitly +my-monorepo/ +├── .env # Which packages use this? +├── apps/ +│ ├── web/ +│ └── api/ +└── packages/ + +// CORRECT - .env files in packages that need them +my-monorepo/ +├── apps/ +│ ├── web/ +│ │ └── .env # Clear: web needs DATABASE_URL +│ └── api/ +│ └── .env # Clear: api needs API_KEY +└── packages/ +``` + +**Problems with root `.env`:** + +- Unclear which packages consume which variables +- All packages get all variables (even ones they don't need) +- Cache invalidation is coarse-grained (root .env change invalidates everything) +- Security risk: packages may accidentally access sensitive vars meant for others +- Bad habits start small — starter templates should model correct patterns + +**If you must share variables**, use `globalEnv` to be explicit about what's shared, and document why. + +### Strict Mode Filtering CI Variables + +By default, Turborepo filters environment variables to only those in `env`/`globalEnv`. CI variables may be missing: + +```json +// If CI scripts need GITHUB_TOKEN but it's not in env: +{ + "globalPassThroughEnv": ["GITHUB_TOKEN", "CI"], + "tasks": { ... } +} +``` + +Or use `--env-mode=loose` (not recommended for production). + +### Shared Code in Apps (Should Be a Package) + +``` +// WRONG: Shared code inside an app +apps/ + web/ + shared/ # This breaks monorepo principles! + utils.ts + +// CORRECT: Extract to a package +packages/ + utils/ + src/utils.ts +``` + +### Accessing Files Across Package Boundaries + +```typescript +// WRONG: Reaching into another package's internals +import { Button } from "../../packages/ui/src/button"; + +// CORRECT: Install and import properly +import { Button } from "@repo/ui/button"; +``` + +### Too Many Root Dependencies + +```json +// WRONG: App dependencies in root +{ + "dependencies": { + "react": "^18", + "next": "^14" + } +} + +// CORRECT: Only repo tools in root +{ + "devDependencies": { + "turbo": "latest" + } +} +``` + +## Common Task Configurations + +### Standard Build Pipeline + +```json +{ + "$schema": "https://v2-8-14-canary-8.turborepo.dev/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", ".next/**", "!.next/cache/**"] + }, + "dev": { + "cache": false, + "persistent": true + } + } +} +``` + +Add a `transit` task if you have tasks that need parallel execution with cache invalidation (see below). + +### Dev Task with `^dev` Pattern (for `turbo watch`) + +A `dev` task with `dependsOn: ["^dev"]` and `persistent: false` in root turbo.json may look unusual but is **correct for `turbo watch` workflows**: + +```json +// Root turbo.json +{ + "tasks": { + "dev": { + "dependsOn": ["^dev"], + "cache": false, + "persistent": false // Packages have one-shot dev scripts + } + } +} + +// Package turbo.json (apps/web/turbo.json) +{ + "extends": ["//"], + "tasks": { + "dev": { + "persistent": true // Apps run long-running dev servers + } + } +} +``` + +**Why this works:** + +- **Packages** (e.g., `@acme/db`, `@acme/validators`) have `"dev": "tsc"` — one-shot type generation that completes quickly +- **Apps** override with `persistent: true` for actual dev servers (Next.js, etc.) +- **`turbo watch`** re-runs the one-shot package `dev` scripts when source files change, keeping types in sync + +**Intended usage:** Run `turbo watch dev` (not `turbo run dev`). Watch mode re-executes one-shot tasks on file changes while keeping persistent tasks running. + +**Alternative pattern:** Use a separate task name like `prepare` or `generate` for one-shot dependency builds to make the intent clearer: + +```json +{ + "tasks": { + "prepare": { + "dependsOn": ["^prepare"], + "outputs": ["dist/**"] + }, + "dev": { + "dependsOn": ["prepare"], + "cache": false, + "persistent": true + } + } +} +``` + +### Transit Nodes for Parallel Tasks with Cache Invalidation + +Some tasks can run in parallel (don't need built output from dependencies) but must invalidate cache when dependency source code changes. + +**The problem with `dependsOn: ["^taskname"]`:** + +- Forces sequential execution (slow) + +**The problem with `dependsOn: []` (no dependencies):** + +- Allows parallel execution (fast) +- But cache is INCORRECT - changing dependency source won't invalidate cache + +**Transit Nodes solve both:** + +```json +{ + "tasks": { + "transit": { "dependsOn": ["^transit"] }, + "my-task": { "dependsOn": ["transit"] } + } +} +``` + +The `transit` task creates dependency relationships without matching any actual script, so tasks run in parallel with correct cache invalidation. + +**How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs. + +### With Environment Variables + +```json +{ + "globalEnv": ["NODE_ENV"], + "globalDependencies": [".env"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"], + "env": ["API_URL", "DATABASE_URL"] + } + } +} +``` + +## Reference Index + +### Configuration + +| File | Purpose | +| ------------------------------------------------------------------------------- | -------------------------------------------------------- | +| [configuration/RULE.md](./references/configuration/RULE.md) | turbo.json overview, Package Configurations | +| [configuration/tasks.md](./references/configuration/tasks.md) | dependsOn, outputs, inputs, env, cache, persistent | +| [configuration/global-options.md](./references/configuration/global-options.md) | globalEnv, globalDependencies, cacheDir, daemon, envMode | +| [configuration/gotchas.md](./references/configuration/gotchas.md) | Common configuration mistakes | + +### Caching + +| File | Purpose | +| --------------------------------------------------------------- | -------------------------------------------- | +| [caching/RULE.md](./references/caching/RULE.md) | How caching works, hash inputs | +| [caching/remote-cache.md](./references/caching/remote-cache.md) | Vercel Remote Cache, self-hosted, login/link | +| [caching/gotchas.md](./references/caching/gotchas.md) | Debugging cache misses, --summarize, --dry | + +### Environment Variables + +| File | Purpose | +| ------------------------------------------------------------- | ----------------------------------------- | +| [environment/RULE.md](./references/environment/RULE.md) | env, globalEnv, passThroughEnv | +| [environment/modes.md](./references/environment/modes.md) | Strict vs Loose mode, framework inference | +| [environment/gotchas.md](./references/environment/gotchas.md) | .env files, CI issues | + +### Filtering + +| File | Purpose | +| ----------------------------------------------------------- | ------------------------ | +| [filtering/RULE.md](./references/filtering/RULE.md) | --filter syntax overview | +| [filtering/patterns.md](./references/filtering/patterns.md) | Common filter patterns | + +### CI/CD + +| File | Purpose | +| --------------------------------------------------------- | ------------------------------- | +| [ci/RULE.md](./references/ci/RULE.md) | General CI principles | +| [ci/github-actions.md](./references/ci/github-actions.md) | Complete GitHub Actions setup | +| [ci/vercel.md](./references/ci/vercel.md) | Vercel deployment, turbo-ignore | +| [ci/patterns.md](./references/ci/patterns.md) | --affected, caching strategies | + +### CLI + +| File | Purpose | +| ----------------------------------------------- | --------------------------------------------- | +| [cli/RULE.md](./references/cli/RULE.md) | turbo run basics | +| [cli/commands.md](./references/cli/commands.md) | turbo run flags, turbo-ignore, other commands | + +### Best Practices + +| File | Purpose | +| ----------------------------------------------------------------------------- | --------------------------------------------------------------- | +| [best-practices/RULE.md](./references/best-practices/RULE.md) | Monorepo best practices overview | +| [best-practices/structure.md](./references/best-practices/structure.md) | Repository structure, workspace config, TypeScript/ESLint setup | +| [best-practices/packages.md](./references/best-practices/packages.md) | Creating internal packages, JIT vs Compiled, exports | +| [best-practices/dependencies.md](./references/best-practices/dependencies.md) | Dependency management, installing, version sync | + +### Watch Mode + +| File | Purpose | +| ------------------------------------------- | ----------------------------------------------- | +| [watch/RULE.md](./references/watch/RULE.md) | turbo watch, interruptible tasks, dev workflows | + +### Boundaries (Experimental) + +| File | Purpose | +| ----------------------------------------------------- | ----------------------------------------------------- | +| [boundaries/RULE.md](./references/boundaries/RULE.md) | Enforce package isolation, tag-based dependency rules | + +## Source Documentation + +This skill is based on the official Turborepo documentation at: + +- Source: `apps/docs/content/docs/` in the Turborepo repository +- Live: https://turborepo.dev/docs diff --git a/.codex/skills/turborepo/command/turborepo.md b/.codex/skills/turborepo/command/turborepo.md new file mode 100644 index 00000000000..8323edcbfab --- /dev/null +++ b/.codex/skills/turborepo/command/turborepo.md @@ -0,0 +1,70 @@ +--- +description: Load Turborepo skill for creating workflows, tasks, and pipelines in monorepos. Use when users ask to "create a workflow", "make a task", "generate a pipeline", or set up build orchestration. +--- + +Load the Turborepo skill and help with monorepo task orchestration: creating workflows, configuring tasks, setting up pipelines, and optimizing builds. + +## Workflow + +### Step 1: Load turborepo skill + +``` +skill({ name: 'turborepo' }) +``` + +### Step 2: Identify task type from user request + +Analyze $ARGUMENTS to determine: + +- **Topic**: configuration, caching, filtering, environment, CI, or CLI +- **Task type**: new setup, debugging, optimization, or implementation + +Use decision trees in SKILL.md to select the relevant reference files. + +### Step 3: Read relevant reference files + +Based on task type, read from `references//`: + +| Task | Files to Read | +| -------------------- | ------------------------------------------------------- | +| Configure turbo.json | `configuration/RULE.md` + `configuration/tasks.md` | +| Debug cache issues | `caching/gotchas.md` | +| Set up remote cache | `caching/remote-cache.md` | +| Filter packages | `filtering/RULE.md` + `filtering/patterns.md` | +| Environment problems | `environment/gotchas.md` + `environment/modes.md` | +| Set up CI | `ci/RULE.md` + `ci/github-actions.md` or `ci/vercel.md` | +| CLI usage | `cli/commands.md` | + +### Step 4: Execute task + +Apply Turborepo-specific patterns from references to complete the user's request. + +**CRITICAL - When creating tasks/scripts/pipelines:** + +1. **DO NOT create Root Tasks** - Always create package tasks +2. Add scripts to each relevant package's `package.json` (e.g., `apps/web/package.json`, `packages/ui/package.json`) +3. Register the task in root `turbo.json` +4. Root `package.json` only contains `turbo run ` - never actual task logic + +**Other things to verify:** + +- `outputs` defined for cacheable tasks +- `dependsOn` uses correct syntax (`^task` vs `task`) +- Environment variables in `env` key +- `.env` files in `inputs` if used +- Use `turbo run` (not `turbo`) in package.json and CI + +### Step 5: Summarize + +``` +=== Turborepo Task Complete === + +Topic: +Files referenced: + + +``` + + +$ARGUMENTS + diff --git a/.codex/skills/turborepo/references/best-practices/RULE.md b/.codex/skills/turborepo/references/best-practices/RULE.md new file mode 100644 index 00000000000..4870784d227 --- /dev/null +++ b/.codex/skills/turborepo/references/best-practices/RULE.md @@ -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 diff --git a/.codex/skills/turborepo/references/best-practices/dependencies.md b/.codex/skills/turborepo/references/best-practices/dependencies.md new file mode 100644 index 00000000000..90902e2970c --- /dev/null +++ b/.codex/skills/turborepo/references/best-practices/dependencies.md @@ -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 +``` diff --git a/.codex/skills/turborepo/references/best-practices/packages.md b/.codex/skills/turborepo/references/best-practices/packages.md new file mode 100644 index 00000000000..85cdf040e44 --- /dev/null +++ b/.codex/skills/turborepo/references/best-practices/packages.md @@ -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 ; +} +``` + +## 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. diff --git a/.codex/skills/turborepo/references/best-practices/structure.md b/.codex/skills/turborepo/references/best-practices/structure.md new file mode 100644 index 00000000000..a1b2e4fa4e8 --- /dev/null +++ b/.codex/skills/turborepo/references/best-practices/structure.md @@ -0,0 +1,270 @@ +# 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://v2-8-14-canary-8.turborepo.dev/schema.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. diff --git a/.codex/skills/turborepo/references/boundaries/RULE.md b/.codex/skills/turborepo/references/boundaries/RULE.md new file mode 100644 index 00000000000..3deb0a41591 --- /dev/null +++ b/.codex/skills/turborepo/references/boundaries/RULE.md @@ -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 diff --git a/.codex/skills/turborepo/references/caching/RULE.md b/.codex/skills/turborepo/references/caching/RULE.md new file mode 100644 index 00000000000..fe6388e284d --- /dev/null +++ b/.codex/skills/turborepo/references/caching/RULE.md @@ -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/ +├── .tar.zst # compressed outputs +├── .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 diff --git a/.codex/skills/turborepo/references/caching/gotchas.md b/.codex/skills/turborepo/references/caching/gotchas.md new file mode 100644 index 00000000000..695c783ee1d --- /dev/null +++ b/.codex/skills/turborepo/references/caching/gotchas.md @@ -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/.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/.json .turbo/runs/.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 diff --git a/.codex/skills/turborepo/references/caching/remote-cache.md b/.codex/skills/turborepo/references/caching/remote-cache.md new file mode 100644 index 00000000000..da76458bd29 --- /dev/null +++ b/.codex/skills/turborepo/references/caching/remote-cache.md @@ -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= +TURBO_TEAM= +``` + +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 diff --git a/.codex/skills/turborepo/references/ci/RULE.md b/.codex/skills/turborepo/references/ci/RULE.md new file mode 100644 index 00000000000..f331c2cf660 --- /dev/null +++ b/.codex/skills/turborepo/references/ci/RULE.md @@ -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 ` 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 ` 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 diff --git a/.codex/skills/turborepo/references/ci/github-actions.md b/.codex/skills/turborepo/references/ci/github-actions.md new file mode 100644 index 00000000000..1cdb34f33f1 --- /dev/null +++ b/.codex/skills/turborepo/references/ci/github-actions.md @@ -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 +``` diff --git a/.codex/skills/turborepo/references/ci/patterns.md b/.codex/skills/turborepo/references/ci/patterns.md new file mode 100644 index 00000000000..447509a1474 --- /dev/null +++ b/.codex/skills/turborepo/references/ci/patterns.md @@ -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 +``` diff --git a/.codex/skills/turborepo/references/ci/vercel.md b/.codex/skills/turborepo/references/ci/vercel.md new file mode 100644 index 00000000000..f21d41ac042 --- /dev/null +++ b/.codex/skills/turborepo/references/ci/vercel.md @@ -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 +``` diff --git a/.codex/skills/turborepo/references/cli/RULE.md b/.codex/skills/turborepo/references/cli/RULE.md new file mode 100644 index 00000000000..63f6f34d7d0 --- /dev/null +++ b/.codex/skills/turborepo/references/cli/RULE.md @@ -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 + +# Shorthand (only for one-off terminal invocations) +turbo +``` + +## 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` | diff --git a/.codex/skills/turborepo/references/cli/commands.md b/.codex/skills/turborepo/references/cli/commands.md new file mode 100644 index 00000000000..c1eb6b2f022 --- /dev/null +++ b/.codex/skills/turborepo/references/cli/commands.md @@ -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/.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 +``` diff --git a/.codex/skills/turborepo/references/configuration/RULE.md b/.codex/skills/turborepo/references/configuration/RULE.md new file mode 100644 index 00000000000..d69973b72a4 --- /dev/null +++ b/.codex/skills/turborepo/references/configuration/RULE.md @@ -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://v2-8-14-canary-8.turborepo.dev/schema.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/**"] + } + } +} +``` diff --git a/.codex/skills/turborepo/references/configuration/global-options.md b/.codex/skills/turborepo/references/configuration/global-options.md new file mode 100644 index 00000000000..dd1a5caef7a --- /dev/null +++ b/.codex/skills/turborepo/references/configuration/global-options.md @@ -0,0 +1,187 @@ +# 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 + +**Deprecated**: The daemon is no longer used for `turbo run` and this option will be removed in version 3.0. The daemon is still used by `turbo watch` and the Turborepo LSP. + +## 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 (only logging errors)` +- Cache hit: `cache hit, replaying logs (no errors) ` + +### `longerSignatureKey` + +Enforce a minimum key length of 32 bytes for `TURBO_REMOTE_CACHE_SIGNATURE_KEY` when `remoteCache.signature` is enabled. Short keys weaken HMAC-SHA256 signatures. Fails the run immediately if the key is too short. + +## 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. diff --git a/.codex/skills/turborepo/references/configuration/gotchas.md b/.codex/skills/turborepo/references/configuration/gotchas.md new file mode 100644 index 00000000000..225bd397aab --- /dev/null +++ b/.codex/skills/turborepo/references/configuration/gotchas.md @@ -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 ` + +```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. diff --git a/.codex/skills/turborepo/references/configuration/tasks.md b/.codex/skills/turborepo/references/configuration/tasks.md new file mode 100644 index 00000000000..a529b513a6d --- /dev/null +++ b/.codex/skills/turborepo/references/configuration/tasks.md @@ -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$/` | 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 | diff --git a/.codex/skills/turborepo/references/environment/RULE.md b/.codex/skills/turborepo/references/environment/RULE.md new file mode 100644 index 00000000000..26ef26739d9 --- /dev/null +++ b/.codex/skills/turborepo/references/environment/RULE.md @@ -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://v2-8-14-canary-8.turborepo.dev/schema.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"] + } + } +} +``` diff --git a/.codex/skills/turborepo/references/environment/gotchas.md b/.codex/skills/turborepo/references/environment/gotchas.md new file mode 100644 index 00000000000..4425ee3984f --- /dev/null +++ b/.codex/skills/turborepo/references/environment/gotchas.md @@ -0,0 +1,141 @@ +# 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://v2-8-14-canary-8.turborepo.dev/schema.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 diff --git a/.codex/skills/turborepo/references/environment/modes.md b/.codex/skills/turborepo/references/environment/modes.md new file mode 100644 index 00000000000..2e655331010 --- /dev/null +++ b/.codex/skills/turborepo/references/environment/modes.md @@ -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' +``` diff --git a/.codex/skills/turborepo/references/filtering/RULE.md b/.codex/skills/turborepo/references/filtering/RULE.md new file mode 100644 index 00000000000..04e19cc86d6 --- /dev/null +++ b/.codex/skills/turborepo/references/filtering/RULE.md @@ -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= +turbo run build -F +``` + +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]` | diff --git a/.codex/skills/turborepo/references/filtering/patterns.md b/.codex/skills/turborepo/references/filtering/patterns.md new file mode 100644 index 00000000000..17b9f1c5656 --- /dev/null +++ b/.codex/skills/turborepo/references/filtering/patterns.md @@ -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... +``` diff --git a/.codex/skills/turborepo/references/watch/RULE.md b/.codex/skills/turborepo/references/watch/RULE.md new file mode 100644 index 00000000000..44bcf13ebe3 --- /dev/null +++ b/.codex/skills/turborepo/references/watch/RULE.md @@ -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 +``` diff --git a/.eslintrc.json b/.eslintrc.json index 40a2b82f7d5..cc7bc2871d1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,39 +1,25 @@ { "root": true, "ignorePatterns": ["**/*"], - "plugins": ["@nx"], "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": { - "@nx/enforce-module-boundaries": [ - "warn", - { - "enforceBuildableLibDependency": true, - "allow": [], - "depConstraints": [ - { - "sourceTag": "*", - "onlyDependOnLibsWithTags": ["*"] - } - ] - } - ] - } - }, { "files": ["*.ts", "*.tsx"], - "extends": ["plugin:@nx/typescript"], + "extends": ["plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "parserOptions": { "ecmaVersion": 2020, "sourceType": "module" }, "rules": { "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-expressions": "off", "@typescript-eslint/no-unused-vars": "off", + "prefer-const": "off", "no-extra-semi": "error" } }, { "files": ["*.js", "*.jsx"], - "extends": ["plugin:@nx/javascript"], + "extends": ["eslint:recommended"], + "parserOptions": { "ecmaVersion": 2020, "sourceType": "module" }, "rules": { "@typescript-eslint/no-useless-constructor": "off", "no-extra-semi": "error" @@ -41,17 +27,13 @@ }, { "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], - "env": { - "jest": true - }, + "env": { "jest": true }, "rules": {} }, { "files": ["*.json"], "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": "off" - } + "rules": {} } ] } diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 58c5bc3c1a4..4a80455c698 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -24,14 +24,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -42,32 +34,26 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Remove cached node_modules - run: rm -rf node_modules .nx - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Install Playwright Browsers - run: pnpm exec playwright install --force + - name: Restore Turborepo cache + uses: actions/cache/restore@v4 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- - name: Install Cypress - # if: steps.browsers-cache.outputs.cache-hit != 'true' run: npx cypress install - - name: Check Code Format - run: npx nx format:check + - name: Check code format + run: node tools/scripts/check-format-changed.mjs - name: Verify Rslib Template Publint Wiring run: node packages/create-module-federation/scripts/verify-rslib-templates.mjs @@ -78,13 +64,11 @@ jobs: - name: Verify Publint Workflow Coverage run: node tools/scripts/verify-publint-workflow-coverage.mjs - - name: Print Number of CPU Cores - run: nproc + - name: Verify Turbo Conventions + run: pnpm run verify:turbo - - name: Run Build for All - run: | - npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 --skip-nx-cache - npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 + - name: Build packages + run: pnpm run build:packages - name: Check Package Publishing Compatibility run: | @@ -100,12 +84,8 @@ jobs: fi done - - name: Warm Nx Cache - run: npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 - - - name: Run Affected Test - timeout-minutes: 10 - run: npx nx affected -t test --parallel=3 --exclude='*,!tag:type:pkg' + - name: Run affected package tests + run: node tools/scripts/run-affected-package-tests.mjs e2e-modern: needs: checkout-install diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index 748e40e4e93..4c439cdb5b5 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -16,47 +16,105 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout PR - uses: actions/checkout@v4 + uses: actions/checkout@v5 + with: + fetch-depth: 0 - name: Setup pnpm uses: pnpm/action-setup@v4 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 20 cache: pnpm + cache-dependency-path: '**/pnpm-lock.yaml' - - name: Install dependencies (PR) + - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build packages (PR) - run: npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 --skip-nx-cache + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + + - name: Build packages (current) + run: pnpm run build:packages - - name: Measure bundle sizes (PR) + - name: Measure bundle sizes (current) run: node scripts/bundle-size-report.mjs --output current.json - - name: Checkout base branch - uses: actions/checkout@v4 + - name: Prepare base worktree + run: | + BASE_REF=${CI_LOCAL_BASE_REF:-${{ github.event.pull_request.base.sha }}} + BASE_PATH=".ci-local-base-${RANDOM}-${RANDOM}" + echo "BASE_REF=$BASE_REF" >> "$GITHUB_ENV" + echo "BASE_PATH=$BASE_PATH" >> "$GITHUB_ENV" + git worktree add "$BASE_PATH" "$BASE_REF" + case "$BASE_REF" in + origin/*) + LOCAL_BASE_REF="${BASE_REF#origin/}" + echo "LOCAL_BASE_REF=$LOCAL_BASE_REF" >> "$GITHUB_ENV" + git -C "$BASE_PATH" branch -f "$LOCAL_BASE_REF" "$BASE_REF" || true + ;; + esac + + - name: Restore Turborepo cache (base worktree) + uses: actions/cache@v5 with: - ref: ${{ github.event.pull_request.base.sha }} - path: base + path: ${{ env.BASE_PATH }}/.turbo + key: ${{ runner.os }}-turbo-base-${{ github.workflow }}-${{ github.job }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-base-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo-base-${{ github.workflow }}- + ${{ runner.os }}-turbo-base- - name: Install dependencies (base) - run: cd base && pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile + working-directory: ${{ env.BASE_PATH }} - name: Build packages (base) - run: cd base && npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 --skip-nx-cache + run: | + BUILD_PACKAGES_SCRIPT="$(node -e "const pkg=require('./package.json'); process.stdout.write(pkg.scripts?.['build:packages'] ?? '')")" + if [ -f turbo.json ]; then + echo "Falling back to Turbo package build on base worktree." + pnpm exec turbo run build --filter=./packages/** --concurrency=20 + elif [ -f nx.json ]; then + echo "Falling back to detached-safe Nx package build on base worktree." + npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 --skip-nx-cache + elif printf '%s' "$BUILD_PACKAGES_SCRIPT" | grep -Eq '(^|[[:space:]])nx[[:space:]].*affected'; then + echo "Detected legacy Nx affected build:packages script on detached base; using deterministic nx run-many instead." + npx nx run-many --targets=build --projects=tag:type:pkg --parallel=4 --skip-nx-cache + elif pnpm run build:packages; then + echo "Built base packages with root build:packages script." + else + echo "Unable to build base packages: missing compatible Turbo, Nx, or build:packages entrypoint" >&2 + exit 1 + fi + working-directory: ${{ env.BASE_PATH }} - name: Measure bundle sizes (base) - run: node scripts/bundle-size-report.mjs --output base.json --packages-dir base/packages + run: node scripts/bundle-size-report.mjs --output base.json --packages-dir "${{ env.BASE_PATH }}/packages" - name: Compare bundle sizes run: node scripts/bundle-size-report.mjs --compare base.json --current current.json --output stats.txt - - name: Upload bundle size stats + - name: Upload bundle size stats artifact uses: actions/upload-artifact@v4 with: name: bundle-size-stats path: stats.txt - retention-days: 5 + + - name: Cleanup base worktree + if: always() + run: | + if [ -n "${BASE_PATH:-}" ] && [ -d "${BASE_PATH}" ]; then + git worktree remove --force "${BASE_PATH}" + fi diff --git a/.github/workflows/devtools.yml b/.github/workflows/devtools.yml index cb28cff7ff2..888a257dc91 100644 --- a/.github/workflows/devtools.yml +++ b/.github/workflows/devtools.yml @@ -11,9 +11,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -env: - PLAYWRIGHT_BROWSERS_PATH: 0 - jobs: main: runs-on: ubuntu-latest @@ -24,14 +21,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -42,57 +31,38 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile && find . -maxdepth 6 -type d \( -name ".cache" -o -name ".modern-js" \) -exec rm -rf {} + + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Affected Build - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Install Playwright browsers + run: pnpm --filter @module-federation/devtools exec playwright install chromium + + - name: Build shared packages + run: pnpm run build:packages - - name: Configuration xvfb - shell: bash + - name: Install xvfb run: sudo apt-get update && sudo apt-get install xvfb - name: E2E Chrome Devtools Dev - uses: nick-fields/retry@v3 - with: - timeout_minutes: 15 - max_attempts: 3 - command: | - npx kill-port 3009 3010 3011 3012 3013 4001 && - pnpm run app:manifest:dev & echo "done" && \ - npx wait-on tcp:3009 tcp:3010 tcp:3011 tcp:3012 tcp:3013 && \ - sleep 10 && - npx nx e2e:devtools chrome-devtools + run: npx kill-port 3009 3010 3011 3012 3013 4001 && pnpm run app:manifest:dev & echo "done" && npx wait-on tcp:3009 tcp:3010 tcp:3011 tcp:3012 tcp:3013 && sleep 10 && pnpm exec turbo run test:e2e --filter=@module-federation/devtools --only - name: E2E Chrome Devtools Prod - uses: nick-fields/retry@v3 - with: - timeout_minutes: 15 - max_attempts: 3 - command: | - npx kill-port 3009 3010 3011 3012 3013 4001 && - npx kill-port 3009 3010 3011 3012 3013 4001 && - pnpm run app:manifest:prod & echo "done" && \ - npx wait-on tcp:3009 tcp:3010 tcp:3011 tcp:3012 tcp:3013 && \ - sleep 30 && - npx nx e2e:devtools chrome-devtools - - - name: kill port - run: npx kill-port 3013 3009 3010 3011 3012 4001; exit 0 + run: npx kill-port 3009 3010 3011 3012 3013 4001 && npx kill-port 3009 3010 3011 3012 3013 4001 && pnpm run app:manifest:prod & echo "done" && npx wait-on tcp:3009 tcp:3010 tcp:3011 tcp:3012 tcp:3013 && sleep 30 && pnpm exec turbo run test:e2e --filter=@module-federation/devtools --only - - name: Kill All Node Processes - run: pkill -f node || true + - name: Kill devtools ports + run: npx kill-port 3013 3009 3010 3011 3012 4001 || true diff --git a/.github/workflows/e2e-manifest.yml b/.github/workflows/e2e-manifest.yml index 71c19574e6e..e5220fed095 100644 --- a/.github/workflows/e2e-manifest.yml +++ b/.github/workflows/e2e-manifest.yml @@ -14,22 +14,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -40,26 +24,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=manifest-webpack-host || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=3008-webpack-host,3009-webpack-provider,3010-rspack-provider,3011-rspack-manifest-provider,3012-rspack-js-entry-provider || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -69,10 +62,10 @@ jobs: exit "$ci_status" fi - - name: E2E Test for Manifest Demo Development + - name: E2E Test for Manifest Demo (dev) if: steps.check-ci.outputs.run-e2e == 'true' - run: node tools/scripts/run-manifest-e2e.mjs --mode=dev + run: pnpm exec turbo run test:e2e --filter=3008-webpack-host - - name: E2E Test for Manifest Demo Production + - name: E2E Test for Manifest Demo (prod) if: steps.check-ci.outputs.run-e2e == 'true' - run: node tools/scripts/run-manifest-e2e.mjs --mode=prod + run: pnpm exec turbo run test:e2e:production --filter=3008-webpack-host diff --git a/.github/workflows/e2e-metro.yml b/.github/workflows/e2e-metro.yml index 87a40ef5d5f..56bb70058d5 100644 --- a/.github/workflows/e2e-metro.yml +++ b/.github/workflows/e2e-metro.yml @@ -43,9 +43,6 @@ jobs: - name: Install pnpm dependencies run: pnpm install --frozen-lockfile - - name: Set Nx base and head SHAs - uses: nrwl/nx-set-shas@v4 - - name: Evaluate Metro affected scope id: affected run: | @@ -106,7 +103,7 @@ jobs: shell: bash - name: Build shared packages - run: npx nx run-many --targets=build --projects=tag:type:pkg + run: pnpm run build:packages shell: bash - name: Install Maestro CLI @@ -195,7 +192,7 @@ jobs: ruby-version: ${{ env.RUBY_VERSION }} - name: Build shared packages - run: npx nx run-many --targets=build --projects=tag:type:pkg + run: pnpm run build:packages shell: bash - name: Install Maestro CLI and iOS Utilities diff --git a/.github/workflows/e2e-modern-ssr.yml b/.github/workflows/e2e-modern-ssr.yml index b143ed14610..7ef80ea910f 100644 --- a/.github/workflows/e2e-modern-ssr.yml +++ b/.github/workflows/e2e-modern-ssr.yml @@ -13,22 +13,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -39,26 +23,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=modernjs || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=@module-federation/modern-js,@module-federation/modern-js-v3,modernjs-ssr-host,modernjs-ssr-remote,modernjs-ssr-remote-new-version,modernjs-ssr-nested-remote,modernjs-ssr-dynamic-remote,modernjs-ssr-dynamic-remote-new-version,modernjs-ssr-dynamic-nested-remote,modernjs-ssr-data-fetch-host,modernjs-ssr-data-fetch-provider,modernjs-ssr-data-fetch-provider-csr || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -68,22 +61,6 @@ jobs: exit "$ci_status" fi - - name: E2E Test for Modern.js SSR + - name: Manifest verification for ModernJS SSR if: steps.check-ci.outputs.run-e2e == 'true' - uses: nick-fields/retry@v3 - with: - max_attempts: 2 - timeout_minutes: 10 - command: | - lsof -ti tcp:3050,3051,3052,3053,3054,3055,3056 | xargs -r kill && - pnpm run app:modern:dev & - sleep 30 && - for port in 3050 3051 3052 3053 3054 3055 3056; do - while true; do - response=$(curl -s http://127.0.0.1:$port/mf-manifest.json) - if echo "$response" | jq empty >/dev/null 2>&1; then - break - fi - sleep 1 - done - done + run: node tools/scripts/run-modern-e2e.mjs --mode=manifest diff --git a/.github/workflows/e2e-modern.yml b/.github/workflows/e2e-modern.yml index 35106f03e33..5e94b5b2a89 100644 --- a/.github/workflows/e2e-modern.yml +++ b/.github/workflows/e2e-modern.yml @@ -14,22 +14,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -40,26 +24,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=modernjs || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=@module-federation/modern-js,@module-federation/modern-js-v3 || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -69,9 +62,6 @@ jobs: exit "$ci_status" fi - - name: E2E Test for ModernJS + - name: Run Modern package test suite if: steps.check-ci.outputs.run-e2e == 'true' - run: npx kill-port --port 4001 && npx nx run-many --target=test:e2e --projects=modernjs --parallel=1 && npx kill-port --port 4001 - - - name: Kill ports - run: npx kill-port --port 4001 + run: pnpm exec turbo run test --filter=@module-federation/modern-js --filter=@module-federation/modern-js-v3 --concurrency=20 diff --git a/.github/workflows/e2e-next-dev.yml b/.github/workflows/e2e-next-dev.yml index db1f1029aac..8a44a3827e5 100644 --- a/.github/workflows/e2e-next-dev.yml +++ b/.github/workflows/e2e-next-dev.yml @@ -7,28 +7,14 @@ jobs: e2e-next-dev: runs-on: ubuntu-latest timeout-minutes: 30 + env: + NEXT_PRIVATE_LOCAL_WEBPACK: 'true' steps: - name: Checkout Repository uses: actions/checkout@v5 with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -39,29 +25,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Set local webpack - run: echo "NEXT_PRIVATE_LOCAL_WEBPACK=true" >> "$GITHUB_ENV" - - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=3000-home || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=@module-federation/3000-home,@module-federation/3001-shop,@module-federation/3002-checkout || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then diff --git a/.github/workflows/e2e-next-prod.yml b/.github/workflows/e2e-next-prod.yml index f4bcdf27981..b0f16200a84 100644 --- a/.github/workflows/e2e-next-prod.yml +++ b/.github/workflows/e2e-next-prod.yml @@ -13,22 +13,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -39,26 +23,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=3000-home || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=@module-federation/3000-home,@module-federation/3001-shop,@module-federation/3002-checkout || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then diff --git a/.github/workflows/e2e-node.yml b/.github/workflows/e2e-node.yml index 773cc3b3bf7..eb23e2c4be5 100644 --- a/.github/workflows/e2e-node.yml +++ b/.github/workflows/e2e-node.yml @@ -14,22 +14,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -40,26 +24,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=node-local-remote,node-remote,node-dynamic-remote-new-version,node-dynamic-remote || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=node-host,node-local-remote,node-remote,node-dynamic-remote-new-version,node-dynamic-remote,node-host-e2e || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -71,4 +64,4 @@ jobs: - name: E2E Node Federation if: steps.check-ci.outputs.run-e2e == 'true' - run: npx nx run-many --target=serve --projects=node-local-remote,node-remote,node-dynamic-remote-new-version,node-dynamic-remote --parallel=10 & echo "done" && sleep 25 && npx nx run-many --target=serve --projects=node-host & sleep 5 && npx wait-on tcp:3333 && npx nx run node-host-e2e:test:e2e + run: pnpm run e2e:node diff --git a/.github/workflows/e2e-router.yml b/.github/workflows/e2e-router.yml index cb9674329ff..c7962ec36bf 100644 --- a/.github/workflows/e2e-router.yml +++ b/.github/workflows/e2e-router.yml @@ -5,7 +5,7 @@ on: workflow_call: jobs: - e2e-runtime: + e2e-router: runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -14,22 +14,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -40,26 +24,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=router-host-2000,router-host-v5-2200,router-host-vue3-2100,router-remote1-2001,router-remote2-2002,router-remote3-2003,router-remote4-2004 || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=host,host-v5,host-vue3,remote1,remote2,remote3,remote4,remote5,remote6 || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -69,6 +62,6 @@ jobs: exit "$ci_status" fi - - name: E2E Test for Runtime Demo + - name: E2E Test for Router if: steps.check-ci.outputs.run-e2e == 'true' run: node tools/scripts/run-router-e2e.mjs --mode=dev diff --git a/.github/workflows/e2e-runtime.yml b/.github/workflows/e2e-runtime.yml index 9b34b74a2aa..0b78178bdf2 100644 --- a/.github/workflows/e2e-runtime.yml +++ b/.github/workflows/e2e-runtime.yml @@ -14,22 +14,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -40,26 +24,35 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=3005-runtime-host || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=runtime-host,runtime-remote1,runtime-remote2 || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -71,4 +64,4 @@ jobs: - name: E2E Test for Runtime Demo if: steps.check-ci.outputs.run-e2e == 'true' - run: npx kill-port --port 3005,3006,3007 && pnpm run app:runtime:dev & echo "done" && sleep 20 && npx nx run-many --target=test:e2e --projects=3005-runtime-host --parallel=1 && lsof -ti tcp:3005,3006,3007 | xargs kill + run: pnpm exec turbo run test:e2e --filter=runtime-host diff --git a/.github/workflows/e2e-shared-tree-shaking.yml b/.github/workflows/e2e-shared-tree-shaking.yml index 959e976c529..05b98b2f7ee 100644 --- a/.github/workflows/e2e-shared-tree-shaking.yml +++ b/.github/workflows/e2e-shared-tree-shaking.yml @@ -4,7 +4,7 @@ on: workflow_call: jobs: - e2e-runtime: + e2e-shared-tree-shaking: runs-on: ubuntu-latest timeout-minutes: 30 steps: @@ -13,22 +13,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -39,22 +23,31 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Install Cypress run: npx cypress install - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 @@ -68,10 +61,10 @@ jobs: exit "$ci_status" fi - - name: E2E Test for Shared Tree Shaking Demo - Mode runtime-infer + - name: E2E Shared Tree Shaking (runtime-infer) if: steps.check-ci.outputs.run-e2e == 'true' - run: npx kill-port --port 3001,3002 && nx run shared-tree-shaking-no-server-host:test:e2e && lsof -ti tcp:3001,3002 | xargs kill + run: npx kill-port --port 3001,3002 && pnpm exec turbo run test:e2e --filter=shared-tree-shaking-no-server-host --only && lsof -ti tcp:3001,3002 | xargs kill - - name: E2E Test for Shared Tree Shaking Demo - Mode server-calc + - name: E2E Shared Tree Shaking (server-calc) if: steps.check-ci.outputs.run-e2e == 'true' - run: npx kill-port --port 3001,3002,3003 && nx run shared-tree-shaking-with-server-host:test:e2e && lsof -ti tcp:3001,3002,3003 | xargs kill + run: npx kill-port --port 3001,3002,3003 && pnpm exec turbo run test:e2e --filter=shared-tree-shaking-with-server-host --only && lsof -ti tcp:3001,3002,3003 | xargs kill diff --git a/.github/workflows/e2e-treeshake.yml b/.github/workflows/e2e-treeshake.yml index fbcc8851cab..3e945e8433f 100644 --- a/.github/workflows/e2e-treeshake.yml +++ b/.github/workflows/e2e-treeshake.yml @@ -16,22 +16,6 @@ jobs: with: fetch-depth: 0 - - name: Cache Tool Downloads - uses: actions/cache@v5 - with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Set Playwright cache status - run: | - if [ -d "$HOME/.cache/ms-playwright" ] || [ -d "$HOME/.cache/Cypress" ]; then - echo "PLAYWRIGHT_CACHE_HIT=true" >> "$GITHUB_ENV" - else - echo "PLAYWRIGHT_CACHE_HIT=false" >> "$GITHUB_ENV" - fi - - name: Setup pnpm uses: pnpm/action-setup@v4 @@ -42,23 +26,32 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Set SKIP_DEVTOOLS_POSTINSTALL environment variable + - name: Export SKIP_DEVTOOLS_POSTINSTALL run: echo "SKIP_DEVTOOLS_POSTINSTALL=true" >> "$GITHUB_ENV" - - name: Install Dependencies + - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run Build for All - run: npx nx run-many --targets=build --projects=tag:type:pkg + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + + - name: Build shared packages + run: pnpm run build:packages - - name: Run condition check script + - name: Check CI conditions id: check-ci run: | ci_status=0 - node tools/scripts/ci-is-affected.mjs --appName=treeshake-server,treeshake-frontend || ci_status=$? + node tools/scripts/ci-is-affected.mjs --appName=@module-federation/treeshake-server,@module-federation/treeshake-frontend || ci_status=$? if [ "$ci_status" -eq 0 ]; then echo "run-e2e=true" >> "$GITHUB_OUTPUT" elif [ "$ci_status" -eq 1 ]; then @@ -70,8 +63,8 @@ jobs: - name: E2E Treeshake Server if: steps.check-ci.outputs.run-e2e == 'true' - run: npx nx run treeshake-server:test + run: pnpm exec turbo run test --filter=@module-federation/treeshake-server --only - name: E2E Treeshake Frontend if: steps.check-ci.outputs.run-e2e == 'true' - run: npx nx run treeshake-frontend:e2e + run: pnpm exec turbo run e2e --filter=@module-federation/treeshake-frontend --only diff --git a/.github/workflows/pkg-pr-new.yml b/.github/workflows/pkg-pr-new.yml index 7bf03c8ae08..944da1cc24c 100644 --- a/.github/workflows/pkg-pr-new.yml +++ b/.github/workflows/pkg-pr-new.yml @@ -35,28 +35,23 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Cache Tool Downloads + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Restore Turborepo cache uses: actions/cache@v5 with: - path: ~/.cache - key: ${{ runner.os }}-toolcache-${{ hashFiles('pnpm-lock.yaml') }} + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-toolcache- - - - name: Remove cached node_modules - run: rm -rf node_modules .nx - - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - - name: Install dependencies - run: pnpm install --frozen-lockfile + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- - name: Build package workspace targets - run: | - # Some package dts builds are intermittently flaky in CI; retry once. - npx nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache || \ - (sleep 5 && npx nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache) + run: pnpm run build:packages - name: Publish pkg.pr.new previews run: node tools/scripts/publish-pkg-pr-new-previews.mjs diff --git a/.github/workflows/release-pull-request.yml b/.github/workflows/release-pull-request.yml index 67227011fb4..ac3f8cfe92a 100644 --- a/.github/workflows/release-pull-request.yml +++ b/.github/workflows/release-pull-request.yml @@ -43,14 +43,24 @@ jobs: cache: 'pnpm' cache-dependency-path: '**/pnpm-lock.yaml' - - name: Set Nx SHA - uses: nrwl/nx-set-shas@v4 - - name: Install Dependencies run: pnpm install --frozen-lockfile --ignore-scripts # assemble-release-plan is forked, so need to build it before execute pnpm changeset version + + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Build Internal Changesets Dependencies - run: npx nx build assemble-release-plan + run: pnpm exec turbo run build --filter=@changesets/assemble-release-plan --concurrency=20 - name: Create Release Pull Request uses: module-federation/actions@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5abf93c848..fe0db77423c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,6 +58,18 @@ jobs: - name: Install deps run: pnpm install --frozen-lockfile + - name: Restore Turborepo cache + uses: actions/cache@v5 + with: + path: | + .turbo + **/.turbo + key: ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}-${{ github.ref_name }}- + ${{ runner.os }}-turbo-${{ github.workflow }}-${{ github.job }}- + ${{ runner.os }}-turbo- + - name: Generate preview version if: github.event.inputs.version == 'next' run: | @@ -67,7 +79,7 @@ jobs: - name: Build and test Packages run: | git fetch origin main - npx nx run-many --targets=build --projects=tag:type:pkg --skip-nx-cache + pnpm run build:packages ls -l packages/*/dist packages/*/package.json - name: Publish latest version diff --git a/.gitignore b/.gitignore index e57188e713f..14967688977 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,10 @@ update-commit-messages.sh /out-tsc /build /docs +/.turbo +**/.turbo/ /.nx +**/.nx/ # dependencies node_modules ./webpack-lib @@ -81,8 +84,6 @@ packages/enhanced/test/js vite.config.*.timestamp* vitest.config.*.timestamp* -.cursor/rules/nx-rules.mdc -.github/instructions/nx.instructions.md .temp-commit-msg # website-new @@ -97,6 +98,10 @@ packages/enhanced/.unpack-cache/** packages/enhanced/.codex/** packages/enhanced/generated/** **/.codex/ +!/.codex/ +!/.codex/skills/ +!/.codex/skills/turborepo/ +!/.codex/skills/turborepo/** .pnpm-store/ apps/rsc-demo/**/build/ apps/rsc-demo/e2e/test-results/ diff --git a/.husky/commit-msg b/.husky/commit-msg index df96e691d62..693dbea0ca6 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -2,4 +2,4 @@ . "$(dirname -- "$0")/_/husky.sh" -npm run commitlint ${1} +pnpm run commitlint ${1} diff --git a/.husky/pre-commit b/.husky/pre-commit index 7a13136e997..3c334246932 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,6 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -npm run lint-fix +pnpm run lint-fix -git add . \ No newline at end of file +git add . diff --git a/.vscode/launch.json b/.vscode/launch.json index bae9489f589..fd83e0d75ae 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,8 +2,8 @@ "version": "0.2.0", "configurations": [ { - "command": "pnpm run nx", - "name": "Run nx", + "command": "pnpm run turbo", + "name": "Run turbo", "request": "launch", "type": "node-terminal" }, @@ -119,9 +119,11 @@ "request": "launch", "runtimeExecutable": "pnpm", "runtimeArgs": [ - "nx", + "--filter", + "${input:package}", + "run", "test", - "${relativeFileDirname}", + "--", "--testFile=${fileBasename}", "--runInBand", "--no-cache" @@ -139,9 +141,11 @@ "request": "launch", "runtimeExecutable": "pnpm", "runtimeArgs": [ - "nx", - "test", + "--filter", "${input:package}", + "run", + "test", + "--", "--runInBand", "--no-cache" ], @@ -158,9 +162,11 @@ "request": "launch", "runtimeExecutable": "pnpm", "runtimeArgs": [ - "nx", - "run-many", - "--target=test", + "exec", + "turbo", + "run", + "test", + "--", "--runInBand", "--no-cache" ], diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7cfb90d1ef4..79ba01f3db5 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,28 +4,20 @@ { "label": "pnpm-build-enhanced", "type": "shell", - // Use nx to build only the 'enhanced' package dependencies if possible - // Adjust 'enhanced' if the nx project name is different - // --- Updated Command to include NVM --- - // This assumes NVM is installed in the default location ($HOME/.nvm) - // and uses the latest installed Node v18. Adjust if needed. - "command": "source $HOME/.nvm/nvm.sh && nvm use 18 && pnpm nx build enhanced", - // args are no longer needed as the full command is specified above - // "args": ["nx", "build", "enhanced"], + "command": "source $HOME/.nvm/nvm.sh && nvm use 20 && pnpm --filter @module-federation/enhanced run build", "problemMatcher": [], "options": { - // Ensure the shell runs commands correctly "shell": { - "executable": "/bin/zsh", // Or your preferred shell like /bin/bash - "args": ["-l", "-c"] // Use login shell args to potentially source NVM automatically, then execute command + "executable": "/bin/zsh", + "args": ["-l", "-c"] } }, "presentation": { - "reveal": "silent", // Don't show the terminal panel unless there's an error + "reveal": "silent", "panel": "dedicated", "clear": true }, - "detail": "Sources NVM, uses Node v18, then builds the 'enhanced' package using nx." + "detail": "Sources NVM, uses Node v20, then builds the enhanced package." } ] } diff --git a/AGENTS.md b/AGENTS.md index c1297648aa3..aaa99da375a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,9 @@ How AI coding agents should operate in this repository. +> **Important migration note (Turborepo):** +> Nx orchestration has been removed from this repository. Prefer Turbo + package scripts for all build/test/lint workflows. + ## Scope and Precedence - Scope: this file applies to the full repository root. @@ -22,13 +25,7 @@ If prose docs conflict with workflows (for example `README.md` or `CONTRIBUTING. - pnpm: `10.28.0` (from `package.json` `packageManager`) - Package manager: pnpm only -Preferred setup for agents: - -```bash -pnpm run setup:codex -``` - -Fallback setup: +Recommended setup for agents: ```bash corepack enable @@ -37,7 +34,7 @@ pnpm install --frozen-lockfile Worktree safety rule: -- In git worktree contexts, prefer `pnpm run nx:safe -- ` to avoid Nx daemon issues. +- In git worktree contexts, prefer running Turbo/package scripts directly (`pnpm exec turbo ...` / `pnpm --filter ... run ...`). ### Worktree Mode (Required in Worktrees) @@ -46,81 +43,122 @@ Use these rules whenever the checkout is a git worktree (typically `git rev-pars 1. Setup/install via: ```bash -pnpm run setup:codex -- --frozen-lockfile +corepack enable +pnpm install --frozen-lockfile ``` -2. Run Nx commands via: +2. Run workspace tasks via Turbo/package scripts: ```bash -pnpm run nx:safe -- +pnpm exec turbo run +pnpm --filter run