Major refactor and dependency update

This commit is contained in:
Talia 2023-02-01 17:38:00 +01:00
parent f0ccbdbacc
commit 23e1a810d9
Signed by: darkwiiplayer
GPG key ID: 7808674088232B3E
18 changed files with 259 additions and 170 deletions

25
blog-dev-1.rockspec Normal file
View file

@ -0,0 +1,25 @@
package = "blog"
version = "dev-1"
source = {
url = ""
}
description = {
homepage = "https://darkwiiplayer.github.io/blog",
license = "Proprietary"
}
dependencies = {
"arrr ~> 2.2",
"glass ~> 1.3.0",
"cmark ~> 0.29",
"fun ~> 0.1.3",
"lua-cjson ~> 2.1",
"restia",
"scaffold ~> 1.1.0",
"shapeshift ~> 1.1.0",
"skooma ~> 0.3",
"streamcsv ~> 1.1.0",
}
build = {
type = "builtin",
modules = { }
}

1
build
View file

@ -1,4 +1,5 @@
#!/bin/sh
tup || exit
rm -rf blog/*
export LUA_PATH='lib/?.lua;lib/?/init.lua;;'
lua build.lua --copy css --copy javascript --output blog

136
build.lua
View file

@ -1,141 +1,39 @@
local arrr = require 'arrr'
local cmark = require 'cmark'
local csv = require 'streamcsv'
local fun = require 'fun'
local json = require 'cjson'
local restia = require 'restia'
local scaffold = require 'scaffold'
local shapeshift = require 'shapeshift'
local yaml = require 'lyaml'
local params do
local is = shapeshift.is
local parse = arrr {
{ "Output directory", "--output", "-o", 'directory' };
{ "Input directory", "--input", "-i", 'directory' };
{ "Copy directory", "--copy", "-c", 'directory', 'repeatable' };
{ "Delete everything first", "--delete", "-d" };
}
local validate = shapeshift.table {
output = shapeshift.default("output", is.string);
input = shapeshift.default(".", is.string);
copy = shapeshift.default({}, shapeshift.all{
is.table,
shapeshift.each(is.string)
});
delete = shapeshift.default(false, shapeshift.is.boolean);
}
params = select(2, assert(validate(parse{...})))
end
-- Project-specific stuff
local paramparser = require 'paramparser'
local params = paramparser(...)
package.loaded.params = params
local config = restia.config.bind('config', {
(require 'restia.config.readfile');
(require 'restia.config.lua');
(require 'restia.config.yaml');
})
package.loaded.config = config
local templates = restia.config.bind('templates', {
(require 'restia.config.skooma');
})
package.loaded.templates = templates
local pages = restia.config.bind('pages', {
(require 'restia.config.skooma');
})
package.loaded.pages = pages
-- General purpose utility functions
local function split(str, pattern)
local result = {}
for item in str:gmatch(pattern) do
table.insert(result, item)
end
return result
end
local function read_post(file)
local content = io.open(file):read("*a")
local head, body = restia.utils.frontmatter(content)
return {
head = head and yaml.load(head) or {};
body = cmark.render_html(cmark.parse_document(body, #body, cmark.OPT_DEFAULT), cmark.OPT_DEFAULT);
}
end
-- Handle JS modules
local modules = csv.file(io.open("modules.csv"), {header = true})
package.loaded.modules = modules
local posts = {}
package.loaded.posts = posts
local config = require 'config'
local pages = require 'pages'
local templates = require 'templates'
local posts = require 'posts'
local tree = {}
for i, path in ipairs(params.copy) do
restia.utils.deepinsert(tree, restia.utils.fs2tab(path), restia.utils.readdir(path))
scaffold.deep(tree, path, scaffold.readdir(path))
end
local validate_head do
local is = shapeshift.is
validate_head = shapeshift.table {
__extra = 'keep';
title = is.string;
date = shapeshift.matches("%d%d%d%d%-%d%d%-%d%d");
file = is.string;
}
local function render(name, data)
return templates.main(templates[name], data)
end
local function parsedate(date)
local year, month, day = date:match("(%d+)%-(%d+)%-(%d+)")
return os.time {
year = tonumber(year);
month = tonumber(month);
day = tonumber(day);
}
end
-- Load Posts
for file in restia.utils.files(params.input, "%.md$") do
local post = read_post(file)
post.head.file = file
assert(validate_head(post.head))
post.head.timestamp = parsedate(post.head.date)
if "string" == type(post.head.tags) then
post.head.tags = split(post.head.tags, "%a+")
end
post.head.slug = post.head.title
:gsub(' ', '_')
:lower()
:gsub('[^a-z0-9-_]', '')
post.head.uri = string.format("/%s/%s.html", post.head.date:gsub("%-", "/"), post.head.slug)
post.path = restia.utils.fs2tab(post.head.uri)
table.insert(posts, post)
end
table.sort(posts, function(a, b)
return a.head.timestamp > b.head.timestamp
end)
local function render(name, ...)
return templates.main(templates[name], ...)
end
local function page(name, ...)
return templates.main(pages[name], ...)
local function page(name, data)
return templates.main(pages[name], data)
end
-- Render Posts
for idx, post in ipairs(posts) do
local body = restia.utils.deepconcat(render("post", post.body, post.head))
local body = tostring(render("post", post))
restia.utils.deepinsert(tree, post.path, body)
scaffold.deep(tree, post.path, body)
end
if params.delete then
@ -162,6 +60,6 @@ tree["posts.json"] = json.encode(
:totable()
)
tree["index.html"] = page("index", posts, tree["posts.json"])
tree["index.html"] = tostring(page("index", tree["posts.json"]))
restia.utils.builddir(params.output, tree)
scaffold.builddir(params.output, tree)

View file

@ -1,10 +1,17 @@
body {
--sidebar: minmax(16em, 1fr);
display: grid;
grid-template-columns: 1fr 60em 1fr;
grid-template-columns: var(--sidebar) 40em var(--sidebar);
gap: 2em;
}
nav {
grid-column: 1;
position: sticky;
height: max-content;
top: 0;
max-height: max-content;
}
nav>* {
}
article {
grid-column: 2;

7
lib/config.lua Normal file
View file

@ -0,0 +1,7 @@
local glass = require 'glass'
return glass.bind('config', {
(require 'glass.raw');
(require 'glass.lua');
(require 'glass.yaml');
(require 'glass.csv');
})

24
lib/paramparser.lua Normal file
View file

@ -0,0 +1,24 @@
local arrr = require 'arrr'
local shapeshift = require 'shapeshift'
return function(...)
local is = shapeshift.is
local parse = arrr {
{ "Output directory", "--output", "-o", 'directory' };
{ "Input directory", "--input", "-i", 'directory' };
{ "Copy directory", "--copy", "-c", 'directory', 'repeatable' };
{ "Include unpublished posts", "--unpublished", "-u", nil };
{ "Delete everything first", "--delete", "-d" };
}
local validate = shapeshift.table {
output = shapeshift.default("output", is.string);
input = shapeshift.default(".", is.string);
copy = shapeshift.default({}, shapeshift.all{
is.table,
shapeshift.each(is.string)
});
unpublished = shapeshift.default(false, shapeshift.is.boolean);
delete = shapeshift.default(false, shapeshift.is.boolean);
}
return select(2, assert(validate(parse{...})))
end

68
lib/posts.lua Normal file
View file

@ -0,0 +1,68 @@
local restia = require 'restia'
local params = require 'params'
local yaml = require 'lyaml'
local shapeshift = require 'shapeshift'
local string = require 'stringplus'
local function parsedate(date)
local year, month, day = date:match("(%d+)%-(%d+)%-(%d+)")
return os.time {
year = tonumber(year);
month = tonumber(month);
day = tonumber(day);
}
end
local validate_head do
local is = shapeshift.is
validate_head = shapeshift.table {
__extra = 'keep';
title = is.string;
date = shapeshift.matches("%d%d%d%d%-%d%d%-%d%d");
file = is.string;
published = shapeshift.default(false, shapeshift.matches("^true$"))
}
end
local function read_post(file)
local content = io.open(file):read("*a")
local head, body = restia.utils.frontmatter(content)
return {
head = head and yaml.load(head) or {};
body = cmark.render_html(cmark.parse_document(body, #body, cmark.OPT_DEFAULT), cmark.OPT_DEFAULT);
}
end
local posts = {}
for file in restia.utils.files(params.input, "^./posts/.*%.md$") do
print("Reading post "..file.."...")
local post = read_post(file)
post.head.file = file
assert(validate_head(post.head))
post.head.timestamp = parsedate(post.head.date)
if "string" == type(post.head.tags) then
post.head.tags = string.split(post.head.tags, "%a+")
end
post.head.slug = post.head.title
:gsub(' ', '_')
:lower()
:gsub('[^a-z0-9-_]', '')
post.head.uri = string.format("/%s/%s.html", post.head.date:gsub("%-", "/"), post.head.slug)
post.path = post.head.uri
if post.head.published or params.unpublished then
table.insert(posts, post)
end
end
table.sort(posts, function(a, b)
return a.head.timestamp > b.head.timestamp
end)
return posts

11
lib/stringplus.lua Normal file
View file

@ -0,0 +1,11 @@
local _string = {}
function _string.split(str, pattern)
local result = {}
for item in str:gmatch(pattern) do
table.insert(result, item)
end
return result
end
return setmetatable(_string, {__index=_G.string})

View file

@ -0,0 +1,16 @@
import output from require 'params'
slots, json = select 1, ...
posts = require 'posts'
post = =>
blogPost {
name: @head.slug
a @head.title, href: "/"..output..@head.uri
}
slots.head title "Index"
slots.head script "window.posts = JSON.parse(`#{json}`)"
slots.head script type: 'module', src: "/#{output}/javascript/BlogPost.js"
slots.head script type: 'module', src: "/#{output}/javascript/LocalDate.js"
return ul [li post p for p in *posts]

View file

@ -1,16 +0,0 @@
import output from require 'params'
post = =>
blogPost {
name: @head.slug
a @head.title, href: "/"..output..@head.uri
}
(json) => {
title "Index"
script "window.posts = JSON.parse(`#{json}`)"
script type: 'module', src: "/#{output}/javascript/BlogPost.js"
script type: 'module', src: "/#{output}/javascript/LocalDate.js"
}, (
ul [li post p for p in *@]
)

4
pages/init.lua Normal file
View file

@ -0,0 +1,4 @@
local glass = require 'glass'
return glass.bind('pages', {
(require 'glass.skooma.html');
})

44
tasks.lua Normal file
View file

@ -0,0 +1,44 @@
local task = require'spooder'.task
local path = table.concat({
"lib/?.lua",
"lib/?/init.lua",
"lua_modules/share/lua/5.4/?.lua",
"lua_modules/share/lua/5.4/?/init.lua",
";",
}, ";")
local cpath = path:gsub(".lua", ".so"):gsub("/share/", "/lib/")
task.build {
description = "Builds the page";
'luarocks install --only-deps *.rockspec';
'tup';
'rm -rf blog/*';
string.format(
[[
export LUA_PATH='%s'
export LUA_CPATH='%s'
lua build.lua --copy css --copy javascript --output blog
]],
path, cpath
)
}
task.deploy {
description = "Deploys the blog to latest version";
depends = "build";
[[
hash=$(git log -1 --format=%h)
cd blog
find . | treh -c
git add --all
if git log -1 --format=%s | grep "$hash$"
then git commit --amend --no-edit
else git commit -m "Update blog to $hash"
fi
git push --force origin page
cd ../
git stash pop || true
]];
}

4
templates/init.lua Normal file
View file

@ -0,0 +1,4 @@
local glass = require 'glass'
return glass.bind('templates', {
(require 'glass.skooma.html');
})

View file

@ -0,0 +1,16 @@
import output from require 'params'
import slotty from require 'skooma'
import 'config'
slots = slotty!
content, data = select 1, ...
html
lang: "english"
* head
* link rel: "stylesheet", href: "/#{output}/css/site.css"
* meta charset: "UTF-8"
* slots.head
* [ link rel: "modulepreload", href: module.url for module in *config.modules when module.preload ]
* body
* content slots, data

View file

@ -1,18 +0,0 @@
import output from require 'params'
import 'modules'
(...) =>
head_content, body_content = @(...)
body_content = head_content unless body_content
render.html(html {
lang: "english"
head {
link rel: "stylesheet", href: "/#{output}/css/site.css"
meta charset: "UTF-8"
head_content
[ link rel: "modulepreload", href: module.url for module in *modules when module.preload ]
}
body {
body_content
}
})

View file

@ -0,0 +1,14 @@
import output from require 'params'
slots, post = select 1, ...
url = (path) -> "/"..output..path
slots.head title post.head.title
slots.head link rel: "stylesheet", href: "/#{output}/css/post.css"
return (=>@)
* nav
* ul
* li a "Index", href: url "/"
* [li a post.head.title, href: url post.head.uri for post in *require("posts")]
* article { h1(post.head.title), post.body }

View file

@ -1,16 +0,0 @@
import output from require 'params'
url = (path) -> "/"..output..path
(attributes) => {
title attributes.title
link rel: "stylesheet", href: "/#{output}/css/post.css"
}, {
nav {
ul {
li a "Index", href: url "/"
[li a post.head.title, href: url post.head.uri for post in *require("posts")]
}
}
article { h1(attributes.title), @ }
}