]> Lady’s Gitweb - GitWikiWeb/blobdiff - build.js
Allow specifying the revision to pull files from
[GitWikiWeb] / build.js
index da0f78391777e5bf058a43030e5ff13b0f539919..bd8ff27ba2c0fe3743ba4132236fd65237c8cfbc 100644 (file)
--- a/build.js
+++ b/build.js
@@ -20,7 +20,7 @@
 //     export GITWIKIWEB=/srv/git/GitWikiWeb
 //     git archive --remote=$GITWIKIWEB HEAD build.js \
 //       | tar -xO \
 //     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
 //
 // 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,
 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";
 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 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 READ_ONLY = {
   configurable: false,
@@ -53,6 +58,8 @@ const READ_ONLY = {
   writable: false,
 };
 
   writable: false,
 };
 
+const NIL = Object.preventExtensions(Object.create(null));
+
 const rawBlock = (strings, ...substitutions) => ({
   tag: "raw_block",
   format: "html",
 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", {
 
 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();
     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?.("/", ":");
 
   /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: "*",
   tag: "bullet_list",
   tight: true,
   style: "*",
@@ -155,7 +162,7 @@ const listOfInternalLinks = (references) => ({
         tag: "list_item",
         children: [{
           tag: "para",
         tag: "list_item",
         children: [{
           tag: "para",
-          children: [{
+          children: [wrapper({
             tag: "link",
             attributes: {
               "data-realm": "internal",
             tag: "link",
             attributes: {
               "data-realm": "internal",
@@ -164,13 +171,34 @@ const listOfInternalLinks = (references) => ({
             },
             reference,
             children: [],
             },
             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);
 function* references(paths) {
   for (const path of paths) {
     const reference = getReferenceFromPath(path);
@@ -194,7 +222,7 @@ class GitWikiWebPage {
   #internalLinks = new Set();
   #externalLinks = new Map();
 
   #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 internalLinks = this.#internalLinks;
     const externalLinks = this.#externalLinks;
     const sections = Object.create(null);
@@ -217,7 +245,7 @@ class GitWikiWebPage {
               );
               if (internalLinks.size) {
                 links_section.push(
               );
               if (internalLinks.size) {
                 links_section.push(
-                  rawBlock`<details>`,
+                  rawBlock`<details open="">`,
                   rawBlock`<summary>on this wiki</summary>`,
                   listOfInternalLinks(internalLinks),
                   rawBlock`</details>`,
                   rawBlock`<summary>on this wiki</summary>`,
                   listOfInternalLinks(internalLinks),
                   rawBlock`</details>`,
@@ -227,7 +255,7 @@ class GitWikiWebPage {
               }
               if (externalLinks.size) {
                 links_section.push(
               }
               if (externalLinks.size) {
                 links_section.push(
-                  rawBlock`<details>`,
+                  rawBlock`<details open="">`,
                   rawBlock`<summary>elsewhere on the Web</summary>`,
                   {
                     tag: "bullet_list",
                   rawBlock`<summary>elsewhere on the Web</summary>`,
                   {
                     tag: "bullet_list",
@@ -274,6 +302,29 @@ class GitWikiWebPage {
             e.children.push(...links_section);
           },
         },
             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 */
+            }
+          },
+        },
         hard_break: {
           enter: (_) => {
             if (titleSoFar != null) {
         hard_break: {
           enter: (_) => {
             if (titleSoFar != null) {
@@ -425,6 +476,20 @@ class GitWikiWebPage {
           },
           exit: (_) => {},
         },
           },
           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, {
       };
     });
     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", {
   const ls = new Deno.Command("git", {
-    args: ["ls-tree", "-rz", "live"],
+    args: ["ls-tree", "-rz", "HEAD"],
     stdout: "piped",
     stderr: "piped",
   }).spawn();
     stdout: "piped",
     stderr: "piped",
   }).spawn();
@@ -522,6 +590,7 @@ class GitWikiWebPage {
                     console.warn(`Djot(${reference}): ${$.render()}`),
                 }),
                 source,
                     console.warn(`Djot(${reference}): ${$.render()}`),
                 }),
                 source,
+                config,
               );
               const reference = `${namespace}:${pageName}`;
               pages.set(reference, page);
               );
               const reference = `${namespace}:${pageName}`;
               pages.set(reference, page);
@@ -543,6 +612,7 @@ class GitWikiWebPage {
             console.warn(`Djot(${reference}): ${$.render()}`),
         }),
         source,
             console.warn(`Djot(${reference}): ${$.render()}`),
         }),
         source,
+        config,
       );
       pages.set(reference, page);
     }
       );
       pages.set(reference, page);
     }
@@ -592,23 +662,44 @@ class GitWikiWebPage {
         } else {
           /* do nothing */
         }
         } 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(),
       })(),
       ...Array.from(
         pages.keys(),
@@ -650,9 +741,41 @@ class GitWikiWebPage {
               const { content, navigation } = (() => {
                 const navigation = [];
                 if (pageRef == "Special:RecentlyChanged") {
               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`<small>(<time dateTime="${dateTime}">`,
+                                str`${humanReadable}`,
+                                rawInline`</time>)</small>`,
+                              ]),
+                            ],
+                          })).children;
+                        } else {
+                          /* do nothing */
+                        }
+                      }
+                    }()).reverse(),
+                  });
                 } else {
                   isNavigationPage = false;
                   return { content: e.children, navigation };
                 } else {
                   isNavigationPage = false;
                   return { content: e.children, navigation };
@@ -668,9 +791,11 @@ class GitWikiWebPage {
                       level: 1,
                       children: [str`${title}`],
                     },
                       level: 1,
                       children: [str`${title}`],
                     },
-                    rawBlock`<details open="">`,
+                    rawBlock`<details id="navigation-about" open="">`,
                     rawBlock`<summary>about this listing</summary>`,
                     rawBlock`<summary>about this listing</summary>`,
+                    rawBlock`<article>`,
                     ...e.children,
                     ...e.children,
+                    rawBlock`</article>`,
                     rawBlock`</details>`,
                   ],
                   navigation: [
                     rawBlock`</details>`,
                   ],
                   navigation: [
@@ -691,7 +816,7 @@ class GitWikiWebPage {
           },
           heading: {
             enter: (e) => {
           },
           heading: {
             enter: (e) => {
-              const attributes = e.attributes ?? Object.create(null);
+              const attributes = e.attributes ?? NIL;
               if (
                 isNavigationPage && e.level == 1 &&
                 attributes?.class == "main"
               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)) {
               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 =
                   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(
                   const { v } = attributes;
                   if (v == null) {
                     children.push(
This page took 0.034638 seconds and 4 git commands to generate.