TypeScript Monorepo Configuration

Monorepo is a development pattern that places multiple projects in the same code repository.

TypeScript supports Monorepo through project references and toolchains, enabling efficient management of multiple packages.


SVG Diagram: Monorepo Structure Background Title Monorepo Project Structure Root directory Root directory (root) package.json | tsconfig.json | lerna.json | turbo.json Package directory packages/ directory Package 1 utils Package 2 ui-components Package 3 hooks Package 4 app Bottom section: Tools Management tools: npm workspaces | yarn workspaces | pnpm | lerna | turbo Arrow marker

Why Monorepo is Needed

When a project contains multiple packages (such as utility libraries, component libraries, and applications), the traditional approach requires maintaining multiple code repositories.

Monorepo places all packages in the same repository, making code sharing more convenient and version management more unified.

TypeScript's project references feature makes type checking and building Monorepo projects more efficient.

Concept:Monorepo (single repository) places multiple related projects in the same code repository, facilitating code sharing and coordinated development.


pnpm Workspace

pnpm is a modern package manager with native support for Workspace features.

Root package.json

{
    "name": "my-monorepo",
    "version": "1.0.0",
    "private": true,

    // Enable pnpm workspace
    "packages": [
        "packages/*"
    ],

    // Development dependencies
    "devDependencies": {
        "typescript": "^5.0.0"
    },

    // Scripts
    "scripts": {
        "build": "pnpm -r run build",
        "clean": "pnpm -r run clean",
        "type-check": "pnpm -r run type-check"
    }
}

pnpm:pnpm's Workspace feature can automatically link packages under the packages directory together.


Project Structure

Create the directory structure for a Monorepo project.

Directory Structure

my-monorepo/
├── packages/
│   ├── utils/# Utility package
│   │   ├── src/
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── ui-components/# UI component package
│   │   ├── src/
│   │   │   ├── Button.tsx
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   └── app/# Application
│       ├── src/
│       │   └── index.tsx
│       ├── package.json
│       └── tsconfig.json
│
├── package.json# Root configuration
├── tsconfig.base.json# Base configuration
└── pnpm-workspace.yaml# pnpm configuration

packages:All packages are placed under the packages directory, each with its own package.json and tsconfig.json.


Base TypeScript Configuration

Create a base configuration for all packages to use.

tsconfig.base.json

{
    // Compiler options
    "compilerOptions": {
        // Target version
        "target": "ES2020",

        // Module system
        "module": "ESNext",

        // Strict mode
        "strict": true,

        // Skip library type checking
        "skipLibCheck": true,

        // Enable ES module interop
        "esModuleInterop": true,

        // Enforce consistent casing in file names
        "forceConsistentCasingInFileNames": true,

        // Module resolution
        "moduleResolution": "bundler",

        // Resolve JSON modules
        "resolveJsonModule": true,

        // Isolated modules
        "isolatedModules": true,

        // Do not generate output files
        "noEmit": true
    }
}

Inheritance:Each package's tsconfig.json inherits the base configuration and only overrides options that need customization.


Utility Package Configuration

Configure TypeScript for the utility package.

packages/utils/tsconfig.json

{
    // Inherit base configuration
    "extends": "../../tsconfig.base.json",

    // Compiler options
    "compilerOptions": {
        // Output directory
        "outDir": "./dist",

        // Declaration file directory
        "declarationDir": "./dist/types",

        // Generate declaration files
        "declaration": true,

        // Generate declarations for entry files
        "declarationMap": true,

        // Module export format
        "module": "ESNext",

        // Enable project references
        "composite": true
    },

    // Included files
    "include": ["src/**/*"],

    // Excluded files
    "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

composite:After enabling project references, TypeScript can incrementally compile this package.


Application Configuration

Configure TypeScript for the main application and reference other packages.

packages/app/tsconfig.json

{
    // Inherit base configuration
    "extends": "../../tsconfig.base.json",

    // Compiler options
    "compilerOptions": {
        // Output directory
        "outDir": "./dist",

        // JSX configuration
        "jsx": "react-jsx",

        // Path aliases
        "baseUrl": ".",
        "paths": {
            "@my-utils/*": ["../utils/src/*"],
            "@my-ui/*": ["../ui-components/src/*"]
        }
    },

    // Project references
    "references": [
        { "path": "../utils" },
        { "path": "../ui-components" }
    ],

    // Included files
    "include": ["src/**/*"],

    // Excluded files
    "exclude": ["node_modules", "dist"]
}

Path aliases:You can configure path aliases to directly reference other packages in the same repository.


Dependencies Between Packages

Declare dependencies on packages in the same repository in package.json.

packages/app/package.json

{
    "name": "@my-org/app",
    "version": "1.0.0",
    "private": true,

    "dependencies": {
        // Reference packages in the same repository
        "@my-org/utils": "workspace:*",
        "@my-org/ui-components": "workspace:*",

        // External dependencies
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
    },

    "devDependencies": {
        // Development dependencies
        "@types/react": "^18.2.0",
        "typescript": "^5.0.0"
    }
}

workspace:Use workspace:* to point to other packages in the same repository, and pnpm will resolve them automatically.


Build Scripts

Create unified build scripts for the Monorepo.

Root package.json

{
    "scripts": {
        // Build all packages
        "build": "pnpm -r run build",

        // Clean all build artifacts
        "clean": "pnpm -r run clean",

        // Type checking
        "type-check": "pnpm -r run type-check",

        // Test all packages
        "test": "pnpm -r run test",

        // Incremental build
        "build:watch": "pnpm -r --parallel run build:watch",

        // Start the application
        "dev": "pnpm --filter @my-org/app run dev"
    }
}

pnpm -r:Recursively execute scripts with the same name in all packages.


Notes

  • Package naming convention:Use the @org-name/package format
  • Independent versions:Each package can be versioned independently
  • workspace protocol:Use workspace:* to reference packages in the same repository
  • Build order:Packages that are depended on need to be built first

Best practices:Monorepo is suitable for managing multiple related projects and can significantly improve code reuse and development efficiency.


Summary

Monorepo is a recommended pattern for modern front-end project management.

  • pnpm Workspace:Native Monorepo support
  • Project references:Enable incremental compilation
  • Path aliases:Conveniently reference packages in the same repository
  • Unified management:Share configuration and dependencies

Recommendation:When a project contains multiple related packages, consider the Monorepo approach first.

Other Extensions