There’s currently an enhancement requested for Eleventy to offer some kind of official asset pipeline. I was thinking about this problem at work (where I’m rebuilding the Science On a Sphere website in Eleventy) and came up with a Clever Hack™ using Eleventy’s computed data, pagination, collections, and filters.
Fair warning: this is probably too clever.
The technique has four components.
- A filter to find the URL for a specific asset to help you load the assets in your layouts
- Some additional watch targets for Eleventy so the auto-reload works
Clear as mud, right? Let’s walk through how I transform the SCSS for this site as an example.
Transforming the assets
First, we have to do the transformation. We create a global data file
_data/styles.js. The data created is an array of
objects with three properties: fileName, hashedFileName, and contents.
At this point we have an array available that contains the names and contents of all of our CSS. Next step is to write out those CSS files.
Writing the asset files
To create the assets, we’ll use the technique for
creating pages from data. If you’re not familiar with this technique, you might want to read
root of my input directory:
Just a couple of things to point out here. I added a computed property
to each page —
assetKey — which we’ll use in a minute to
look up the asset URLs when we need them.
I set the permalink (the URL at which the assets will be available)
based on the environment (
NODE_ENV). In production we use
the hashed file name for cache busting, otherwise we just use the file
name (because otherwise our dev directory would fill up with CSS files
as we work on our styles).
Finally, I add each of these pages to a “_styles” collection. This will make it easier to find these pages later when we need their URLs. I chose to prefix the tag with an “_” just to avoid the possibility of a collision with a tag I might want to use for my blog or something. (Plus, I’ve been a Python programmer for a long time, so an “_” always implies something internal to me.)
Nota bene: you have to set
addAllPagesToCollections: true in your pagination
options, otherwise only the first asset ends up in the collection. I
spent a lot of time trying to figure out why only one of my two
stylesheets was working. 😅
Looking up the asset URLs
The last major piece of the puzzle is adding a filter that makes it
easy to grab the URL for any of your assets. In your
.eleventy.js configuration you can add this filter:
Triggering rebuilds with a new watch target
And finally, to make sure that Eleventy knows to pay attention to our
assets and rebuild the site, you can add this to your
.eleventy.js as well:
Now, whenever you make a change to your style, Eleventy will rebuild your CSS and reload the site.
Loading assets in your layouts
Now that all the pieces are in place, we can load our assets fairly easily in our layouts.
This assumes that we started with a file:
src/_scss/global.scss. Remember we set the asset key for
each asset as the file name, this was so that we can look it up
without having to know whether we’re using the hashed file name or
not. Plus, I think it helps you understand what’s going on in the
What’s to like
There are a number of things I like about this solution.
It didn’t require any additional dependencies to run tasks (no
Webpack), it’s all done by
wiring up the dev tools (
sass) in Eleventy
- I don’t have ridiculously complicated NPM scripts to watch my SCSS and my HTML and then have to try to coordinate the two
The URLs are robust: asset location is controled by the
permalinkof the JS template, and anything that references the assets automatically gets the right URL, so it’s easy to change my mind and move
styles/global.cssif I want, or set up a path prefix
What’s not to like
This feels a bit too clever to me. A new person coming to a project
using this technique would likely be utterly mystified regarding how
assets are processed. One might expect an asset pipeline like this to
be configured in
package.json, not hidden amongst other data files in
files, it’s just
scripts.js instead of
styles.js. But I’d also like to be able to handle images
in a way that’s similar to
Wagtail’s image processing. Wagtail’s custom image tags allow you to specify in your template
sizes, crops, and aspect ratios of images, and then it will generate
them for you based on the original uploaded image. I’d love to be able
to store only a single, original image in my repo and have the build
process generate all the needed image sizes and formats based on where