Build System
The Sustentus monorepo uses Turborepo to orchestrate builds, manage dependencies, and cache outputs efficiently.
Overview
The build system provides:
- Task orchestration: Run tasks across multiple packages
- Dependency awareness: Build packages in the correct order
- Intelligent caching: Skip unchanged work
- Parallel execution: Run independent tasks simultaneously
- Remote caching: Share cache across team (when configured)
Turborepo Configuration
Configuration is in turbo.json at the repository root:
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env"],
"globalEnv": [],
"remoteCache": {
"enabled": true
},
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"dev": {
"persistent": true,
"cache": false,
"inputs": ["$TURBO_DEFAULT$", ".env*"]
},
"lint": {
"inputs": ["$TURBO_DEFAULT$"],
"outputs": []
},
"clean": {
"cache": false,
"inputs": [],
"outputs": []
},
"start": {
"dependsOn": ["build"],
"persistent": true,
"cache": false
},
"env:pull": {
"persistent": false,
"cache": true,
"inputs": ["$TURBO_DEFAULT$", ".env*"]
}
}
}Task Definitions
- build:
dependsOn: ["^build"]builds package dependencies first; caches Next.js builds (.next/, excluding the cache) and package builds (dist/). Source files and.env*invalidate the cache. - dev:
persistent: true,cache: false— runs dev servers indefinitely, watching source and.env*. - lint: lints all source files; no outputs to cache; runs in parallel across packages.
- clean: never cached; runs each package’s clean script (e.g.
rm -rf dist).
Dependency Graph
Turborepo derives the build order from package.json dependencies. The workspace:* protocol links to the local workspace package:
{
"dependencies": {
"@sustentus/ui": "workspace:*"
}
}Build order:
@sustentus/ui(no dependencies)@sustentus/services(no dependencies)@sustentus/marketing(depends on@sustentus/ui)@sustentus/storybook(depends on@sustentus/ui)@sustentus/docs(no dependencies)
@sustentus/ui and @sustentus/services build in parallel (no dependencies); then marketing and storybook build in parallel (both depend on ui). A change to @sustentus/ui triggers a rebuild of every downstream app.
Caching
Turbo hashes all input files, and restores cached outputs when a matching hash exists rather than re-running the task. Cache invalidates when input files, environment variables, global dependencies, or task configuration change.
- Local cache:
.turbo/cache/(gitignored) - Remote cache: Vercel Remote Cache (when configured)
# Skip cache (always run)
turbo run build --force
# View cache hits/misses
turbo run build --summarizePackage Scripts
Root Scripts
Defined in root package.json:
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "eslint .",
"clean": "turbo run clean",
"format": "prettier --write \"**/*.{ts,tsx,md}\""
}
}Filter Scripts
Convenience scripts for specific packages:
{
"scripts": {
"marketing:dev": "turbo run dev --filter=@sustentus/marketing",
"marketing:build": "turbo run build --filter=@sustentus/marketing",
"ui:dev": "turbo run dev --filter=@sustentus/ui",
"ui:build": "turbo run build --filter=@sustentus/ui",
"packages:build": "turbo run build --filter=@sustentus/ui --filter=@sustentus/services",
"packages:dev": "turbo run dev --filter=@sustentus/ui --filter=@sustentus/services"
}
}Filters also accept globs (--filter='./apps/*') and dependency selectors (--filter=@sustentus/marketing... for a package and its dependencies, --filter=...@sustentus/ui for a package and its dependents).
Build Outputs
- Next.js apps:
.next/directory (server pages, client bundles, static assets, cache). - TSUP packages:
dist/directory —index.js,index.d.ts,index.js.map, andglobals.css(UI package).
All build outputs are cached by Turborepo, gitignored, and cleaned by pnpm clean.
Environment Variables
.env invalidates the cache globally via globalDependencies. Tasks also watch .env* via their inputs (covering .env, .env.local, .env.development, .env.production). In CI, set env vars before building.