core/formatters/config.xml.formatter.ts
core/formatters/config.xml.formatter.ts is a file in GovLab Quality. 45 lines of code and 14 definitions.
import type { XmlElement } from "#types/emitter.types";
import { defineFormatter } from "#core/registries/formatter.registry";
import { xmlRulesetTree } from "#core/converters/emitter.xml.converter";
const XML_INDENT = 2;
const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>';
const XML_ESCAPES = new Map<string, string>([
['"', """],
["&", "&"],
["<", "<"],
[">", ">"],
]);
const escapeXml = function escapeXml(text: string): string {
let escaped = "";
for (const ch of text) {
escaped += XML_ESCAPES.get(ch) ?? ch;
}
return escaped;
};
const renderAttrs = function renderAttrs(attrs: Record<string, string> | undefined): string {
return Object.entries(attrs ?? {})
.map(([key, value]) => ` ${key}="${escapeXml(value)}"`)
.join("");
};
const renderElement = function renderElement(element: XmlElement, indent: number): string[] {
const pad = " ".repeat(indent);
const attrs = renderAttrs(element.attrs);
const children = element.children ?? [];
const text = element.text ?? "";
if (children.length === 0 && text === "") {
return [`${pad}<${element.tag}${attrs}/>`];
}
if (children.length === 0) {
return [`${pad}<${element.tag}${attrs}>${escapeXml(text)}</${element.tag}>`];
}
const childLines = children.flatMap((child) => renderElement(child, indent + XML_INDENT));
return [`${pad}<${element.tag}${attrs}>`, ...childLines, `${pad}</${element.tag}>`];
};
export const serializeXml = function serializeXml(root: XmlElement, doctype?: string): string {
const head = typeof doctype === "string" && doctype.length > 0 ? [XML_DECLARATION, doctype] : [XML_DECLARATION];
return `${[...head, ...renderElement(root, 0)].join("\n")}\n`;
};
defineFormatter({
format: "xml",
render: (input) => serializeXml(xmlRulesetTree(input.descriptor, input.concepts), input.descriptor.xmlDoctype),
});