Rewriting the static site generator, again [2026-02-06]
Previously, the SSG for this website was a series of C, bash, and python scripts which took in chunks of HTML and precariously pasted them together to give the illusion of a functioning website. It was annoying to add new webpages, change the template base or options used per file, or have dynamicaly generated content (at generation time). I therefore opted to rewrite the SSG, again. The question is then, how should it be done? The primary issue is that pages consist of data (text, markup, whatever), but sometimes we want to generate information, for example the page which indexes articles should automatically generate the HTML content listing all available articles, therefore pages should also be code. When you consider it in this manner, the solution to such a predicament is obvious—Lisp (which tends to be the solution to every problem). Within a few hours I was able to put together a new, better SSG written in Lisp, where all the pages are also written in Lisp. Here's a basic example of a page:
(define template "template/article.rkt")
(define title "Something important")
(define page
`(
(p "Here's a picture of something very important..."
(img (@ (src "/r/important-image.png"))))
))Writing a page with code-generated content doesn't require any special handling...; get all the articles
(define articles
(for/list ([file (in-directory "pages/articles")]
#:when (regexp-match? #rx"\\.rkt$" (path->string file)))
(let* ([title (dynamic-require file 'title)]
[date (dynamic-require file 'date)]
[path (string-append "/" (path->string (path-only (dynamic-require file 'path))))])
`(li (@ (class "entry"))
(a (@ (href ,path)) ,path)
(span ,title)
(span ,date)
)
)))
; actual page content
(define page
`(
(h2 "Articles")
(ul (@ (class "entry-list")) ,articles)
))
Because of the ease of extensibility, I was also able to remove the only source of the accursed JavaScript on this website, which was for LaTeX rendering. Now, all LaTeX is statically rendered as SVGs at generation time, and it's no more difficult to write in the page`(p "Did you know that " (tex-inl "$1 + 2 = 3$"))The actual site generator itself is currently very simple; there's definitely more functionality to be added, but with the new architecture that won't be nearly as painful to do now. As it stands it looks like this:(for ([p (in-directory "pages")]
#:when (regexp-match? #rx"\\.rkt$" (path->string p)))
(begin (define path (dynamic-require p 'path))
(define template (dynamic-require p 'template))
)
(block
(make-directory* (path-only (build-path (string->path ".build") path)))
(call-with-output-file (build-path (string->path ".build") path)
(lambda (out)
(write-html ((dynamic-require template 'template) p) out)
)
#:exists 'replace)
)