X-Git-Url: https://git.ladys.computer/GitWikiWeb/blobdiff_plain/74fb42b4f38c2e7e87495cf999e5382f113476d4..38a7374d66f9a54f621aa192dcbbd573396e007a:/build.js diff --git a/build.js b/build.js index da0f783..bd8ff27 100644 --- a/build.js +++ b/build.js @@ -20,7 +20,7 @@ // export GITWIKIWEB=/srv/git/GitWikiWeb // git archive --remote=$GITWIKIWEB HEAD build.js \ // | tar -xO \ -// | deno run -A - ~/public/wiki $GITWIKIWEB +// | deno run -A - ~/public/wiki $GITWIKIWEB current // // The directory `~/public/wiki` (or whatever you specify as the first // argument to `deno run -A -`) **will be deleted** and a new static @@ -37,7 +37,11 @@ import { emptyDir, ensureDir, -} from "https://deno.land/std@0.195.0/fs/mod.ts"; +} from "https://deno.land/std@0.196.0/fs/mod.ts"; +import { + JSON_SCHEMA, + parse as parseYaml, +} from "https://deno.land/std@0.196.0/yaml/mod.ts"; import djot from "npm:@djot/djot@0.2.3"; import { Parser } from "npm:htmlparser2@9.0.0"; import { DomHandler, Element, Text } from "npm:domhandler@5.0.3"; @@ -46,6 +50,7 @@ import domSerializer from "npm:dom-serializer@2.0.0"; const DESTINATION = Deno.args[0] ?? "~/public/wiki"; const REMOTE = Deno.args[1] ?? "/srv/git/GitWikiWeb"; +const REV = Deno.args[2] ?? "HEAD"; const READ_ONLY = { configurable: false, @@ -53,6 +58,8 @@ const READ_ONLY = { writable: false, }; +const NIL = Object.preventExtensions(Object.create(null)); + const rawBlock = (strings, ...substitutions) => ({ tag: "raw_block", format: "html", @@ -87,7 +94,7 @@ const getDOM = (source) => { const getRemoteContent = async (pathName) => { const getArchive = new Deno.Command("git", { - args: ["archive", `--remote=${REMOTE}`, "HEAD", pathName], + args: ["archive", `--remote=${REMOTE}`, REV, pathName], stdout: "piped", stderr: "piped", }).spawn(); @@ -143,7 +150,7 @@ const getReferenceFromPath = (path) => /Sources\/([A-Z][0-9A-Za-z]*\/[A-Z][0-9A-Za-z]*)\.djot$/u.exec(path) ?.[1]?.replace?.("/", ":"); -const listOfInternalLinks = (references) => ({ +const listOfInternalLinks = (references, wrapper = ($) => $) => ({ tag: "bullet_list", tight: true, style: "*", @@ -155,7 +162,7 @@ const listOfInternalLinks = (references) => ({ tag: "list_item", children: [{ tag: "para", - children: [{ + children: [wrapper({ tag: "link", attributes: { "data-realm": "internal", @@ -164,13 +171,34 @@ const listOfInternalLinks = (references) => ({ }, reference, children: [], - }], + })], }], }; }, ), }); +const diffReferences = async (hash, againstHead = false) => { + const diff = new Deno.Command("git", { + args: [ + "diff-tree", + "-r", + "-z", + "--name-only", + "--no-renames", + "--diff-filter=AM", + ...(againstHead ? [hash, "HEAD"] : [hash]), + ], + stdout: "piped", + stderr: "piped", + }).spawn(); + const [diffNames] = await Promise.allSettled([ + new Response(diff.stdout).text(), + new Response(diff.stderr).text(), + ]).then(logErrorsAndCollectResults); + return references(diffNames.split("\0")); // returns an iterable +}; + function* references(paths) { for (const path of paths) { const reference = getReferenceFromPath(path); @@ -194,7 +222,7 @@ class GitWikiWebPage { #internalLinks = new Set(); #externalLinks = new Map(); - constructor(namespace, name, ast, source) { + constructor(namespace, name, ast, source, config) { const internalLinks = this.#internalLinks; const externalLinks = this.#externalLinks; const sections = Object.create(null); @@ -217,7 +245,7 @@ class GitWikiWebPage { ); if (internalLinks.size) { links_section.push( - rawBlock`
`, + rawBlock`
`, rawBlock`on this wiki`, listOfInternalLinks(internalLinks), rawBlock`
`, @@ -227,7 +255,7 @@ class GitWikiWebPage { } if (externalLinks.size) { links_section.push( - rawBlock`
`, + rawBlock`
`, rawBlock`elsewhere on the Web`, { tag: "bullet_list", @@ -274,6 +302,29 @@ class GitWikiWebPage { e.children.push(...links_section); }, }, + emph: { + enter: (_) => {}, + exit: (e) => { + const attributes = e.attributes ?? NIL; + const { as } = attributes; + if (as) { + delete attributes.as; + if ( + as == "b" || as == "cite" || as == "i" || as == "u" + ) { + return [ + rawInline`<${as}>`, + ...e.children, + rawInline``, + ]; + } else { + /* do nothing */ + } + } else { + /* do nothing */ + } + }, + }, hard_break: { enter: (_) => { if (titleSoFar != null) { @@ -425,6 +476,20 @@ class GitWikiWebPage { }, exit: (_) => {}, }, + symb: { + enter: (e) => { + const { alias } = e; + const codepoint = /^U\+([0-9A-Fa-f]+)$/u.exec(alias)?.[1]; + if (codepoint) { + return str`${ + String.fromCodePoint(parseInt(codepoint, 16)) + }`; + } else { + const resolved = config.symbols?.[alias]; + return resolved != null ? str`${resolved}` : e; + } + }, + }, }; }); Object.defineProperties(this, { @@ -449,8 +514,11 @@ class GitWikiWebPage { } { + const config = await getRemoteContent("config.yaml").then((yaml) => + parseYaml(yaml, { schema: JSON_SCHEMA }) + ); const ls = new Deno.Command("git", { - args: ["ls-tree", "-rz", "live"], + args: ["ls-tree", "-rz", "HEAD"], stdout: "piped", stderr: "piped", }).spawn(); @@ -522,6 +590,7 @@ class GitWikiWebPage { console.warn(`Djot(${reference}): ${$.render()}`), }), source, + config, ); const reference = `${namespace}:${pageName}`; pages.set(reference, page); @@ -543,6 +612,7 @@ class GitWikiWebPage { console.warn(`Djot(${reference}): ${$.render()}`), }), source, + config, ); pages.set(reference, page); } @@ -592,23 +662,44 @@ class GitWikiWebPage { } else { /* do nothing */ } - const diff = new Deno.Command("git", { - args: [ - "diff", - "-z", - "--name-only", - "--no-renames", - "--diff-filter=AM", - commit, - ], - stdout: "piped", - stderr: "piped", - }).spawn(); - const [diffNames] = await Promise.allSettled([ - new Response(diff.stdout).text(), - new Response(diff.stderr).text(), - ]).then(logErrorsAndCollectResults); - return [...references(diffNames.split("\0"))]; + const results = new Array(6); + const seen = new Set(); + let recency = 5; + let current; + do { + const show = new Deno.Command("git", { + args: [ + "show", + "-s", + "--format=%H%x00%cI%x00%cD", + recency ? `HEAD~${5 - recency}` : commit, + ], + stdout: "piped", + stderr: "piped", + }).spawn(); + const [ + [hash, dateTime, humanReadable], + ] = await Promise.allSettled([ + new Response(show.stdout).text().then((rev) => + rev.trim().split("\0") + ), + new Response(show.stderr).text(), + ]).then(logErrorsAndCollectResults); + const refs = []; + current = hash; + for ( + const ref of (await diffReferences(current, !recency)) + ) { + if (seen.has(ref)) { + /* do nothing */ + } else { + refs.push(ref); + seen.add(ref); + } + } + results[recency] = { dateTime, hash, humanReadable, refs }; + } while (recency-- > 0 && current && current != commit); + return results; })(), ...Array.from( pages.keys(), @@ -650,9 +741,41 @@ class GitWikiWebPage { const { content, navigation } = (() => { const navigation = []; if (pageRef == "Special:RecentlyChanged") { - navigation.push( - listOfInternalLinks(recentlyChanged), - ); + navigation.push({ + tag: "bullet_list", + attributes: { class: "recent-changes" }, + tight: true, + style: "*", + children: Array.from(function* () { + for ( + const [index, result] of recentlyChanged + .entries() + ) { + if (result != null) { + const { + dateTime, + humanReadable, + refs, + } = result; + yield* listOfInternalLinks(refs, (link) => ({ + tag: index == 0 ? "span" : "strong", + attributes: { "data-recency": `${index}` }, + children: [ + link, + ...(index == 0 ? [] : [ + str` `, + rawInline`()`, + ]), + ], + })).children; + } else { + /* do nothing */ + } + } + }()).reverse(), + }); } else { isNavigationPage = false; return { content: e.children, navigation }; @@ -668,9 +791,11 @@ class GitWikiWebPage { level: 1, children: [str`${title}`], }, - rawBlock`
`, + rawBlock``, ], navigation: [ @@ -691,7 +816,7 @@ class GitWikiWebPage { }, heading: { enter: (e) => { - const attributes = e.attributes ?? Object.create(null); + const attributes = e.attributes ?? NIL; if ( isNavigationPage && e.level == 1 && attributes?.class == "main" @@ -726,15 +851,15 @@ class GitWikiWebPage { if (attributes["data-realm"] == "internal") { delete e.reference; if (redLinks.has(reference)) { - e.destination = `/Special:NotFound?path=/${reference}`; + e.destination = + `/Special:NotFound?path=/${reference}`; attributes["data-notfound"] = ""; } else { e.destination = `/${reference}`; } if (children.length == 0) { const section = - pages.get(reference)?.sections?.main ?? - Object.create(null); + pages.get(reference)?.sections?.main ?? NIL; const { v } = attributes; if (v == null) { children.push(