Monorepo Structure
Directory layout, Turborepo configuration, and development workflow for the pnpm monorepo.
Overview
ScaleRocket is a pnpm workspace monorepo powered by Turborepo. It contains three apps, four shared packages, and a Supabase project -- all in one repository.
Directory Structure
scalerocket/
├── apps/
│ ├── web/ # Next.js marketing site
│ ├── app/ # Vite + React dashboard (SPA)
│ └── ops/ # Vite + React admin panel
├── packages/
│ ├── ui/ # Shared component library
│ ├── config/ # Site config, pricing, constants
│ ├── types/ # TypeScript types (DB, API)
│ └── emails/ # Email templates + sending
├── supabase/
│ ├── functions/ # Edge Functions
│ ├── migrations/ # Database migrations
│ └── config.toml # Supabase local config
├── turbo.json # Turborepo pipeline config
├── pnpm-workspace.yaml # Workspace definition
└── package.json # Root scriptsapps/ -- Applications
apps/web -- Marketing Site
- Stack: Next.js (App Router)
- Purpose: Landing page, pricing, blog, SEO pages
- Port:
http://localhost:3000 - Deploys to: Vercel
apps/app -- User Dashboard
- Stack: Vite + React + React Router
- Purpose: Authenticated user experience (dashboard, settings, billing)
- Port:
http://localhost:5173 - Deploys to: Vercel
apps/ops -- Admin Panel
- Stack: Vite + React
- Purpose: Internal admin dashboard (users, subscriptions, blog)
- Port:
http://localhost:5174 - Deploys to: Vercel (restricted access)
packages/ -- Shared Code
| Package | Import As | Purpose |
|---|---|---|
packages/ui | @saas/ui | Button, Card, Input, Toast, ThemeProvider |
packages/config | @saas/config | site.ts, pricing.ts, constants |
packages/types | @saas/types | Database types, API types |
packages/emails | @saas/emails | React Email templates, send utility |
supabase/ -- Backend
supabase/functions/-- Deno-based Edge Functionssupabase/migrations/-- SQL migration filessupabase/config.toml-- Local development configuration
Turborepo Configuration
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^build"]
},
"type-check": {
"dependsOn": ["^build"]
}
}
}Key points:
builddepends on^build-- packages are built before apps that use them.devis not cached and runs persistently.- Turborepo caches build outputs for faster rebuilds.
pnpm Workspace
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*".npmrc Configuration
The root .npmrc file contains:
shamefully-hoist=trueThis setting is required for pnpm workspaces with shared packages. By default, pnpm creates a strict node_modules structure where each package only sees its declared dependencies. With shamefully-hoist=true, dependencies are hoisted to the root node_modules/, which is necessary because Vite resolves dependencies from the app context and needs hoisted packages to find shared dependencies from workspace packages like @saas/ui and @saas/config.
Without this setting, you may encounter "module not found" errors when apps try to use dependencies declared in shared packages.
Dev Commands
# Start everything (all apps + Supabase)
pnpm dev
# Start a specific app
pnpm --filter web dev # Marketing site
pnpm --filter app dev # User dashboard
pnpm --filter ops dev # Admin panel
# Start Supabase locally
pnpm supabase start
# Start Edge Functions
pnpm supabase functions serveBuild Pipeline
# Build all apps and packages
pnpm build
# Build a specific app
pnpm --filter web build
pnpm --filter app build
# Type-check everything
pnpm type-check
# Lint everything
pnpm lintTurborepo handles the dependency graph automatically. When you run pnpm build, it:
- Builds
packages/typesfirst (no dependencies) - Builds
packages/configandpackages/ui(may depend on types) - Builds
packages/emails(depends on ui for components) - Builds all three apps in parallel (depend on packages)
Adding a New App
- Create the app directory:
mkdir apps/my-app
cd apps/my-app
pnpm init- Add workspace dependencies:
pnpm --filter my-app add @saas/ui @saas/config @saas/types --workspace- The app is automatically picked up by the pnpm workspace and Turborepo.
Adding a New Package
- Create the package directory:
mkdir packages/my-package
cd packages/my-package
pnpm init- Set the package name in
package.json:
{
"name": "@saas/my-package",
"main": "./src/index.ts"
}- Use it in any app:
pnpm --filter app add @saas/my-package --workspaceDone reading? Mark this page as complete.