mirror of https://github.com/theNewDynamic/gohugo-theme-ananke.git

Patrick Kollitsch
yesterday 3d7b46278db46d25cfa086478df22dbfb3ae8cf9
build(deps): update dependencies and fix migration issues
6 files modified
4029 ■■■■■ changed files
.github/workflows/quickstart.yml 4 ●●●●● patch | view | raw | blame | history
.gitignore 4 ●●●●● patch | view | raw | blame | history
package-lock.json 3699 ●●●●● patch | view | raw | blame | history
package.json 48 ●●●●● patch | view | raw | blame | history
scripts/synch-social-icons.ts 272 ●●●●● patch | view | raw | blame | history
tests/e2e/accessibility.spec.ts 2 ●●●●● patch | view | raw | blame | history
.github/workflows/quickstart.yml
@@ -20,13 +20,13 @@
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
        with:
          persist-credentials: false
          submodules: false
      - name: Set up Node.js
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e
        uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020
        with:
          node-version: "24"
.gitignore
@@ -6,3 +6,7 @@
# wireit
.wireit
# Local project-management working files
/PROJECT.md
/TODO.md
package-lock.json
Diff too large
package.json
@@ -25,32 +25,32 @@
    "hugo-module"
  ],
  "dependencies": {
    "cssnano": "8.0.1",
    "postcss": "8.5.15",
    "postcss-preset-env": "11.3.0",
        "cssnano": "9.0.5",
        "postcss": "8.5.28",
        "postcss-preset-env": "11.5.4",
    "tachyons": "4.12.0"
  },
  "devDependencies": {
    "@axe-core/playwright": "4.10.2",
    "@biomejs/biome": "2.4.16",
    "@github/markdownlint-github": "0.8.0",
    "@playwright/test": "1.60.0",
    "@release-it/conventional-changelog": "11.0.0",
    "@types/node": "25.9.1",
    "dotenv": "17.4.2",
    "lint-staged": "17.0.7",
    "lockfile-lint": "5.0.0",
    "markdownlint": "0.40.0",
    "markdownlint-cli2": "0.22.1",
        "@axe-core/playwright": "4.13.0",
        "@biomejs/biome": "2.5.14",
        "@github/markdownlint-github": "0.8.4",
        "@playwright/test": "1.63.0",
        "@release-it/conventional-changelog": "12.0.2",
        "@types/node": "26.6.3",
        "dotenv": "18.0.4",
        "lint-staged": "17.6.0",
        "lockfile-lint": "5.0.1",
        "markdownlint": "0.41.1",
        "markdownlint-cli2": "0.23.3",
    "markdownlint-rule-extended-ascii": "0.2.1",
    "markdownlint-rule-list-duplicates": "0.0.1",
    "markdownlint-rule-relative-links": "5.1.0",
        "markdownlint-rule-relative-links": "5.1.3",
    "markdownlint-rule-search-replace": "1.2.0",
    "markdownlint-rule-title-case-style": "0.4.3",
    "release-it": "20.2.0",
    "simple-git-hooks": "2.13.1",
    "typescript": "6.0.3",
    "yaml": "2.7.0"
        "release-it": "21.1.0",
        "simple-git-hooks": "2.14.0",
        "typescript": "7.0.2",
        "yaml": "2.9.1"
  },
  "scripts": {
    "hook:commit": "lint-staged --config .lintstagedrc.js",
@@ -84,5 +84,13 @@
    "pre-commit": "npm run hook:commit",
    "pre-push": "npm run hook:push"
  },
  "type": "module"
    "type": "module",
    "overrides": {
        "markdownlint-rule-title-case-style": {
            "markdownlint": "$markdownlint"
        }
    },
    "allowScripts": {
        "simple-git-hooks@2.14.0": true
    }
}
scripts/synch-social-icons.ts
@@ -27,9 +27,9 @@
 * - node src/scripts/update-social-icons.ts --json
 */
import { promises as fs } from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { promises as fs } from "node:fs";
import path from "node:path";
import process from "node:process";
type CliOptions = {
    dir: string;
@@ -45,15 +45,15 @@
};
type FileResultStatus =
    | 'same'
    | 'changed'
    | 'missing'
    | 'would-create'
    | 'created'
    | 'would-delete'
    | 'deleted'
    | 'updated'
    | 'error';
    | "same"
    | "changed"
    | "missing"
    | "would-create"
    | "created"
    | "would-delete"
    | "deleted"
    | "updated"
    | "error";
