cover

TypeScript monorepo bundling for dummies

Tue Nov 14 2023

It might just be me, but I always felt like frontend development was always a step ahead when it came to monorepos and bundling dependencies together. Frameworks like Next.js are like magic, they just work. Backend development has always been trying to catch up.

Tools like tsup and potentially even Turbopack in the future, already improved things a lot and are moving backend development into a good direction. While these tools are definitely amazing, and I can only praise the developers working on them, I somehow can't shake the feeling that somehow things are too complicated under the hood for a lot of people.

Implementing tsup in existing projects can be cumbersome and can result in days of debugging internal esbuild (which is what tsup uses under the hood) code to understand what exactly is going wrong and how you can work around it.

At the end of the day, what do you want from a bundler in a monorepo? You want it to build the consuming application and have the internal shared packages available to the consumer. That's it, nothing more. Maybe minify the code along the way and throw in some source maps, but that's pretty much it.

So... what if we simply copied over the compiled directories of the internal packages to dist/node_modules of the consuming application -- essentially bundling everything together. That way node can resolve the packages straight from the nearest node_modules.

Introducing bndl, a basic TypeScript transpiling and bundling tool for (primarily backend) monorepos. It uses SWC under the hood so it benefits from the speed improvements that it brings over tsc. It aims to be a near drop in replacement for people already accustomed to tsc and uses the tsconfig.json already present in your project.

bndl goes through the monorepo, builds the current app (or package) with swc, identifies which dependencies are used by the consumer and copies them over to the compiled directory node_modules of said consumer.

The result? A dist that contains everything for your app to run. Simply copy the dist over to a Docker image and run it. Check it out here.