Monorepo Structure
Understanding Turborepo, PNPM workspaces, and workspace organization
Monorepo Structure
Portfolio OS uses a monorepo architecture powered by Turborepo and PNPM workspaces for efficient development and code sharing.
Why Monorepo?
Benefits:
- Code Sharing: Share components, utilities, and types across apps
- Atomic Changes: Update multiple apps in a single commit
- Consistent Tooling: Single ESLint, TypeScript, and Prettier config
- Faster Development: Changes to packages immediately available
- Better Collaboration: See the full system in one place
Workspace Configuration
PNPM Workspaces
packages:
- "apps/*"
- "packages/*"
This tells PNPM to treat apps/* and packages/* as workspaces.
Root package.json
{
"name": "portfolio-os",
"private": true,
"scripts": {
"dev": "turbo run dev --parallel",
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "^2.5.8",
"prettier": "^3.0.3",
"typescript": "^5.3.3"
}
}
Note:
The root is marked "private": true to prevent accidental publishing to npm.
Complete Directory Structure
Turborepo Configuration
turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^lint"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]
},
"typecheck": {
"dependsOn": ["^build"]
}
}
}
Pipeline Explained
| Task | dependsOn | Meaning |
|---|---|---|
build | ^build | Build dependencies first |
dev | - | Run independently |
lint | ^lint | Lint dependencies first |
test | ^build | Build before testing |
Note:
The ^ symbol means "run this task in dependencies first"
Package Dependencies
How Packages Reference Each Other
Use workspace:* to link to workspace packages:
{
"name": "@mindware-blog/site",
"dependencies": {
"@mindware-blog/ui": "workspace:*",
"@mindware-blog/lib": "workspace:*",
"@mindware-blog/utils": "workspace:*",
"next": "^14.2.32",
"react": "^18.3.1"
}
}
This links to the local package, not npm.
Dependency Graph
Working with the Monorepo
Running Commands
# Run in all workspaces
pnpm dev
pnpm build
pnpm lint
pnpm test
Adding Dependencies
# Add to site app
pnpm add react-hook-form --filter=@mindware-blog/site
# Add dev dependency
pnpm add -D @types/node --filter=@mindware-blog/dashboard
Creating New Workspaces
# Create new app directory
mkdir -p apps/admin
cd apps/admin
# Initialize package
pnpm init
# Edit package.json
{
"name": "@mindware-blog/admin",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3003",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@mindware-blog/ui": "workspace:*",
"next": "^14.2.32",
"react": "^18.3.1"
}
}
Shared Configuration
TypeScript
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
{
"extends": "@mindware-blog/tsconfig/nextjs.json",
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
ESLint
module.exports = {
extends: ["next", "prettier"],
rules: {
"@next/next/no-html-link-for-pages": "off",
},
}
module.exports = {
root: true,
extends: ["@mindware-blog/eslint-config-custom"],
}
Cache Management
Turborepo Cache
# View cache info
pnpm turbo run build --dry-run
# Clear Turbo cache
pnpm turbo run build --force
# Disable cache for one run
pnpm turbo run dev --no-cache
PNPM Cache
# View store info
pnpm store status
# Prune store
pnpm store prune
# Clear cache
rm -rf node_modules
pnpm store prune
pnpm install
Performance Tips
1. Use Filters Wisely
# Only build what you need
pnpm build --filter=@mindware-blog/site
# Build with dependencies
pnpm build --filter=@mindware-blog/site...
2. Leverage Turbo Cache
# First run (slow)
pnpm build
# Second run (instant if no changes)
pnpm build
3. Parallel Development
# Start multiple apps in parallel
pnpm dev --parallel
Troubleshooting
Dependency Not Found
# Reinstall dependencies
rm -rf node_modules
pnpm install
Circular Dependencies
Error: Circular dependency detected
Solution: Refactor code to break the cycle or move shared code to a common package.
Build Failures
# Clean Turbo cache
rm -rf .turbo
# Clean Next.js cache
rm -rf apps/site/.next
rm -rf apps/dashboard/.next
# Rebuild
pnpm build
Note:
For persistent issues, see Troubleshooting Guide
Best Practices
- Keep packages focused: Each package should have a single responsibility
- Avoid circular dependencies: Structure packages in layers
- Use workspace protocol: Always use
workspace:*for internal dependencies - Leverage caching: Let Turborepo cache builds and tests
- Filter commands: Use
--filterto work on specific workspaces
Next Steps
- Package System - Learn about individual packages
- Development Workflow - Understand the process
- Scripts Reference - Explore automation
Note:
The monorepo structure provides powerful development benefits. Master it to work efficiently across the entire codebase.