---
name: monorepo
description: Monorepo setup with pnpm workspaces, centralized aliases, and Turborepo. Use when creating or managing multi-package repositories.
---

# Monorepo Setup

## 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,
});
```
