Huy Nguyen

The static site generator for this site


What it is

Just an (at the time of writing) a-bit-more-than-100-line Python script.

Why don't you just use [INSERT SSG NAME HERE]?

I could have, yes. But I don't need almost all features. At the moment, all I need is including and processing some HTML boilerplate, like meta, header, and footer.

Most importantly, I consider myself too "new" to web dev to use any of those funny magic tools. I barely even know HTML and CSS. If something on my site breaks or doesn't look right, I don't think I can easily point out if it's due to my HTML/CSS being sloppy or my configuration for the SSG not working.

To conclude, I want only one feature, and I want maximum control, so I figured it wouldn't be too difficult to just make my own.

How does it work?

Key definition and usage

The script can just be run like

python3 scripts/ssg.py [template-file] [output-file]

And the template file is processed then put in the output location. If I want to process a bunch of files, I can just use some Makefile magic. Of course, the Makefile is also under the root of this site. Note that the file assumes GNU Make.

Every template file starts with a list of key-values, followed by an empty line, then followed by the HTML content.

In the key-value list, each line defines a key and its value. The format is key=value. To save me the trouble of trimming whitespaces, any spaces before or after the equal sign are the key name or the value. So, foo = bar defines a key "foo " with value " bar". Notice the trailing and leading spaces. Also, to avoid Prettier from messing up with the format, I do allow a line to start with opening HTML tag <. Practically speaking, this allows me to put <!-- prettier-ignore --> before the key-value list.

In the HTML content, the defined keys can be expanded by surrounding its name with percentage signs, so, %key% expands key to its value. You can escape a percentage sign with a backslash \%.

Value expansion rules

I figured quite quickly that, a lot of keys will be quite large, so I should put them in a file, and instruct the script to go to that file and copy the content of the file as the value for the key. In that case, prefix the file name with a pound #. For example:

footer_content=#template/footer.html

Sometimes, I also want the referred file to be processed as a template file as well. What that means is, I want:

  • all the keys defined in that file to also be included,
  • the processor, when processing that file, to use some already defined keys,
  • and of course, any reference to a keys to be expanded.

So, when the file name is preceded by an exclamation mark !, the processor recursively walks in and process that file as well. Of course, this introduces a few issues:

  • Circular dependency will make the processor never stop. I just need to be careful, right? So, no built-in mechanism to detect for now.
  • The same key defined twice. I just let the processor err out.

This is kind of a bad SSG

Yes, and I won't deny that. You still have to put quite a bit of boilerplate at the start of each file, circular dependency issues are currently not checked, unintuitive necessity for at least one empty line between the key-value definitions and the actual content. But what could I ask for in a 100-line Python script?

Overall, it's served me well. And it's also quite fast, given the language it's written in. Granted, the Makefile also helps a lot here, so that I only need to build the parts that changed.