Up to Main Index Up to Journal for April, 2025
JOURNAL FOR WEDNESDAY 30TH APRIL, 2025
______________________________________________________________________________
SUBJECT: Bash static site generator for tutorial rewrite
DATE: Wed 30 Apr 21:52:24 BST 2025
Last time in the journal I wrote about re-writing the Mere ICE tutorial. I had
a few goals for the re-write:
• Tutorial should be proper web pages, not source in the editor.
• The tutorial should have live editable, executable examples.
• Allow multiple examples per page.
• It should look nice and fit the WolfMUD website style.
• Tutorial and examples must be easy to write and low maintenance.
At the time I also said:
I don’t think I can make the pages of the tutorial simpler than that. The
code is in <code></code> blocks. There are call-outs for “try this” and
“tips” sections using <blockquote></blockquote>.
Unfortunately, even with includes, there is quite a bit of boiler-plate at
the start of each page. By the way, I am hand-coding everything - no
frameworks.
I’ve since had time to mull over my approach to the re-write. As a result I’m
now using Markdown. Specifically, on Debian, I am using the “lowdown” Markdown
processor. And… I’ve written a tiny static site generator (sitegen) in Bash to
help. Even handles the includes/boilerplate. The full source code, 50 lines:
#!/bin/bash
# sitegen: A simple static site generator.
#
# Released into the public domain under “The Unlicense” license.
# SPDX-License-Identifier: Unlicense
hlp=$(cat <<EOT
Usage: ./sitegen [-a] [-v]
-a = rebuild all
-v = verbose output
EOT
)
options="--html-no-escapehtml --html-no-skiphtml"
all=0
verbose=0
while getopts ":ahv" Option; do
case $Option in
a ) all=1;;
v ) verbose=1;;
h ) echo -e "$hlp"; exit;;
* ) echo -e "Invalid option: ${Option}\n${hlp}"; exit;;
esac
done
shift $(($OPTIND - 1))
for md in `ls ./*.md`; do
html=${md/#./..}
html=${html/%.md/.html}
if [ "$all" != "1" ] && [ "$md" -ot "$html" ]; then
[ "$verbose" == "1" ] && echo "Not modified: $md → ${html}"
continue
fi
[ "$verbose" == "1" ] && echo "Processing: ${md} → ${html}"
lowdown $options $md > ./body.inc
grep -q "</code>" ./body.inc
code=$?
if [ "$code" -eq 0 ]; then
[ "$verbose" == "1" ] && echo " Including Javascript and wasm."
cat head.inc body.inc foot.inc > $html
else
[ "$verbose" == "1" ] && echo " No code found."
cat nojs-head.inc body.inc foot.inc > $html
fi
done
[ "$verbose" == "1" ] && echo "Finished!"
My working directory structure then looks like this, note I’ve only included
the .md and .html for the index and whats-new pages here for brevity:
ice
|-- tutorial.css
|-- index.html
|-- whats-new.html
|-- ice.js
|-- wasm_exec.js
|-- ice.wasm
`-- md-src
|-- sitegen
|-- body.inc
|-- foot.inc
|-- head.inc
|-- nojs-head.inc
|-- index.md
`-- whats-new.md
I write Markdown in the md-src directory. This directory also contains the
head.inc, nojs-head.inc and foot.inc include files. The main ice directory is
where the HTML, CSS, Javascript and WASM live. Development is then as easy as:
>vim index.md
>./sitegen
>
When sitegen is run it finds *.md newer than the corresponding *.html,
processes the *.md to create body.inc HTML, top and tails body.inc with the
relevant includes and saves the new HTML in the parent directory. An example
rebuild with verbose output turned on that shows the checking and rebuilding:
>./sitegen -v
Not modified: ./contents.md → ../contents.html
Not modified: ./debugging-programs.md → ../debugging-programs.html
Processing: ./index.md → ../index.html
Including Javascript and wasm.
Not modified: ./variables.md → ../variables.html
Not modified: ./what-is-mere.md → ../what-is-mere.html
Processing: ./whats-new.md → ../whats-new.html
No code found.
Not modified: ./white-space-and-punctuation.md → ../white-space-and-pu…
Not modified: ./writing-code.md → ../writing-code.html
Finished!
>
A single HTML page takes less than 50ms to build. Building the current 8 pages
takes 125ms. When I run sitegen it only rebuilds pages where the *.md has been
changed, unless I pass in the ‘-a’ flag to rebuild all files.
The script isn’t perfect. If I modify one of the *.inc include files I have to
remember to use ‘-a’ and rebuild everything. If I delete or rename a *.md file
I have to delete or rename the associated *.html file. Besides fixing those
issues, there are a few nice things I can do in the future. For instance,
making a Git hook to automatically generate the HTML when the Markdown is
pushed to the live site. Eventually md-src will be moved outside of the ice
directory, which will be public on the live web server.
I’m currently treating this as an experiment. Depending on how it goes, and so
far it’s going pretty well, I might revisit the main WolfMUD website ;)
The sitegen script is free to all — Take it, hack it, make it yours. If you
find sitegen useful, please drop me an email and let me know. Any enhancements
sent my way are greatly appreciated as well: diddymus@wolfmud.org
Oh! I guess I am using a framework now after all… *sigh*
--
Diddymus
Up to Main Index Up to Journal for April, 2025