Pickle, a static site generator

by Coranna Howard in Dev

Three years after deciding I wanted to make my own site generator, I have finally done so. The breaking point was Jekyll 3.0 ceasing to layer layout and content metadata.

Working around this with the design of Allopoeia and my homesite would have been very annoying, as I supplied configuration defaults within layout metadata for pages, to say nothing of dealing with Jekyll again. So, instead, I started looking around for an alternative system. Surely, by now, there would be a site generator without crippled templating!

After struggling with Hugo and its atrocious templating via Go's text/template package, I gave up, and decided it would be far more worthwhile to make my own system.

The result is Pickle (the same name I came up with years ago), which took just under two weeks of focus before it was functional enough to start refactoring my websites. It uses togo and the tiny mmx web server library for the internals, but the majority of it is written in Lua.

The distribution of language sits at 1,216loc of Lua & 711loc of C++, just under 2,000loc total. That's rather slim for a site generator, I'd imagine. Of course, this doesn't say how much is saved by using external libraries, but it's no different from the situation with Jekyll and Hugo, which make extensive use of their language ecosystems.

togo had yet to see Lua integration, and was lacking on the filesystem interface, so significant upstart work was needed alongside Pickle, accounting for the span of time before Pickle was functional. In fact, more code was touched in togo: 2,559loc across 58 files.

Some of the discovery work for Lua integration was already done with Quanta (forthcoming), but was rudimentary. Taking the time to do it right within togo has improved the situation greatly.

The templating in Pickle is straight-up Lua, the syntax of which is inspired in part by lua-resty-template, which itself is similar to Mustache and is inspired by yet others, I'm sure. The parser is a meager 221loc of C++, and the whole Lua interface is only 185loc.

Lovely, but not the greatest implementation. Layering like I was with Jekyll isn't really possible. Ironically, I didn't even design my system with the very feature that I left Jekyll for removing.

If you have a metatable on a Lua chunk redirecting top-level indexes to the provided template context, you only ever see the first branch. From x.y.z, you supply context[x] and never hear if the terminating index resulted in a nil. This is fine, that's just how Lua works.

I imagined using a proxy all the way until it terminated could be possible, but there are immediate red flags at the suggestion. There's no metamethod for "evaluate", so there'd be no way to tell when it terminates. At the last access, the call site would just receive the proxy, leaving no option to try alternative paths from the root.

The other obvious route is rewriting the indexes during template transformation, but that frankly sounds like too much hassle & too much complexity for a feature that isn't really necessary.

Instead, in refactoring my websites, I just split out layouts that were layering and supplied the defaults directly in the content constructors during filtering. This makes much more sense, and centralizes the properties of a page. This was not even possible in vanilla Jekyll (as far as I'm aware), so I still have the upper hand despite lacking its 2.0-style layering.

There are other benefits, as well. With Jekyll, I had to create "includes" for basic often-used functionality: in essence, plain old functions. This was an absolute mess. With Pickle, I can write all of that in pure Lua, if I wanted to, though the unbridled power of Lua right in templates means I don't have to.

Liquid, Jekyll's template language, was simply too restricted to be useful. It results in verbose and ugly code.

Once the disgust for Jekyll was out of the way, I finally did some much-needed maintenance on my websites. My homesite is now more cohesive, and Allopoeia is less janky. Overall, I only shaved off maybe 200-300 lines from the combined changes to the websites, but the quality is greater.

Pickle takes a different approach from most static site generators I've seen. It gives you the basic functionality, the fundamentals, but makes the user build the scaffolding. Stuff that would be considered core functionality in other systems is nowhere to be seen. There is no concept of a "layout" or even a "page". It has filters, which produce outputs, and that's it. In this way, it's not really a "site generator" in the true, it's just a complicated data transformation system that happens to have a web server attached.

I've already thought of other uses for it outside of websites. I often write emails in Markdown on local storage, with an informal metadata syntax that lives only in my head. I usually don't send emails in HTML, but it could be useful for transformation of other properties (and statistics & analysis, which I love).

Back to websites, though, this means you have to write some code upfront, specific to the structure of your site. Mine is a meager core at 307loc, shared between my homesite and Allopoeia. Not much baggage, and I don't have to wade around the mire of instituted structure by the generator itself. If something isn't working for the website, I can change the structure without disrupting anything else that uses Pickle.

All in all, a good adventure, not just for my websites, but for togo as well. Pickle has some sore spots left to mend, but it is a much welcome reprise over the previous state of affairs.