// 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
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";
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,
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();
});
const getReferenceFromPath = (path) =>
- /Sources\/([A-Z][0-9A-Za-z]*\/[A-Z][0-9A-Za-z]*)\.djot$/u.exec(path)
- ?.[1]?.replace?.("/", ":");
+ /Sources\/([A-Z][0-9A-Za-z]*(?:\/[A-Z][0-9A-Za-z]*)+)\.djot$/u
+ .exec(path)?.[1]?.replace?.("/", ":"); // only replaces first slash
const listOfInternalLinks = (references, wrapper = ($) => $) => ({
tag: "bullet_list",
),
});
-const diffReferences = async (hash) => {
+const diffReferences = async (hash, againstHead = false) => {
const diff = new Deno.Command("git", {
args: [
"diff-tree",
"--name-only",
"--no-renames",
"--diff-filter=AM",
- hash,
+ ...(againstHead ? [hash, "HEAD"] : [hash]),
],
stdout: "piped",
stderr: "piped",
#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);
const links_section = [];
if (internalLinks.size || externalLinks.size) {
links_section.push(
- rawBlock`<footer>`,
rawBlock`<nav id="links">`,
{
tag: "heading",
}
links_section.push(
rawBlock`</nav>`,
- rawBlock`</footer>`,
);
} else {
/* do nothing */
}
- 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`</${as}>`,
- ];
- } else {
- /* do nothing */
- }
- } else {
- /* do nothing */
- }
+ e.children.push(
+ rawBlock`<footer>`,
+ rawBlock`${"\uFFFF"}`, // footnote placeholder
+ ...links_section,
+ rawBlock`</footer>`,
+ );
},
},
hard_break: {
e.attributes ??= {};
const { attributes, reference, destination } = e;
if (
- /^(?:[A-Z][0-9A-Za-z]*|[@#])?:(?:[A-Z][0-9A-Za-z]*)?$/u
+ /^(?:[A-Z][0-9A-Za-z]*|[@#])?:(?:[A-Z][0-9A-Za-z]*(?:\/[A-Z][0-9A-Za-z]*)*)?$/u
.test(reference ?? "")
) {
const [namespacePrefix, pageName] = splitReference(
String.fromCodePoint(parseInt(codepoint, 16))
}`;
} else {
- return {
- "--8": str`${"—\u2060:\u202F"}`, // reverse puppyprick
- "8--": str`${"\u202F:\u2060—"}`, // forward puppyprick
- sp: rawInline` `, // space
- nbsp: str`${"\xA0"}`, // no‐break space
- cgj: str`${"\u034F"}`, // combining grapheme joiner
- ensp: str`${"\u2002"}`, // enspace
- emsp: str`${"\u2003"}`, // emspace
- figsp: str`${"\u2007"}`, // figure space
- zwsp: str`${"\u200B"}`, // zero‐width space
- zwnj: str`${"\u200C"}`, // zero‐width nonjoiner
- zwj: str`${"\u200D"}`, // zero‐width joiner
- nnbsp: str`${"\u202F"}`, // narrow no‐break space
- mathsp: str`${"\u205F"}`, // math space
- wj: str`${"\u2060"}`, // word joiner
- fwsp: str`${"\u3000"}`, // fullwidth space
- }[alias] ?? e;
+ const resolved = config.symbols?.[alias];
+ return resolved != null ? str`${resolved}` : e;
}
},
},
}
{
+ // Patches for Djot HTML renderer.
+ const { HTMLRenderer: { prototype: htmlRendererPrototype } } = djot;
+ const { inTags: upstreamInTags } = htmlRendererPrototype;
+ htmlRendererPrototype.inTags = function (
+ tag,
+ node,
+ newlines,
+ extraAttrs = undefined,
+ ) {
+ const attributes = node.attributes ?? NIL;
+ if ("as" in attributes) {
+ const newTag = attributes.as;
+ delete attributes.as;
+ return upstreamInTags.call(
+ this,
+ newTag,
+ node,
+ newlines,
+ extraAttrs,
+ );
+ } else {
+ return upstreamInTags.call(
+ this,
+ tag,
+ node,
+ newlines,
+ extraAttrs,
+ );
+ }
+ };
+}
+{
+ 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();
console.warn(`Djot(${reference}): ${$.render()}`),
}),
source,
+ config,
);
const reference = `${namespace}:${pageName}`;
pages.set(reference, page);
console.warn(`Djot(${reference}): ${$.render()}`),
}),
source,
+ config,
);
pages.set(reference, page);
}
}
const results = new Array(6);
const seen = new Set();
- let recency = 5;
+ const maxRecency = Math.max(config.max_recency | 0, 0);
+ let recency = maxRecency;
let current;
do {
const show = new Deno.Command("git", {
"show",
"-s",
"--format=%H%x00%cI%x00%cD",
- recency ? `HEAD~${5 - recency}` : commit,
+ recency ? `HEAD~${maxRecency - recency}` : commit,
],
stdout: "piped",
stderr: "piped",
]).then(logErrorsAndCollectResults);
const refs = [];
current = hash;
- for (const ref of (await diffReferences(current))) {
+ for (
+ const ref of (await diffReferences(current, !recency))
+ ) {
if (seen.has(ref)) {
/* do nothing */
} else {
seen.add(ref);
}
}
- results[recency] = { dateTime, humanReadable, refs };
+ results[recency] = { dateTime, hash, humanReadable, refs };
} while (recency-- > 0 && current && current != commit);
return results;
})(),
},
};
});
+ const renderedAST = djot.renderAST(ast);
const doc = getDOM(template);
- const result = getDOM(`${djot.renderHTML(ast)}`);
+ const result = getDOM(djot.renderHTML(ast, {
+ overrides: {
+ raw_block: (node, context) => {
+ if (node.format == "html" && node.text == "\uFFFF") {
+ if (context.nextFootnoteIndex > 1) {
+ const result = context.renderNotes(ast.footnotes);
+ context.nextFootnoteIndex = 1;
+ return result;
+ } else {
+ return "";
+ }
+ } else {
+ return context.renderAstNodeDefault(node);
+ }
+ },
+ },
+ }));
const headElement = domutils.findOne(
(node) => node.name == "head",
doc,
"GitWikiWeb: Template did not include a <gitwikiweb-content> element.",
);
} else {
- for (const node of result) {
+ for (const node of [...result]) {
domutils.prepend(contentElement, node);
}
domutils.removeElement(contentElement);
{ createNew: true },
),
);
+ promises.push(
+ Deno.writeTextFile(
+ `${DESTINATION}/${pageRef}/index.ast`,
+ renderedAST,
+ { createNew: true },
+ ),
+ );
promises.push(
Deno.writeTextFile(
`${DESTINATION}/${pageRef}/source.djot`,