Title: Build-a-blog Date: 2024-06-17T14:46:36-04:00 --- I want to share my thought process for how to go about building a static blog generator from scratch. There will be nothing ground breaking here - in fact this software will not be good. So turn back now if you're expecting the new [Hugo][hugo]. Actually you should probably stop reading and just use [Hugo][Hugo]. In case you are still interested, the goal is to take 1 afternoon + caffeine + some DIY spirit → _something_ resembling a static site/blog generator. And I hope by the end of this post you might be inspired to build your own generation scripts, maybe in a new language you always wanted to try. Lets see how hard this will be. Here are the requirements for this blog: * Generate an index with recent list of posts. * Generate each individual post written in markdown -> html * Support some metadata in each post * A post title should have a slug * Generate RSS That boils down to: 1. Read some files 2. Parse markdown, maybe parse a header with some key/values. 3. Template strings So there is 1 "exotic" feature in parsing/rendering Markdown as HTML that will need some thought. The rest is just file and string manipulation. Lets get it on. ## Picking the tool for the job Most scripting languages would be fine tools for this task. But how to handle Markdown? I've had [Crystal][1] in the back of my mind for this task. It is a nice general purpose language that included Markdown in the stdlib! But unfortunately Markdown was removed in [0.31.0][2]. Other than that, I'm not sure any other languages include a well rounded Markdown implementation out of the box. I'll likely end up building the site in docker with an alpine image down the road, so just a quick search in alpines repos to see what could be useful: ```shell ❯ docker run --rm -it alpine / # apk update fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/community/x86_64/APKINDEX.tar.gz v3.18.6-263-g77db018514d [https://dl-cdn.alpinelinux.org/alpine/v3.18/main] v3.18.6-263-g77db018514d [https://dl-cdn.alpinelinux.org/alpine/v3.18/community] OK: 20079 distinct packages available / # apk search markdown discount-2.2.7c-r1 discount-dev-2.2.7c-r1 discount-libs-2.2.7c-r1 kdepim-addons-23.04.3-r0 markdown-1.0.1-r3 markdown-doc-1.0.1-r3 py3-docstring-to-markdown-0.12-r1 py3-docstring-to-markdown-pyc-0.12-r1 py3-html2markdown-0.1.7-r3 py3-html2markdown-pyc-0.1.7-r3 py3-markdown-3.4.3-r1 py3-markdown-it-py-2.2.0-r1 py3-markdown-it-py-pyc-2.2.0-r1 py3-markdown-pyc-3.4.3-r1 ``` [`py3-markdown` in alpine][3] is the popular [`python-markdown`][4]. It's mature and available as a package in my [home distro][5]. Incredible. ## Let's build First, lets read 1 post file and render some html. I'll store posts in `posts/` like `posts/build_a_blog.md`. And we'll store the HTML output in the same directory: `posts/build_a_blog.html`. ```python import re import logging import markdown destpath_re = re.compile(r'\.md$') logging.basicConfig(encoding='utf-8', level=logging.INFO) def render_post(fpath): destpath = destpath_re.sub('.html', fpath) logging.info("opening %s for parsing, dest %s", fpath, destpath) # from: https://python-markdown.github.io/reference/ with open(fpath, "r", encoding="utf-8") as input_file: logging.info("reading %s", fpath) text = input_file.read() logging.info("parsing %s", fpath) out = markdown.markdown(text) with open(destpath, "w", encoding="utf-8", errors="xmlcharrefreplace") as output_file: logging.info("writing to %s", destpath) output_file.write(out) if __name__ == '__main__': render_post('posts/build_a_blog.md') ``` And if we run it. ```shell ❯ python3 ./main.py INFO:root:opening posts/build_a_blog.md for parsing, dest posts/build_a_blog.html INFO:root:reading posts/build_a_blog.md INFO:root:parsing posts/build_a_blog.md INFO:root:writing to posts/build_a_blog.html ❯ head posts/build_a_blog.html

Build-a-blog

I want to share my thought process for how one would go about building a static blog generator from scratch.