Skip to main content

@presetter/preset-monorepo

Introduced in v8.0.0 - A comprehensive preset designed specifically for TypeScript monorepos with intelligent workspace management and unified tooling. Updated in v8.3 with broader linting and workspace test discovery.

Purposeโ€‹

This preset solves the "monorepo configuration hell" problem by providing:

  • ๐Ÿ—๏ธ Zero configuration duplication across packages
  • ๐Ÿงช Unified testing with workspace-based test running
  • ๐Ÿ“ Type-safe monorepo with TypeScript project references
  • โšก Instant package setup - new packages work immediately
  • ๐Ÿ”„ Always synchronized configurations across the entire workspace

Key Featuresโ€‹

Context-Aware Configurationโ€‹

The preset intelligently adapts based on where it's applied:

  • Repository Root: Generates workspace-level configurations with project references
  • Individual Packages: Minimal configuration that inherits from root

Unified Toolingโ€‹

Built on top of two powerful presets:

  • @presetter/preset-esm - Modern ES modules configuration
  • @presetter/preset-strict - Enhanced linting and type safety

Workspace Testingโ€‹

Aggregated test running across all packages with:

  • Unified coverage reporting
  • Multi-format output (text, HTML, JSON, LCOV)
  • 100% coverage thresholds
  • Intelligent test discovery

Installationโ€‹

Repository Setupโ€‹

# Install at repository root
npm install --save-dev presetter @presetter/preset-monorepo

# Create configuration
echo "export { default } from '@presetter/preset-monorepo';" > presetter.config.ts

# Bootstrap entire monorepo
npx presetter bootstrap --projects . --projects packages/*/

Package Manager Integrationโ€‹

Works seamlessly with all major package managers:

# npm workspaces
npm install --save-dev presetter @presetter/preset-monorepo

# pnpm workspaces
pnpm add -D presetter @presetter/preset-monorepo

# yarn workspaces
yarn add --dev presetter @presetter/preset-monorepo

Configurationโ€‹

Basic Setupโ€‹

presetter.config.ts (root)
// Simple monorepo configuration
export { default } from '@presetter/preset-monorepo';

Advanced Customizationโ€‹

presetter.config.ts (root)
import monorepo from '@presetter/preset-monorepo';

export default {
...monorepo,
// Override test patterns
override: {
'vitest.config.ts': {
test: {
workspace: [
'packages/*/vitest.config.ts',
'apps/*/vitest.config.ts',
],
coverage: {
thresholds: {
global: {
branches: 90, // Relax from 100%
functions: 95,
lines: 95,
statements: 95,
},
},
},
},
},
},
};

Real-World Configuration (Root vs Package Overrides)โ€‹

This pattern is used in production monorepos to apply different overrides at the repo root vs. individual packages:

presetter.config.ts
import { asset, preset } from 'presetter';
import monorepo from '@presetter/preset-monorepo';

import type { ViteUserConfig } from 'vitest/config';

export default preset('my-org', {
extends: [monorepo],
variables: {
target: 'ES2024',
},
override: {
assets: (context) =>
context.isRepoRoot
? {
'.gitignore': ['.notes', '.drafts'],
'vitest.config.ts': asset<{ default: ViteUserConfig }>((current) => ({
...current,
default: {
...current?.default,
test: {
...current?.default?.test,
projects: ['*/*/vitest.config{,.int,.e2e}.ts'],
},
},
})),
}
: {
'eslint.config.ts': {
default: [
{
name: 'monorepo:override',
rules: {
'max-lines': 'off',
},
},
],
},
},
},
});

Package-Level Customizationโ€‹

packages/my-package/presetter.config.ts
import base from '../../presetter.config.ts';

export default {
...base,
// Package-specific overrides
override: {
'vitest.config.ts': {
test: {
// Package-specific test configuration
setupFiles: ['./test/setup.ts'],
environment: 'jsdom', // For packages that need DOM
},
},
},
};

Generated Filesโ€‹

Repository Rootโ€‹

When applied to the monorepo root, generates:

eslint.config.tsโ€‹

// Monorepo-wide linting configuration
export default [
{
ignores: [
'**/lib/**', // Build outputs
'**/types/**', // Generated types
'**/generated/**', // Generated files
'**/node_modules/**',
],
},
// ... ESLint rules from preset-strict
];

tsconfig.jsonโ€‹

{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2022",
"baseUrl": "."
},
"files": [],
"references": [] // Manually add package references
}

vitest.config.tsโ€‹

// Workspace test runner
export default defineConfig({
test: {
workspace: ['*/*/vitest.config{,.int,.e2e}.ts'],
coverage: {
thresholds: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
reporter: ['text', 'html', 'clover', 'json', 'lcov'],
excludeAfterRemap: true,
},
},
});

Individual Packagesโ€‹

For packages, generates minimal configuration that inherits from root:

  • Removes .prettierrc.json (uses root configuration)
  • Inherits all other configurations from base presets
  • Maintains package-specific package.json scripts

Usage Examplesโ€‹

Development Workflowโ€‹

