diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml
index 58559c43..876dcd00 100644
--- a/.github/workflows/website.yml
+++ b/.github/workflows/website.yml
@@ -26,6 +26,14 @@ jobs:
- name: š„ Clone and checkout repository
uses: actions/checkout@v3
+ # We can remove this step once `ubuntu-latest` has Node.js 22 or newer for its native TypeScript support. See:
+ # https://github.com/actions/runner-images?tab=readme-ov-file#available-images
+ # https://nodejs.org/en/learn/typescript/run-natively
+ - name: š¦ Install the latest Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "latest"
+
- name: šø Install Zola
uses: taiki-e/install-action@v2
with:
@@ -77,7 +85,8 @@ jobs:
MODE: prod
run: |
cd website
- npm run install-fonts
+ npm ci
+ npm run lint
zola --config config.toml build --minify
- name: š¤ Publish to Cloudflare Pages
diff --git a/website/build-scripts/generate-editor-structure.js b/website/.build-scripts/generate-editor-structure.ts
similarity index 73%
rename from website/build-scripts/generate-editor-structure.js
rename to website/.build-scripts/generate-editor-structure.ts
index a2d9d2ef..6664c084 100644
--- a/website/build-scripts/generate-editor-structure.js
+++ b/website/.build-scripts/generate-editor-structure.ts
@@ -1,21 +1,12 @@
-const fs = require("fs");
-const path = require("path");
+/* eslint-disable no-console */
-/**
- * Escapes characters that have special meaning in HTML.
- * @param {string} text The text to escape.
- * @returns {string} The escaped text.
- */
-function escapeHtml(text) {
- return text.replace(//g, ">");
-}
+import fs from "fs";
+import path from "path";
-/**
- * Parses a single line of the input text.
- * @param {string} line The line to parse.
- * @returns {{ level: number, text: string, link: string | undefined }}
- */
-function parseLine(line) {
+type Entry = { level: number; text: string; link: string | undefined };
+
+/// Parses a single line of the input text.
+function parseLine(line: string) {
const linkRegex = /`([^`]+)`$/;
const linkMatch = line.match(linkRegex);
let link = undefined;
@@ -25,7 +16,10 @@ function parseLine(line) {
link = `https://github.com/GraphiteEditor/Graphite/blob/master/${filePath}`;
}
- const textContent = line.replace(/^[\sāāāā]*/, "").replace(linkRegex, "").trim();
+ const textContent = line
+ .replace(/^[\sāāāā]*/, "")
+ .replace(linkRegex, "")
+ .trim();
const indentation = line.indexOf(textContent);
// Each level of indentation is 4 characters.
const level = Math.floor(indentation / 4);
@@ -33,14 +27,8 @@ function parseLine(line) {
return { level, text: textContent, link };
}
-/**
- * Recursively builds the HTML list from the parsed nodes.
- * @param {Array} nodes The array of parsed node objects.
- * @param {number} currentIndex The current index in the nodes array.
- * @param {number} currentLevel The current indentation level.
- * @returns {{html: string, nextIndex: number}}
- */
-function buildHtmlList(nodes, currentIndex, currentLevel) {
+/// Recursively builds the HTML list from the parsed nodes.
+function buildHtmlList(nodes: Entry[], currentIndex: number, currentLevel: number) {
if (currentIndex >= nodes.length) {
return { html: "", nextIndex: currentIndex };
}
@@ -68,7 +56,7 @@ function buildHtmlList(nodes, currentIndex, currentLevel) {
} else {
escapedText = [escapeHtml(node.text)];
}
-
+
let role = "message";
if (node.link) role = "subsystem";
else if (hasDeeperChildren) role = "submessage";
@@ -76,8 +64,10 @@ function buildHtmlList(nodes, currentIndex, currentLevel) {
const partOfMessageFromNamingConvention = ["Message", "MessageHandler", "MessageContext"].some((suffix) => node.text.replace(/(.*)<.*>/g, "$1").endsWith(suffix));
const partOfMessageViolatesNamingConvention = node.link && !partOfMessageFromNamingConvention;
- const violatesNamingConvention = partOfMessageViolatesNamingConvention ? "(violates naming convention ā should end with 'Message', 'MessageHandler', or 'MessageContext')" : "";
-
+ const violatesNamingConvention = partOfMessageViolatesNamingConvention
+ ? "(violates naming convention ā should end with 'Message', 'MessageHandler', or 'MessageContext')"
+ : "";
+
if (hasDirectChildren) {
html += `
${escapedText}${linkHtml}${violatesNamingConvention}`;
const childResult = buildHtmlList(nodes, i + 1, node.level + 1);
@@ -96,12 +86,16 @@ function buildHtmlList(nodes, currentIndex, currentLevel) {
return { html, nextIndex: i };
}
+function escapeHtml(text: string) {
+ return text.replace(//g, ">");
+}
+
const inputFile = process.argv[2];
const outputFile = process.argv[3];
if (!inputFile || !outputFile) {
console.error("Error: Please provide the input text and output HTML file paths as arguments.");
- console.log("Usage: node generate-editor-structure.js