Alexander "PapaTutuWawa
caef031d48
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
168 lines
9.0 KiB
Markdown
168 lines
9.0 KiB
Markdown
+++
|
|
title = "Jekyll Is Cool, But..."
|
|
date = "2020-09-29"
|
|
template = "post.html"
|
|
aliases = [ "/Static-Site-Generator.html" ]
|
|
+++
|
|
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!
|
|
|
|
<!-- more -->
|
|
|
|
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.
|