Basic, Beautiful & Buildless
—How big is this damn autocomplete library?!
—I need to polish this form a bit but if I have to add a dependency, then I need to set up some sort of build process, and then this “small” project is no longer small.
—Do I really want to add a script directly from a CDN? What happens if they change something? Is it even safe?
—Why is everything to do with JavaScript such a pain in the ass?!
When there’s nothing small or fun about putting together a “fun small website”
There is a diversity of lightweight static site generators available. My personal favourite – the one I used to make this site – is Eleventy but there’s one in every programming language.
Static site builders handle HTML and markup quite well. That’s what they do. Even CSS – at least CSS of the complexity a small site needs – can be handled quite well by many of them.
But as soon as you need to add scripting things get quite complicated.
All of our dependency management systems are built around bundling and npm
and there is no such thing as a straightforward bundler. Even the most lightweight and accessible of bundlers turns a fun small project into a not-so-fun major project.
And don’t even get me started on webpack
.
You could switch to a more ‘batteries included’ static site framework, but most of those end up requiring literal megabytes of JavaScript and, again, we’ve left “fun” and “small” behind in the rear view mirror.
People talk about “buildless”, using browser support for modules instead of a bundler, but it’s hard to know even where to start.
The “Buildless” utopia feels so distant
JavaScript on the web has never been simple. Even before ES module bundling, we had browserify
and Common JS modules. Before that we were using jQuery plugins and loading everything into global variables, which is as messy as that sounds.
And before that, at the beginning, browsers barely had any cross-platform compatibility.
Web dev has never really been small or simple.
But it did use to be smaller and simpler and it definitely used to be entirely buildless.
If you wanted to add autocomplete to a text input on a simple site, you just added a jQuery plugin, maybe even linking to it directly from a CDN (Content Delivery Network).
Of course, it was that straightforward because you were ignoring a bunch of problems:
- Messy. Over time, even “small” jQuery projects became hard to manage.
- Insecure. Even with
https
, which wasn’t a given back then, linking directly to a script on a CDN was insecure. - Global. Everything you added had to hang off the page’s global state and untangling that could get complicated.
- Opaque use of bandwidth. Even though jQuery plugin scripts never had the file sizes you see in modern JavaScript packages, any time you load a dependency off a CDN or package manager, you lose a bit of insight into its file sizes you would have had if you were working with its files directly.
But, still…
The uncomplicated workflow of “just link to this plugin, everything will work” is hard to beat.
If only it could be done safely, securely, and simply.
It can’t.
Small tools for small projects
But even though it can’t be done simply, it can be done automatically and, more importantly, it can be done in the browser.
That means somebody else – like me, for instance – can build tools that do the complicated and involved things necessary for you to be able to:
- Figure out the correct URLs for loading the package from a CDN like
esm.sh
. - Pinning their version to ensure you will consistently get the same package even though new versions get released.
- Load them safely, using subresource integrity (SRI) hashes that prevent the browser from even loading them if they’ve been changed.
- Map them to friendly package names you can use in your scripts using import maps.
- Automatically include a polyfill for import maps so you don’t have to create a separate bundle for older browsers.
- Create
modulepreload
link elements for every dependency module that a package imports to minimise the performance impact of not bundling everything. - Calculates the overall payload size of each individual packages so you know what you’re in for even before you add it to your project.
And then deliver all of those features in a single textbox containing the markup you need to copy and paste into the head of your small website so you can get on with the fun part of actually making something.
That’s what the Instant Import Map generator is for.
You only need to type in the package names, optionally, version numbers, and click the button. You’ll get an overview of the payload size and the markup you need to copy in a textarea
.
It’s not going to be enough for complex projects or web apps, but it should help you get up and running using JS modules and imports when making a small website or a quick project.
Once the markup is in the head
of your template, you can import your module scripts using a type="module"
script anywhere below it and the import map will mean you can use whichever package you’re importing using their names, not their URL:
import DOMPurify from "dompurify";
function sanitizeFragment (markup) {
return DOMPurify.sanitize(markup, {
FORBID_TAGS: ["style"],
FORBID_ATTR: ["style"],
RETURN_DOM_FRAGMENT: true,
});
}
It can look just like your regular, run-of-the-mill bundled code.
Just try it out for yourself:
And if you just want the size calculator, you can find that over here:
Caveats
- The Instant Import Map tool uses the
esm.sh
CDN. Not everything works with their system. Most web-oriented ES module packages should, though. - The tool has a module walker that parses through the
imports
, both static and dynamic, of a module and if the import values are URL strings, it will fetch and walk through them as well. - My quick and dirty module walker doesn’t handle
wasm
files so if you import a package that useswasm
that file won’t be included in the preload elements or in the size calculations. - The module walker itself is a bit messy. It has test coverage but I’m sure you can find packages that break it.
- It includes the
importmap
polyfill by default. If you don’t need it, you can edit it from the markup yourself. - The
esm.sh
CDN minifies scripts automatically but doesn’t do tree-shaking without additional configuration, so none of the imports or calculations use tree-shaking. - Some dependencies, such as React, are likely to require custom configuration which are beyond the scope of this project.
I’m sure there are more that I’m forgetting. If you run into issues, let me know. Just bear in mind that this isn’t intended for large apps or big projects. It’s a small tool for small sites.
If you find this tool helpful, let me know! I have ideas for a few other “buildless” tools, but it’s hard to tell whether people actually like what you’ve done, esp. when you’re like me and haven’t bothered to add analytics to something you’ve launched. 🙂