Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/scripts
diff options
context:
space:
mode:
authorLeitwolf112026-11-05 05:56:00 +0300
committerLeitwolf112026-11-05 05:56:00 +0300
commit6f114e3df68d0b20c7e58c5ffb8c46070317008a (patch)
tree2b282d5ba7cdee5ac453d8cfd8699ea77a824af4 /scripts
downloadtazel.net-6f114e3df68d0b20c7e58c5ffb8c46070317008a.tar.gz
Build the site of Tazel with Nuxt
tazel.net is the site of Tazel itself: a Nuxt 4 site generated to static files, in en-US, de-DE, ru-RU and he-IL, light by default with a dark theme that is kept per browser. - Home: the release and its kind, what Tazel does, the steps from a project file to a program, and a sample. - Docs: Markdown through Nuxt Content, highlighted by Shiki (Kotlin, shell, PowerShell, and Tala through the grammar of the compiler repository), with a tree of the pages on the left, the headings of the page on the right, and the previous and next pages below. Installation, a first project, and releases and support are written in every language; the reference (commands, project file, lockfile, plugins, how Tazel works) is copied from the Documents of Tazel by scripts/sync-docs.mjs, and a language without a page shows the English one with a note. - Every page has its canonical address, hreflang alternates and a sitemap entry; generation fails on a link to a page that does not exist. CI checks the format, the types and the language helpers, generates the site and runs Dependency Review on pull requests.
Diffstat (limited to 'scripts')
-rw-r--r--scripts/sync-docs.mjs93
1 files changed, 93 insertions, 0 deletions
diff --git a/scripts/sync-docs.mjs b/scripts/sync-docs.mjs
new file mode 100644
index 0000000..c36cbdb
--- /dev/null
+++ b/scripts/sync-docs.mjs
@@ -0,0 +1,93 @@
+// SPDX-FileCopyrightText: 2026 Progmasoft <support@progmasoft.com>
+// SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1
+
+// Copies the reference of Tazel from a checkout of Progmasoft/tazel into the English docs:
+//
+// node scripts/sync-docs.mjs ../tazel
+//
+// The title of each document becomes its front matter, links between the documents become
+// links between pages of the site, and links to other files of the repository go to GitHub.
+
+import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
+import { dirname, join, resolve } from "node:path";
+
+const checkout = process.argv[2];
+if (!checkout) {
+ console.error("usage: node scripts/sync-docs.mjs <checkout of Progmasoft/tazel>");
+ process.exit(2);
+}
+
+const repository = "https://github.com/Progmasoft/tazel/blob/main";
+const documents = [
+ { source: "CLI.md", target: "2.reference/1.commands.md", path: "/docs/reference/commands" },
+ {
+ source: "PROJECT_FILES.md",
+ target: "2.reference/2.project-file.md",
+ path: "/docs/reference/project-file",
+ },
+ { source: "LOCKFILE.md", target: "2.reference/3.lockfile.md", path: "/docs/reference/lockfile" },
+ { source: "PLUGINS.md", target: "3.extending/1.plugins.md", path: "/docs/extending/plugins" },
+ {
+ source: "ARCHITECTURE.md",
+ target: "4.internals/1.architecture.md",
+ path: "/docs/internals/architecture",
+ },
+];
+const pages = new Map(documents.map((document) => [document.source, document.path]));
+
+function link(target) {
+ const [file, anchor] = target.split("#", 2);
+ const suffix = anchor === undefined ? "" : `#${anchor}`;
+ if (file === "") return target;
+ const page = pages.get(file);
+ if (page) return `${page}${suffix}`;
+ if (file.startsWith("../")) return `${repository}/${file.slice(3)}${suffix}`;
+ return `${repository}/Documents/${file}${suffix}`;
+}
+
+function convert(text, source) {
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
+ const spdx = lines.filter((line) => line.startsWith("<!-- SPDX-"));
+ const body = lines.filter((line) => !line.startsWith("<!-- SPDX-"));
+ const titleIndex = body.findIndex((line) => line.startsWith("# "));
+ if (titleIndex < 0) throw new Error(`${source} has no title`);
+ const title = body[titleIndex].slice(2).replace(/`/g, "");
+ body.splice(0, titleIndex + 1);
+ while (body[0] === "") body.shift();
+ const firstParagraph = [];
+ for (const line of body) {
+ if (line === "") break;
+ firstParagraph.push(line);
+ }
+ const paragraph = firstParagraph
+ .join(" ")
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1")
+ .replace(/[`*]/g, "")
+ .replace(/"/g, "'");
+ // The first sentence describes the page to search engines.
+ const description = paragraph.match(/^.*?\.(?=\s|$)/)?.[0] ?? paragraph;
+ const converted = body
+ .join("\n")
+ .replace(/\]\(((?!https?:|mailto:|#)[^)\s]+)\)/g, (_, target) => `](${link(target)})`);
+ return [
+ "---",
+ `title: ${JSON.stringify(title)}`,
+ `description: ${JSON.stringify(description)}`,
+ `source: ${JSON.stringify(`${repository}/Documents/${source}`)}`,
+ "---",
+ "",
+ ...spdx,
+ "",
+ converted.trimEnd(),
+ "",
+ ].join("\n");
+}
+
+const root = resolve(import.meta.dirname, "../content/en-US/docs");
+for (const document of documents) {
+ const text = readFileSync(join(checkout, "Documents", document.source), "utf8");
+ const target = join(root, document.target);
+ mkdirSync(dirname(target), { recursive: true });
+ writeFileSync(target, convert(text, document.source));
+ console.log(`${document.source} -> content/en-US/docs/${document.target}`);
+}