Major refactor and dependency update
This commit is contained in:
parent
f0ccbdbacc
commit
23e1a810d9
18 changed files with 259 additions and 170 deletions
25
blog-dev-1.rockspec
Normal file
25
blog-dev-1.rockspec
Normal 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
1
build
|
@ -1,4 +1,5 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
tup || exit
|
tup || exit
|
||||||
rm -rf blog/*
|
rm -rf blog/*
|
||||||
|
export LUA_PATH='lib/?.lua;lib/?/init.lua;;'
|
||||||
lua build.lua --copy css --copy javascript --output blog
|
lua build.lua --copy css --copy javascript --output blog
|
||||||
|
|
136
build.lua
136
build.lua
|
@ -1,141 +1,39 @@
|
||||||
local arrr = require 'arrr'
|
|
||||||
local cmark = require 'cmark'
|
local cmark = require 'cmark'
|
||||||
local csv = require 'streamcsv'
|
local csv = require 'streamcsv'
|
||||||
local fun = require 'fun'
|
local fun = require 'fun'
|
||||||
local json = require 'cjson'
|
local json = require 'cjson'
|
||||||
local restia = require 'restia'
|
local restia = require 'restia'
|
||||||
|
local scaffold = require 'scaffold'
|
||||||
local shapeshift = require 'shapeshift'
|
local shapeshift = require 'shapeshift'
|
||||||
local yaml = require 'lyaml'
|
|
||||||
|
|
||||||
local params do
|
-- Project-specific stuff
|
||||||
local is = shapeshift.is
|
local paramparser = require 'paramparser'
|
||||||
local parse = arrr {
|
local params = paramparser(...)
|
||||||
{ "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
|
|
||||||
package.loaded.params = params
|
package.loaded.params = params
|
||||||
|
local config = require 'config'
|
||||||
local config = restia.config.bind('config', {
|
local pages = require 'pages'
|
||||||
(require 'restia.config.readfile');
|
local templates = require 'templates'
|
||||||
(require 'restia.config.lua');
|
local posts = require 'posts'
|
||||||
(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 tree = {}
|
local tree = {}
|
||||||
|
|
||||||
for i, path in ipairs(params.copy) do
|
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
|
end
|
||||||
|
|
||||||
local validate_head do
|
local function render(name, data)
|
||||||
local is = shapeshift.is
|
return templates.main(templates[name], data)
|
||||||
validate_head = shapeshift.table {
|
|
||||||
__extra = 'keep';
|
|
||||||
title = is.string;
|
|
||||||
date = shapeshift.matches("%d%d%d%d%-%d%d%-%d%d");
|
|
||||||
file = is.string;
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parsedate(date)
|
local function page(name, data)
|
||||||
local year, month, day = date:match("(%d+)%-(%d+)%-(%d+)")
|
return templates.main(pages[name], data)
|
||||||
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], ...)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Render Posts
|
-- Render Posts
|
||||||
for idx, post in ipairs(posts) do
|
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
|
end
|
||||||
|
|
||||||
if params.delete then
|
if params.delete then
|
||||||
|
@ -162,6 +60,6 @@ tree["posts.json"] = json.encode(
|
||||||
:totable()
|
: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)
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
body {
|
body {
|
||||||
|
--sidebar: minmax(16em, 1fr);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 60em 1fr;
|
grid-template-columns: var(--sidebar) 40em var(--sidebar);
|
||||||
gap: 2em;
|
gap: 2em;
|
||||||
}
|
}
|
||||||
nav {
|
nav {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
|
position: sticky;
|
||||||
|
height: max-content;
|
||||||
|
top: 0;
|
||||||
|
max-height: max-content;
|
||||||
|
}
|
||||||
|
nav>* {
|
||||||
}
|
}
|
||||||
article {
|
article {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
|
|
7
lib/config.lua
Normal file
7
lib/config.lua
Normal 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
24
lib/paramparser.lua
Normal 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
68
lib/posts.lua
Normal 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
11
lib/stringplus.lua
Normal 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})
|
16
pages/index.html.skooma.yue
Normal file
16
pages/index.html.skooma.yue
Normal 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]
|
|
@ -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
4
pages/init.lua
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
local glass = require 'glass'
|
||||||
|
return glass.bind('pages', {
|
||||||
|
(require 'glass.skooma.html');
|
||||||
|
})
|
44
tasks.lua
Normal file
44
tasks.lua
Normal 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
4
templates/init.lua
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
local glass = require 'glass'
|
||||||
|
return glass.bind('templates', {
|
||||||
|
(require 'glass.skooma.html');
|
||||||
|
})
|
16
templates/main.html.skooma.yue
Normal file
16
templates/main.html.skooma.yue
Normal 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
|
|
@ -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
|
|
||||||
}
|
|
||||||
})
|
|
14
templates/post.html.skooma.yue
Normal file
14
templates/post.html.skooma.yue
Normal 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 }
|
|
@ -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), @ }
|
|
||||||
}
|
|
Loading…
Reference in a new issue