Feep! » Blog » Post

Building a blog

I've been writing blog posts, but so far they've just been markdown files in a folder and there hasn't been any way to read them. Today I spent a few minutes working on adding them to the web interface.

My experiences have given me a dim view of static site generators, at least for the kind of things I want to use them for. I spend a bunch of time reading the documentation to set them up and get my pages all templated out. Then whenever I want to write something I have to reread the documentation to figure out how to run the generator again, and if I haven't touched it in a while probably also deal with upgrading it. Eventually I have an idea for something I want to do, and discover that whichever generator I'm using doesn't quite support that feature and I have to fiddle with its guts. I've complained about this before:

The thing about static site builders is that it's really easy to get started building one, and very difficult to make one that meets everyone's needs. The result of this is a sort of Life-Cycle of the Static Site Generator:

  1. Look at the huge list of open-source static site generators (literally hundreds!)
  2. Try some out, and fail to find one that works exactly right.
  3. Start writing a new one that fits better.
  4. Gradually extend it and make it more general.
  5. Get excited about how useful it is, and publish it as an open-source project so others can use it, adding it to the already enormous list.
  6. Someone else starts at step 1.

I've taken to just building a new one one to meet whatever my immediate need is, and developing a sort of mental toolkit of techniques rather than trying to smush an existing program into the particular requirements of a specific project. You can get surprisingly far with just a handful of lines of code.

Two years later my opinion hasn't changed, so once again I've eschewed a prebuilt solution and knocked together my own. This is the entirety of the blog system as it currently stands:

const fs = require('fs/promises')
const md = require('markdown-it')({})
md.use(require('markdown-it-highlightjs'), {auto: false, code: false})

module.exports = app => {
	app.get('/blog/post/:post', async (req, res) => {
		const postid = req.params.post
		if (!/^[0-9a-z-]+$/.test(postid)) {
			return res.status(404).render('404')
		}
		const content_markdown = await fs.readFile(`${__dirname}/../blog/${postid}.md`, 'utf-8')
			.then(null, e => {if (e.code === 'ENOENT') {return null} else {throw e}})
		if (!content_markdown) {
			return res.status(404).render('404')
		}
		const title = content_markdown.split('\n')[0].replace(/^#/, '').trim()
		const content_html = md.render(content_markdown)
		
		res.render('blogpost', {title, content_html})
	})
}

This is only serving the posts page; I haven't gotten round to making an index or categories yet. Most of the code was copied from another static site I built, and the rest is largely Express boilerplate. Right now it's serving live from the same daemon as the actual search engine; I'll probably change this to statically generate the files so that the server doesn't need to understand what's going on. (In fact I'm not entirely sure why I didn't do that to begin with, but because the code is so small right now it's a very easy change to make.)

This code will grow and expand, but because it's only doing exactly what it needs to it'll remain a lot simpler than any off-the-shelf solution and be a lot easier to modify as my needs change.