Qubyte Codes

About this blog 3

It's been a while since the last entry about how I've built this blog. Since it is constantly evolving, now seems as good a time as ever to write about some of the changes I've made.

Netlify

The biggest shift has been the move from a DigitalOcean droplet, with a linux install managed by me, to Netlify. The service they provides hooks into GitHub, and builds when new things are pushed. It also lets me try new things out on hard-to-guess subdomains by opening pull requests (useful for trying things out which might otherwise be risky, like updated security headers).

Webmentions

I've said before that I'm not a fan of comments. They're complex to manage and open to abuse. However, I do like the IndieWeb convention of using webmentions from site a to site b to notify b that a links to it. I use a Netlify deploy hook to call a glitch. This little service scans the updated blog for new links to other sites, and sends those sites webmentions. This is currently disabled and I'm monitoring the logs to make sure it doesn't do anything unintentional. It'll probably go active this weekend once I've enabled signature checking.

On the receiving side, my blog has a webmention form which Netlify provides a simple backend to. I can act upon each web mention from there. This form also allows me to include a manual webmention form at the bottom of each blog post. Other blogs can discover the form by looking for a link tag with rel="webmention", which is found in the head:

<link href="/webmention" rel="webmention">

Since I don't expect a large volume of mentions, I'll add each to the front matter of the blog post being mentioned. When a post has mentions, a section listing them is added at the end of the post.

Payments

A nice CLI utility called thanks was recently published. It scans your project for all the modules it uses and lists, in order of most relied upon, collectives and authors along with their payment links. At the time of writing, it contains a little database of module authors and collectives, and links to ways to pay them.

There's an obvious scaling problem which is addressed in this issue. The issue suggests a new field in the package.json file of a module which thanks can discover, shifting the burden onto the module authors.

Since I'm keen on IndieWeb stuff, the issue led me to wonder if there is an IndieWeb rel attribute for payments (much like that used for discovering webmention endpoints). It turns out that there is and it's rel="payment". It doesn't see too much use in the wild, and it should not be the primary means of discovering payment methods by thanks, but as a fallback it could be useful. thanks would check for payment links on the project home page (from the homepage property of the package.json file), followed by author URLs if provided.

I've added a link tag with this attribute to the blog as well. I don't expect it to get used, but one can always hope! Perhaps I'll become a professional blogger.

I write my posts in markdown, and use marked to process them. Marked doesn't know which anchors will link out to external domains, so it compiles then to <a> tags with only an href attribute. When linking to external sources, the current best practice is to add a rel="noopener" attribute. To do this, I've slightly customised the anchor renderer of marked to add these in:

const markedd = require('marked');
const urlResolve = require('url').resolve;
const renderer = new marked.Renderer();
const oldLinkRenderer = renderer.link;

// In the actual code, this is fed in to a function, not a module-scoped const.
const baseUrl = 'https://qubyte.codes';

renderer.link = (href, title, text) => {
  const fullyQualified = urlResolve(baseUrl, href);
  const rendered = oldLinkRenderer.call(renderer, href, title, text);

  if (fullyQualified.startsWith(baseUrl)) {
    return rendered;
  }

  return rendered.replace('<a ', '<a target="_blank" rel="noopener" ');
};

marked.setOptions({
  // Some unrelated stuff skipped.
  renderer
});

It's a hack for sure, but it works!

Mathematics

Toward the end of last year, I wrote a post that required some typeset mathematics. I used to write a lot in LaTeX, and so I already knew of MathJax (and was saddened to find that MathML in the browser failed to catch on). I knew I needed to convince marked to use it, but marked currently lacks extensibility so I couldn't define new blocks. Instead I adjusted the behaviour of code blocks to treat blocks labelled as mathematics differently:

const mathjax = require('mathjax-node');
const xml2js = require('xml2js');
const marked = require('marked');
const highlight = require('highlight.js');

const renderer = new marked.Renderer();
const codeRenderer = renderer.code;

// This function is synchronous, so I couldn't call MathJax
// in here (it's async), and must do in the highlight method.
renderer.code = function (code, lang, escaped) {
  if (lang === 'mathematics') {
    return code;
  }

  return codeRenderer.call(this, code, lang, escaped);
};

function highlight(code, language, callback) {
  // Non-mathematics code is syntax highlighted.
  if (language !== 'mathematics') {
    return callback(null, highlight.highlight(language, code).value);
  }

  // Render with MathJax.
  mathjax.typeset({ math: code, format: 'TeX', svg: true }, ({ errors, svg }) => {
    if (errors) {
      return callback(errors);
    }

    // MathJax puts some unnecessary inline style in the output
    // which my server response headers disagree with. The below
    // strips those out, and adds a "mathematics" classname to
    // the resultant SVG for styling.
    xml2js.parseString(rendered, (err, xmlobj) => {
      if (err) {
        return callback(err);
      }

      delete xmlobj.svg.$.style;

      xmlobj.svg.$.class = 'mathematics';

      const builder = new xml2js.Builder({ headless: true });

      callback(null, builder.buildObject(xmlobj));
    });
  });
},

marked.setOptions({
  highlight,
  renderer
});

I'm very happy with how it turned out, but I do look forward to a more direct means to add functionality to marked.

Not a progressive web app

For a while this blog had a service worker and an app manifest. You could even "install" it on android phones. The caching strategy I was using was a bit wonky though. Any time hashes changed (common when I updated CSS, since each update to the CSS gets a new filename for caching reasons), a visit to any page would lead to the entire blog being downloaded. In the end I came to the conclusion that being a progressive web app was not good for readers bandwidth in this case and the only reason for me to have it was vanity.

A gutted service worker remains purely to clear the cache, so that a new script can be loaded which will uninstall the service worker for good.

Refactoring

Since the blog software (the static site generator) was built, async-await became a feature of JavaScript. The generator has been substantially revised to make use of promises and async-await, and is far more compact and readable as a result. The code comes in at a little under 300 source lines, not counting templates.