DEV Community

Cover image for Elegant Hybrid TS project’s build
Matti Bar-Zeev
Matti Bar-Zeev

Posted on

2

Elegant Hybrid TS project’s build

Oh BTW, This post was 100% human written with no AI assistance whatsoever. All typos and bad grammar are my own. Enjoy :)


I doubt you remember, but back at the time I wrote a piece about Hybrid NPM package through TypeScript Compiler (TSC), which explained how you can leverage TSC to produce 2 artifact types (ESM and CJS) from a single source code.
I thought it was pretty clever. Hell, I still do 😉

Recently though, I came across one of Matt Pocock’s TS course lessons (which I highly recommend on btw) that suggested a more elegant way to achieve that. In this post I will migrate to that way and explain what I do so your AI agent won’t get left behind ;)

Note: It would be best if you go over the Hybrid NPM package through TypeScript Compiler (TSC) article, so you will get the context of what I’m about to write on.


I have this package in my Pedalboard monorepo, called @pedalboard/media-loader (that you should totally check out if you’re a React dev struggling with media loading performance), which has a build process that generates 2 types of artifacts - ESM and CJS. In order to support that I’ve created 2 different tsconfig.json files and in the build script I’m running them both.
It looks something like this:

"build": "tsc --project tsconfig.esm.json & tsc --project tsconfig.cjs.json",
Enter fullscreen mode Exit fullscreen mode

Although there is nothing wrong with it, it kinda requires this plumbing in the package.json file and all-in-all feels a bit of a brute-force-primitive. Fortunately there is a more elegant way to achieve that using the references config.
Here is how:

Both the tsconfig.esm.json and tsconfig.cjs.json are in the root dir of the package, and to them I will add the “main” config file - tsconfig.json, and its content looks like this:

{
   "references": [
       {
           "path": "./tsconfig.esm.json"
       },
       {
           "path": "./tsconfig.cjs.json"
       }
   ],
   "files": []
}
Enter fullscreen mode Exit fullscreen mode

Let’s go over it real quick - it has a references array which has 2 paths defined in it, one for each config file, and it has an empty array for the files config, to ensure that the tsconfig.json does not enforce any type checking, but rather leaves that to the referenced projects.

Now we need to change the build script. Instead of what we have, we now write this:

"build": "tsc -b",
Enter fullscreen mode Exit fullscreen mode

Notice the -b param, which tells TSC to run it in build mode, thus taking into consideration the different references in the main tsconfig.json file. In other words, without the -b param, it won’t work as we expect it to.

And that is it!

When we run our build script now, TSC will run both configurations and create both ESM and CJS artifacts.
There is a lot more you can do with the project references like cache type check results (for faster builds) or define dependencies between projects, but in this case what we have is enough.

The code is available on Github @pedalboard/media-loader

Be seeing you

Photo by Trnava University on Unsplash

Top comments (0)

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post

👋 Kindness is contagious

Please show some love ❤️ or share a kind word in the comments if you found this useful!

Got it!