DEV Community

David Matějka for Contember

Posted on

Unleashing TypeScript performance in monorepos with TSX

In the software development world, TypeScript-based monorepo projects are increasingly popular. Yet, they come with a unique set of challenges, the most notable being the time-consuming process of TypeScript compilation during development.

At Contember, we've faced this challenge head-on. Our solution is a combination of the TypeScript Execute tool (tsx) and Node.js' --conditions option. This approach allows us to run TypeScript source directly in Node.js, significantly accelerating the development process by eliminating the need for continuous recompilation.

In this article, we'll walk you through how we implemented this efficient strategy, enhancing the speed and overall quality of our TypeScript development in a monorepo environment. Let's dive in!

The problem

Monorepos

Monorepos, or monolithic repositories, are a codebase management strategy where the code for many projects or packages is stored in the same repository. This structure can bring numerous advantages, such as simplified dependency management, code sharing across projects, and unified versioning. However, when working with TypeScript within a monorepo, the continuous compilation process can slow down the development workflow.

TypeScript

TypeScript is a statically-typed superset of JavaScript that enhances the language with type safety and other modern features. While TypeScript brings many advantages, it adds an extra step to the workflow: it must be compiled into JavaScript before it can be run in Node.js.

In a large project or a monorepo with multiple interdependent packages, waiting for TypeScript compilation can become a significant bottleneck in the development process. To streamline this workflow, we need a way to run TypeScript source code directly in Node.js without the intermediate compilation step.

The tools

tsx

TypeScript Execute (tsx) is a powerful command-line tool that enhances Node.js to run TypeScript and ESM files directly. It leverages the esbuild compiler to provide an insanely fast on-demand TypeScript and ESM compilation.

To add tsx to your project, install it as a dev dependency:

yarn add --dev tsx
Enter fullscreen mode Exit fullscreen mode

The tsx designed to replace the node command for running TypeScript and ESM in both commonjs and module package types. For instance, to run a TypeScript file, you would simply use the command tsx ./file.ts.

Node.js' --conditions Option

The --conditions option is a feature in Node.js that allows you to specify custom conditions for resolving exports. It works in conjunction with the "exports" field in your package's package.json file, giving you control over how module exports are resolved based on certain conditions.

For instance, by defining conditional exports in your package.json file, you could specify different entry points for your package based on whether it's being imported using CommonJS or ESM syntax, or whether it's being run in a development or production environment.

By running your Node.js program with the --conditions flag, such as node --conditions=development app.js, you can enable custom resolution conditions for your module exports.

Combined, these two tools can dramatically improve your workflow in a TypeScript-based monorepo by allowing you to run TypeScript source code directly in Node.js, bypassing the need for continuous TypeScript compilation. Let's move on to explore how to implement this strategy.

The Solution

Configuring conditional exports in your monorepo

The first step is to set up conditional exports in your package.json file for each package in your monorepo. This configuration specifies the entry point that Node.js should use based on the given condition. For our purpose, we'll create a "typescript" condition that points to the TypeScript source files. You should also define a default export with a compiled source:

{
  "exports": {
    "typescript": "./src/index.ts",
    "default": "./dist/src/index.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

In this setup, Node.js will import the TypeScript source files directly when the typescript condition is active. In other scenarios, it will default to importing the compiled JavaScript files from the dist directory.

Running TypeScript directly using tsx and conditions

Once your conditional exports are set up, you can use the tsx tool along with the --conditions=typescript option to run your TypeScript source code directly in Node.js:

yarn tsx --conditions=typescript ./src/run.ts
Enter fullscreen mode Exit fullscreen mode

This command activates the typescript condition. As a result, when Node.js resolves imports from your packages within the monorepo, it uses the TypeScript source files specified by the "typescript" conditional export. This eliminates the need for continuous TypeScript compilation, allowing for a smoother and faster development cycle.

And that's it! You've now configured your TypeScript-based monorepo to run TypeScript source code directly in Node.js. In the next section, we'll discuss some practical examples where this solution is beneficial.

Bonus: watch mode with TSX

A great advantage of using TSX in your TypeScript monorepo is its watch mode feature. With this, TSX not only allows you to execute TypeScript source code directly in Node.js, but it can also watch for changes in your TypeScript files and automatically restart your application whenever modifications are detected.

This can be particularly useful during the development phase of your project, where changes are frequent and real-time feedback is crucial for a smooth and efficient workflow.

You can activate the watch mode by running the following command:

tsx watch --conditions=typescript ./file.ts
Enter fullscreen mode Exit fullscreen mode

With this command, TSX will monitor ./file.ts and its dependencies. When any of them change, TSX will rerun the script with the typescript condition. This means your Node.js server gets restarted automatically whenever you make changes to any of the TypeScript source files in your dependencies, giving you immediate feedback and a dynamic development experience.

Conclusion

In TypeScript-based monorepos, the overhead of continuous TypeScript compilation can slow down development. By combining the TypeScript Execute (tsx) tool with Node.js' --conditions option, we have been able to run TypeScript source code directly when developing our Contember platform. This approach has not only reduced the turnaround time for changes but also streamlined the integration of monorepo packages, greatly enhancing our development workflow.

Top comments (0)