Static Web Site Generation (Part 3)
May 20, 2023
Previous posts in the series: Part 1, Part 2.
This is the latest installment of a series detailing how I maintain this website through a
custom-built static web site generator I call webgen
. Earlier posts described the general
architecture of the tool, which is based on the idea of injecting HTML content files into templates
to produce pages that can be statically served using a bog-standard file-based web server. (In the
case of this website, Github Pages.)
What I'd like to talk about today is the support I coded into webgen
for maintaining this blog. It
builds directly onto the Markdown processing I described in my last post.
Rather than being abstract and general like I've been until now, though, let me show how this very
blog is set up. The reason is less pedagogical than expedient: blog support in webgen
is very much
geared towards how I thought about setting up this blog in the first place and how I want to work on
my posts. Let's just say that it's not the most generic tool I've ever written.
You can follow along at https://github.com/rpucella/rpucella.github.io. The blog is hosted under
/blog/
. In that folder, there's a webgen
source folder .__src
holding templates. The template for
the website as a whole is /.__src/CONTENT.template
. (Note that I'm using .__src
instead of the
__src
folders that I described in earlier posts. The leading dot is optional in webgen
, and I'm
using it here because on Github Pages it prevents the folder from being served. Not that it would
greatly matter if it were, but it's slightly cleaner.)
Source folder /blog/.__src/
contains a subfolder POSTS/
understood by webgen
and holding the blog
posts source. Each blog post is in its own subfolder in POSTS/
. For instance, this post
is in /blog/.__src/POSTS/static-web-site-generation-3/
and contains two files, the Markdown source document
of the post index.md
, and the image file sunset.jpg
for this image:
(Photo by Joshua Woroniecki on Unsplash)
The Markdown source document starts with metadata for the title and date of posting:
---
title: Static Web Site Generation (Part 3)
date: 2023-05-20
---
*Previous posts in the series: [Part 1](/blog/post/static-web-site-generation),
[Part 2](/blog/post/static-web-site-generation-2)*.
...
Running webgen
will find /blog/.__src/POSTS/
, recognize it as a collection of blog posts, and
generate both the posts content proper and a summary page. Let's see how that's done.
The posts content is generated by first creating a subfolder /blog/post/
, and copying every
subfolder in /blog/.__src/POSTS/
to /blog/post/
. This means, in particular, that the subfolder name
chosen in POSTS/
for the post becomes part of the route for serving the post on the website. The
only adjustment made to the copied folder is that the source file index.md
is
moved to a source subfolder .__src/
of the copied folder.
Generating /blog/post/
is the first thing webgen
does before all other generations. Thus, subsequent
generation passes will translate all Markdown files into HTML content files, including those source
index.md
files that have been placed in the appropriate subfolders of /blog/post/
. As usual with
webgen
, the generated index.content
files will get converted to HTML files by injecting
them into the website template. All in all, once /blog/post/
is created and __src/POSTS/
subfolders
are copied, the rest of the process relies on how webgen
works independently of any blog
support.
Posts for this blog are formatted with the help of a template /blog/.__src/MARKDOWN.template
that
knows how to handle the title and date present in the metadata of the various index.md
files:
<main>
<article class="post">
<h1 class="title">{{ .Title }}</h3>
<div class="date">{{ .FormattedDate }}</div>
<div class="body">
{{ .Body }}
</div>
{{if .Reading}}
<div class="reading">{{ .Reading }}</div>
{{end}}
</article>
</main>
Just like all other webgen
templates, the syntax is simply that of Go's html/template
package.
As part of the creation of /blog/post/
, webgen
also creates a summary page — basically a table of
content for the blog, listing all the posts. The tool simply pulls all the metadata from all the
posts it copies to /blog/post/
and collects them into /blog/.__src/index.content
using a dedicated
posts summary template /blog/.__src/SUMMARY.template
:
<main>
<h1>Close Encounters of the Logical Kind</h1>
<p>Infrequent riffs about software development, computer science, and mathematics.</p>
{{ range .Posts }}
<article class="summary">
<div class="title"><a href="/blog/post/{{ .Key }}">{{ .Title }}</a></div>
<div class="date">{{ .FormattedDate }}</div>
</article>
{{ end }}
</main>
The resulting index.content
file will get converted to a final HTML file by webgen
in the usual way.
Full disclosure: I'm not entirely happy with how summarizing is done. Among other things, it
requires a template SUMMARY.template
, unlike elsewhere in webgen
where templates are entirely
optional. It also does not support pagination, so this won't scale if this blog ever gets large. I
expect, however, that I'll have a sense of what a better approach is by then. Meanwhile, I'll live
with the current design.
Aside from the summary page generation, I'm really happy about how blog support came out. It fits
seamlessly with the cascaded generation philosophy: blog post generation merely lays out the
structure of the blog posts hierarchy, and doesn't need to know anything about how the final content
gets turned into final HTML.
This is obviously not a complete blog hosting solution. It lacks basic features such as tagging or
previous/next post navigation. Those should be straightforward to add, though, when I have a bit of
free time. Stay tuned.
The Ebony Tower (by John Fowles)
Previous posts in the series: Part 1, Part 2.
This is the latest installment of a series detailing how I maintain this website through a
custom-built static web site generator I call webgen
. Earlier posts described the general
architecture of the tool, which is based on the idea of injecting HTML content files into templates
to produce pages that can be statically served using a bog-standard file-based web server. (In the
case of this website, Github Pages.)
What I'd like to talk about today is the support I coded into webgen
for maintaining this blog. It
builds directly onto the Markdown processing I described in my last post.
Rather than being abstract and general like I've been until now, though, let me show how this very
blog is set up. The reason is less pedagogical than expedient: blog support in webgen
is very much
geared towards how I thought about setting up this blog in the first place and how I want to work on
my posts. Let's just say that it's not the most generic tool I've ever written.
You can follow along at https://github.com/rpucella/rpucella.github.io. The blog is hosted under
/blog/
. In that folder, there's a webgen
source folder .__src
holding templates. The template for
the website as a whole is /.__src/CONTENT.template
. (Note that I'm using .__src
instead of the
__src
folders that I described in earlier posts. The leading dot is optional in webgen
, and I'm
using it here because on Github Pages it prevents the folder from being served. Not that it would
greatly matter if it were, but it's slightly cleaner.)
Source folder /blog/.__src/
contains a subfolder POSTS/
understood by webgen
and holding the blog
posts source. Each blog post is in its own subfolder in POSTS/
. For instance, this post
is in /blog/.__src/POSTS/static-web-site-generation-3/
and contains two files, the Markdown source document
of the post index.md
, and the image file sunset.jpg
for this image:
(Photo by Joshua Woroniecki on Unsplash)
The Markdown source document starts with metadata for the title and date of posting:
---
title: Static Web Site Generation (Part 3)
date: 2023-05-20
---
*Previous posts in the series: [Part 1](/blog/post/static-web-site-generation),
[Part 2](/blog/post/static-web-site-generation-2)*.
...
Running webgen
will find /blog/.__src/POSTS/
, recognize it as a collection of blog posts, and
generate both the posts content proper and a summary page. Let's see how that's done.
The posts content is generated by first creating a subfolder /blog/post/
, and copying every
subfolder in /blog/.__src/POSTS/
to /blog/post/
. This means, in particular, that the subfolder name
chosen in POSTS/
for the post becomes part of the route for serving the post on the website. The
only adjustment made to the copied folder is that the source file index.md
is
moved to a source subfolder .__src/
of the copied folder.
Generating /blog/post/
is the first thing webgen
does before all other generations. Thus, subsequent
generation passes will translate all Markdown files into HTML content files, including those source
index.md
files that have been placed in the appropriate subfolders of /blog/post/
. As usual with
webgen
, the generated index.content
files will get converted to HTML files by injecting
them into the website template. All in all, once /blog/post/
is created and __src/POSTS/
subfolders
are copied, the rest of the process relies on how webgen
works independently of any blog
support.
Posts for this blog are formatted with the help of a template /blog/.__src/MARKDOWN.template
that
knows how to handle the title and date present in the metadata of the various index.md
files:
<main>
<article class="post">
<h1 class="title">{{ .Title }}</h3>
<div class="date">{{ .FormattedDate }}</div>
<div class="body">
{{ .Body }}
</div>
{{if .Reading}}
<div class="reading">{{ .Reading }}</div>
{{end}}
</article>
</main>
Just like all other webgen
templates, the syntax is simply that of Go's html/template
package.
As part of the creation of /blog/post/
, webgen
also creates a summary page — basically a table of
content for the blog, listing all the posts. The tool simply pulls all the metadata from all the
posts it copies to /blog/post/
and collects them into /blog/.__src/index.content
using a dedicated
posts summary template /blog/.__src/SUMMARY.template
:
<main>
<h1>Close Encounters of the Logical Kind</h1>
<p>Infrequent riffs about software development, computer science, and mathematics.</p>
{{ range .Posts }}
<article class="summary">
<div class="title"><a href="/blog/post/{{ .Key }}">{{ .Title }}</a></div>
<div class="date">{{ .FormattedDate }}</div>
</article>
{{ end }}
</main>
The resulting index.content
file will get converted to a final HTML file by webgen
in the usual way.
Full disclosure: I'm not entirely happy with how summarizing is done. Among other things, it
requires a template SUMMARY.template
, unlike elsewhere in webgen
where templates are entirely
optional. It also does not support pagination, so this won't scale if this blog ever gets large. I
expect, however, that I'll have a sense of what a better approach is by then. Meanwhile, I'll live
with the current design.
Aside from the summary page generation, I'm really happy about how blog support came out. It fits seamlessly with the cascaded generation philosophy: blog post generation merely lays out the structure of the blog posts hierarchy, and doesn't need to know anything about how the final content gets turned into final HTML.
This is obviously not a complete blog hosting solution. It lacks basic features such as tagging or previous/next post navigation. Those should be straightforward to add, though, when I have a bit of free time. Stay tuned.