New blog post
This commit is contained in:
parent
65fbf22573
commit
153c659dbc
161
content/blog/2020-09-29-Static-Site-Generator.md
Normal file
161
content/blog/2020-09-29-Static-Site-Generator.md
Normal file
@ -0,0 +1,161 @@
|
||||
<!-- title: Jekyll Is Cool, But... -->
|
||||
<!-- render: yes -->
|
||||
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.
|
Reference in New Issue
Block a user