Monorepo
Turborepo-powered monorepo for scalable TypeScript development
Zeno uses Turborepo for monorepo management, providing fast builds, intelligent caching, and a clean separation of concerns across packages.
Why a Monorepo?
Even if you only ship a single application, a monorepo brings real benefits:
- Shared tooling — TypeScript configs, Tailwind presets, Vitest setup, and linting rules live in dedicated packages. Every app inherits them without copy-pasting.
- Atomic changes — Update a UI component and the app that consumes it in a single commit, with a single CI run that validates everything together.
- Incremental adoption — Start with one app. When a second project appears (a marketing site, an admin panel, a mobile BFF), it plugs into the same workspace instantly.
- Enforced boundaries — Packages expose explicit public APIs, preventing accidental coupling between unrelated parts of the codebase.
In short, the monorepo is not about having many apps today — it is about keeping your codebase modular and ready to grow.
Turborepo & Remote Caching
Turborepo analyses the dependency graph declared in turbo.json and runs only the tasks whose inputs have actually changed. Locally, results are stored in a file-system cache so repeated builds are nearly instant.
The same mechanism extends to CI through Turborepo Remote Caching. When enabled, build artifacts are stored in a shared cloud cache (hosted by Vercel or self-hosted). This means:
- A task that was already computed on another developer's machine or in a previous CI run is never re-executed — the cached output is downloaded instead.
- CI pipelines become significantly faster because only genuinely new work is performed.
- The cache is content-addressed and scoped to your team, so it is both safe and deterministic.
Structure
zeno/
├── apps/ # Applications
│ └── docs/ # Documentation site (@zeno-lib/docs)
├── packages/ # Shared libraries
│ ├── ui/ # React components (@zeno-lib/ui)
│ ├── typescript/ # TypeScript configs (@zeno-lib/typescript)
│ ├── tailwind/ # Tailwind config (@zeno-lib/tailwind)
│ ├── supabase/ # Supabase integration (@zeno-lib/supabase)
│ ├── vitest/ # Vitest config (@zeno-lib/vitest)
│ └── e2e/ # Playwright E2E tests (@zeno-lib/e2e)
├── turbo.json # Turborepo task configuration
└── pnpm-workspace.yaml # Workspace definitionCommands
# Development
pnpm dev # Start all dev servers
# Building
pnpm build # Build all packages
# Type checking
pnpm types:check # Check TypeScript across all packages
# Testing
pnpm test # Run unit tests
pnpm e2e # Run E2E tests
# Linting
pnpm lint # Check code quality
pnpm lint:fix # Auto-fix issues
# CI
pnpm ci # Run full CI pipelineFiltering
Run commands for a specific package using Turborepo filters:
pnpm turbo run dev --filter @zeno-lib/docs
pnpm turbo run build --filter @zeno-lib/uiAdding Dependencies
# Add to the root workspace
pnpm add -D <package> -w
# Add to a specific package
pnpm add <package> --filter @zeno-lib/uiWorkspace References
Reference internal packages in any package.json using workspace:*:
{
"dependencies": {
"@zeno-lib/ui": "workspace:*",
"@zeno-lib/typescript": "workspace:*"
}
}