an 11ty tip-slash-hack

I’m building a little 11ty-based tool at work, to dynamically generate checklists to guide the release of a piece of software we build. I’m not going to say the release process is overly complicated or anything, but the current printed docs run to over 20 pages when printed out (lots of screenshots), and they’re generally at a level of “what” rather than “how” — there’s some room for improvement, let’s say.

In this little tool, I’m building a custom page for each release. The individual release pages only contain front-matter with some release-specific variables (like version number, release date, etc). There’s a shared page template that handles rendering a checklist,

I’m using computed data in a couple different ways in this tool: I have the release steps written up in a particular JSON structure in a file under _data_. I also have a _data/eleventyComputed.js script that does some reorganization of that checklist data, and provides that munged form under a different top level key name. All that works great, and was really trivial to set up — as usual, the harder part was figuring out how I wanted to structure the data in the first place, to make it easy to edit and extend, and then how I wanted to transform it to better fit how I wanted to display it using that shared page template.

The problem I ran into is, there are some spots in the original JSON file where I want to replace a placeholder with something specific to a particular version of the software being released — something that’s going to be slightly different for every release, driven by the specific release, and set in the front matter for that release’s page. (Again, think version numbers, release date, that sort of thing.) The problem is, the computed data only gets computed once, before the rendering of the individual release pages happens — and I couldn’t figure out a way to get Liquid to recursively evaluate while rendering a template.

Eventually, I was able to find this Github issue, which gave me enough clues that I figured out how to give a filter access to the data cascade, which let me use String.replace() to turn my placeholder into the custom per-release value I was looking for. It ended up looking a little like this:

eleventyConfig.addFilter(
  "interpolate",
  function (str: string): string {
    // this can't be a fat-arrow function,
    // because we need access to `this`
    return str.replace(
      "__PLACEHOLDER__",
      this.context.environments.version_number
    );
});

Again, that version_number value is set in the front matter for a particular release, like so:

---
version_number: 10
---

Hopefully this ends up being helpful for somebody else…