I was bored on a Saturday afternoon, and thought that my personal site could do with some simplification. In the past I have used static site generators such as Astro and Jekyl. But these were surplus to needs, and instead I thought it’s time to do the Software Engineer thing and re-invent the wheel.
All I needed was a script that traversed a directory of markdown files, converted them to html, and then inserted them into a simple HTML template. It turns out that this is very simple code to write, and can be done in a few hundred lines of Golang, or JS, or <pick your poison>.
In my case, the processMarkdownFile is the core of this, converting a file of markdown into html and injecting it into a template html file:
1// processMarkdownFile processes a single markdown file and returns the generated HTML2func processMarkdownFile(filePath, template string) (string, string, *BlogPost, error) {3 // Parse frontmatter4 var meta FrontMatter5 content, err := frontmatter.Parse(strings.NewReader(string(fileContent)), &meta)67 // Parse markdown to HTML8 htmlContent := markdown.ToHTML(content, nil, nil)910 // Replace template placeholders11 output := strings.Replace(template, "{{title}}", title, -1)12 output = strings.Replace(output, "{{content}}", string(htmlContent), -1)1314 // ...rest of the function...15}
The other need was for an index page to list all my posts. This can be done by sorting and slicing them in date descending order, which I extracted from the standardised file name format ([yyyy-mm-dd]-[title-bits].md):
1// Sort posts by date in descending order (newest first)2sort.Slice(posts, func(i, j int) bool {3 return posts[i].Date.After(posts[j].Date)4})
Generating the post list:
1for _, post := range posts {2 if post.Date.IsZero() {3 continue // Skip posts without dates4 }56 formattedDate := post.Date.Format("January 2, 2006")7 contentBuilder.WriteString(fmt.Sprintf("<li><strong>%s</strong> - <a href=\"%s\">%s</a><p>%s</p></li>\n",8 formattedDate, post.OutputFile, post.Title, post.Description))9}
Supporting frontmatter is handy for metadata, but not absolutely essential:
1---2title: My Custom Page Title3description: A brief description of the page content4---
Parsed with:
1content, err := frontmatter.Parse(strings.NewReader(string(fileContent)), &meta)
For more details, checkout the README and source code in the repo.