127 lines
2.5 KiB
Markdown
127 lines
2.5 KiB
Markdown
---
|
|
name: monorepo
|
|
description: Anthony Fu's monorepo setup conventions using pnpm workspaces
|
|
---
|
|
|
|
# Monorepo Setup
|
|
|
|
Conventions for setting up and managing monorepos.
|
|
|
|
## pnpm Workspaces
|
|
|
|
Use pnpm workspaces for monorepo management:
|
|
|
|
```yaml
|
|
# pnpm-workspace.yaml
|
|
packages:
|
|
- 'packages/*'
|
|
```
|
|
|
|
## Scripts Convention
|
|
|
|
Have scripts in each package, and use `-r` (recursive) flag at root,
|
|
Enable ESLint cache for faster linting in monorepos.
|
|
|
|
```json
|
|
// root package.json
|
|
{
|
|
"scripts": {
|
|
"build": "pnpm run -r build",
|
|
"test": "vitest",
|
|
"lint": "eslint . --cache --concurrency=auto"
|
|
}
|
|
}
|
|
```
|
|
|
|
In each package's `package.json`, add the scripts.
|
|
|
|
```json
|
|
// packages/*/package.json
|
|
{
|
|
"scripts": {
|
|
"build": "tsdown",
|
|
"prepack": "pnpm build"
|
|
}
|
|
}
|
|
```
|
|
|
|
## ESLint Cache
|
|
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"lint": "eslint . --cache --concurrency=auto"
|
|
}
|
|
}
|
|
```
|
|
|
|
## Turborepo (Optional)
|
|
|
|
For monorepos with many packages or long build times, use Turborepo for task orchestration and caching.
|
|
|
|
See the dedicated Turborepo skill for detailed configuration.
|
|
|
|
## Centralized Alias
|
|
|
|
For better DX across Vite, Nuxt, Vitest configs, create a centralized `alias.ts` at project root:
|
|
|
|
```ts
|
|
// alias.ts
|
|
import fs from 'node:fs'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { join, relative } from 'pathe'
|
|
|
|
const root = fileURLToPath(new URL('.', import.meta.url))
|
|
const r = (path: string) => fileURLToPath(new URL(`./packages/${path}`, import.meta.url))
|
|
|
|
export const alias = {
|
|
'@myorg/core': r('core/src/index.ts'),
|
|
'@myorg/utils': r('utils/src/index.ts'),
|
|
'@myorg/ui': r('ui/src/index.ts'),
|
|
// Add more aliases as needed
|
|
}
|
|
|
|
// Auto-update tsconfig.alias.json paths
|
|
const raw = fs.readFileSync(join(root, 'tsconfig.alias.json'), 'utf-8').trim()
|
|
const tsconfig = JSON.parse(raw)
|
|
tsconfig.compilerOptions.paths = Object.fromEntries(
|
|
Object.entries(alias).map(([key, value]) => [key, [`./${relative(root, value)}`]]),
|
|
)
|
|
const newRaw = JSON.stringify(tsconfig, null, 2)
|
|
if (newRaw !== raw)
|
|
fs.writeFileSync(join(root, 'tsconfig.alias.json'), `${newRaw}\n`, 'utf-8')
|
|
```
|
|
|
|
Then update the `tsconfig.json` to use the alias file:
|
|
|
|
```json
|
|
{
|
|
"extends": [
|
|
"./tsconfig.alias.json"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Using Alias in Configs
|
|
|
|
Reference the centralized alias in all config files:
|
|
|
|
```ts
|
|
// vite.config.ts
|
|
import { alias } from './alias'
|
|
|
|
export default defineConfig({
|
|
resolve: { alias },
|
|
})
|
|
```
|
|
|
|
```ts
|
|
// nuxt.config.ts
|
|
import { alias } from './alias'
|
|
|
|
export default defineNuxtConfig({
|
|
alias,
|
|
})
|
|
```
|