Last December, I switched my blog over to a static page generated by a vanilla NodeJS script. So far, its been a dream come true. Thanks to Cesar Devers' design skills, the blog is far more readable than the old standard WordPress template. Also, I can write my posts in Markdown, which has done wonders for my workflow. I also have more direct control over my layout and analytics, so I can use KeenIO to track some basic metrics. Getting to this new blog infrastructure was an adventure. I had brief flings with many static site generators before settling on my current setup. In this article, I'll explain why I decided to skip out on all the established tools and just write my own solution.
My First Adventures with Static Site Generators
First off, my former coworker Steve Francia got me excited about his project hugo. As the most avid Victor Hugo reader I know, I couldn't resist trying hugo out. The fact that hugo itself is a single statically linked executable meant that I could run a single command to regenerate my site, and hugo has excellent performance.
Of course, with hugo, the reality lagged behind the marketing pitch. While you do write your articles in Markdown with hugo, you need to design your layout using Golang's html/template
package. I'm sure somebody out there likes Go's HTML templating, but, as somebody who's used to more sophisticated tools like Jade, it makes gouging my eyes out with a rusty fork seem like an appealing alternative. Also, being the fastest static site generator is like being the restaurant with the cleanest bathroom: sure, I prefer a faster generator, but I wouldn't pick one generator over another because one shaves off 100ms. I'd rather use a tool that's fast enough and optimizes for developer sanity rather than a tool that sacrifices developer sanity for non-essential performance benefits.
Long story short, I came to realize that hugo wasn't the right tool for me. I realized that the one advantage of hugo was the fact that it was a single binary. Installing a Markdown parser on every machine I wanted to write on would be a pain. But, what if I had a development setup where installing external modules didn't require manipulating my PATH
and potentially clobbering other software on my machine? Enter NodeJS.
Diving Into NodeJS Static Site Generators
When I dove in to NodeJS static site generators I found several cool alternatives, including Wintersmith, Metalsmith, and Wukong. At first, Metalsmith (written by the fine people over at Segment) was a dream come true. Wukong seemed even more flashy with ES6 and Co integration - Wukong is to Metalsmith as Koa is to Express. Metalsmith gave me the flexibility I needed to write my layouts in whatever language I wanted and then integrate Markdown posts into my templates. I could then orchestrate all this functionality either with a config file or Gulp-style plugins in a NodeJS script.
So why did I not just use Metalsmith? The more I worked with Metalsmith, the more I realized that it had far too many bells and whistles that I will never use. The subset of Metalsmith functionality that I was using was trivial. I wanted to create a Jade template, load up a bunch of Markdown files, and generate a bunch of HTML files by combining the two. Once I installed jade and marked, using Metalsmith for this task seemed like overkill. I could use Metalsmith, but then when I had difficulties, I would have to dive in to the internals. When I thought about it, the problem that I was trying to solve wasn't complex enough to use an external framework.
Writing My Own
When I was exploring Metalsmith, I realized that, fundamentally, all I wanted to do was
- Read a bunch of Jade files
- Read a bunch of Markdown files
- Compile the Markdown files and insert them into Jade files
- Write out a bunch of static HTML files
Not exactly rocket science when you think about it. In more low-level languages like C and Go, the task of installing a Markdown parser is a little cumbersome, but in NodeJS it's trivial. Similarly, if you had to compile a couple hundred pages you'd want to run your read operations in parallel. Of course, NodeJS gives you parallel file I/O for free. In other words, static site generators try to solve problems that NodeJS has already solved for you. The only real exception is standardization: if I had to maintain 100 static sites, I would standardize all of them on Metalsmith so I could work in one environment. But, for my one-off blog, writing a single node script has significant benefits.
- No need for plugins. Metalsmith, like Gulp, has a whole class of plugins like metalsmith-jade and metalsmith-markdown. Plugins are lightweight wrappers in theory, but in practice they come with complexity baggage of their own. For instance, gulp-uglify mangles variable names by default, whereas the underlying uglify-js module does not. Bypassing the plugin layer lets you avoid unpleasant surprises like this.
- Simpler data pipeline. One thing I struggled to do in Metalsmith was generate a list of "most recent posts" on the right of every article. Never quite figured out how to do this with Metalsmith, but the task is trivial once you're responsible for loading your own files.
How It's Worked Out
So far, I've been happy with how my home-baked static site has been doing. I have a great deal of flexibility in adding new features on the generator side in addition to the UI and analytics improvements. I enjoyed playing around with NodeJS static site generators. But, I don't think the problem they're solving is complex enough to warrant using one, at least as long as you're maintaining one or two static sites.