#!/usr/bin/env lua5.1 --[[ This is a Gemtext→HTML static site generator. This program: * Searches src/ for directories, then mirrors the directory tree to html/ * Reads all files in src/, attempts to render them from Gemtext to html, then outputs to the appropriate .html file in html/ * Reads files in src/w/ for h2 elements with dates in YYYY-MM-DD format, using them to render an RSS feed to html/index.xml; entries in the feed are sorted in descending order by file name so be sure to name them by date * Recursively copies files from static/ to html/ This site generator depends on LuaFileSystem and geez https://lunarmodules.github.io/luafilesystem/ https://pixelo789.codeberg.page/geez It is assumed that LuaFileSystem is installed using LuaRocks, and that geez's geez.lua is placed in the same directory as this file. ]]-- local lfs = require "lfs" local function iteratefiles(path, mode) local dattr = lfs.attributes(path) assert(dattr.mode == "directory") for file in lfs.dir(path) do if file ~= "." and file ~= ".." then local filepath = ("%s/%s"):format(path, file) local attr = lfs.attributes(filepath) if attr.mode == mode then coroutine.yield(filepath) end if attr.mode == "directory" then iteratefiles(filepath, mode) end end end end local function eachfile(path, mode) return coroutine.wrap(function() iteratefiles(path, mode) end) end local dirs = {} for dir in eachfile("src", "directory") do table.insert(dirs, dir) end do -- Set up output directory local hattr = lfs.attributes "html" if hattr and hattr.mode ~= "directory" then os.execute "rm html" hattr = nil end if not hattr then lfs.mkdir("html") end os.execute "cp -r static/* html" end -- Recreate the src/ directory tree in html/ for _, dir in ipairs(dirs) do local path = dir:gsub("^src", "html") lfs.mkdir(path) end -- File Rendering local files = {} for file in eachfile("src", "file") do table.insert(files, file) end table.sort(files, function(a, b) return a > b end) local htmltemplate = "" do local file = io.open("templates/index.html", "r") or error "failed to load templates/index.html" for line in file:lines() do htmltemplate = htmltemplate .. line .. "\n" end end local geez = require "geez" local function rendergemtext(path) local source = io.open(path, "r") local ast = geez.GeminiAST() local gemtext = "" local isroot = path:match "^src/index.gmi" local ispost = path:match "^src/w/" -- Manually insert links at the top of pages if not isroot then gemtext = gemtext .. "=> /index.gmi vtrlx.ca (Victoria Lacroix)\n" end if ispost then gemtext = gemtext .. "=> /posts.gmi All posts\n" end for line in source:lines() do gemtext = gemtext .. line .. "\n" end ast:load_str(gemtext) local html = ast:get_html_doc { indent_level = 1, } local path = path:gsub("^src", "html"):gsub(".gmi$", ".html") local dest = io.open(path, "w") local title = html:match "

([^\n]*)

" local date = html:match "

%d%d%d%d%-%d%d%-%d%d

" if not title then error(("no title: %s"):format(path)) elseif ispost and not date then error(("no date on post: %s"):format(path)) end local page = htmltemplate:format(title, html) dest:write(page) dest:close() return html end local atomfeed = [[ Victoria Lacroix's Writings %sT12:00:00-05:00 https://vtrlx.ca/index.xml ]] local entrytemplate = [[ %s %sT12:00:00-05:00 %sT12:00:00-05:00 Victoria Lacroix %s %s ]] local latest = "" local function addarticle(path, html) local title = html:match "

([^\n]*)

" local date = html:match "

(%d%d%d%d%-%d%d%-%d%d)

" -- The last header containing "Update (xxxx-xx-xx)" where xxxx-xx-xx is a date will be treated as the last update time. local updated = date assert(title, date, updated) for m in html:gmatch "

Update%s+%((%d%d%d%d%-%d%d%-%d%d)%)

" do updated = m end if updated > latest then latest = updated end local url = "https://vtrlx.ca/" .. path:gsub("^src/", ""):gsub(".gmi$", ".html") html = geez.escape_text(html) local entry = entrytemplate:format(title, date, updated, url, url, url, html) atomfeed = atomfeed .. entry end for _, path in ipairs(files) do if not path:match ".gmi$" then error(("unaccepted file type: %s"):format(path)) end local html = rendergemtext(path) if path:match "^src/w/%d%d%d%d%-%d%d%-%d%d%-.*.gmi$" then addarticle(path, html) end end do -- Save RSS/atom/whatever feed to /index.xml atomfeed = atomfeed:gsub("%%s", latest) .. "
\n" local feed = io.open("html/index.xml", "w") feed:write(atomfeed) feed:close() end -- Copy this script to main.lua in the rendered site, so that the latest version is live os.execute "cp main.lua html/main.lua"