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