type FileResult = {
    fileName: string;
@@ -93,20 +93,20 @@
type ProcessingTask =
    | {
        kind: 'upstream';
            kind: "upstream";
        fileName: string;
    }
    | {
        kind: 'local-only';
            kind: "local-only";
        fileName: string;
    };
const DEFAULT_DIRECTORY = 'assets/ananke/socials';
const DEFAULT_BRANCH = 'develop';
const SIMPLE_ICONS_OWNER = 'simple-icons';
const SIMPLE_ICONS_REPO = 'simple-icons';
const DEFAULT_DIRECTORY = "assets/ananke/socials";
const DEFAULT_BRANCH = "develop";
const SIMPLE_ICONS_OWNER = "simple-icons";
const SIMPLE_ICONS_REPO = "simple-icons";
const SIMPLE_ICONS_RAW_BASE =
    'https://raw.githubusercontent.com/simple-icons/simple-icons';
    "https://raw.githubusercontent.com/simple-icons/simple-icons";
const EXIT_SUCCESS = 0;
const EXIT_FAILURE = 1;
@@ -115,9 +115,10 @@
 * Print CLI help.
 */
function printHelp(): void {
    const scriptName = path.basename(process.argv[1] ?? 'update-social-icons.ts');
    const scriptName = path.basename(process.argv[1] ?? "update-social-icons.ts");
    console.log(`
    console.log(
        `
Usage:
  ${scriptName} [options]
@@ -167,7 +168,8 @@
  * deleted       local file missing upstream and was deleted
  * updated       local file was replaced because --apply was used
  * error         processing failed
`.trim());
`.trim(),
    );
}
/**
@@ -192,81 +194,84 @@
    for (let index = 0; index < argv.length; index += 1) {
        const arg = argv[index];
        if (arg === undefined) {
            continue;
        }
        if (arg === '--help') {
        if (arg === "--help") {
            options.help = true;
            continue;
        }
        if (arg === '--apply') {
        if (arg === "--apply") {
            options.apply = true;
            continue;
        }
        if (arg === '--delete') {
        if (arg === "--delete") {
            options.deleteMissing = true;
            continue;
        }
        if (arg === '--only-changed') {
        if (arg === "--only-changed") {
            options.onlyChanged = true;
            continue;
        }
        if (arg === '--json') {
        if (arg === "--json") {
            options.json = true;
            continue;
        }
        if (arg === '--verbose') {
        if (arg === "--verbose") {
            options.verbose = true;
            continue;
        }
        if (arg === '--all') {
        if (arg === "--all") {
            options.all = true;
            continue;
        }
        if (arg === '--dir') {
        if (arg === "--dir") {
            const value = argv[index + 1];
            if (!value) {
                throw new Error('Missing value for --dir');
                throw new Error("Missing value for --dir");
            }
            options.dir = value;
            index += 1;
            continue;
        }
        if (arg.startsWith('--dir=')) {
            options.dir = arg.slice('--dir='.length);
        if (arg.startsWith("--dir=")) {
            options.dir = arg.slice("--dir=".length);
            continue;
        }
        if (arg === '--branch') {
        if (arg === "--branch") {
            const value = argv[index + 1];
            if (!value) {
                throw new Error('Missing value for --branch');
                throw new Error("Missing value for --branch");
            }
            options.branch = value;
            index += 1;
            continue;
        }
        if (arg.startsWith('--branch=')) {
            options.branch = arg.slice('--branch='.length);
        if (arg.startsWith("--branch=")) {
            options.branch = arg.slice("--branch=".length);
            continue;
        }
        if (arg === '--concurrency') {
        if (arg === "--concurrency") {
            const value = argv[index + 1];
            if (!value) {
                throw new Error('Missing value for --concurrency');
                throw new Error("Missing value for --concurrency");
            }
            const parsed = Number.parseInt(value, 10);
            if (!Number.isInteger(parsed) || parsed < 1) {
                throw new Error('--concurrency must be a positive integer');
                throw new Error("--concurrency must be a positive integer");
            }
            options.concurrency = parsed;
@@ -274,11 +279,11 @@
            continue;
        }
        if (arg.startsWith('--concurrency=')) {
            const value = arg.slice('--concurrency='.length);
        if (arg.startsWith("--concurrency=")) {
            const value = arg.slice("--concurrency=".length);
            const parsed = Number.parseInt(value, 10);
            if (!Number.isInteger(parsed) || parsed < 1) {
                throw new Error('--concurrency must be a positive integer');
                throw new Error("--concurrency must be a positive integer");
            }
            options.concurrency = parsed;
@@ -314,7 +319,7 @@
    return entries
        .filter((entry) => entry.isFile())
        .map((entry) => entry.name)
        .filter((fileName) => fileName.toLowerCase().endsWith('.svg'))
        .filter((fileName) => fileName.toLowerCase().endsWith(".svg"))
        .sort((left, right) => left.localeCompare(right));
}
@@ -328,13 +333,13 @@
    const apiUrl = new URL(
        `https://api.github.com/repos/${encodeURIComponent(SIMPLE_ICONS_OWNER)}/${encodeURIComponent(SIMPLE_ICONS_REPO)}/contents/icons`,
    );
    apiUrl.searchParams.set('ref', branch);
    apiUrl.searchParams.set("ref", branch);
    const response = await fetch(apiUrl.toString(), {
        method: 'GET',
        method: "GET",
        headers: {
            Accept: 'application/vnd.github+json',
            'User-Agent': 'davidsneighbour-social-icon-updater',
            Accept: "application/vnd.github+json",
            "User-Agent": "davidsneighbour-social-icon-updater",
        },
    });
@@ -347,19 +352,21 @@
    const data: unknown = await response.json();
    if (!Array.isArray(data)) {
        throw new Error('Unexpected response while retrieving upstream icon listing');
        throw new Error(
            "Unexpected response while retrieving upstream icon listing",
        );
    }
    const fileNames = data
        .flatMap((entry: unknown) => {
            if (
                typeof entry === 'object' &&
                typeof entry === "object" &&
                entry !== null &&
                'type' in entry &&
                'name' in entry &&
                entry.type === 'file' &&
                typeof entry.name === 'string' &&
                entry.name.toLowerCase().endsWith('.svg')
                "type" in entry &&
                "name" in entry &&
                entry.type === "file" &&
                typeof entry.name === "string" &&
                entry.name.toLowerCase().endsWith(".svg")
            ) {
                return [entry.name];
            }
@@ -381,10 +388,10 @@
 */
async function fetchRemoteSvg(remoteUrl: string): Promise<string | null> {
    const response = await fetch(remoteUrl, {
        method: 'GET',
        method: "GET",
        headers: {
            Accept: 'image/svg+xml,text/plain;q=0.9,*/*;q=0.8',
            'User-Agent': 'davidsneighbour-social-icon-updater',
            Accept: "image/svg+xml,text/plain;q=0.9,*/*;q=0.8",
            "User-Agent": "davidsneighbour-social-icon-updater",
        },
    });
@@ -393,7 +400,9 @@
    }
    if (!response.ok) {
        throw new Error(`Request failed with HTTP ${response.status} for ${remoteUrl}`);
        throw new Error(
            `Request failed with HTTP ${response.status} for ${remoteUrl}`,
        );
    }
    return await response.text();
@@ -434,8 +443,8 @@
                fileName,
                localPath,
                remoteUrl,
                status: 'missing',
                message: 'No matching upstream file found',
                status: "missing",
                message: "No matching upstream file found",
            };
        }
