Add Plan 1: Monorepo Foundation implementation plan

8 tasks covering: root workspace files, shared TypeScript config,
shared ESLint config, placeholder packages, placeholder apps,
Docker Compose, dependency installation + verification, test dirs.
This commit is contained in:
2026-04-06 13:56:00 +02:00
parent c3c2e19e4f
commit d0d335d829

View File

@@ -0,0 +1,965 @@
# Plan 1: Monorepo Foundation — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Scaffold a Turborepo + pnpm monorepo with shared TypeScript and ESLint configs, placeholder packages for all planned workspaces, and Docker Compose for local development.
**Architecture:** Turborepo orchestrates builds across pnpm workspaces. Shared config packages (`@repo/typescript-config`, `@repo/eslint-config`) provide consistent tooling. All apps and packages are created as empty placeholders with correct `package.json` files so the workspace graph is valid from the start. Docker Compose provides PostgreSQL for local development.
**Tech Stack:** Turborepo 2.x, pnpm 9.x, TypeScript 5.x, ESLint 9.x (flat config), Vitest, Docker Compose, PostgreSQL 16
---
## File Map
| File | Responsibility |
|---|---|
| `package.json` | Root workspace manifest, delegates to turbo |
| `pnpm-workspace.yaml` | Declares workspace packages |
| `turbo.json` | Task pipeline (build, dev, lint, test, typecheck) |
| `.npmrc` | pnpm workspace settings |
| `.gitignore` | Ignore patterns for Turborepo + pnpm + Node.js |
| `packages/typescript-config/package.json` | Shared TS config package manifest |
| `packages/typescript-config/base.json` | Base TypeScript config |
| `packages/typescript-config/nextjs.json` | Next.js TypeScript config |
| `packages/typescript-config/react-library.json` | React library TypeScript config |
| `packages/eslint-config/package.json` | Shared ESLint config package manifest |
| `packages/eslint-config/base.js` | Base ESLint flat config |
| `packages/eslint-config/next.js` | Next.js ESLint config |
| `packages/eslint-config/react-internal.js` | React library ESLint config |
| `packages/core/package.json` | Placeholder — clean architecture core |
| `packages/core/tsconfig.json` | Extends @repo/typescript-config/base |
| `packages/api/package.json` | Placeholder — tRPC routers |
| `packages/api/tsconfig.json` | Extends @repo/typescript-config/base |
| `packages/api-client/package.json` | Placeholder — React Query hooks |
| `packages/api-client/tsconfig.json` | Extends @repo/typescript-config/react-library |
| `packages/cms-core/package.json` | Placeholder — Payload CMS definition |
| `packages/cms-core/tsconfig.json` | Extends @repo/typescript-config/base |
| `packages/cms-client/package.json` | Placeholder — Dual-mode Payload client |
| `packages/cms-client/tsconfig.json` | Extends @repo/typescript-config/base |
| `packages/ui/package.json` | Placeholder — shadcn/ui + Atomic Design |
| `packages/ui/tsconfig.json` | Extends @repo/typescript-config/react-library |
| `apps/web-next/package.json` | Placeholder — Next.js reference app |
| `apps/web-next/tsconfig.json` | Extends @repo/typescript-config/nextjs |
| `apps/web-tanstack/package.json` | Placeholder — TanStack Start reference app |
| `apps/web-tanstack/tsconfig.json` | Extends @repo/typescript-config/base |
| `apps/cms/package.json` | Placeholder — Payload admin shell |
| `apps/cms/tsconfig.json` | Extends @repo/typescript-config/nextjs |
| `apps/storybook/package.json` | Placeholder — Storybook instance |
| `apps/storybook/tsconfig.json` | Extends @repo/typescript-config/react-library |
| `docker-compose.yml` | PostgreSQL service for local dev |
| `.env.example` | Environment variable template |
---
### Task 1: Root workspace files
**Files:**
- Create: `package.json`
- Create: `pnpm-workspace.yaml`
- Create: `turbo.json`
- Create: `.npmrc`
- Create: `.gitignore`
- Create: `.env.example`
- [ ] **Step 1: Create root package.json**
```json
{
"name": "template",
"private": true,
"packageManager": "pnpm@9.15.4",
"engines": {
"node": ">=20"
},
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\""
},
"devDependencies": {
"prettier": "^3.5.0",
"turbo": "^2.4.0",
"typescript": "^5.8.0"
}
}
```
- [ ] **Step 2: Create pnpm-workspace.yaml**
```yaml
packages:
- "apps/*"
- "packages/*"
```
- [ ] **Step 3: Create turbo.json**
```json
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^lint"]
},
"test": {
"dependsOn": ["^build"]
},
"typecheck": {
"dependsOn": ["^typecheck"]
}
}
}
```
- [ ] **Step 4: Create .npmrc**
```
auto-install-peers=true
enable-pre-post-scripts=true
```
- [ ] **Step 5: Create .gitignore**
```
# Dependencies
node_modules
# Turbo
.turbo
# Build outputs
dist
build
.next
out
storybook-static
# Environment
.env
.env.local
.env.*.local
# Testing
coverage
# OS
.DS_Store
Thumbs.db
# IDE
.vscode
.idea
*.swp
# Debug
npm-debug.log*
pnpm-debug.log*
# Superpowers brainstorm sessions
.superpowers/
```
- [ ] **Step 6: Create .env.example**
```
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/template
# Payload CMS
PAYLOAD_SECRET=your-secret-here
# App URLs
NEXT_PUBLIC_APP_URL=http://localhost:3000
CMS_URL=http://localhost:3001
```
- [ ] **Step 7: Commit**
```bash
git add package.json pnpm-workspace.yaml turbo.json .npmrc .gitignore .env.example
git commit -m "feat: scaffold root workspace files (Turborepo + pnpm)"
```
---
### Task 2: Shared TypeScript config package
**Files:**
- Create: `packages/typescript-config/package.json`
- Create: `packages/typescript-config/base.json`
- Create: `packages/typescript-config/nextjs.json`
- Create: `packages/typescript-config/react-library.json`
- [ ] **Step 1: Create package.json**
```json
{
"name": "@repo/typescript-config",
"private": true,
"version": "0.0.0"
}
```
- [ ] **Step 2: Create base.json**
This is the base TypeScript config used by all packages. Includes `experimentalDecorators` and `emitDecoratorMetadata` required by InversifyJS.
```json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"declaration": true,
"declarationMap": true,
"isolatedModules": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noUncheckedIndexedAccess": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 3: Create nextjs.json**
```json
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "preserve",
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"incremental": true,
"plugins": [{ "name": "next" }]
}
}
```
- [ ] **Step 4: Create react-library.json**
```json
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "./base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx"
}
}
```
- [ ] **Step 5: Commit**
```bash
git add packages/typescript-config/
git commit -m "feat: add shared TypeScript config package (@repo/typescript-config)"
```
---
### Task 3: Shared ESLint config package
**Files:**
- Create: `packages/eslint-config/package.json`
- Create: `packages/eslint-config/base.js`
- Create: `packages/eslint-config/next.js`
- Create: `packages/eslint-config/react-internal.js`
- [ ] **Step 1: Create package.json**
```json
{
"name": "@repo/eslint-config",
"private": true,
"version": "0.0.0",
"type": "module",
"exports": {
"./base": "./base.js",
"./next": "./next.js",
"./react-internal": "./react-internal.js"
},
"devDependencies": {
"@eslint/js": "^9.20.0",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"eslint": "^9.20.0",
"eslint-config-prettier": "^10.1.0",
"eslint-plugin-turbo": "^2.4.0",
"typescript-eslint": "^8.25.0"
}
}
```
- [ ] **Step 2: Create base.js**
```javascript
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier";
import tseslint from "typescript-eslint";
import turboPlugin from "eslint-plugin-turbo";
export default [
{ ignores: ["dist/**", "node_modules/**"] },
js.configs.recommended,
...tseslint.configs.recommended,
eslintConfigPrettier,
{
plugins: { turbo: turboPlugin },
rules: {
"turbo/no-undeclared-env-vars": "warn",
},
},
];
```
- [ ] **Step 3: Create next.js**
```javascript
import baseConfig from "./base.js";
export default [
...baseConfig,
{ ignores: [".next/**", "out/**"] },
];
```
- [ ] **Step 4: Create react-internal.js**
```javascript
import baseConfig from "./base.js";
export default [...baseConfig];
```
- [ ] **Step 5: Commit**
```bash
git add packages/eslint-config/
git commit -m "feat: add shared ESLint config package (@repo/eslint-config)"
```
---
### Task 4: Placeholder packages (core, api, api-client, cms-core, cms-client, ui)
**Files:**
- Create: `packages/core/package.json`
- Create: `packages/core/tsconfig.json`
- Create: `packages/core/src/index.ts`
- Create: `packages/api/package.json`
- Create: `packages/api/tsconfig.json`
- Create: `packages/api/src/index.ts`
- Create: `packages/api-client/package.json`
- Create: `packages/api-client/tsconfig.json`
- Create: `packages/api-client/src/index.ts`
- Create: `packages/cms-core/package.json`
- Create: `packages/cms-core/tsconfig.json`
- Create: `packages/cms-core/src/index.ts`
- Create: `packages/cms-client/package.json`
- Create: `packages/cms-client/tsconfig.json`
- Create: `packages/cms-client/src/index.ts`
- Create: `packages/ui/package.json`
- Create: `packages/ui/tsconfig.json`
- Create: `packages/ui/src/index.ts`
- [ ] **Step 1: Create packages/core/package.json**
```json
{
"name": "@repo/core",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 2: Create packages/core/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"types": ["reflect-metadata"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 3: Create packages/core/src/index.ts**
```typescript
// @repo/core — Clean Architecture core package
// Layers: entities, application, infrastructure, interface-adapters, di
export {};
```
- [ ] **Step 4: Create packages/api/package.json**
```json
{
"name": "@repo/api",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 5: Create packages/api/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 6: Create packages/api/src/index.ts**
```typescript
// @repo/api — tRPC router definitions
export {};
```
- [ ] **Step 7: Create packages/api-client/package.json**
```json
{
"name": "@repo/api-client",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 8: Create packages/api-client/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/react-library.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 9: Create packages/api-client/src/index.ts**
```typescript
// @repo/api-client — Shared React Query hooks
export {};
```
- [ ] **Step 10: Create packages/cms-core/package.json**
```json
{
"name": "@repo/cms-core",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 11: Create packages/cms-core/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 12: Create packages/cms-core/src/index.ts**
```typescript
// @repo/cms-core — Payload CMS config, collections, hooks, globals
export {};
```
- [ ] **Step 13: Create packages/cms-client/package.json**
```json
{
"name": "@repo/cms-client",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 14: Create packages/cms-client/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 15: Create packages/cms-client/src/index.ts**
```typescript
// @repo/cms-client — Dual-mode Payload client (local + HTTP)
export {};
```
- [ ] **Step 16: Create packages/ui/package.json**
```json
{
"name": "@repo/ui",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"scripts": {
"build": "tsc --noEmit",
"lint": "eslint .",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 17: Create packages/ui/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/react-library.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
```
- [ ] **Step 18: Create packages/ui/src/index.ts**
```typescript
// @repo/ui — shadcn/ui + Atomic Design component library
export {};
```
- [ ] **Step 19: Commit**
```bash
git add packages/core/ packages/api/ packages/api-client/ packages/cms-core/ packages/cms-client/ packages/ui/
git commit -m "feat: add placeholder packages (core, api, api-client, cms-core, cms-client, ui)"
```
---
### Task 5: Placeholder apps (web-next, web-tanstack, cms, storybook)
**Files:**
- Create: `apps/web-next/package.json`
- Create: `apps/web-next/tsconfig.json`
- Create: `apps/web-tanstack/package.json`
- Create: `apps/web-tanstack/tsconfig.json`
- Create: `apps/cms/package.json`
- Create: `apps/cms/tsconfig.json`
- Create: `apps/storybook/package.json`
- Create: `apps/storybook/tsconfig.json`
- [ ] **Step 1: Create apps/web-next/package.json**
```json
{
"name": "@repo/web-next",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "echo 'placeholder'",
"dev": "echo 'placeholder'",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@repo/api-client": "workspace:*",
"@repo/ui": "workspace:*"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 2: Create apps/web-next/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/nextjs.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "next-env.d.ts"],
"exclude": ["node_modules"]
}
```
- [ ] **Step 3: Create apps/web-tanstack/package.json**
```json
{
"name": "@repo/web-tanstack",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "echo 'placeholder'",
"dev": "echo 'placeholder'",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@repo/api-client": "workspace:*",
"@repo/ui": "workspace:*"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 4: Create apps/web-tanstack/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
```
- [ ] **Step 5: Create apps/cms/package.json**
```json
{
"name": "@repo/cms",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "echo 'placeholder'",
"dev": "echo 'placeholder'",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@repo/cms-core": "workspace:*"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 6: Create apps/cms/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/nextjs.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "next-env.d.ts"],
"exclude": ["node_modules"]
}
```
- [ ] **Step 7: Create apps/storybook/package.json**
```json
{
"name": "@repo/storybook",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build": "echo 'placeholder'",
"dev": "echo 'placeholder'",
"lint": "eslint ."
},
"dependencies": {
"@repo/ui": "workspace:*"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*"
}
}
```
- [ ] **Step 8: Create apps/storybook/tsconfig.json**
```json
{
"extends": "@repo/typescript-config/react-library.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
```
- [ ] **Step 9: Commit**
```bash
git add apps/
git commit -m "feat: add placeholder apps (web-next, web-tanstack, cms, storybook)"
```
---
### Task 6: Docker Compose
**Files:**
- Create: `docker-compose.yml`
- [ ] **Step 1: Create docker-compose.yml**
```yaml
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: template
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:
```
- [ ] **Step 2: Commit**
```bash
git add docker-compose.yml
git commit -m "feat: add Docker Compose with PostgreSQL for local dev"
```
---
### Task 7: Install dependencies and verify workspace
- [ ] **Step 1: Install pnpm if not available**
Run: `corepack enable && corepack prepare pnpm@9.15.4 --activate`
Expected: pnpm is available
- [ ] **Step 2: Run pnpm install**
Run: `pnpm install`
Expected: Installs all workspace dependencies, creates `pnpm-lock.yaml`, no errors.
- [ ] **Step 3: Verify Turborepo sees all workspaces**
Run: `pnpm turbo run build --dry`
Expected: Output lists all 10 packages/apps:
- `@repo/typescript-config`
- `@repo/eslint-config`
- `@repo/core`
- `@repo/api`
- `@repo/api-client`
- `@repo/cms-core`
- `@repo/cms-client`
- `@repo/ui`
- `@repo/web-next`
- `@repo/web-tanstack`
- `@repo/cms`
- `@repo/storybook`
- [ ] **Step 4: Run turbo build**
Run: `pnpm build`
Expected: All workspaces build successfully (placeholder builds echo 'placeholder' or tsc --noEmit with no errors on empty src/index.ts).
- [ ] **Step 5: Verify Docker Compose**
Run: `docker compose up -d postgres && docker compose ps`
Expected: PostgreSQL container running, healthy.
Run: `docker compose down`
Expected: Clean shutdown.
- [ ] **Step 6: Commit lockfile**
```bash
git add pnpm-lock.yaml
git commit -m "chore: add pnpm lockfile"
```
---
### Task 8: Create test directory structure
**Files:**
- Create: `tests/unit/.gitkeep`
- Create: `tests/integration/.gitkeep`
- Create: `tests/e2e/.gitkeep`
- [ ] **Step 1: Create test directories**
```bash
mkdir -p tests/unit tests/integration tests/e2e
touch tests/unit/.gitkeep tests/integration/.gitkeep tests/e2e/.gitkeep
```
- [ ] **Step 2: Commit**
```bash
git add tests/
git commit -m "feat: add test directory structure (unit, integration, e2e)"
```