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 thepage_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:
- It requires JavaScript for loading an image, which is a task that the browser is already good at;
- Implementing it with JavaScript may lead to content moving around as the images are loaded in, which results in a terrible user experience;
- 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.