Speed up your TypeScript monorepo with esbuild
Tags: typescript, monorepo, webdev, node
Published: 2021-11-12 18:21:27

---
title: Speed up your TypeScript monorepo with esbuild
published: true
date: 2021-11-12 18:21:00 UTC
tags: #typescript #monorepo #webdev #node
canonical_url: https://mmazzarolo.com/blog/2021-11-06-speed-up-your-typescript-monorepo-with-esbuild/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pstsjag63qnrrk0qdgie.png
---

[TypeScript](https://www.typescriptlang.org/) monorepos are a great way to organize medium-to-big size projects. TypeScript improves the developer experience by adding type-checking and a deep IDE integration. And using a monorepo helps in scaling your project(s).

Compared to plain JavaScript, however, TypeScript adds an additional compilation layer to your project, which may slow down the developer experience. While the native TypeScript compiler is not _that_ slow (IMHO), it’s still something you need to take into account if you’re planning to build a large codebase. But what if there was a way to speed up the TypeScript compilation by using a different compiler?

Enter [esbuild](https://esbuild.github.io/): a fast JavaScript bundler that claims to be >10x faster than similar projects (webpack, rollup + terser, parcel 2). I’ve been using esbuilt for a couple of TypeScript projects and have been surprised by how well it performs.

## 🍊 Tangerine monorepo

While learning esbuild, I haven’t found many examples of how to integrate it within TypeScript monorepos. So I created my own template: [🍊 tangerine-monorepo](https://github.com/mmazzarolo/tangerine-monorepo), a “minimal” TypeScript-based Node.js monorepo setup fully powered by esbuild.

### Features

- Uses [TypeScript](https://www.typescriptlang.org/) to write code, tests, and scripts.
- Uses [esbuild](https://esbuild.github.io/) to compile your TypeScript codebase, tests, and scripts.
- Uses [tsc CLI](https://www.typescriptlang.org/docs/handbook/compiler-options.html) to type-check the codebase without emitting the compiled files (since they’re handled by esbuild). No need to keep [TypeScript’s Project References](https://www.typescriptlang.org/docs/handbook/project-references.html) up-to-date.
- Uses [esbuild-runner](https://github.com/folke/esbuild-runner) to run scripts on the fly.
- Uses [Yarn workspaces](https://yarnpkg.com/features/workspaces) to make it easy to work within the monorepo.
- Uses [Ultra runner](https://github.com/folke/ultra-runner) to run scripts from the project root.
- Uses a shareable ESLint config and Jest config(./packages/jest-config) to provide an extensible linting and testing setup.
- Uses esbuild + [nodemon](https://github.com/remy/nodemon) to reload the server in development mode (even when workspace dependencies are changed).

### Workspaces

Tangerine monorepo includes five workspaces:

- `packages/is-even`: The simplest workspace — it doesn’t depend on any other worskpace. It’s a Node.js module that exposes an `isEven` function that tells if the input number is even. It includes a CLI script that invokes the function from your terminal, and a test file, both written in TypeScript. The CLI script runs using esbuild-runner, which uses esbuild to compile it on the fly.
- `package/is-odd`: Depends on `packages/is-even`. It’s a Node.js module that exposes an `isOdd` function that tells if the input number is odd (by invoking `isEven` and checking if it’s false). It includes a CLI script and a test file.
- `package/server`: Depends on both `packages/is-odd` and `packages/is-even`. It’s a Node.js Express server that exposes two routes that invoke `isEven` and `isOdd`. It uses nodemon to reload the server in development mode.
- `packages/jest-config`: Shared Jest config that uses esbuild to compile your tests and your codebase.
- `packages/eslint-config`: Shared ESLint config.

All the workspaces use esbuild to compile the TypeScript codebase. Be it for building, testing, or running CLI scripts, the compilation is instantaneous compared to the native TypeScript compiler (you can quickly test the difference by temporarily swapping esbuild with tsc).

The tsc CLI is used only to type-check the codebase (without emitting the compiled files — since they’re handled by esbuild). I expect people usually use the IDE integration to type-check the code anyway and explicitly invoke the tsc CLI only in specific use cases (such as pre-commit hooks).

Each workspace’s package.json is pointing the `main` and `types` entry to `src/index.ts`. Which might look strange at first, given that it’s uncompiled code… see [“You might not need TypeScript project references” on the Turborepo blog](https://turborepo.com/posts/you-might-not-need-typescript-project-references) for an explanation. This pattern has been working fine for my use cases so far (especially while using esbuild). Still, you might want to update these entries to suit your needs (e.g., when shipping packages to npm).