Previously, I discussed some various possible ways to structure the coding of a website, and why I decided to rebuild this site around the static site generator Wintersmith. Today, it’s time to dive a little deeper into what that actually entailed. Don’t worry if you’re not a technical reader; I’ll try to keep it all fairly straightforward.
The Plain People Of The Internet: Hang on a minute there, now! You told us up there at the top, you were going to keep all this nice and straightforward for us non-technical Plain People. This isn’t sounding very non-technical to us now.
Ah, but I promised I would try. And look, so far, all I’ve done is listed stuff and told you why I needed to use it.
The Plain People Of The Internet: You’re not going to be enticing people to this Wintersmith malarkey, though, are you? Us Plain People don’t want something that means we need to learn three different languages! We want something nice and simple with a box on-screen we can write words in!
The Plain People Of The Internet: Right. You’re not convincing me, though.
Well, just stick with it and we’ll see how it goes.
Wintersmith, indeed, is designed for its behaviour to be changeable and extendable. By default it only has a fairly small set of capabilities. It takes a “content tree”, a particular set of files and folders, and a set of templates. Markdown files in the content tree are converted to HTML, merged with a template, and written to an output file. JSON files are treated in almost the same way, almost as content files without any actual content aside from a block of metadata. Other filetypes, such as images, are copied through to the output unchanged. So, to take this article you’re reading as an example: it started out as a file called
articles/we-can-rebuild-it-we-have-the-technology-part-two/index.md. That file starts with this metadata block, which as is normal for Markdown metadata, is in YAML:
--- title: We can rebuild it! We have the technology! (part two) template: article.pug date: 2020-09-28 20:09:00 ... ---
I’ve configured Wintersmith to use a default output filename based on the date and title in the metadata of each article. This file, therefore, will be merged with the
article.pug template and output as
2020/09/28/we-can-rebuild-it-we-have-the-technology-part-two/index.html, so its URI will nicely match the equivalent in Wordpress. So there you go, we have a page for each blog post, almost right out of the box.
That’s fine for individual article pages, but what about the home page of the blog? Well, Wintersmith is designed to use plugins for various things, including page generation; and if you create a new Wintersmith site using its blog template, you will get a file called
paginator.coffee added to your site’s plugins folder, plus a reference in the site configuration file
config.json to make sure it gets loaded.
"plugins": [ "./plugins/paginator.coffee" ]
The code in
paginator.coffee defines a class called
PaginatorPage, which describes a page consisting of a group of articles. It then calls a Wintersmith API function called
registerGenerator, to register a generator function. The generator function looks over every article in the
content/articles folder, slices them up into blocks of your favoured articles-per-page value, and creates a
PaginatorPage object for each block of articles. These are then output as
page/3/index.html and so on. There, essentially, is the basis of a blog.
If you’ve used something like Wordpress, or if you’re a regular reader of this site, you’ll know most blogs have a bit more to them than that. They have features to categorise and file articles, such as categories and tags, and they also have date-based archives so it’s easy to, say, go and read everything posted in May 2008 or any other arbitrary month of your choice. Well, I thought, that’s straightforward. All we have to do there is to reuse the
paginator.coffee plugin, and go in and fiddle with the code. So, I copied the logic from
paginator.coffee and produced
tagulator.coffee to handle the different types of archive page. Pure copy-and-paste code would result in a lot of duplication, so to prevent that, I also created an additional “plugin” called
common.coffee. Any code that is repeated across more than one of the page-generator plugins was pulled out into a function in
common.coffee, so that then it can be called from anywhere in the generator code that needs it. Moreover, this blog and the garden blog are structured as separate Wintersmith sites, so I pulled out all of the CoffeeScript code (including the supplied but now much-altered
paginator.coffee) into a separate
shared directory tree, equally distant from either blog. The plugins section of the configuration file now looked like this:
"plugins": [ "../shared/wintersmith/plugins/common.coffee", "../shared/wintersmith/plugins/paginator.coffee", "../shared/wintersmith/plugins/categoriser.coffee", "../shared/wintersmith/plugins/tagulator.coffee", "../shared/wintersmith/plugins/archiver.coffee" ]
The original paginator page generation function has now turned into the below: note how the only logic here is that which slices up the list of articles into pages, because everything else has been moved out into other functions. The
getArticles function weeds out any maybe-articles that don’t meet the criteria for being an article properly, such as not having a template defined.
env.registerGenerator 'paginator', (contents, callback) -> articles = env.helpers.getArticles contents numPages = Math.ceil articles.length / options.perPage pages =  for i in [0...numPages] pageArticles = articles.slice i * options.perPage, (i + 1) * options.perPage pages.push new PaginatorPage i + 1, numPages, pageArticles env.helpers.pageLinker pages rv = env.helpers.addPagesToOutput pages, 'default' callback null, rv
This is the simplest of all the page-generators: the others have slightly more complex requirements, such as creating a fake “Uncategorised posts” category, or labelling the archive page for January 1970 as “Undated posts”.
There we go: my Wintersmith installations are now reproducing pretty much all of the different types of archive that Wordpress was handling dynamically for me before. The next time I come back to this topic, we’ll move onto the template side of things, including some nasty performance issues I found and then sorted out along the way.