Scaffold
frontend

Sed as a template engine

But why?

I initially wanted a minimal static site generator that would allow me to leverage my knowledge of Linux and the GNU utils. I also intended to make a fairly simple personal website that only I would edit, hence no need for a fancy framework sprawling with features.

There are different solutions out there, just type "static site generator in bash" in your search engine and you will find some really interesting minimalist projects, but ultimately I wanted to see what I could come up with.

The first thing that I decided was that I wouldn't use Markdown to template articles and pages. In my opinion, it doesn't make much sense to replace a markup language (HTML) with another one (Markdown), it just creates an abstraction that will probably leak at some point in time. Keeping the "source" file as close as possible to the "built" file seemed like a good heuristic.

I finally settled for a Makefile and a bunch of shell scripts, with all the templating handled by a sed one-liner:

sed -Ee "s:\[\[(.*)\]\]:\1:e" index.html

What this command does is scan the file for sections delimited by double square brackets, run the command written inside and replace the section (including the square brackets) with the output of the command. The final :e flag is the instruction that tells sed to interpret the text as shell script. In the following example, it is used to insert an HTML snippet that is repeated across the site.

<body>
    [[ cat src/html_fragments/fr_navbar.html ]]
    <main id="main-cont">
        <div id="main-title-cont" >
            <h1 id="main-title">TERMINALLY OFFLINE.</h1>
            <h3 class="page-subtext">

Since sed runs the code with /bin/sh, any command line tool can be used to generate text. You can read files, you have access to environment variables, you can use pipes, etc.

One thing to note however: the sed command above is only valid if you have one "bracketed" instruction per line, otherwise the .* pattern will overflow the first closing brackets and include all the text between ]] and [[. A better, albeit more obscure expression would be:

sed -Ee "s:\[\[([^\[\]]*)\]\]:\1:e" index.html

Here we take care to exclude brackets from the matched pattern with [^\[\]] so there is no overflow. You may choose other symbols and patterns to mark out sections but all the symbols have to be absent from any command you could type inside so brackets or curly brackets may not be ideal.

This command can be wrapped in a simple Makefile, as shown below. Other filters can be subsequently introduced, like regexes to remove all the comments, "minifying" files, etc.

SRC_DIR := src
BUILD_DIR := public

$(BUILD_DIR)/%.html: $(SRC_DIR)/%.html $(EN_DEP)
    @tput setaf 3; echo "==> Building $@"; tput sgr0
    @sed -Ee "s:\{\{(.*)\}\}:\1:e" $< > $@

And voilĂ !, you have the most minimalist-but-convenient template engine I could come up with. This website is actually built with this technique, with some other additional scripts to handle various tasks. Of course, this approach is convenient only if you are already familiar with the command line and are willing to write "raw" HTML/CSS. I don't claim this is the best approach, but it is simple and elegant, at least in my opinion.