@@ -447,31 +456,31 @@
                    fileName,
                    localPath,
                    remoteUrl,
                    status: 'would-create',
                    message: 'Upstream file exists and would be created locally',
                    status: "would-create",
                    message: "Upstream file exists and would be created locally",
                };
            }
            await fs.writeFile(localPath, remoteContent, 'utf8');
            await fs.writeFile(localPath, remoteContent, "utf8");
            return {
                fileName,
                localPath,
                remoteUrl,
                status: 'created',
                message: 'Upstream file existed and was created locally',
                status: "created",
                message: "Upstream file existed and was created locally",
            };
        }
        const localContent = await fs.readFile(localPath, 'utf8');
        const localContent = await fs.readFile(localPath, "utf8");
        if (localContent === remoteContent) {
            return {
                fileName,
                localPath,
                remoteUrl,
                status: 'same',
                message: 'Local file matches upstream',
                status: "same",
                message: "Local file matches upstream",
            };
        }
@@ -480,29 +489,31 @@
                fileName,
                localPath,
                remoteUrl,
                status: 'changed',
                message: 'Upstream file differs from local file',
                status: "changed",
                message: "Upstream file differs from local file",
            };
        }
        await fs.writeFile(localPath, remoteContent, 'utf8');
        await fs.writeFile(localPath, remoteContent, "utf8");
        return {
            fileName,
            localPath,
            remoteUrl,
            status: 'updated',
            message: 'Local file replaced with upstream version',
            status: "updated",
            message: "Local file replaced with upstream version",
        };
    } catch (error: unknown) {
        const message =
            error instanceof Error ? error.message : 'Unknown error while processing file';
            error instanceof Error
                ? error.message
                : "Unknown error while processing file";
        return {
            fileName,
            localPath,
            remoteUrl,
            status: 'error',
            status: "error",
            message,
        };
    }
