This repository has been archived on 2024-02-04. You can view files and clone it, but cannot push or open issues or pull requests.
blog.polynom.me/content/blog/2020-09-29-Static-Site-Generator.md

8.9 KiB

I love static site generators. They are really cool pieces of software. Give them some configuration files, maybe a bit of text and you receive a blog or a homepage. Neat!

For a long time, I have been using Jekyll as my static site generator of choice. Mostly, because it is one of the most famous ones out there and thus there are tons of plugins, documentation and templates to get started. It was nice, until I wished it would do a bit more...

During some time off, I wanted to do an overhaul of my infrastructure. Make things cleaner, document more things and finally do those tasks that I have been pushing aside for quite some time. One of those things is to make all my webpages, which today only include this blog and my XMPP invite page, share common assets. This got started after I wrote the invitation page and thought that it looked pretty good.

So off I went to create a subdomain for my own "CDN", generate a TLS certificate and... I got stuck. I wanted to have Jekyll generate two seperate versions of my pages for me depending on what I wanted to do: One with local assets for local previewing and testing and one with my "CDN" attached. As such I would have liked to have three files: _config.dev.yml, _config.deploy.yml and _config.common.yml, where _config.common.yml contained data shared between both the deployed and the locally developed version and the other two just contain a variable that either points to a local folder or my "CDN". However, I have not found a way to do this. Looking back, I perhaps would have been able to just specify the common config first and then specify another config file to acomplish this, but now I am in love with another piece of software.

Additionally, I would have liked to integrate the entire webpage building process more with my favourite build system, GNU Make. But Jekyll feels like it attempts to do everything by itself. And this may be true: Jekyll tries to do as much as possible to cater to as many people as possible. As such, Jekyll is pretty powerful, until you want to change things.

Introducing makesite

While casually browsing the Internet, I came across a small Github repository for a static page generator. But this one was different. The entire repository was just a Python script and some files to demonstrate how it works. The script itself was just 232 lines of code. The Readme stated that it did one thing and that the author was not going to just add features. If someone wanted a new feature, he or she was free to just add it by themself. Why am I telling you all this? Because this is the - in my opinion - best static site generator I have ever used.

Simplicity

makesite is very simple. In its upstream version, it just generates user defined pages, renders a blog from Markdown to HTML and generates a RSS feed. It does templating, but without using heavy and fancy frameworks like Jinja2. The "Getting Started" section of makesite is shorter than the ones of other static site generators, like Jekyll and Hugo.

This may seem like a bad thing. If it does not do thing X, then I cannot use it. But that is where makesite's beauty comes in. You can just add it. The code is very short, well documented and extensible. It follows the "suckless philosophy". In my case, I added support for loading different variables based on the file makesite is currently compiling, copying and merging different asset folders - and ignoring certain files - and specifying variables on the command line. Would I upstream those changes? Probably not as they are pretty much unique to my own needs and my own usecase. And that is why makesite is so nice: Because it is not a static site generator, it is your static site generator.

Speed

makesite is fast... Really fast. In the time my Makefile has compiled my page and tar-balled it, ready for deployment, Jekyll is still building it. And that makes sense, Jekyll is pretty powerful and does a lot. But for me, I do not need all this power. This blog is not so difficult to generate, my invite page is not difficult to generate, so why would I need all this power?

# Jekyll version
> time make build
# [...]
make build  1.45s user 0.32s system 96% cpu 1.835 total

# makesite version
> time make build
# [...]
make build  0.35s user 0.06s system 100% cpu 0.406 total

Buildsystem Integration

In case of Jekyll, Jekyll pretty much is your buildsystem. This is not so great, if you already have a favourite buildsystem that you would prefer to use, since it does not integrate well. makesite, on the other hand, does just the bare minimum and thus gives the buildsystem much more to work with. In my case, makesite just builds my blog or my other pages. If I want to preview them, then my Makefile just starts a local webserver with python -m http.server 8080. If I want to deploy, then my Makefile tar-balls the resulting directory.

# [...]

serve: ${OPTIMIZED_IMAGES}
        python ../shared-assets/makesite.py \
                -p params.json \
                -v page_assets=/assets \
                -v build_time="${BUILD_DATE}" \
                --assets ../shared-assets/assets \
                --assets ./assets \
                --copy-assets \
                --ignore ../shared-assets/assets/img \
                --ignore assets/img/raw \
                --include robots.txt \
                --blog \
                --rss
        cd _site/ && python -m http.server 8080

build: ${OPTIMIZED_IMAGES}
        python ../shared-assets/makesite.py \
                -p params.json \
                -v page_assets=https://cdn.polynom.me \
                -v build_time="${BUILD_DATE}" \
                --assets ./assets \
                --copy-assets \
                --ignore assets/img/raw \
                --include robots.txt \
                --blog \
                --rss
        tar -czf blog.tar.gz _site

This is an excerpt from the Makefile of this blog. It may seem verbose when Jekyll does all this for you, but it gives me quite a lot of power. For example:

  • -v page_assets=... (only in my version) gives me the ability to either use local assets or my "CDN" for deploying;
  • --copy-assets --assets ./assets (only in my version) allows me to copy my static assets over, so that everything is ready for deployment. If I want to use all assets, including the shared ones, then I just add another --assets ../shared-assets/assets and change the page_assets variable;
  • conditionally decide if I want a blog and/or an RSS feed with --blog and --rss
  • -v allows me to pass variables directly from the commandline so that I can inject build-time data, like e.g. the build date

If I wanted to, I could now also add a minifier on the build target or page signing with Signed Pages. It would be more difficult with Jekyll, while it is just adding a command to my Makefile.

Another great thing here is the usage of ${OPTIMIZED_IMAGES}: In my blog I sometimes use images. Those images have to be loaded and, especially if they are large, take some time until you can fully see them. I could implement something using JavaScript and make the browser load the images lazily, but this comes with three drawbacks:

  1. It requires JavaScript for loading an image, which is a task that the browser is already good at;
  2. Implementing it with JavaScript may lead to content moving around as the images are loaded in, which results in a terrible user experience;
  3. Some people may block JavaScript for security and privacy, which would break the site if I were to, for example, write a post that is filled with images for explanations.

The target ${OPTIMIZED_IMAGES} in my Makefile automatically converts my raw images into progressive JPEGs, if new images are added. However, this rebuild does not happen every time. It only happens when images are changed or added. Progressive JPEGs are a kind of JPEG where the data can be continously loaded in from the server, first showing the user a low quality version which progressively gets higher quality. With Jekyll I probably would have had to install a plugin that I can only use with Jekyll, while now I can use imagemagick, which I have already installed for other use cases.

Conclusion

Is makesite easy? It depends. If you want to generate a website with blog that fits exactly the way upstream wrote the script, yes. If you want to do something different, it becomes more difficult as you then have to patch makesite yourself.

Per default, makesite is more limited than other static site generators out there, but that is, in my opinion, where makesite's versatility and customizability comes from. From now on, I will only use makesite for my static pages.