Building a blog with Metalsmith

Feb 11, 2015

I recently transitioned this blog from Jekyll to Metalsmith. I'm glad I started with Jekyll but I wanted more control. I decided to switch to Metalsmith so that I could write my own build scripts in a language I knew (JavaScript).

There are tons of excellent static site generators available. See staticsitegenerators.net for a list. I've used DocPad before (for the professorp.co.uk website). I chose Metalsmith because it offered the most bare-bones setup with minimal restrictions.

This post will not be a full guide on how to use Metalsmith. I'm assuming that anyone interested in Metalsmith is willing to get their hands dirty. Instead, this post will simply list some of the useful plugins I've come across and some other hacks I've made up.

Paragraph count

In one of my very early posts, I wrote about paragraph counts in Jekyll. I like to have links saying "Read 15 remaining paragraphs..." at the end of my excerpts. I think it's better than not giving any information on the length of an article and more useful than giving a raw wordcount.

Jekyll was rather limited in how it could count paragraphs. I ended up using a pretty awkward Liquid Template hack. Thankfully JavaScript can do much better. Everything after the <!--more--> tag is passed through the paraCount() function. It counts everything I feel qualifies as a paragraph including: <p>, <ul>, <ol>, <pre> and <table> elements.

function paraCount(text) {
  return text.match(/<(p|ul|ol|pre|table)>[\s\S]*?<\/\1>/g).length;
}

Post organisation

I keep all my posts in pretty much the same format as I used with Jekyll. For example:

src/posts/2014-08-11-hello-world.md

I use the handy metalsmith-date-in-filename plugin to extract the dates. I also use the metalsmith-permalinks and metalsmith-collections plugins rename and organise posts.

RSS feed

There's a very handy metalsmith-feed plugin for generating RSS feeds from collections.

It works great although I like to substitute all relative URLs with absolute ones so that they work correctly when viewed in an RSS reader:

  .use(feed({
    collection: 'posts',
    limit: 10,
    destination: 'feed.xml'
  }))
  .use(function(files) {
    data = files['feed.xml'];
    data.contents = new Buffer(data.contents.toString()
        .replace(/(src|href)="\//g, '$1="https://davidxmoody.com/'));
  })

Markdown and Pygments

Jekyll uses Pygments, a syntax highlighter written in Python. It's pretty good and has support for tons of languages. It also supports the console language for mixing in bash prompts with output from commands. I use that in several of my older posts and didn't want to lose it.

I spent far too long hacking together my own Markdown plugin which could do syntax highlighting with Pygments. I ran into a lot of problems with marked and Pygments. The complications arise because Pygments is an external Python library and thus has to be called asynchronously. Marked does support this. However, it was tricky to tie it all together. Additionally, both marked and Pygments add in a <pre> element wrapping the code and I had to hack together a way to get around having two <pre> elements at once.

This blog is currently using Pygments but I regret it. It wasn't worth the effort. I'm even considering going back to highlight.js for the simplicity and speed improvement.

If you are dead set on using Pygments with Metalsmith then you might want to refer to this file. It is a modified version of the metalsmith-markdown plugin which handles asynchronous Pygments highlighting.

Misc

Final thoughts

Overall, Metalsmith is great. It's supposed to be extremely simple and rely on plugins to do the heavy lifting. It accomplishes that well. I would happily use it for another project.

My only complaint is that it doesn't do incremental builds. A small change in one post causes a full rebuild with my current setup. It's understandable why Metalsmith doesn't include this (it would be really complex). I may try to implement a custom solution where each individual post is only updated when that post changes but all other files are regenerated every time.

[Edit: I recently created created and published my first Metalsmith plugin. It's a plugin to check files for internal broken links. See this blog post for how I did it or go straight to the npm package.]