Olm - Yet Another Static Site Generator

I manage a website that was generated by the Pelican static site generator. I think it's a great idea when writing blogs to keep the content separate from the presentation and ideally in as simple a format as possible. In this way I think you have the best chance of a future proof site. Static site generators, combined with a markup language like markdown, are perfect for this and Pelican met most of my needs. But a few features I wanted turned out to be quite difficult to implement.

I was inspired by this blog post to try and make my ideal static site generator. Once I started progress was quite quick so I kept it up and have now replaced Pelican on the site I manage. I endeavoured to make it as much a 'drop in' replacement as possible and to make it useful for other people as well.

The main features are:

You can find the code repo here.

It's also on PIP so you can install it with:

pip install olm

Why is it better?

Speed and caching

Out of the box, it's faster than Pelican. This is achieved by switching to the Mistune markdown parser, which is blazingly fast in comparison to the standard Python Markdown. It also assumes the input files will be utf-8. I think Pelican (via docutils) might be doing some encoding guesswork to support more codecs which is inherently slow.

If you also enable caching it is even faster because it only rewrites the files that are changed. It also features a 'trigger' system to rebuild dependent pages. For example, if you have an index page that displays summaries of all your articles you would want it to update when you update the 'summary' metadata on an article. Olm builds a list of all the changes between each build and then rewrites any files that are triggered by those changes. For the default file types (articles, pages, index) these triggers are configurable in the settings and you can even subscribe to specific metadata changes.

If you want some numbers, I have mentioned that I am currently using Olm on a site. Previously with Pelican it would take approximately 1 minute and 30 seconds to rebuild (the server on which it runs is not particularly impressive). With Olm a non-cached completely fresh build takes 17 seconds. With caching enabled some changes only require a 1.5 second rebuild! So Olm is at worst 5 times and at best 60 faster for my use case than Pelican.

Customisable caching

So it might be disingenuous to insinuate that Olm is better just because it caches. Pelican of course does the same thing and so can be much quicker than I have suggested. However it is extremely hard to extend this. In my case I have pages that were generated based on the metadata in other pages and this did not seem to play well with Pelicans caching.

In Olm I have attempted to make adding new pages and file types easier and to expose the caching system in such as way that it is extensible.

Subsites

This might be a fairly niche requirement but Olm supports subsites. They can have different settings, themes (including js and css) and plugins to the main site but inherit where alternatives are not provided. They are built along with the main site so there's less overhead to manage multiple subsites.

Plugins

The plugin system is heavily influenced by Pelican. Olm fires a number of Blinker Signals throughout the build process and plugins can subscribe to these signals and modify the site state and context. The plugins are simple Python functions and are easy to write. There are no restrictions on what plugins can change which I think contrasts with other generators (like Pelican) where a lot of the state is immutable. I'm sure there's a good stability/safety reasons for that but I think it should be up to you if you break your own site and many of my gripes with Pelican were related to this lack of access to the internals.

One of the advantages of this state modification is that integrating deeply with Olm is easy from a plugin. If you add a new file type (e.g. author pages, or article statistics pages) you can inherit from the main Source class to define your file and then add files to the main all_files list which will automatically handle caching for you. You can define your own trigger file types, and define when the new files should be triggered.

Future

I have a long TODO list for Olm. It's still in the early stages and I am likely to make breaking changes in the coming weeks but I do hope it will get to the state where someone else could use it. Look out for v1.0.0 😉