Monorepo Guide
import { Aside, FileTree } from ‘@astrojs/starlight/components’;
Overview
Status: Current structure is sound — optimization opportunities documented below. Project type: Multi-subdomain platform (7 independent deployable units) Package manager: pnpm (workspace protocol) Build tool: Turborepo
What’s Working Well
- Scoped package naming —
@brettjohnson/www,@brettjohnson/lib, etc. — prevents namespace collisions and enables clean cross-package imports - Turbo integration — task pipeline with dependency ordering; global cache for faster builds; filter support (
--filter=@brettjohnson/www) - Shared packages —
config-eslint,config-tailwind,config-typescript,lib,ui— reduces drift across apps - pnpm workspaces —
workspace:*protocol correctly used throughout
Folder Structure
Shared Packages — What Goes Where
| Package | Purpose | Used by |
|---|---|---|
@brettjohnson/ui | React component library (Button, Card, Nav, etc.) | www, book, media, podcast, training, admin |
@brettjohnson/lib | TypeScript types, Zod schemas, utilities | www, api, book, admin |
@brettjohnson/config-eslint | ESLint rules | All apps |
@brettjohnson/config-tailwind | Tailwind theme + CSS tokens | All frontend apps |
@brettjohnson/config-typescript | TypeScript compiler options | All apps |
What belongs in @brettjohnson/lib
- TypeScript types used across apps (
InquiryFormData,ServiceTier, etc.) - Zod validation schemas (add before M4-T1)
- Supabase client wrapper
- External API client utilities (HubSpot, Resend)
- Shared constants (fee ranges, engagement types)
What belongs in @brettjohnson/ui
- Reusable React components (Button, Card, Nav, Footer, etc.)
- Shared hooks used across apps
- Brand design token re-exports
What stays in the app
- Page-specific components (Hero, Services grid, Testimonials)
- App-specific business logic
- Internal admin-only code
Dependency Management
Adding a dependency to a specific app
pnpm add <package> --filter @brettjohnson/wwwAdding a shared dev dependency (all packages)
pnpm add -D <package> -wUsing a workspace package
{ "dependencies": { "@brettjohnson/ui": "workspace:*" }}Turborepo Task Pipeline
The turbo.json task graph ensures correct build ordering:
type-check ──► buildlint ──────────►test ──────────► (requires ^build)Running tasks
pnpm dev # All apps in parallelpnpm dev:www # Main site only (port 3000)pnpm dev:api # API only (port 3001)pnpm build # Full production build (all apps)pnpm lint # ESLint across all appspnpm type-check # tsc --noEmit across all appspnpm test # Jest across all appsSelective builds (faster in CI)
# Build only changed packages + their dependentsturbo build --filter=...[HEAD^1]
# Build a specific app and its dependenciesturbo build --filter=@brettjohnson/www...Remote caching
Set TURBO_TOKEN and TURBO_TEAM in Vercel/GitHub Actions to enable shared remote cache across CI runs.
Adding a New App
-
Create the directory under
apps/:Terminal window cp -r apps/book apps/newapp# Update package.json name + port -
Add a
dev:newappscript to rootpackage.json -
Add a project entry to
apps/api/lib/milestones.tsstatus tracker -
Create a
vercel.jsonin the new app pointing to itsdistor.nextdirectory -
Add the subdomain to
apps/docs/src/content/docs/architecture/infrastructure.md
Versioning Strategy
All apps release together — unified versioning (v1.0.0-mvp bumps every package simultaneously). semantic-release runs on merge to main and determines the next version from Conventional Commits.
See Versioning Policy for details.