+
+/**
+ * Returns the provided value escaped such that it is usable in a
+ * double‐quoted attribute value string.
+ */
+const toAttributeValue = (value) =>
+ `${value}`.replaceAll(/[\x22\x26\x3C\x3E]/gu, ($) => {
+ switch ($) {
+ case "\x26":
+ return "&";
+ case "\x22":
+ return """;
+ case "\x3C":
+ return "<";
+ case "\x3E":
+ return ">";
+ }
+ });
+
+/** Returns an X·M·L start tag derived from this.*/
+const toStartTag = function () {
+ const name = `${this.name}`;
+ const namespace = `${this.namespace ?? ""}`;
+ const attributes = Object(this.attributes);
+ return "".concat(
+ ...function* () {
+ yield `<${name}`;
+ if (namespace) {
+ // A namespace was specified.
+ const colonIndex = name.indexOf(":");
+ if (colonIndex != -1) {
+ // The node name has a prefix.
+ yield ` xmlns:${name.substring(0, colonIndex)}="${
+ toAttributeValue(namespace)
+ }"`;
+ } else {
+ // The node name is unprefixed.
+ yield ` xmlns="${toAttributeValue(namespace)}"`;
+ }
+ } else {
+ // No namespace was specified.
+ /* do nothing */
+ }
+ for (const [attName, attValue] of Object.entries(attributes)) {
+ // Iterate over attributes and serialize them.
+ yield ` ${attName}="${toAttributeValue(attValue)}"`;
+ }
+ yield ">";
+ }(),
+ );
+};
+
+/**
+ * Returns a node name derived from the provided Ecmascript property
+ * name by lowercasing uppercase ascii letters and prefixing them with
+ * a hyphen.
+ */
+const toNodeName = (ecmascriptName) =>
+ "".concat(
+ ...function* () {
+ for (const character of ecmascriptName) {
+ yield /[A-Z]/u.test(character)
+ ? `-${character.toLowerCase()}`
+ : character;
+ }
+ }(),
+ );