CLAUDE.md
Monorepo Project Guidelines
Overview
This is a monorepo managed with Turborepo/pnpm (or Nx). Multiple packages and apps share code through internal packages.
Tech Stack
- Monorepo Tool: Turborepo (or Nx)
- Package Manager: pnpm (with workspaces)
- Build: Each package has its own build config
- Shared Config: ESLint, TypeScript, Prettier configs as packages
Project Structure
├── apps/
│ ├── web/ # Next.js frontend
│ ├── api/ # Backend service
│ ├── mobile/ # React Native app
│ └── docs/ # Documentation site
├── packages/
│ ├── ui/ # Shared UI components
│ ├── database/ # Database client & schema
│ ├── utils/ # Shared utilities
│ ├── types/ # Shared TypeScript types
│ ├── config-eslint/ # Shared ESLint config
│ ├── config-typescript/ # Shared tsconfig
│ └── config-tailwind/ # Shared Tailwind config
├── turbo.json # Turborepo config
├── pnpm-workspace.yaml # Workspace definition
└── package.json # Root package.json
Development Commands
# Install all dependencies
pnpm install
# Run all apps in development
pnpm dev
# Run specific app
pnpm dev --filter=web
pnpm dev --filter=api
# Build all packages
pnpm build
# Build specific package and its dependencies
pnpm build --filter=web...
# Run tests across all packages
pnpm test
# Run linting
pnpm lint
# Add dependency to specific package
pnpm add lodash --filter=web
# Add internal package as dependency
pnpm add @repo/ui --filter=web --workspace
Workspace Configuration
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"]
}
}
}
Internal Packages
Creating a New Package
# Create package directory
mkdir -p packages/new-package/src
cd packages/new-package
# Initialize package.json
cat > package.json << 'EOF'
{
"name": "@repo/new-package",
"version": "0.0.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"dev": "tsup src/index.ts --format cjs,esm --dts --watch"
}
}
EOF
Using Internal Packages
// apps/web/package.json
{
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/utils": "workspace:*"
}
}
// apps/web/app/page.tsx
import { Button } from '@repo/ui'
import { formatDate } from '@repo/utils'
Shared Configurations
TypeScript Config
// packages/config-typescript/base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true
}
}
// apps/web/tsconfig.json
{
"extends": "@repo/config-typescript/nextjs.json",
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
ESLint Config
// packages/config-eslint/next.js
module.exports = {
extends: ['next/core-web-vitals', './base.js'],
// shared rules
}
// apps/web/.eslintrc.js
module.exports = {
extends: ['@repo/config-eslint/next'],
}
Best Practices
Package Boundaries
- Keep packages focused and small
- Avoid circular dependencies between packages
- Use
@repo/prefix for internal packages - Define clear public APIs (index.ts exports)
Dependency Management
- Hoist common dependencies to root when possible
- Pin versions for consistency across packages
- Use
workspace:*for internal dependencies - Keep devDependencies at package level when specific
Caching
- Configure proper
outputsin turbo.json - Use remote caching in CI (Vercel Remote Cache, Nx Cloud)
- Don't cache
devtasks - Include hash inputs for environment-dependent builds
CI/CD
# .github/workflows/ci.yml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Test
run: pnpm test
# Only run affected tasks
- name: Build affected
run: pnpm turbo build --filter=...[origin/main]
Common Patterns
Shared UI Components
// packages/ui/src/button.tsx
export interface ButtonProps {
variant?: 'primary' | 'secondary'
children: React.ReactNode
}
export function Button({ variant = 'primary', children }: ButtonProps) {
return <button className={styles[variant]}>{children}</button>
}
// packages/ui/src/index.ts
export { Button } from './button'
export type { ButtonProps } from './button'
Shared Database Package
// packages/database/src/client.ts
import { PrismaClient } from '@prisma/client'
export const db = new PrismaClient()
export * from '@prisma/client'
// Used in apps
import { db, User } from '@repo/database'
Troubleshooting
- Package not found: Run
pnpm installafter adding workspace dependency - Type errors in IDE: Restart TypeScript server after package changes
- Cache issues: Run
pnpm turbo cleanto clear Turborepo cache - Circular dependency: Check imports with
madge --circular