diff --git a/content/blog/2020-09-29-Static-Site-Generator.md b/content/blog/2020-09-29-Static-Site-Generator.md new file mode 100644 index 0000000..4d6f04b --- /dev/null +++ b/content/blog/2020-09-29-Static-Site-Generator.md @@ -0,0 +1,161 @@ + + +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*](https://github.com/jekyll/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](https://git.polynom.me/PapaTutuWawa/blog.polynom.me) +and my [XMPP invite page](https://git.polynom.me/polynom.me/xmpp-invite-web), +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](https://github.com/sunainapai/makesite) 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*"](https://suckless.org/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. + +```makefile +# [...] + +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](https://github.com/tasn/webext-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.