This document explains how the modern tooling works across the react-creme Turborepo monorepo.
The monorepo uses a hybrid approach:
- Root-level coordination via Turbo
- Package-level execution for actual linting/checking
bun lintWhat happens:
- Turbo runs
oxlinttask across all packages (fast pre-check) - Turbo then runs
linttask across all packages (full ESLint) - Each package has its own
oxlintscript that runs locally - Turbo orchestrates and caches results
Monorepo flow:
Root: turbo run oxlint
├─> packages/lib: oxlint .
├─> packages/documentation: oxlint .
└─> packages/storybook: oxlint .
Then:
Root: turbo run lint
├─> packages/lib: eslint + stylelint
├─> packages/documentation: eslint + stylelint
└─> packages/storybook: (builds first)
Root command:
bun oxlintWhat it does:
- Runs oxlint on entire monorepo from root
- Uses
.oxlintrc.jsonconfiguration - 50-100x faster than ESLint
- Used in pre-commit hooks for instant feedback
Turbo orchestration:
turbo run oxlint- Runs
oxlintscript in each package - Parallelized across packages
- Results are cached
Root command:
bun knip # Check all packages
bun knip:production # Production deps onlyWhat it does:
- Analyzes entire monorepo structure
- Uses
.knip.jsonwith workspace configuration - Finds unused:
- Files
- Exports
- Dependencies
- Dev dependencies
Monorepo configuration:
- Configured in
.knip.jsonwith workspace awareness - Understands Turbo dependency graph
- Ignores appropriate files per package
Located: .husky/pre-commit
Workflow:
bun oxlint- Fast check on entire repo (from root)npx lint-staged- Runs on staged files only:- Prettier formatting
- ESLint fixing
- Stylelint fixing
Why this order?
- Oxlint catches obvious issues in <1 second
- Lint-staged does thorough fixing on changed files
- Prevents slow commits while maintaining quality
Each package has these scripts:
{
"oxlint": "oxlint .",
"eslint": "eslint ./components/**/*.{tsx,ts}",
"lint": "bun eslint && bun lint:css && bun prettier:check",
"lint:css": "stylelint ./components/**/*.scss ./design/**/*.scss"
}{
"oxlint": "oxlint .",
"lint": "eslint \"components/**/*.{js,jsx,ts,tsx}\" && bun lint:css && bun prettier:check",
"lint:css": "stylelint ./components/**/*.scss"
}{
"oxlint": "oxlint ."
}Located in turbo.json:
{
"oxlint": {
"cache": true, // Results are cached
"outputs": [] // No file outputs
},
"lint": {
"dependsOn": ["^build"], // Needs dependencies built first
"outputs": []
},
"knip": {
"cache": false, // Always runs (checks for changes)
"outputs": []
}
}# Run across all packages (turbo orchestrates)
bun lint # oxlint + full lint
bun oxlint # Fast check only
bun knip # Check unused code
bun format # Format all packages
bun test # Test all packages
# Run with filters
bun lint --filter=react-creme # Lib only
bun lint --filter=*docu* # Documentation onlycd packages/lib
bun oxlint # Run oxlint on lib only
bun lint # Run full lint on lib only
bun eslint # Run ESLint onlyIn .github/workflows/CI.yml:
# Build job includes linting
- run: bun lint # Runs oxlint + ESLint via Turbo
# Can also run individually
- run: bun oxlint
- run: bun knipTurbo caching works in CI:
- Lint results are cached
- Only changed packages are re-linted
- Faster CI times
- 50-100x faster than ESLint
- Catches 70%+ of issues instantly
- Perfect for pre-commit hooks
- Parallelized across CPU cores
- Parallel execution across packages
- Smart caching of results
- Dependency-aware task running
- Incremental - only runs what changed
# Without oxlint (ESLint only)
bun lint → ~15-20 seconds
# With oxlint first
bun oxlint → 0.5 seconds ✓
bun lint → ~15 seconds (if oxlint passes)
# With Turbo caching (no changes)
bun lint → <1 second (cache hit)Solution:
bun install # Install dependenciesCheck:
- Package has
oxlintscript inpackage.json turbo.jsonhasoxlinttask defined- Run
bun installafter adding new packages
Check:
- Husky is installed:
bun prepare - Pre-commit hook is executable:
chmod +x .husky/pre-commit - You're in a git repository
Solution:
- Add to
ignoreDependenciesin.knip.json - Add to
ignorepatterns - Check workspace configuration
- Always use root commands for consistency
- Let Turbo orchestrate - don't bypass it
- Trust the cache - Turbo knows when to invalidate
- Run
bun oxlintbefore committing manually - Use
bun knipperiodically to clean up - Filter for focused work:
--filter=package-name
When adding a new package:
-
Add
oxlintscript to package.json:{ "scripts": { "oxlint": "oxlint .", "lint": "..." } } -
Add workspace to
.knip.json:{ "workspaces": { "packages/new-package": { "entry": ["..."], "project": ["..."] } } } -
Test:
bun oxlint --filter=new-package bun lint --filter=new-package
✅ Monorepo-aware: All tools understand workspace structure ✅ Turbo-orchestrated: Parallel execution with caching ✅ Fast feedback: Oxlint pre-checks in <1 second ✅ Comprehensive: Full ESLint + Stylelint coverage ✅ CI-optimized: Caching works in GitHub Actions ✅ Pre-commit: Quality gates before commit