@@ -534,8 +545,8 @@
                fileName,
                localPath,
                remoteUrl,
                status: 'missing',
                message: 'No matching upstream file found',
                status: "missing",
                message: "No matching upstream file found",
            };
        }
@@ -544,8 +555,8 @@
                fileName,
                localPath,
                remoteUrl,
                status: 'would-delete',
                message: 'No matching upstream file found; local file would be deleted',
                status: "would-delete",
                message: "No matching upstream file found; local file would be deleted",
            };
        }
@@ -555,18 +566,20 @@
            fileName,
            localPath,
            remoteUrl,
            status: 'deleted',
            message: 'No matching upstream file found; local file deleted',
            status: "deleted",
            message: "No matching upstream file found; local file deleted",
        };
    } catch (error: unknown) {
        const message =
            error instanceof Error ? error.message : 'Unknown error while processing file';
            error instanceof Error
                ? error.message
                : "Unknown error while processing file";
        return {
            fileName,
            localPath,
            remoteUrl,
            status: 'error',
            status: "error",
            message,
        };
    }
@@ -587,7 +600,7 @@
        return true;
    }
    return status !== 'same';
    return status !== "same";
}
/**
@@ -598,18 +611,20 @@
 */
function printResult(result: FileResult, verbose: boolean): void {
    const prefixByStatus: Record<FileResultStatus, string> = {
        same: 'SAME',
        changed: 'CHANGED',
        missing: 'MISS',
        'would-create': 'WOULD-CREATE',
        created: 'CREATED',
        'would-delete': 'WOULD-DELETE',
        deleted: 'DELETED',
        updated: 'UPDATED',
        error: 'ERR',
        same: "SAME",
        changed: "CHANGED",
        missing: "MISS",
        "would-create": "WOULD-CREATE",
        created: "CREATED",
        "would-delete": "WOULD-DELETE",
        deleted: "DELETED",
        updated: "UPDATED",
        error: "ERR",
    };
    console.log(`${prefixByStatus[result.status]} ${result.fileName} - ${result.message}`);
    console.log(
        `${prefixByStatus[result.status]} ${result.fileName} - ${result.message}`,
    );
    if (verbose) {
        console.log(`  local:  ${result.localPath}`);
@@ -625,15 +640,17 @@
 */
function buildSummary(results: FileResult[]): Summary {
    return {
        same: results.filter((entry) => entry.status === 'same').length,
        changed: results.filter((entry) => entry.status === 'changed').length,
        missing: results.filter((entry) => entry.status === 'missing').length,
        wouldCreate: results.filter((entry) => entry.status === 'would-create').length,
        created: results.filter((entry) => entry.status === 'created').length,
        wouldDelete: results.filter((entry) => entry.status === 'would-delete').length,
        deleted: results.filter((entry) => entry.status === 'deleted').length,
        updated: results.filter((entry) => entry.status === 'updated').length,
        errors: results.filter((entry) => entry.status === 'error').length,
        same: results.filter((entry) => entry.status === "same").length,
        changed: results.filter((entry) => entry.status === "changed").length,
        missing: results.filter((entry) => entry.status === "missing").length,
        wouldCreate: results.filter((entry) => entry.status === "would-create")
            .length,
        created: results.filter((entry) => entry.status === "created").length,
        wouldDelete: results.filter((entry) => entry.status === "would-delete")
            .length,
        deleted: results.filter((entry) => entry.status === "deleted").length,
        updated: results.filter((entry) => entry.status === "updated").length,
        errors: results.filter((entry) => entry.status === "error").length,
    };
}
@@ -643,8 +660,8 @@
 * @param summary Summary counts.
 */
function printSummary(summary: Summary): void {
    console.log('');
    console.log('Summary');
    console.log("");
    console.log("Summary");
    console.log(`  same:          ${summary.same}`);
    console.log(`  changed:       ${summary.changed}`);
    console.log(`  updated:       ${summary.updated}`);
@@ -668,14 +685,16 @@
    total: number,
    active: number,
): void {
    process.stdout.write(`\rProcessing ${processed}/${total} (active: ${active})`);
    process.stdout.write(
        `\rProcessing ${processed}/${total} (active: ${active})`,
    );
}
/**
 * Clear the progress line from the terminal.
 */
function clearProgressLine(): void {
    process.stdout.write('\r\x1b[K');
    process.stdout.write("\r\x1b[K");
}
/**
@@ -713,7 +732,8 @@
            active += 1;
            try {
                const result = await worker(items[currentIndex]);
                // Index is bounds-checked above; TInput itself may include undefined.
                const result = await worker(items[currentIndex] as TInput);
                results[currentIndex] = result;
            } finally {
                active -= 1;
@@ -752,7 +772,9 @@
    const stats = await fs.stat(targetDirectory).catch(() => null);
    if (!stats || !stats.isDirectory()) {
        throw new Error(`Directory does not exist or is not a directory: ${targetDirectory}`);
        throw new Error(
            `Directory does not exist or is not a directory: ${targetDirectory}`,
        );
    }
    const localFiles = await getLocalSvgFiles(targetDirectory);
@@ -781,7 +803,7 @@
        for (const fileName of upstreamFiles) {
            tasks.push({
                kind: 'upstream',
                kind: "upstream",
                fileName,
            });
        }
@@ -789,7 +811,7 @@
        for (const fileName of localFiles) {
            if (!upstreamSet.has(fileName)) {
                tasks.push({
                    kind: 'local-only',
                    kind: "local-only",
                    fileName,
                });
            }
@@ -822,7 +844,7 @@
        for (const fileName of localFiles) {
            tasks.push({
                kind: 'upstream',
                kind: "upstream",
                fileName,
            });
        }
@@ -836,37 +858,39 @@
            console.log(`Found ${upstreamFiles.length} upstream SVG file(s).`);
            console.log(`Found ${localFiles.length} local SVG file(s).`);
        } else {
            console.log(`Checking ${localFiles.length} local SVG file(s) in ${targetDirectory}`);
            console.log(
                `Checking ${localFiles.length} local SVG file(s) in ${targetDirectory}`,
            );
        }
        console.log(`Using simple-icons branch: ${options.branch}`);
        console.log(`Concurrency: ${options.concurrency}`);
        console.log(
            options.apply
                ? 'Apply mode enabled. Changed files will be written.'
                : 'Dry run mode enabled. No files will be written.',
                ? "Apply mode enabled. Changed files will be written."
                : "Dry run mode enabled. No files will be written.",
        );
        if (options.deleteMissing) {
            console.log(
                options.apply
                    ? 'Delete mode enabled. Local files missing upstream will be deleted.'
                    : 'Delete mode enabled. Local files missing upstream will be reported as would-delete.',
                    ? "Delete mode enabled. Local files missing upstream will be deleted."
                    : "Delete mode enabled. Local files missing upstream will be reported as would-delete.",
            );
        }
        if (options.onlyChanged) {
            console.log('Only changed results will be shown.');
            console.log("Only changed results will be shown.");
        }
        console.log('');
        console.log("");
    }
    const results = await runTasksWithConcurrency<ProcessingTask, FileResult>({
        items: tasks,
        concurrency: options.concurrency,
        worker: async (task): Promise<FileResult> => {
            if (task.kind === 'upstream') {
            if (task.kind === "upstream") {
                const result = await processUpstreamFile(
                    targetDirectory,
                    task.fileName,
@@ -874,7 +898,7 @@
                    options.apply,
                );
                if (result.status === 'missing' && !options.all) {
                if (result.status === "missing" && !options.all) {
                    return await processLocalOnlyFile(
                        targetDirectory,
                        task.fileName,
@@ -938,13 +962,13 @@
        printSummary(summary);
    }
    const hasErrors = results.some((entry) => entry.status === 'error');
    const hasErrors = results.some((entry) => entry.status === "error");
    process.exit(hasErrors ? EXIT_FAILURE : EXIT_SUCCESS);
}
main().catch((error: unknown) => {
    const message =
        error instanceof Error ? error.message : 'Unknown fatal error';
        error instanceof Error ? error.message : "Unknown fatal error";
    console.error(`Fatal error: ${message}`);
    process.exit(EXIT_FAILURE);
});
tests/e2e/accessibility.spec.ts
@@ -1,4 +1,4 @@
import AxeBuilder from "@axe-core/playwright";
import { AxeBuilder } from "@axe-core/playwright";
import { expect, test } from "@playwright/test";
/**