Turborepo
Rule
Scope: Monorepo tasks and package setup.
Just-in-Time Packages
- MUSTExport source TypeScript from packages via
exports(nodistbuilds). - SHOULDUse source-mapped imports for better DX.
{
"name": "@project/utils",
"exports": {
".": "./src/index.ts",
"./*": "./src/*.ts"
}
}
Task Configuration
- MUST
devtasks are persistent and uncached. - MUST
build,typecheck,lint,testdepend on upstream (^task). - SHOULDKeep
turbo.jsonminimal — only define tasks that need configuration.
Caching
- MUSTDeclare
outputsfor every task that writes to disk ("outputs": ["dist/**"]). Without this, results aren't cached. - MUSTInclude environment variables in
envkey so cache invalidates on change. UseglobalEnvfor variables affecting all tasks. - MUSTAdd
.envfiles toinputs— Turbo doesn't auto-detect.envchanges. - SHOULDAvoid root
.envfiles. Place them in packages that need them for explicit dependency tracking.
Package Configuration
- SHOULDUse package-level
turbo.jsonwith"extends": ["//"]for package-specific overrides instead ofpackage#taskentries in root config. - MUSTDeclare workspace dependencies in
package.json("@repo/types": "workspace:*") for^buildto trigger correctly.
CI
- SHOULDUse
turbo run build(notturbo build) in scripts and CI for reproducibility. - SHOULDUse
--affectedin CI to run only changed packages plus their dependents. - MUSTUse
pnpm install --frozen-lockfilein CI for reproducible builds.
Anti-Patterns
| Never | Why | Instead |
|---|---|---|
Chain with && in root scripts | Bypasses Turbo's dependency graph | Use turbo run |
prebuild scripts building deps | Duplicates Turbo's work | Declare package dependencies |
../ in inputs | Implicit cross-package deps | Use $TURBO_ROOT$/ |
--parallel flag | Ignores dependency order | Configure dependsOn |
| Cross-package relative imports | Tight coupling | Import from package entry points |