Org-Mode Static Site

I like org-mode, and I want to generate content for a static site (blog, cv, etc). I’ll use emacs to edit and export org-mode files to html, then tweak the results for aesthetics.

tldr

Do this after using org-export-to-html for each org file in the pages/ and posts/ directories:

./gen.ijs pages/ posts/ templates/page.html

Using this script:

#!/usr/bin/env jj
usage =: {{)n
usage:
   ./gen.ijs  "path/to/pages/"  "path/to/posts/"  "path/to/template.html"
Results:
pages/foo.html => public/foo.html
posts/bar.html => public/posts/bar.html
=> public/index.html
}}
{{ if. 3~:#args =: 2}.y do. exit 1[echo usage end. }} ARGV  NB. right number of args?

'pages posts template' =: args
pages =: 1 dir pages,'/*html'  NB. pages don't have any particular naming convention
posts =: 1 dir posts,'20*-*-*-*html'  NB. posts start with a date like 2021-02-18-
template =: fread template

stem =: {{ '/'&taketo &.|. y }}  NB. 'a/b/c' => 'c'
titleof =: {{ '.'taketo 11}. '- 'charsub stem y }}  NB. '/a/b/2021-10-10-a-b-c.html' => 'a b c'
dateof =: {{ 10{. stem y }}  NB. '2021-10-10-foo-bar.html' => '2021-10-10'
tag =: {{ '<', x, '>', y, '>',~ '</', 0{::;: x }}  NB. 'a href="foo"' tag 'y' => '<a href="foo">y</a>'
link =: {{ ('a href="',x,'"') tag y }}
gist =: {{ 'p class="post"' tag y link ('span class="post-name"' tag titleof name),'span class="post-date"'&tag dateof name =. stem y }}

post_proc =: {{
 page =. fread y
 name =. stem y
 post_date =. dateof name
 post_title =. titleof name
 extra_style =. ,>'<style type="text/css">pre.src.*</style>' rxall page
 t =. template rplc '<!-- TITLE -->'; extra_style, 'title' tag post_title
 t =. t rplc '<!-- CANON -->';'<link rel="canonical" href="alexshroyer.com/posts/',name,'">'
 r =. ('public/posts/',name) fwrite~ t rplc'<!-- CONTENT -->';'<!-- NAV -->',~ '<body>'takeafter'</body>'taketo page
 if. r>0 do. echo 'wrote public/posts/',name, ' (',(":r),' bytes)' end.}}

page_proc =: {{
 page =. fread y
 name =. stem y
 page_title =. titleof name
 t =. template rplc '<!-- TITLE -->'; 'title' tag page_title
 t =. t rplc '<!-- CANON -->';'<link rel="canonical" href="alexshroyer.com/',name,'">'
 r =. ('public/',name) fwrite~ t rplc'<!-- CONTENT -->';'<body>'takeafter'</body>'taketo page
 if. r>0 do. echo 'wrote public/',name, ' (',(":r),' bytes)' end.}}

toc =: {{)n <div id="preamble" class="status"><h1 class="title">Posts</h1></div> <div class="content"><div class="outline-2"> <!-- GISTS --> </div></div> }}
gen_index =: {{
 t =. template rplc '<!-- TITLE -->'; 'title' tag 'Home'
 t =. t rplc '<!-- CANON -->';'<link rel="canonical" href="alexshroyer.com">'
 names =. \:~ 'public/'&takeafter each 1 dir 'public/posts/20**-**-**-*html'
 r =. 'public/index.html' fwrite~ t rplc '<!-- CONTENT -->' ; toc rplc '<!-- GISTS -->'; LF joinstring gist each names
 if. r>0 do. echo 'wrote public/index.html (', (":r), ' bytes)' end. }}

post_proc each posts
page_proc each pages
gen_index ''
exit ''

Creating

For the most part, write and edit in org-mode. When it’s time to publish something, generate the corresponding html from within emacs. Preview the page - org-mode links should become html links, as long as the linked page is also published as html.

First, put org mode files and images under one folder:

+ site/2020-01-18-first-post.org
+ site/2021-10-08-example-post.org
+ site/images/background.png
+ site/images/favicon.ico
+ site/my-projects.org
+ site/style.css

Converting

Next, convert org to html:

+ site/2020-01-18-first-post.html
site/2020-01-18-first-post.org
+ site/2021-10-08-example-post.html
site/2021-10-08-example-post.org
site/images/background.png
site/images/favicon.ico
+ site/my-projects.html
site/my-projects.org
site/style.css

Post-Processing

Post-process the html. This means extracting the body from the html files, and fitting it into the page template for the site. I don’t want to mess with org’s html conversion, so I’ll mostly throw away the exported header and replace it with my template. However, some bits need to be spliced in, such as the canonical url, keywords, and page title. For simplicity, extract these bits from the original org file.

Generate Index

Finally, generate a homepage (index.html). This page has links to all posts, and possibly short excerpts from each one. If posts have metadata like “tags”, they can show up in the index page too.

site/2020-01-18-first-post.html
site/2020-01-18-first-post.org
site/2021-10-08-example-post.html
site/2021-10-08-example-post.org
site/images/background.png
site/images/favicon.ico
+ site/index.html
site/my-projects.html
site/my-projects.org
site/style.css