Monorepo Navigator
Tier: POWERFUL Category: Engineering / Build Systems Maintainer: Claude Skills Team
Overview
Navigate, manage, and optimize monorepos at any scale. Covers Turborepo, Nx, pnpm workspaces, and Lerna/Changesets for cross-package impact analysis, selective builds on affected packages only, dependency graph visualization, remote caching configuration, migration from multi-repo to monorepo with preserved git history, and coordinated package publishing with automated changelogs.
Keywords
monorepo, Turborepo, Nx, pnpm workspaces, Changesets, dependency graph, remote cache, affected packages, selective builds, cross-package impact, npm publishing, workspace protocol
Core Capabilities
- Impact Analysis
-
Determine which apps break when a shared package changes
-
Trace dependency chains from leaf packages to root apps
-
Visualize impact as Mermaid dependency graphs
-
Calculate blast radius for any file change
- Selective Execution
-
Run tests/builds only for affected packages (not everything)
-
Filter by changed files since a git ref
-
Scope commands to specific packages and their dependents
-
Skip unchanged packages in CI for faster feedback
- Build Optimization
-
Remote caching with Turborepo (Vercel) or Nx Cloud
-
Incremental builds with proper input/output configuration
-
Parallel execution with dependency-aware scheduling
-
Artifact sharing between CI jobs
- Publishing
-
Changesets for coordinated versioning across packages
-
Automated changelog generation per package
-
Pre-release channels (alpha, beta, rc)
-
workspace:* protocol replacement during publish
When to Use
-
Multiple packages/apps share code (UI components, utils, types, API clients)
-
Build times are slow because everything rebuilds on every change
-
Migrating from multiple repos to a single monorepo
-
Publishing npm packages with coordinated versioning
-
Teams work across packages and need unified tooling
Tool Selection Decision Matrix
Requirement Turborepo Nx pnpm Workspaces Changesets
Simple task runner Best Good N/A N/A
Remote caching Built-in Nx Cloud N/A N/A
Code generation No Best N/A N/A
Dependency management N/A N/A Best N/A
Package publishing N/A N/A N/A Best
Plugin ecosystem Limited Extensive N/A N/A
Config complexity Minimal Moderate Minimal Minimal
Recommended modern stack: pnpm workspaces + Turborepo + Changesets
Monorepo Structure
my-monorepo/ ├── apps/ │ ├── web/ # Next.js frontend │ │ ├── package.json # depends on @repo/ui, @repo/utils │ │ └── ... │ ├── api/ # Express/Fastify backend │ │ ├── package.json # depends on @repo/db, @repo/utils │ │ └── ... │ └── mobile/ # React Native app │ ├── package.json │ └── ... ├── packages/ │ ├── ui/ # Shared React components │ │ ├── package.json # @repo/ui │ │ └── ... │ ├── utils/ # Shared utilities │ │ ├── package.json # @repo/utils │ │ └── ... │ ├── db/ # Database client + schema │ │ ├── package.json # @repo/db │ │ └── ... │ ├── types/ # Shared TypeScript types │ │ ├── package.json # @repo/types (no runtime deps) │ │ └── ... │ └── config/ # Shared configs (tsconfig, eslint) │ ├── tsconfig.base.json │ └── eslint.base.js ├── turbo.json # Turborepo pipeline config ├── pnpm-workspace.yaml # Workspace package locations ├── package.json # Root scripts, devDependencies └── .changeset/ # Changeset config └── config.json
Turborepo Configuration
turbo.json
{ "$schema": "https://turbo.build/schema.json", "globalDependencies": ["/.env.*local"], "globalEnv": ["NODE_ENV", "CI"], "tasks": { "build": { "dependsOn": ["^build"], "inputs": ["src/", "tsconfig.json", "package.json"], "outputs": ["dist/", ".next/", "!.next/cache/"], "env": ["NEXT_PUBLIC_*"] }, "test": { "dependsOn": ["^build"], "inputs": ["src/", "tests/", "vitest.config.*"], "outputs": ["coverage/"] }, "lint": { "dependsOn": ["^build"], "inputs": ["src/", ".eslintrc.*", "tsconfig.json"] }, "typecheck": { "dependsOn": ["^build"], "inputs": ["src/", "tsconfig.json"] }, "dev": { "cache": false, "persistent": true } } }
Key Turborepo Commands
Run all tasks
turbo run build
Run only affected packages (compared to main)
turbo run build test --filter='...[origin/main]'
Run for a specific package and its dependencies
turbo run build --filter=@repo/web...
Run for a specific package only (no deps)
turbo run test --filter=@repo/ui
Dry run to see what would execute
turbo run build --dry=json
View dependency graph
turbo run build --graph=graph.html
Summarize cache usage
turbo run build --summarize
pnpm Workspace Configuration
pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
Cross-Package References
// packages/ui/package.json { "name": "@repo/ui", "version": "0.0.0", "main": "./src/index.ts", "types": "./src/index.ts", "dependencies": { "@repo/types": "workspace:*" } }
// apps/web/package.json { "name": "@repo/web", "dependencies": { "@repo/ui": "workspace:", "@repo/utils": "workspace:" } }
Workspace Commands
Install all workspace dependencies
pnpm install
Add a dependency to a specific package
pnpm add zod --filter @repo/api
Add a workspace package as dependency
pnpm add @repo/utils --filter @repo/web --workspace
Run a script in a specific package
pnpm --filter @repo/web dev
Run a script in all packages that have it
pnpm -r run build
List all packages
pnpm -r ls --depth -1
Impact Analysis
Find All Dependents of a Changed Package
Using turbo to see what depends on @repo/ui
turbo run build --filter='...@repo/ui' --dry=json |
jq '.tasks[].package' -r | sort -u
Manual: search for imports of a package
grep -r "from '@repo/ui'" apps/ packages/ --include=".ts" --include=".tsx" -l
Dependency Graph Visualization
Generate HTML visualization
turbo run build --graph=dependency-graph.html
Generate DOT format for custom rendering
turbo run build --graph=deps.dot
Quick Mermaid diagram from package.json files
echo "graph TD" for pkg in packages//package.json apps//package.json; do name=$(jq -r '.name' "$pkg") jq -r '.dependencies // {} | keys[] | select(startswith("@repo/"))' "$pkg" | while read dep; do echo " $name --> $dep" done done
Remote Caching
Turborepo Remote Cache (Vercel)
Login to Vercel (one-time)
turbo login
Link repo to Vercel team
turbo link
CI: set environment variables
TURBO_TOKEN=<vercel-token>
TURBO_TEAM=<team-slug>
Verify remote cache works
turbo run build --summarize
Look for "Remote cache: hit" entries
Self-Hosted Remote Cache
Using ducktape/turborepo-remote-cache
docker run -p 3000:3000
-e STORAGE_PROVIDER=local
-e STORAGE_PATH=/cache
ducktape/turborepo-remote-cache
Configure turbo to use it
turbo.json:
{ "remoteCache": { "apiUrl": "http://cache-server:3000" } }
CI/CD with Affected Packages Only
.github/workflows/ci.yml
name: CI on: pull_request:
jobs: build-and-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # needed for --filter comparisons
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
# Only lint/test/build affected packages
- run: turbo run lint test build --filter='...[origin/main]'
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
Publishing with Changesets
Setup
Install changesets
pnpm add -D -w @changesets/cli @changesets/changelog-github
Initialize
pnpm changeset init
.changeset/config.json
{ "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", "changelog": ["@changesets/changelog-github", { "repo": "org/repo" }], "commit": false, "fixed": [], "linked": [["@repo/ui", "@repo/utils"]], "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch" }
Publishing Workflow
1. Developer adds a changeset for their changes
pnpm changeset
Interactive: select packages, bump type (patch/minor/major), summary
2. Before release: consume changesets and bump versions
pnpm changeset version
Updates package.json versions and CHANGELOG.md files
3. Publish to npm
pnpm changeset publish
Replaces workspace:* with real versions and publishes
Migration: Multi-Repo to Monorepo
1. Preserve git history using filter-repo
In each source repo:
git filter-repo --to-subdirectory-filter packages/ui git filter-repo --to-subdirectory-filter apps/api
2. Create monorepo and merge histories
mkdir monorepo && cd monorepo && git init git remote add ui ../old-ui-repo git fetch ui --no-tags git merge ui/main --allow-unrelated-histories
git remote add api ../old-api-repo git fetch api --no-tags git merge api/main --allow-unrelated-histories
3. Set up workspace configuration
Add pnpm-workspace.yaml, turbo.json, root package.json
4. Update internal imports
Change "ui-package" imports to "@repo/ui"
Change npm versions to "workspace:*"
5. Verify
pnpm install turbo run build test
Common Pitfalls
Pitfall Fix
Running turbo run build without --filter on every PR Always use --filter='...[origin/main]' in CI
workspace:* breaks npm publish Use pnpm changeset publish which replaces automatically
All packages rebuild when unrelated file changes Tune inputs in turbo.json to exclude docs, config files
Shared tsconfig breaks type-checks across packages Each package extends root but overrides rootDir /outDir
Git history lost during migration Use git filter-repo --to-subdirectory-filter before merging
Remote cache misses in CI Verify TURBO_TOKEN and TURBO_TEAM; check with --summarize
Import cycles between packages Use madge --circular to detect; refactor shared code to a new package
Best Practices
-
Root package.json has no runtime dependencies — only devDependencies and scripts
-
Always scope commands with --filter in CI — running everything defeats the monorepo purpose
-
Remote cache is not optional — without it, monorepo CI is slower than multi-repo
-
Shared configs extend from root — tsconfig.base.json, eslint.base.js, vitest shared config
-
packages/types is pure TypeScript — no runtime code, no dependencies, fastest to build
-
Changesets over manual versioning — never hand-edit package.json versions in a monorepo
-
Impact analysis before merging shared package changes — check affected packages, communicate blast radius
-
Keep workspace: for internal deps* — real version ranges are for external npm packages only