This is a fantastic article from Julia Evans about duking it out with modern front-end tooling. Julia has made a bunch of Vue projects and typically uses no build process at all:
I usually have an
index.html
file, ascript.js
file, and then I do a<script src="script.js">
and that’s it.
Which I think is awesome and probably means those projects will last a damn long time. But, I understand a desire to move into some more modern dev tools, if nothing else to unlock the whole npm ecosystem. Basically, Julia wanted to do:
import Vue from 'vue';
That straight up implies npm and a bundler, because that syntax is bundler-specific (it’s invalid ES modules syntax). This used to pretty much imply webpack, rollup, or Parcel, but that’s starting to change and heavily suggest Vite instead.
But Julia didn’t want to reach for tooling that did too much ():
But I stopped using those tools and went back to my old
<script src="script.js">
system because I don’t understand what thosevue-cli-service
andvite
are doing and I didn’t feel confident that I could fix them when they break. So I’d rather stick to a setup that I actually understand.
So, with Vite being too much, Julia reached for esbuild. I can’t say I understand the details, but Vite uses esbuild internally for some things:
Vite uses esbuild instead of Rollup for dependency pre-bundling. This results in significant performance improvements in terms of cold server start and re-bundling on dependency invalidations.
So reaching for esbuild directly is a bit of a step down the complexity ladder.
It was so close to being a home run, but of course the dang complex nature of the front-end ecosystem struck again. The assumed copy of Vue that esbuild grabbed from the import Vue from 'vue';
was vue.runtime.esm.js
, which is the version of Vue designed to be run in the browser, not the version that includes a compiler. Ughghk. Feels like they should be two separate packages or something. But Julia ultimately battled through that and it was a win:
I don’t understand everything
esbuild
is doing, but it feels a lot more approachable and transparent than the [webpack]-based tools that I’ve used previously.
All this is one more reason I’m still bullish on Skypack. You can use modern import
statements like that and still not need a build process at all.
Small correction (in an area I’ve had to become alllll too familiar with over the past few years):
This isn’t quite right. It is, in fact, perfectly valid ES modules syntax, because the ES spec defines the string identifier part of an import statement as being something which implementations are free to define. So:
The Node ESM support which came with Node 12 LTS is totally spec-compliant.
So is Ember’s classic custom module resolution scheme (slowly-being-phased-out in favor of Node-compatible resolution).
So is Deno’s URL-based scheme.
So is (and this is the key bit here) the standard which browsers have landed on of requiring URLs.
That’s a small nit but raises what I think is an important point. There’s sometimes a tendency to treat what the browsers do as defining what is and isn’t the spec. And there’s an element of truth to that, because they’re usually some of the biggest voices in helping define the spec: but JavaScript is, unlike CSS, not something which only the browsers define.
Here, the net is that that code snippet implies a non-browser context, at least for today, because browsers have chosen to require URLs and have no implemented modules for the standard library. In the future, that might change, though. Then, instead of having to hang things off of global names all the time (
Number.isNaN()
) it might be possible to write imports instead (in ascript
tag in a browser!):(There is no current motion on that which I’m aware of, but it’s a good idea and it’s a thing that has been batted around, and it helps to illustrate the point.)
Bare module specifiers would be supported in the browser with import maps. However, a reasonable way of generating an import map would rely on a build step.
Bare specifiers with standard library built-in namespaces/objects would be interesting, as well, but I don’t think that’s covered with import maps.
Yeah, all I need is something to get the hash of the files and insert them and change the file names. But no tool can do that for me without wanting to do a lot more. Maybe some tool can do that for me. But I haven’t found it.
It would be nice if it could transpile my TypeScript files too. But just the former would be nice and good enough.