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
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"
}
}
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
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
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)