# Root level - affects entire monorepo
npm run typecheck # Check types across all packages
npm run lint # Lint and format entire workspace
npm run test # Run all tests with unified coverage

# Package level
cd packages/my-package
npm run build # Build this package
npm run test:watch # Watch tests for this package

Testing Strategyโ€‹

# Run all tests
npm run test

# Run tests for specific packages
npm run test -- packages/core

# Run with coverage
npm run test:coverage

# Integration tests only
npm run test:int

# End-to-end tests only
npm run test:e2e

CI/CD Integrationโ€‹

.github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'

- run: npm ci
- run: npm run bootstrap
- run: npm run typecheck
- run: npm run lint
- run: npm run test
- run: npm run build

Best Practicesโ€‹

Repository Structureโ€‹

my-monorepo/
โ”œโ”€โ”€ packages/
โ”‚ โ”œโ”€โ”€ core/ # Core package
โ”‚ โ”œโ”€โ”€ utils/ # Utility package
โ”‚ โ””โ”€โ”€ components/ # UI components
โ”œโ”€โ”€ apps/
โ”‚ โ”œโ”€โ”€ web/ # Web application
โ”‚ โ””โ”€โ”€ api/ # API server
โ”œโ”€โ”€ tools/
โ”‚ โ””โ”€โ”€ build-config/ # Shared build tools
โ”œโ”€โ”€ presetter.config.ts
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ tsconfig.json

TypeScript Project Referencesโ€‹

Manually configure TypeScript project references for optimal type checking:

tsconfig.json (root)
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" },
{ "path": "./packages/components" },
{ "path": "./apps/web" },
{ "path": "./apps/api" }
]
}

Package Dependenciesโ€‹

Establish clear dependency hierarchies:

packages/components/package.json
{
"dependencies": {
"@my-org/core": "workspace:*",
"@my-org/utils": "workspace:*"
}
}

Test Organizationโ€‹

Organize tests by type across the workspace:

packages/core/
โ”œโ”€โ”€ src/
โ”‚ โ””โ”€โ”€ index.ts
โ”œโ”€โ”€ spec/
โ”‚ โ”œโ”€โ”€ unit/ # Unit tests
โ”‚ โ”œโ”€โ”€ integration/ # Integration tests
โ”‚ โ””โ”€โ”€ e2e/ # End-to-end tests
โ”œโ”€โ”€ vitest.config.ts # Package test config
โ””โ”€โ”€ package.json

Variablesโ€‹

Inherits configuration variables from @presetter/preset-esm:

VariableDefaultDescription
source"src"Source code directory
output"lib"Build output directory
test"spec"Test files directory
types"types"TypeScript declarations
generated"generated"Generated files directory
target"ES2022"TypeScript compilation target

Troubleshootingโ€‹

Issue: Package not found in workspaceโ€‹

Problem: New package not detected by workspace tools

Solution: Re-run bootstrap to include new packages:

npx presetter bootstrap --projects . --projects packages/*/

Issue: Type errors across packagesโ€‹

Problem: TypeScript can't resolve cross-package types

Solution: Add package references to root tsconfig.json:

{
"references": [
{ "path": "./packages/new-package" }
]
}

Issue: Test coverage issuesโ€‹

Problem: Coverage reporting incorrect for workspace

Solution: Ensure all packages have proper test configurations:

# Verify workspace test pattern
npm run test -- --reporter=verbose

Issue: Linting configuration conflictsโ€‹

Problem: Individual packages have conflicting ESLint rules

Solution: Remove package-level ESLint configs and use workspace configuration:

# Remove conflicting configs
find packages -name "eslint.config.*" -delete
find packages -name ".eslintrc.*" -delete

Migration from Other Setupsโ€‹

From Lernaโ€‹

# Remove Lerna configuration
rm lerna.json

# Install preset-monorepo
npm install --save-dev presetter @presetter/preset-monorepo

# Bootstrap workspace
npx presetter bootstrap --projects . --projects packages/*/

From Nxโ€‹

# Remove Nx configuration
rm -rf nx.json .nxignore tools/

# Install preset-monorepo
npm install --save-dev presetter @presetter/preset-monorepo

# Adapt workspace structure
npx presetter bootstrap --projects . --projects packages/*/

From Manual Setupโ€‹

# Clean existing configurations
find . -name "tsconfig.json" -not -path "./node_modules/*" -delete
find . -name "eslint.config.*" -not -path "./node_modules/*" -delete
find . -name "vitest.config.*" -not -path "./node_modules/*" -delete

# Install and bootstrap
npm install --save-dev presetter @presetter/preset-monorepo
npx presetter bootstrap --projects . --projects packages/*/

Next Stepsโ€‹

  • New packages: Simply create package directory and re-run bootstrap
  • Custom configurations: Use the override system for package-specific needs
  • Advanced features: Explore preset composition for complex requirements
  • Performance: Consider incremental builds and selective testing for large workspaces

The preset-monorepo is perfect for teams managing complex TypeScript monorepos who want unified tooling without configuration overhead.