I recently dug into a new tool that I wanted to explore called es-build. It’s a super-fast typescript compiler and JS bundler that competes with babel, rollup and webpack in certain ways.
You point it at an entry-point file and tell it to bundle it up for the browser (or node) and it does the rest. It converts typescript out of the box, without relying on
tsc. The developer’s benchmarks show es-build is many times faster compiling most projects.
I like webpack, I think it has a lot of powerful features and is the most robust tool to reach for when you’re building a large project. But, I enjoy playing around with other tools when I can. And I’ve been enjoying the process of building my own framework and toolchain to really understand what’s possible with frontend tools. I like the approach of imperative over declarative syntaxes. And webpack is very declarative most of the time. The mixing of declarative and imperative code in webpack configs is the reason it’s so powerful but it also confuses beginners.
So I set out to write a live dev server and production build script using typescript and no webpack. I still pulled some tools off the shelf and might switch some of them out as I continue to learn.
The first tool I found while researching was Estrella built by Rasmus Andersson – who’s kinda brilliant. It encourages a simple approach, it’s API is clear, and it reminds me of Gulp. If you want to understand what really happens when you run
npm run dev then this approach will help with that. It takes away some of the magic. Of course abstractions are still useful, we don’t have to examine every part of the stack. But code is better for configuration on something that is so critical to getting work done and so often fails and stumbles in real-world scenarios. In my experience local dev environment setup is one of the biggest sources of confusion and frustration when new developers join the team. Too much magic gets in the way of learning and fixing your own code.
So when a user run’s
npm run dev first we use es-build to build the dev.ts file, then we run it with a command line option of
I’m not bundling this build script because live-server is not happy with bundling, so we just set the format to CommonJS and let it run like a plain old node app. I’ll probably find an alternative to live-server eventually.
What’s first in the dev server? The same thing you would have in any script, a bunch of constants for configuration!
// for configuring this script's command line arguments, see also
I know this is a little silly, because I was just complaining about configuration, but some of this stuff is already properly abstracted, and the variable name’s meaning should be clear to developers.
I create a few helper functions then I start the actual build process.
After the static files I start dealing with CSS files. In my project I need to process my CSS files before I run my Typescript Build because otherwise type checking will fail. Normally es-build doesn’t even do type-checking but estrella starts up a tsc process to do that for us in parallel with the build process so we get feedback about type errors on the command line while the dev server is running.
The postcss pipeline is abstracted away and configured in it’s own file
postcss.config[.prod].js and postcss uses the tailwind plugin which consumes
tailwind.config.js. It’s a little frustrating to still have so many config files, but those tools normally configured in this way and all the documentation around tailwind will point developers towards those files anyway so I didn’t want to break existing mental models for those tools.
When I call the postcss command inside my helpers I pass a different set of plugins and options if it’s in production mode to purge the CSS files and minify them with cssnano.
After the CSS is ready we will run esbuild, start our live development server, and start watching for any file changes we care about.
What does this get us? A nice utility script inside the repo, that handles both dev and production build workflows in a way that is easy to modify and update for any developer that can ready JS already. It has far less magic than a webpack config, at the expense of being more verbose.
I have considered putting all of the build and developer tool configuration in one big file with lots of comments… it would be an interesting experiment. We could bring everything to imperative run our whole developer tool-chain:
postcssand plugins like