Static Site Generator

Static Site Generator

Static Site Generator

This project is a Static Site Generator. It is written in Go, and makes heavy use of Go's templates; you will need to be familiar with these to be able to use this project. They are a little weird in places.

Install the ssg command by running:

$ go install

The command ssg has the following flags:

The go doc shows all the fields that are available to the templates.


Order of operations

  1. All files in the input directory are loaded and parsed before any templates are run. Thus all templates have access to the meta-data of all input files.
  2. Once all files have been loaded, the inner templates for all markdown files which have output = true are run. The result of running these inner templates must be valid markdown. The markdown is then converted to HTML, and is stored in the ContentInner field.
  3. Next, all files have their inner templates run if they've not been already run, and then their outer template if it has been specified. The result of running the outer templates is stored in the ContentOuter field. If no outer template is specified then the ContentOuter field will have the same value as the ContentInner field.
  4. After that, all tag-templates are run.
  5. Finally, write everything to the output directory.


Example 1

This example, if it exists as a file within the input directory, would have a corresponding file in the output directory, which would contain the result of transforming this input file from Markdown to HTML.

Example 2

This example shows how to generate an RSS feed for all the Posts. You might put this example in a file rss.xml:

output = true
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="">
    <title>{{ html .Site.Title }}</title>
    <link>{{ .Site.BaseURL }}</link>
    <description>Recent content on {{ html .Site.Title }}</description>
    <generator>{{ html .Generator }}</generator>
    <copyright>Copyright © {{ .Now.Year }}, {{ .Site.Author }}</copyright>
    <lastBuildDate>{{ .Now.Format .RFC1123Z }}</lastBuildDate>
      <url>{{- .Site.BaseURL -}}logo.png</url>
      <title>{{ html .Site.Title }}</title>
      <link>{{ .Site.BaseURL }}</link>

    <atom:link href="{{ .Page.AbsoluteURL }}" rel="self" type="application/rss+xml" />

    {{- $dot := . -}}
    {{- range $post := .Global.Posts }}
      <title>{{ html $post.Meta.Title }}</title>
      <link>{{ $post.AbsoluteURL }}</link>
      <pubDate>{{ $post.Meta.Date.Format $dot.RFC1123Z }}</pubDate>
      <guid>{{ $post.AbsoluteURL }}</guid>
        {{ $post.ContentInner | printf "%s" | html }}
    {{- end }}

Example 3

The inner template can call other templates. For example, this file could be at posts/series/onions/

title = "Onions: the revenge"
date = 2021-12-09T11:01:09Z
tags = ['onions']
output = true
template = "templates/post.html"

In this series:

{{ template "templates/" (index .Global.PostsByTag "onions").OldestFirst }}

For many people, onions make them cry...

It specifies a outer template that should be found at templates/post.html (which presumably is responsible for turning the HTML-from-markdown into a fully valid HTML page); and in the inner template, it calls templates/ That file could look like this:

output = false

{{ range $page := . }}
* [{{html $page.Meta.Title}}]({{$page.AbsoluteURL}})
{{- end }}

It sets output = false because this file is only useful as a template to be used by others, so it should not have a corresponding output file for itself. This template generates markdown (which is hinted at by the fact it has a .md extension). So, the inner template of is run (which calls templates/, and the result of that is assumed to be valid markdown and then converted to HTML. The result of all of that is passed to the outer template (templates/post.html), and the result of that will be stored in the output directory at posts/series/onions/post3/index.html.