Writing your build scripts in TypeScript

Note: I've since switched to tsm instead of @digitak/esrun in some of my projects. They both use esbuild under the hood, and either should work well.

Continued blog infra rewrite

I had a lot of fun getting full service worker rendering working for this blog, and have continued to noodle on a number of improvements since then.

Some of them are bigger than others (I'm looking forward to writing a dedicated post about the Workbox plugin I built that improves runtime caching for hashed URLs!), but a quick one is a plug for the @digitak/esrun module.

esbuild is great at build time

I've been very happy using esbuild extensively in my blog's build process. It's provided (almost) zero-config TypeScript transpilation and bundling that runs so much faster that others tool with similar feature sets.

One thing that bothered me, though, was that my build scripts produce a number of assets that are consumed at runtime by my HTML and JavaScript, but because my build process didn't understand TypeScript types, I didn't have a lot of confidence that all the data structures matched up, and that if I refactored code in one place, I'd remember to update my generated assets.

Writing my build scripts in TypeScript would solve that, but adding in another build step to transpile my scripts before I could start my real build process sounded clunky. I know that ts-node and a few other options have been around for a while, but I haven't heard great things about their speed.

esrun is great at runtime

Some more Googling led me to @digitak/esrun, which is a light wrapper on top of esbuild that will automatically transpile TypeScript source files before running them as command line scripts.

After adding @digitak/esrun to devDependencies, and rewriting my build scripts in TypeScript, my package.json's build script went from

{
  "scripts": {
    "build": "node src/build/main.js"
  }
}

to

{
  "scripts": {
    "build": "esrun src/build/main.ts"
  }
}

That's all it took! I now have confidence that comes from sharing types between my build and runtime environments, and the increase in build time due to transpilation is completely negligible.