Euphenix

2019-08-15

I ended up having to move some sites from Weebly to something more affordable and reliable, since most of them sit around for months without changes. I also think their stance of charging for TLS is silly, and the lack of an export function for your content is just evil.

That said, they are good for their CMS part, which allows relatively fast prototyping of sites, and eventually I'm also looking for a replacement for this, but for now it's not urgent.

After having a look around, i decided on moving them to Netlify. They have a pretty good free tier with proper TLS and form handling. Deploys are also quite easy to automate in different ways.

I evaluated a bunch of different static site generators (most of them listed on https://www.staticgen.com/), the ones I liked the most were Hugo and Hakyll.

What also caught my eye was https://styx-static.github.io/styx-site/, which is implemented entirely in Nix and looked very good.

I started with porting the site of Finesco, which my wife originally made on Weebly. It's not too fancy and apart from the two blogs it looked like it would be easy to reimplement in Hugo.

Turns out that Hugo wasn't flexible enough after all, given its compiled nature I struggled trying to make some kind of archive page for each year, and ended up with a lot of hacks around taxonomies.

That's when I started looking at Hakyll. I've been dabbling with Haskell for a long time, and quickly got the site ported from Hugo, with a few extra features to boot.

After that, I tried building a Docker image with Nix for site, and it turned out that the image would be in the gigabyte range instead of the few megabyte i was hoping for.

Apparently responsible for this was the pandoc dependency, which prevented me from compiling statically, and I wasn't versed enough in Haskell to figure out how to inject a precompiled pandoc instead. (Now I might be able to do it, but good luck even finding that option form their docs).

Since I was on a learning journey anyway, I then took a look at Styx, but noped out of there pretty soon when I realized that the templates are also written in Nix, and it had a huge performance penalty and very weak editor support.

I still was intrigued by the idea of having something as simple as Hugo but with the power of Nix for generating my sites, and so I started to work on a little project in my spare time to flesh out that idea.

After a couple of iterations, I finally arrived at this:

let
  euphenix = (import (fetchTarball {
    url =
      "https://github.com/manveru/euphenix/archive/eaaee37df12e8fccced3a4ac93402d6b3e5fcf54.tar.gz";
  }) { }).extend (self: super: {
    parseMarkdown =
      super.parseMarkdown.override { flags = { prismjs = true; }; };
  });
  inherit (euphenix.lib) take;
  inherit (euphenix) build sortByRecent loadPosts;
in euphenix.build {
  rootDir = ./.;
  layout = ./templates/layout.liquid;
  favicon = ./static/img/favicon.svg;

  variables = { liveJS = true; };

  expensiveVariables = rec {
    posts = sortByRecent (loadPosts "/blog/" ./blog);
    latestPosts = take 5 posts;
  };
}

That is the whole configuration needed to build the site you're reading right now. The rest of it is pretty standard HTML, CSS, and a few sprinkles of Javascript for eye-candy and syntax-highlighting (until I generate that statically as well).

I'm still pondering using a different templating engine or implementing the templating myself instead of relying on Infuse. Infuse was great for getting started, but it's originally meant for configuration files and simpler use-cases.

Just getting a list of required variables from the template automatically would be great without having to do that with some unreliable regular expressions.

Another relatively easy option would be to use Liquid, which already has a massive user-base and I'm pretty familiar with it. It's also closer in syntax to Go templates than many alternatives, so wouldn't require a lot of effort to port the Infuse templates.

For now though, I'll keep the expensiveVariables hack around until I get time again to build something better. I hope the name is warning enough that people won't rely on it too much.

The trick with expensiveVariables is that you annotate in the front-matter of tmeplates which variables are required for rendering. And while that's not a DRY solution, it's not used often and should be relatively straight-forward. I just need a better name for it.

So right now you can write something like this:

<!--
requires: latestPosts
-->
{{ range .latestPosts }}{{ .meta.title  }}{{ end }}

And through the magic of lazyness in Nix, the page will only be rebuilt if latestPosts has changed.

Anyway, this is it for today, there are many more topics that could be covered by proper documentation, but writing like this makes me think things through a bit better.

If you read until now, you may as well check the project: Euphenix