Skip to content

Instantly share code, notes, and snippets.

@plugnburn
Last active May 26, 2024 11:20
Show Gist options
  • Save plugnburn/c2f7cc3807e8934b179e to your computer and use it in GitHub Desktop.
Save plugnburn/c2f7cc3807e8934b179e to your computer and use it in GitHub Desktop.
Statix - the simplest static website generator in bash

Statix - the simplest static website generator in Bash

Statix is a stand-alone Bash script aimed at generating full-featured, routable static websites from reusable HTML snippets. It features the most basic templating engine ever possible but allows to organize your content in a SEO-friendly way. All Statix-based websites contain these parts:

  • Templates: a directory where all HTML templates are stored
  • Route configuration: a file that maps each publicly accessible template to a SEO-friendly URL
  • Assets: a directory with optional files copied to the output website directory with no processing.

This script is also lightweight. Aside from some standard file management commands such as cp, mkdir and rm, the only serious dependency for Statix is GNU Grep compiled with PCRE support (i.e. the version that supports -P flag, included in most Linux distributions).

Templates

In Statix, a template is a simple HTML file (or its partial) where also several special directives are allowed:

  • Include block (<!--#include:other-template.html-->) is a block that allows including another template to reuse existing HTML code.
  • Set block (<!--#set:variable=some new value-->) is a block that allows setting a variable to the specific string value.
  • Use block (<!--@variable-->) is a block that inserts a previously set variable value.

Note that if a variable is set twice, the first set block occurence overrides any others. So if you want to set some page-specific variables and want to be sure they will not be overwritten by any included templates, please put the appropriate set blocks at the very top of the page.

Route configuration

To let Statix know the entire structure of your website, it's mandatory to specify all routing in a separate file (say, routes.conf). This file contains the mapping of a physical template name (relative to your templates directory) and logical URL (relative to the supposed website root). Note that in order to avoid any building errors all URLs must end in / (forward slash). Physical names and URLs are separated with a colon (:). Each mapping pair is on a new line.

Example of a typical routes.conf file:

index.html:/
about.html:/about-us/
contact.html:/contact/
work.html:/portfolio/

Build process

Building the website out of the source materials (templates, routes and assets) is as simple as calling the script with all necessary parameters:

/path/to/statix.sh -r <route config> -t <template directory> -o <output directory> [-a <asset directory>]

Everything but asset directory is mandatory here. If <asset directory> is not specified, output will contain just the generated HTML tree and nothing else will be copied. If <output directory> doesn't exist, it will be created, but if it does and is not empty, it will be completely overwritten, so be careful! After the build completes, you can transfer the output directory contents wherever you want to host it.

#!/bin/bash
#Statix - the simplest static website generator in bash
#(c) Revoltech 2015
#stubs
ROUTEFILE=''
TPLDIR=''
OUTDIR=''
ASSETDIR=''
#routines
function showUsage {
echo "Usage: $0 -r <route config> -t <template directory> -o <output directory> [-a <asset directory>]" 1>&2
exit 1
}
function prerenderTemplate {
local TPLFILE="${TPLDIR}/$1"
local TPLCONTENT="$(<$TPLFILE)"
local L=''
local INCLUDES=$(grep -Po '<!--\s*#include:.*?-->' "$TPLFILE")
OLDIFS="$IFS"
IFS=$'\n'
for L in $INCLUDES; do
local INCLFNAME=$(echo -n "$L"|grep -Po '(?<=#include:).*?(?=-->)')
local INCLFCONTENT="$(prerenderTemplate ${INCLFNAME})"
TPLCONTENT="${TPLCONTENT//$L/$INCLFCONTENT}"
done
IFS="$OLDIFS"
echo -n "$TPLCONTENT"
}
function renderTemplate {
local TPLTEXT="$(prerenderTemplate $1)"
local SETS=$(echo -n "$TPLTEXT"|grep -Po '<!--#set:.*?-->')
local L=''
OLDIFS="$IFS"
IFS=$'\n'
for L in $SETS; do
local SET=$(echo -n "$L"|grep -Po '(?<=#set:).*?(?=-->)')
local SETVAR="${SET%%=*}"
local SETVAL="${SET#*=}"
TPLTEXT="${TPLTEXT//$L/}"
TPLTEXT="${TPLTEXT//<!--@${SETVAR}-->/${SETVAL}}"
done
IFS="$OLDIFS"
echo -n "$TPLTEXT"
}
#read all option variables
while getopts ':r:t:o:a:' OPT; do
case "$OPT" in
r) ROUTEFILE="$OPTARG" ;;
t) TPLDIR="$OPTARG" ;;
o) OUTDIR="$OPTARG" ;;
a) ASSETDIR="$OPTARG" ;;
*) showUsage ;;
esac
done
shift $((OPTIND-1))
if [[ -z "$ROUTEFILE" ]] || [[ -z "$TPLDIR" ]] || [[ -z "$OUTDIR" ]] ; then
showUsage
fi
#run main action
mkdir -p "$OUTDIR"
rm -rf "${OUTDIR}"/*
if [[ "$ASSETDIR" ]]; then cp -rd "$ASSETDIR" "${OUTDIR}/";fi
ROUTELIST="$(<$ROUTEFILE)"
OLDIFS="$IFS"
IFS=$'\n'
for ROUTE in $ROUTELIST; do
TPLNAME="${ROUTE%%:*}"
TPLPATH="${ROUTE#*:}"
if [[ "$TPLNAME" && "$TPLPATH" ]]; then
mkdir -p "${OUTDIR}${TPLPATH}"
renderTemplate "$TPLNAME" > "${OUTDIR}${TPLPATH}index.html"
fi
done
IFS="$OLDIFS"
echo "Website saved at $OUTDIR"
@joseluis
Copy link

joseluis commented Sep 18, 2016

Hey, I really love your simple script for static web generation. I found it very inspiring.

I want to make my own version, and I'd prefer to start by forking yours and then add several modifications and improvements I have in mind. But I don't see a license that explicitly allows it, and I don't see any other means of contact, so I can only ask you in the comments.

Would you be ok with that, or do you prefer that I don't use your code as a base, meaning I'll have to start from scratch?

Thank you!

@balupton
Copy link

balupton commented Oct 5, 2016

I concur with @joeluis - would be great for a https://spdx.org/licenses/ license mentioned

@plugnburn
Copy link
Author

@joselius, You can use any of my gists in your projects unless explicitly said otherwise.

@joseluis
Copy link

joseluis commented Nov 11, 2016

Thanks @plugnburn,

I finally ended up creating a static website generator from scratch and named it webera.

I believe it's already sufficiently dissimilar from yours so that the inspiration wont suppose a problem. And as a work in progress it will also keep diverging more and more. My intention is to make it more versatile than simple, without making it complicated.

Anyhow I'll be happy to receive any comments. Or none. Thank you again.

@ayjayt
Copy link

ayjayt commented Sep 29, 2017

Wow, I made something that is so close to this you'd think it's plagiarism. Even some of the tokens and keywords!

edit: okay, mines been updated so that you can embed arbitrary bash commands into any file- that's it! would love it if you took a look at it, since we both think alike github.com/circuitandcode/contate

@g--o
Copy link

g--o commented Sep 22, 2020

i continued this a bit: https://github.com/g--o/xstatix

@plugnburn
Copy link
Author

i continued this a bit: https://github.com/g--o/xstatix

Cool, because I even forgot about this project altogether.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment