Under the hood

Hello World!

This short sentence is usually used by programmers in the very first program they wrote. It does not feel enough for a first post, so I decided to briefly describe the technical solution for this blog.

How it begins

Some time ago I've decided that I want to start my technical blog. Unfortunately for me, I am a little bit idealist, so I cannot decide for long what should power my blog.

Hosted platforms like Medium or WordPress are definitely not for me, because I would like to have full control of my website.

Static site generator seems like a perfect solution to me, blog does not require any dynamic content. But somehow I do not like Jekyll which is the default solution for Github Pages.

I know that I want something simpler, much simpler.

Solution

I came up with the simplest solution which does not require writing pure HTML. Own static site generator written in node.js

Posts are written in Markdown and converted to HTML using markdown-it

const nunjucks = require('nunjucks')
const md= require('markdown-it')()
const meta = require('markdown-it-meta')
const title = require('markdown-it-title')
const prism = require('markdown-it-prism')
const include = require('markdown-it-include')
const fs = require('fs')

const inputFile = process.argv[2]
const outputFile = process.argv[3]

const env = new nunjucks.Environment(new nunjucks.FileSystemLoader(''))

md.use(meta)
md.use(prism)
md.use(title)
md.use(include)

const src = fs.readFileSync(inputFile, "utf8")
const ctx = {}
const contentHtml = md.render(src, ctx)

const html = env.render('src/html/post.njk', {
    content: contentHtml,
    title: ctx.title,
    meta: md.meta
})
fs.writeFileSync(outputFile, html)

All .njk files are converted to HTML using nunjucks. For example, this is how index.html page is generated

const nunjucks = require('nunjucks')
const fs = require('fs')

const env = new nunjucks.Environment(new nunjucks.FileSystemLoader('./'))

const inputFile = process.argv[2]
const outputFile = process.argv[3]

const html = env.render(inputFile)
fs.writeFileSync(outputFile, html)

Sass sources are also converted using simple javascript

const sass = require('sass')
const fs = require('fs')

const inputFile = process.argv[2]
const outputFile = process.argv[3]

const result = sass.renderSync({ file: inputFile })
fs.writeFileSync(outputFile, result.css)

And finally, all together is glued up by good old make

PAGES_SRC=$(wildcard *.njk)
PAGES=$(patsubst %.njk, target/%.html, $(PAGES_SRC))

POSTS_SRC=$(filter-out README.md, $(wildcard *.md))
POSTS=$(patsubst %.md, target/%.html, $(POSTS_SRC))

all :  target $(PAGES) $(POSTS) target/style.css target/posts.html target/CNAME target/.nojekyll
.PHONY: all

target/%.html : %.md
	npm run post $< $@

target/%.html : %.njk
	npm run page $< $@

target/style.css : src/sass/*.sass
	npm run css src/sass/main.sass $@

target/posts.html : $(POSTS_SRC)
	npm run posts ./ $@

target/CNAME : CNAME
	cp $< $@

target/.nojekyll :
	touch $@

target :
	mkdir target

clean:
	rm -rf target
.PHONY: clean

Hosting & CI

Since Github Pages by default only supports Jekyll or static files, I've used Github Actions to automatically generate static files on any changes to master branch.

name: Build and Publish
on:
  push:
    branches: [ master ]
jobs:
  build-and-publish :
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎️
        uses: actions/checkout@v4

      - name: Install and Build 🔧
        run: |
          npm install
          make

      - name: Publish 🚀
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          branch: public 
          folder: target

Source code

Since this blog is currently hosted on Github Pages, all source code is publicly available here.