#!/usr/bin/env node
import { readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { XMLParser, XMLValidator } from "fast-xml-parser";
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "@_",
preserveOrder: true,
});
function collectIds(value, ids) {
if (Array.isArray(value)) {
for (const entry of value) collectIds(entry, ids);
return;
}
if (!value || typeof value !== "object") return;
const attributes = value[":@"];
if (attributes && typeof attributes === "object" && typeof attributes["@_id"] === "string") {
ids.push(attributes["@_id"]);
}
for (const entry of Object.values(value)) collectIds(entry, ids);
}
function escapeXml(value) {
return value
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function replaceRootAttribute(openingTag, name, value) {
const attribute = new RegExp(`\\s${name}=(?:"[^"]*"|'[^']*')`, "i");
const withoutExisting = openingTag.replace(attribute, "");
return withoutExisting.replace(/>$/, ` ${name}="${escapeXml(value)}">`);
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
export function ensureSvgAccessibility(svg, { title, description, idBase }) {
const xmlResult = XMLValidator.validate(svg);
if (xmlResult !== true) throw new Error(`invalid XML: ${xmlResult.err.msg}`);
const titleId = `${idBase}-title`;
const descriptionId = `${idBase}-desc`;
const priorTitle = new RegExp(
`
]*\\bid=["']${escapeRegExp(titleId)}["'][^>]*>[\\s\\S]*?<\\/title>`,
"i"
);
const priorDescription = new RegExp(
`]*\\bid=["']${escapeRegExp(descriptionId)}["'][^>]*>[\\s\\S]*?<\\/desc>`,
"i"
);
const withoutPriorAccessibleName = svg.replace(priorTitle, "").replace(priorDescription, "");
const match = withoutPriorAccessibleName.match(/