Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
270 lines
4.9 KiB
Markdown
270 lines
4.9 KiB
Markdown
# Repository Structure
|
|
|
|
Detailed guidance on structuring a Turborepo monorepo.
|
|
|
|
## Workspace Configuration
|
|
|
|
### pnpm (Recommended)
|
|
|
|
```yaml
|
|
# pnpm-workspace.yaml
|
|
packages:
|
|
- "apps/*"
|
|
- "packages/*"
|
|
```
|
|
|
|
### npm/yarn/bun
|
|
|
|
```json
|
|
// package.json
|
|
{
|
|
"workspaces": ["apps/*", "packages/*"]
|
|
}
|
|
```
|
|
|
|
## Root package.json
|
|
|
|
```json
|
|
{
|
|
"name": "my-monorepo",
|
|
"private": true,
|
|
"packageManager": "pnpm@9.0.0",
|
|
"scripts": {
|
|
"build": "turbo run build",
|
|
"dev": "turbo run dev",
|
|
"lint": "turbo run lint",
|
|
"test": "turbo run test"
|
|
},
|
|
"devDependencies": {
|
|
"turbo": "latest"
|
|
}
|
|
}
|
|
```
|
|
|
|
Key points:
|
|
|
|
- `private: true` - Prevents accidental publishing
|
|
- `packageManager` - Enforces consistent package manager version
|
|
- **Scripts only delegate to `turbo run`** - No actual build logic here!
|
|
- Minimal devDependencies (just turbo and repo tools)
|
|
|
|
## Always Prefer Package Tasks
|
|
|
|
**Always use package tasks. Only use Root Tasks if you cannot succeed with package tasks.**
|
|
|
|
```json
|
|
// packages/web/package.json
|
|
{
|
|
"scripts": {
|
|
"build": "next build",
|
|
"lint": "eslint .",
|
|
"test": "vitest",
|
|
"typecheck": "tsc --noEmit"
|
|
}
|
|
}
|
|
|
|
// packages/api/package.json
|
|
{
|
|
"scripts": {
|
|
"build": "tsc",
|
|
"lint": "eslint .",
|
|
"test": "vitest",
|
|
"typecheck": "tsc --noEmit"
|
|
}
|
|
}
|
|
```
|
|
|
|
Package tasks enable Turborepo to:
|
|
|
|
1. **Parallelize** - Run `web#lint` and `api#lint` simultaneously
|
|
2. **Cache individually** - Each package's task output is cached separately
|
|
3. **Filter precisely** - Run `turbo run test --filter=web` for just one package
|
|
|
|
**Root Tasks are a fallback** for tasks that truly cannot run per-package:
|
|
|
|
```json
|
|
// AVOID unless necessary - sequential, not parallelized, can't filter
|
|
{
|
|
"scripts": {
|
|
"lint": "eslint apps/web && eslint apps/api && eslint packages/ui"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Root turbo.json
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://turborepo.dev/schema.v2.json",
|
|
"tasks": {
|
|
"build": {
|
|
"dependsOn": ["^build"],
|
|
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
|
|
},
|
|
"lint": {},
|
|
"test": {
|
|
"dependsOn": ["build"]
|
|
},
|
|
"dev": {
|
|
"cache": false,
|
|
"persistent": true
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Directory Organization
|
|
|
|
### Grouping Packages
|
|
|
|
You can group packages by adding more workspace paths:
|
|
|
|
```yaml
|
|
# pnpm-workspace.yaml
|
|
packages:
|
|
- "apps/*"
|
|
- "packages/*"
|
|
- "packages/config/*" # Grouped configs
|
|
- "packages/features/*" # Feature packages
|
|
```
|
|
|
|
This allows:
|
|
|
|
```
|
|
packages/
|
|
├── ui/
|
|
├── utils/
|
|
├── config/
|
|
│ ├── eslint/
|
|
│ ├── typescript/
|
|
│ └── tailwind/
|
|
└── features/
|
|
├── auth/
|
|
└── payments/
|
|
```
|
|
|
|
### What NOT to Do
|
|
|
|
```yaml
|
|
# BAD: Nested wildcards cause ambiguous behavior
|
|
packages:
|
|
- "packages/**" # Don't do this!
|
|
```
|
|
|
|
## Package Anatomy
|
|
|
|
### Minimum Required Files
|
|
|
|
```
|
|
packages/ui/
|
|
├── package.json # Required: Makes it a package
|
|
├── src/ # Source code
|
|
│ └── button.tsx
|
|
└── tsconfig.json # TypeScript config (if using TS)
|
|
```
|
|
|
|
### package.json Requirements
|
|
|
|
```json
|
|
{
|
|
"name": "@repo/ui", // Unique, namespaced name
|
|
"version": "0.0.0", // Version (can be 0.0.0 for internal)
|
|
"private": true, // Prevents accidental publishing
|
|
"exports": { // Entry points
|
|
"./button": "./src/button.tsx"
|
|
}
|
|
}
|
|
```
|
|
|
|
## TypeScript Configuration
|
|
|
|
### Shared Base Config
|
|
|
|
Create a shared TypeScript config package:
|
|
|
|
```
|
|
packages/
|
|
└── typescript-config/
|
|
├── package.json
|
|
├── base.json
|
|
├── nextjs.json
|
|
└── library.json
|
|
```
|
|
|
|
```json
|
|
// packages/typescript-config/base.json
|
|
{
|
|
"compilerOptions": {
|
|
"strict": true,
|
|
"esModuleInterop": true,
|
|
"skipLibCheck": true,
|
|
"moduleResolution": "bundler",
|
|
"module": "ESNext",
|
|
"target": "ES2022"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Extending in Packages
|
|
|
|
```json
|
|
// packages/ui/tsconfig.json
|
|
{
|
|
"extends": "@repo/typescript-config/library.json",
|
|
"compilerOptions": {
|
|
"outDir": "dist",
|
|
"rootDir": "src"
|
|
},
|
|
"include": ["src"],
|
|
"exclude": ["node_modules", "dist"]
|
|
}
|
|
```
|
|
|
|
### No Root tsconfig.json
|
|
|
|
You likely don't need a `tsconfig.json` in the workspace root. Each package should have its own config extending from the shared config package.
|
|
|
|
## ESLint Configuration
|
|
|
|
### Shared Config Package
|
|
|
|
```
|
|
packages/
|
|
└── eslint-config/
|
|
├── package.json
|
|
├── base.js
|
|
├── next.js
|
|
└── library.js
|
|
```
|
|
|
|
```json
|
|
// packages/eslint-config/package.json
|
|
{
|
|
"name": "@repo/eslint-config",
|
|
"exports": {
|
|
"./base": "./base.js",
|
|
"./next": "./next.js",
|
|
"./library": "./library.js"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Using in Packages
|
|
|
|
```js
|
|
// apps/web/.eslintrc.js
|
|
module.exports = {
|
|
extends: ["@repo/eslint-config/next"],
|
|
};
|
|
```
|
|
|
|
## Lockfile
|
|
|
|
A lockfile is **required** for:
|
|
|
|
- Reproducible builds
|
|
- Turborepo to understand package dependencies
|
|
- Cache correctness
|
|
|
|
Without a lockfile, you'll see unpredictable behavior.
|