git:20260307.47a5dbc to git:20260522.be57c0b

2 added, 399 removed. Audit A to A.

---
name: monorepo-management
description: Master monorepo management with Turborepo, Nx, and pnpm workspaces to build efficient, scalable multi-package repositories with optimized builds and dependency management. Use when setting up monorepos, optimizing builds, or managing shared dependencies.
---
# Monorepo Management
Build efficient, scalable monorepos that enable code sharing, consistent tooling, and atomic changes across multiple packages and applications.
## When to Use This Skill
- Setting up new monorepo projects
- Migrating from multi-repo to monorepo
- Optimizing build and test performance
- Managing shared dependencies
- Implementing code sharing strategies
- Setting up CI/CD for monorepos
- Versioning and publishing packages
- Debugging monorepo-specific issues
## Core Concepts
### 1. Why Monorepos?
**Advantages:**
- Shared code and dependencies
- Atomic commits across projects
- Consistent tooling and standards
- Easier refactoring
- Simplified dependency management
- Better code visibility
**Challenges:**
- Build performance at scale
- CI/CD complexity
- Access control
- Large Git repository
### 2. Monorepo Tools
**Package Managers:**
- pnpm workspaces (recommended)
- npm workspaces
- Yarn workspaces
**Build Systems:**
- Turborepo (recommended for most)
- Nx (feature-rich, complex)
- Lerna (older, maintenance mode)
## Turborepo Setup
### Initial Setup
```bash
# Create new monorepo
npx create-turbo@latest my-monorepo
cd my-monorepo
# Structure:
# apps/
# web/ - Next.js app
# docs/ - Documentation site
# packages/
# ui/ - Shared UI components
# config/ - Shared configurations
# tsconfig/ - Shared TypeScript configs
# turbo.json - Turborepo configuration
# package.json - Root package.json
```
### Configuration
```json
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
}
}
}
```
```json
// package.json (root)
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"clean": "turbo run clean && rm -rf node_modules"
},
"devDependencies": {
"turbo": "^1.10.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
},
"packageManager": "pnpm@8.0.0"
}
```
### Package Structure
```json
// packages/ui/package.json
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./button": {
"import": "./dist/button.js",
"types": "./dist/button.d.ts"
}
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts",
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
"lint": "eslint src/",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"@repo/tsconfig": "workspace:*",
"tsup": "^7.0.0",
"typescript": "^5.0.0"
},
"dependencies": {
"react": "^18.2.0"
}
}
```
- ## pnpm Workspaces
-
- ### Setup
-
- ```yaml
- # pnpm-workspace.yaml
- packages:
- - "apps/*"
- - "packages/*"
- - "tools/*"
- ```
-
- ```json
- // .npmrc
- # Hoist shared dependencies
- shamefully-hoist=true
-
- # Strict peer dependencies
- auto-install-peers=true
- strict-peer-dependencies=true
-
- # Performance
- store-dir=~/.pnpm-store
- ```
-
- ### Dependency Management
-
- ```bash
- # Install dependency in specific package
- pnpm add react --filter @repo/ui
- pnpm add -D typescript --filter @repo/ui
-
- # Install workspace dependency
- pnpm add @repo/ui --filter web
-
- # Install in all packages
- pnpm add -D eslint -w
-
- # Update all dependencies
- pnpm update -r
-
- # Remove dependency
- pnpm remove react --filter @repo/ui
- ```
-
- ### Scripts
-
- ```bash
- # Run script in specific package
- pnpm --filter web dev
- pnpm --filter @repo/ui build
-
- # Run in all packages
- pnpm -r build
- pnpm -r test
-
- # Run in parallel
- pnpm -r --parallel dev
-
- # Filter by pattern
- pnpm --filter "@repo/*" build
- pnpm --filter "...web" build # Build web and dependencies
- ```
-
- ## Nx Monorepo
-
- ### Setup
-
- ```bash
- # Create Nx monorepo
- npx create-nx-workspace@latest my-org
-
- # Generate applications
- nx generate @nx/react:app my-app
- nx generate @nx/next:app my-next-app
-
- # Generate libraries
- nx generate @nx/react:lib ui-components
- nx generate @nx/js:lib utils
- ```
-
- ### Configuration
-
- ```json
- // nx.json
- {
- "extends": "nx/presets/npm.json",
- "$schema": "./node_modules/nx/schemas/nx-schema.json",
- "targetDefaults": {
- "build": {
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"],
- "cache": true
- },
- "test": {
- "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
- "cache": true
- },
- "lint": {
- "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
- "cache": true
- }
- },
- "namedInputs": {
- "default": ["{projectRoot}/**/*", "sharedGlobals"],
- "production": [
- "default",
- "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
- "!{projectRoot}/tsconfig.spec.json"
- ],
- "sharedGlobals": []
- }
- }
- ```
-
- ### Running Tasks
-
- ```bash
- # Run task for specific project
- nx build my-app
- nx test ui-components
- nx lint utils
-
- # Run for affected projects
- nx affected:build
- nx affected:test --base=main
-
- # Visualize dependencies
- nx graph
-
- # Run in parallel
- nx run-many --target=build --all --parallel=3
- ```
-
- ## Shared Configurations
-
- ### TypeScript Configuration
-
- ```json
- // packages/tsconfig/base.json
- {
- "compilerOptions": {
- "strict": true,
- "esModuleInterop": true,
- "skipLibCheck": true,
- "forceConsistentCasingInFileNames": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "incremental": true,
- "declaration": true
- },
- "exclude": ["node_modules"]
- }
-
- // packages/tsconfig/react.json
- {
- "extends": "./base.json",
- "compilerOptions": {
- "jsx": "react-jsx",
- "lib": ["ES2022", "DOM", "DOM.Iterable"]
- }
- }
-
- // apps/web/tsconfig.json
- {
- "extends": "@repo/tsconfig/react.json",
- "compilerOptions": {
- "outDir": "dist",
- "rootDir": "src"
- },
- "include": ["src"],
- "exclude": ["node_modules", "dist"]
- }
- ```
-
- ### ESLint Configuration
-
- ```javascript
- // packages/config/eslint-preset.js
- module.exports = {
- extends: [
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended",
- "plugin:react/recommended",
- "plugin:react-hooks/recommended",
- "prettier",
- ],
- plugins: ["@typescript-eslint", "react", "react-hooks"],
- parser: "@typescript-eslint/parser",
- parserOptions: {
- ecmaVersion: 2022,
- sourceType: "module",
- ecmaFeatures: {
- jsx: true,
- },
- },
- settings: {
- react: {
- version: "detect",
- },
- },
- rules: {
- "@typescript-eslint/no-unused-vars": "error",
- "react/react-in-jsx-scope": "off",
- },
- };
-
- // apps/web/.eslintrc.js
- module.exports = {
- extends: ["@repo/config/eslint-preset"],
- rules: {
- // App-specific rules
- },
- };
- ```
-
- ## Code Sharing Patterns
-
- ### Pattern 1: Shared UI Components
-
- ```typescript
- // packages/ui/src/button.tsx
- import * as React from 'react';
-
- export interface ButtonProps {
- variant?: 'primary' | 'secondary';
- children: React.ReactNode;
- onClick?: () => void;
- }
-
- export function Button({ variant = 'primary', children, onClick }: ButtonProps) {
- return (
- <button
- className={`btn btn-${variant}`}
- onClick={onClick}
- >
- {children}
- </button>
- );
- }
-
- // packages/ui/src/index.ts
- export { Button, type ButtonProps } from './button';
- export { Input, type InputProps } from './input';
-
- // apps/web/src/app.tsx
- import { Button } from '@repo/ui';
-
- export function App() {
- return <Button variant="primary">Click me</Button>;
- }
- ```
-
- ### Pattern 2: Shared Utilities
-
- ```typescript
- // packages/utils/src/string.ts
- export function capitalize(str: string): string {
- return str.charAt(0).toUpperCase() + str.slice(1);
- }
-
- export function truncate(str: string, length: number): string {
- return str.length > length ? str.slice(0, length) + "..." : str;
- }
-
- // packages/utils/src/index.ts
- export * from "./string";
- export * from "./array";
- export * from "./date";
-
- // Usage in apps
- import { capitalize, truncate } from "@repo/utils";
- ```
-
- ### Pattern 3: Shared Types
-
- ```typescript
- // packages/types/src/user.ts
- export interface User {
- id: string;
- email: string;
- name: string;
- role: "admin" | "user";
- }
-
- export interface CreateUserInput {
- email: string;
- name: string;
- password: string;
- }
-
- // Used in both frontend and backend
- import type { User, CreateUserInput } from "@repo/types";
- ```
-
- ## Build Optimization
-
- ### Turborepo Caching
-
- ```json
- // turbo.json
- {
- "pipeline": {
- "build": {
- // Build depends on dependencies being built first
- "dependsOn": ["^build"],
-
- // Cache these outputs
- "outputs": ["dist/**", ".next/**"],
-
- // Cache based on these inputs (default: all files)
- "inputs": ["src/**/*.tsx", "src/**/*.ts", "package.json"]
- },
- "test": {
- // Run tests in parallel, don't depend on build
- "cache": true,
- "outputs": ["coverage/**"]
- }
- }
- }
- ```
-
- ### Remote Caching
-
- ```bash
- # Turborepo Remote Cache (Vercel)
- npx turbo login
- npx turbo link
-
- # Custom remote cache
- # turbo.json
- {
- "remoteCache": {
- "signature": true,
- "enabled": true
- }
- }
- ```
-
- ## CI/CD for Monorepos
-
- ### GitHub Actions
-
- ```yaml
- # .github/workflows/ci.yml
- name: CI
-
- on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
- jobs:
- build:
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0 # For Nx affected commands
-
- - uses: pnpm/action-setup@v2
- with:
- version: 8
-
- - uses: actions/setup-node@v3
- with:
- node-version: 18
- cache: "pnpm"
-
- - name: Install dependencies
- run: pnpm install --frozen-lockfile
-
- - name: Build
- run: pnpm turbo run build
-
- - name: Test
- run: pnpm turbo run test
-
- - name: Lint
- run: pnpm turbo run lint
-
- - name: Type check
- run: pnpm turbo run type-check
- ```
-
- ### Deploy Affected Only
+ ## Detailed patterns and worked examples
- ```yaml
- # Deploy only changed apps
- - name: Deploy affected apps
- run: |
- if pnpm nx affected:apps --base=origin/main --head=HEAD | grep -q "web"; then
- echo "Deploying web app"
- pnpm --filter web deploy
- fi
- ```
+ Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
## Best Practices
1. **Consistent Versioning**: Lock dependency versions across workspace
2. **Shared Configs**: Centralize ESLint, TypeScript, Prettier configs
3. **Dependency Graph**: Keep it acyclic, avoid circular dependencies
4. **Cache Effectively**: Configure inputs/outputs correctly
5. **Type Safety**: Share types between frontend/backend
6. **Testing Strategy**: Unit tests in packages, E2E in apps
7. **Documentation**: README in each package
8. **Release Strategy**: Use changesets for versioning
## Common Pitfalls
- **Circular Dependencies**: A depends on B, B depends on A
- **Phantom Dependencies**: Using deps not in package.json
- **Incorrect Cache Inputs**: Missing files in Turborepo inputs
- **Over-Sharing**: Sharing code that should be separate
- **Under-Sharing**: Duplicating code across packages
- **Large Monorepos**: Without proper tooling, builds slow down
## Publishing Packages
```bash
# Using Changesets
pnpm add -Dw @changesets/cli
pnpm changeset init
# Create changeset
pnpm changeset
# Version packages
pnpm changeset version
# Publish
pnpm changeset publish
```
```yaml
# .github/workflows/release.yml
- name: Create Release Pull Request or Publish
uses: changesets/action@v1
with:
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
```