Toolchainless
26 Feb 2019
I have a complicated relationship with build tools. I like TypeScript, Gulp, and the like. I think that compilers are the new frameworks. Heck, even this static site is built with Gatsby; an overcomplicated toolchain to generate HTML files if there ever was one.
But build tools are a Roach Motel in your stack: once you add them in, you're unlikely to ever abandon 'em. And it's never been easier to start a project with a ludicrously complicated toolchain. You're just one npx create-react-app
away from a running project, powered by 1023 dependencies in node_modules. Easy, but not simple.
That's the bargain we've made to improve our ergonomics. React and Vue and Angular are so incredible that we use them in spite of their toolchains, not because of them. But I'm excited about new methodologies that are breaking that link, and letting us develop without tools for the first time since the good ol' jQuery days.
You might not need build tools
Some tools have gone away because browsers got better. With features like CSS Custom Properties, you might not need Sass anymore. And you can decompose your app into ES Modules and load as needed using import/export statements; no Webpack or Rollup required.
Some build tools went away because your editor got smarter. I love TypeScript, but Visual Studio Code's inference works pretty damn well for JavaScript as well; providing autocomplete and lots of other goodies.
Some build tools can be removed because CDNs are making a comeback. unpkg delivers up all of NPM via a simple URL, so you don't have to pull in Webpack just to load a dependency.
And new libraries are taking advantage of the toolchainless approach. Take htm - an in-browser project that uses ES6 tagged template literals to generate Hyperscript in the browser. You can drop it into a Preact app and remove the JSX transpilation step:
render({ page }) {
return html`
<div class="app">
<${Header} name="ToDo's (${page})" />
<button onClick=${() => this.addTodo()}>Add Todo</button>
<${Footer}>footer content here<//>
</div>
`;
In fact, you can get a full Preact + htm project up and running with a single import statement:
import {
html,
Component,
render,
} from 'https://unpkg.com/htm/preact/standalone.mjs';
Drop that into a script tag in your index.html
file and you've got a working Preact app that can run anywhere.
Pizza Compass
I recently rebuilt an old project, Pizza Compass, a PWA that points you to the closest pizza. The app I cloned claims to be the most important app ever made, which I can't deny.
The first version of Pizza Compass was built in 2013 using jQuery, before toolchains took over. When I decided to rebuild it using modern UI components, I went with a toolchainless approach.
Built with the Preact + htm stack above, the codebase is modern and clean. And it felt great. I can still build with components:
const PizzaCompass = ({ loc, heading, currentLoc }) => {
const b = bearing(currentLoc, loc);
const headingDelta = 180 - (heading - b);
const distance = Math.floor(distanceFrom(currentLoc, loc) * 10) / 10;
return html`
<div class="app">
<header><h1>${loc.name}</h1></header>
<${Pizza} rotation=${headingDelta} />
<footer><h1>${distance} km</h1></footer>
</div>
`;
};
But I'm not beholden to any toolchain. Builds are instantaneous because there's nothing to build. The project doesn't even contain a package.json
file. Hell, I can run the whole thing using Python's SimpleHTTPServer.
The whole thing is 150-ish lines of Preact code, and when I'm done I can push to GitHub and have Netlify deploy the folder directly. It takes less than a second.
How far does this scale?
I don't know. Eventually you'll probably reach a point where you want to add in tooling for asset optimization, or smarter bundling, or supporting IE11.
But for a weekend project like this, it worked great. And if it ever gets big enough, I can check in the tooling Roach Motel then.
I like this approach because it aligns with the Principle of Least Power. Not every project needs to have 1023 dependencies in their node_modules
. And the more you default to using an opaque toolchain for everything, the more likely you'll get bit with opaque errors.
Toolchains are a chainsaw. And these days, you don't have to use a chainsaw for everything.