Managing TypeScript Configurations in a Monorepo

Managing TypeScript Configurations in a Monorepo

Monorepos are a popular way to organize multiple related projects in a single repository. They simplify dependency management, make it easier to refactor across boundaries, and streamline the development process. However, managing TypeScript configurations in a monorepo can become cumbersome without a systematic approach. This blog post will guide you through setting up and managing TypeScript configurations (tsconfig.json files) in a monorepo for different types of projects, such as libraries, Node.js applications, and React applications.

The Challenge

In a monorepo, projects often share common TypeScript configurations, such as compiler options and library inclusions. However, certain projects, like a React application vs. a Node.js service, require specific TypeScript configurations. Manually copying configurations between projects is error-prone and hard to maintain. The solution? Abstract common configurations into base configuration files and extend them in individual projects.

Structuring Your Monorepo

Consider a monorepo structure where your TypeScript configuration templates live under a tools/tsconfig directory, and your projects are organized under packages, categorized into apps and modules:

monorepo/
├── packages/
│   ├── apps/
│   │   ├── api/ (Node.js app)
│   │   └── react-app/ (React app)
│   └── modules/
│       └── my-module/ (Shared library)
└── tools/
    └── tsconfig/
        ├── tsconfig.base.json
        ├── tsconfig.library.json
        ├── tsconfig.node-app.json
        └── tsconfig.react-app.json

Base TypeScript Configurations

tsconfig.base.json - The Core Configuration

This file contains compiler options common across all projects.

{
  "compilerOptions": {
    "target": "es2020",
    "module": "esnext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": "."
  },
  "exclude": ["node_modules"]
}

Specialized Configurations

tsconfig.library.json for Libraries

{
  "extends": "@monorepo/tsconfig/tsconfig.base.json",
  "compilerOptions": {
    "declaration": true,
    "outDir": "./lib"
  },
  "include": ["src/**/*"]
}

tsconfig.node-app.json for Node.js Applications

{
  "extends": "@monorepo/tsconfig/tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "module": "commonjs"
  },
  "include": ["src/**/*"]
}

tsconfig.react-app.json for React Applications

{
  "extends": "@monorepo/tsconfig/tsconfig.base.json",
  "compilerOptions": {
    "jsx": "react-jsx",
    "lib": ["dom", "dom.iterable", "esnext"]
  },
  "include": ["src/**/*"]
}

Project Configuration Examples

Projects extend the appropriate base configuration. Here's how a tsconfig.json in the React app would look:

{
  "extends": "@monorepo/tsconfig/tsconfig.react-app.json",
  "compilerOptions": {
    "baseUrl": "./src"
  },
  "include": ["src/**/*"]
}

Conclusion

This setup simplifies managing TypeScript configurations in a monorepo by centralizing common settings and allowing for project-specific overrides. By abstracting these configurations into a package, we ensure consistency across projects and ease the maintenance burden. As your monorepo grows, this approach scales efficiently, allowing you to focus on development rather than configuration management.