@office-open/xml 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/parse.ts","../src/stringify.ts","../src/stringify-element.ts","../src/escape.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/parse.ts","../src/stringify.ts","../src/stringify-element.ts","../src/escape.ts"],"mappings":";;iBAgBgB,YAAY;iBAgBZ,gBAAgB;iBA4ChB,MAAM,mBAAmB,UAAU,eAAe;iBA0XlD,gBAAgB,cAAc;;;iBC/b9B,UAAU,IAAI,SAAS,UAAU;;;iBCIjC,iBAAiB,IAAI;;;iBCPrB,UAAU;iBA6BV,MAAM,QAAQ;iBAsBd,SAAS,QAAQ;iBAejB,iBAAiB,aAAa;iBAoB9B,QACd,cACA,aAAa,SAAS,wDACtB"}
|
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ const ENTITY_MAP = {
|
|
|
8
8
|
"'": "'"
|
|
9
9
|
};
|
|
10
10
|
const ENTITY_PATTERN = /&(?:amp|lt|gt|quot|apos|#x[0-9a-fA-F]+|#[0-9]+);/g;
|
|
11
|
+
const HAS_CONTENT = /\S/;
|
|
11
12
|
function unescapeXml(str) {
|
|
12
13
|
if (str.indexOf("&") === -1) return str;
|
|
13
14
|
return str.replace(ENTITY_PATTERN, (match) => {
|
|
@@ -74,7 +75,7 @@ function parse(xmlString, options) {
|
|
|
74
75
|
if (trim) text = text.trim();
|
|
75
76
|
if (ignoreText) continue;
|
|
76
77
|
if (text.length > 0) {
|
|
77
|
-
if (captureSpaces ||
|
|
78
|
+
if (captureSpaces || (trim ? true : HAS_CONTENT.test(text)) || isPreserveContext(stack)) {
|
|
78
79
|
const parent = stack[stack.length - 1];
|
|
79
80
|
const elements = parent.elements;
|
|
80
81
|
const last = elements === void 0 ? void 0 : elements[elements.length - 1];
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["stringifyChildren"],"sources":["../src/parse.ts","../src/escape.ts","../src/stringify.ts","../src/stringify-element.ts"],"sourcesContent":["import type { Element, ParseOptions } from \"./types\";\n\nconst ENTITY_MAP: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \""\": '\"',\n \"'\": \"'\",\n};\n// Matches the five named entities plus numeric character references\n// (A decimal, B hex).\nconst ENTITY_PATTERN = /&(?:amp|lt|gt|quot|apos|#x[0-9a-fA-F]+|#[0-9]+);/g;\n\nexport function unescapeXml(str: string): string {\n // Fast path: entities all start with '&', and OOXML parts overwhelmingly\n // contain none (a 63 MB worksheet measured zero occurrences). The regex\n // scan + replace setup per call showed up to ~12% of large-file parse\n // profiles, so gate it on the sentinel byte.\n if (str.indexOf(\"&\") === -1) return str;\n return str.replace(ENTITY_PATTERN, (match) => {\n if (ENTITY_MAP[match] !== undefined) return ENTITY_MAP[match];\n // Numeric character reference: strip \"&#\" prefix and \";\" suffix.\n const body = match.slice(2, -1);\n const code =\n body[0] === \"x\" || body[0] === \"X\" ? parseInt(body.slice(1), 16) : parseInt(body, 10);\n return Number.isFinite(code) && code >= 0 ? String.fromCodePoint(code) : match;\n });\n}\n\nexport function nativeTypeValue(value: string): string | number | boolean {\n if (value === \"\") return value;\n // Digit-only fast path — plain integers are the most common numeric shape\n // in OOXML (row/column indexes, sizes, ids). At most 15 digits is always\n // exact in float64, so the scan replaces the Number() + String(n)\n // round-trip (the String(n) side allocates) without re-checking losslessness.\n const neg = value.charCodeAt(0) === 0x2d; /* - */\n const start = neg ? 1 : 0;\n const digits = value.length - start;\n if (digits > 0 && digits <= 15) {\n // Leading zeros (\"00992297\") must stay strings; only a lone \"0\" passes.\n // \"-0\" falls through too — Number coerces it to -0 whose String() is \"0\",\n // so the slow path must keep it a string.\n const head = value.charCodeAt(start);\n if (head !== 0x30 || (!neg && digits === 1)) {\n let n = 0;\n let allDigits = true;\n for (let i = start; i < value.length; i++) {\n const c = value.charCodeAt(i);\n if (c < 0x30 || c > 0x39) {\n allDigits = false;\n break;\n }\n n = n * 10 + (c - 0x30);\n }\n if (allDigits) return neg ? -n : n;\n }\n }\n const n = Number(value);\n // Only coerce when lossless: leading zeros (\"00992297\"), exponential\n // notation (\"1e5\"), and a leading sign (\"+5\") must stay strings so hex-like\n // values (rsid, color) survive parse → stringify round-trips byte-exact.\n if (!isNaN(n) && String(n) === value) return n;\n // Length gate before toLowerCase: every non-numeric attribute value paid\n // two throwaway lowercase strings (cell refs, format names, ids …), which\n // parse profiles attributed to GC pressure.\n if (value.length === 4 || value.length === 5) {\n const lower = value.toLowerCase();\n if (lower === \"true\") return true;\n if (lower === \"false\") return false;\n }\n return value;\n}\n\nexport function parse(xmlString: string, options?: ParseOptions): Element {\n const captureSpaces = options?.captureSpacesBetweenElements ?? false;\n const trim = options?.trim ?? false;\n const ignoreDeclaration = options?.ignoreDeclaration ?? false;\n const ignoreText = options?.ignoreText ?? false;\n const ignoreComment = options?.ignoreComment ?? false;\n const ignoreCdata = options?.ignoreCdata ?? false;\n const ignoreDoctype = options?.ignoreDoctype ?? false;\n const nativeTypeAttributes = options?.nativeTypeAttributes ?? false;\n // Lookup set for deferred elements (raw inner-XML capture). Undefined when\n // the option is absent so the common path pays one truthiness check.\n const deferSet =\n options?.deferElements !== undefined && options.deferElements.length > 0\n ? new Set(options.deferElements)\n : undefined;\n\n // Namespace normalization (see ParseOptions.normalizeNamespaces): in-scope\n // prefix → canonical-prefix bindings, copy-on-write per element that carries\n // xmlns declarations. `prefixes === undefined` means nothing is bound yet\n // (the common subtree shares the root layer object untouched).\n const nsTable =\n options?.normalizeNamespaces !== undefined\n ? new Map(Object.entries(options.normalizeNamespaces))\n : undefined;\n interface NsLayer {\n prefixes: Map<string, string> | undefined;\n /** Canonical form of the default namespace; undefined = none/unknown. */\n defaultCanonical: string | undefined;\n }\n const nsRootLayer: NsLayer = { prefixes: undefined, defaultCanonical: undefined };\n const nsStack: NsLayer[] = [nsRootLayer];\n\n const result: Element = {};\n const stack: Element[] = [result];\n\n let i = 0;\n const len = xmlString.length;\n\n while (i < len) {\n // Text node: read up to the next '<'. Pure-whitespace nodes (indentation)\n // are dropped below unless captureSpaces is on, but leading/trailing\n // spaces of nodes that have content are preserved.\n if (xmlString.charCodeAt(i) !== 0x3c /* < */) {\n const start = i;\n // Single-char indexOf runs as a native memchr scan — far faster than a\n // JS byte-by-byte loop (the outer check already guarantees i is not '<').\n const lt = xmlString.indexOf(\"<\", i);\n i = lt === -1 ? len : lt;\n let text = unescapeXml(xmlString.slice(start, i));\n if (trim) text = text.trim();\n if (ignoreText) continue;\n if (text.length > 0) {\n if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack)) {\n // Text-node hot path, inlined from addField(\"text\"): one lookup of\n // the last child covers both the adjacent-merge case (same shape as\n // addField — a split CDATA/text run must reassemble) and the fresh\n // push. addField stays for the cold node types.\n const parent = stack[stack.length - 1]!;\n const elements = parent.elements;\n const last = elements === undefined ? undefined : elements[elements.length - 1];\n if (last !== undefined && last.type === \"text\") {\n last.text = (last.text as string) + text;\n } else {\n const node: Element = { type: \"text\", text };\n if (elements === undefined) {\n parent.elements = [node];\n } else {\n elements.push(node);\n }\n }\n }\n }\n continue;\n }\n\n i++;\n\n // <? processing instruction / declaration\n if (xmlString.charCodeAt(i) === 0x3f /* ? */) {\n const end = xmlString.indexOf(\"?>\", i + 1);\n if (end === -1) break;\n const body = xmlString.slice(i + 1, end);\n i = end + 2;\n\n const xmlMatch = body.match(/^xml\\s+(.*)$/s);\n if (xmlMatch) {\n if (!ignoreDeclaration) {\n if (!result.declaration) {\n result.declaration = {};\n }\n const attrs = parseAttributes(xmlMatch[1] ?? \"\");\n if (nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n result.declaration.attributes = attrs;\n }\n }\n continue;\n }\n\n // !-- comment\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 3) === \"!--\") {\n const end = xmlString.indexOf(\"-->\", i + 3);\n if (end === -1) break;\n const comment = xmlString.slice(i + 3, end);\n i = end + 3;\n if (!ignoreComment) {\n if (trim) addField(peek(stack), \"comment\", comment.trim());\n else addField(peek(stack), \"comment\", comment);\n }\n continue;\n }\n\n // ![CDATA[\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 8) === \"![CDATA[\") {\n const end = xmlString.indexOf(\"]]>\", i + 8);\n if (end === -1) break;\n const cdata = xmlString.slice(i + 8, end);\n i = end + 3;\n if (!ignoreCdata) {\n if (trim) addField(peek(stack), \"cdata\", cdata.trim());\n else addField(peek(stack), \"cdata\", cdata);\n }\n continue;\n }\n\n // <!DOCTYPE\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 9) === \"!DOCTYPE\") {\n const end = xmlString.indexOf(\">\", i + 9);\n if (end === -1) break;\n const doctype = xmlString.slice(i + 9, end).trim();\n i = end + 1;\n if (!ignoreDoctype) {\n addField(peek(stack), \"doctype\", doctype);\n }\n continue;\n }\n\n // </ closing tag\n if (xmlString.charCodeAt(i) === 0x2f /* / */) {\n const end = xmlString.indexOf(\">\", i + 1);\n if (end === -1) break;\n i = end + 1;\n stack.pop();\n if (nsTable !== undefined) nsStack.pop();\n continue;\n }\n\n // < opening tag\n const tagNameEnd = findTagNameEnd(xmlString, i);\n const tagName = xmlString.slice(i, tagNameEnd);\n let pos = tagNameEnd;\n\n // Namespace bindings for this element: inherit the parent layer; the\n // attribute scan clones it on the first xmlns declaration it meets.\n const nsParent = nsTable !== undefined ? nsStack[nsStack.length - 1]! : undefined;\n let nsPrefixes = nsParent?.prefixes;\n let nsDefault = nsParent?.defaultCanonical;\n let nsDeclared = false;\n\n // Attribute scan, inlined from parseAttributesFromXml: called once per\n // opening tag, the `{ attrs, pos }` wrapper was one allocation per element.\n // `attrs` is allocated lazily on the first attribute — tags without\n // attributes (the majority in data-heavy parts) must not pay a record\n // allocation.\n let attrs: Record<string, string> | undefined;\n while (pos < len) {\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n if (pos >= len || xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) {\n break;\n }\n\n const nameStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== 0x3d) {\n if (xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) break;\n pos++;\n }\n // XML's Eq production allows whitespace around '='; trim it off the\n // name so `Target = \"…\"` resolves like `Target=\"…\"`.\n const name = xmlString.slice(nameStart, pos).trim();\n\n if (xmlString.charCodeAt(pos) !== 0x3d) break;\n pos++;\n\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n\n const quote = xmlString.charCodeAt(pos);\n if (quote !== 0x22 && quote !== 0x27) break;\n pos++;\n const valueStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;\n const value = unescapeXml(xmlString.slice(valueStart, pos));\n pos++;\n\n if (nsTable !== undefined && (name === \"xmlns\" || name.startsWith(\"xmlns:\"))) {\n if (attrs === undefined) attrs = {};\n const canonical = nsTable.get(value);\n if (name === \"xmlns\") {\n nsDefault = canonical;\n if (canonical !== undefined && canonical !== \"\") {\n // Known non-default canonical form: upgrade the declaration to a\n // prefixed one (xmlns=\"uri\" → xmlns:w=\"uri\") so the rewritten\n // element names stay declared.\n attrs[`xmlns:${canonical}`] = value;\n } else {\n attrs[name] = value;\n }\n } else {\n const prefix = name.slice(6);\n if (canonical !== undefined && canonical !== \"\") {\n attrs[`xmlns:${canonical}`] = value;\n } else if (canonical === \"\" && !(\"xmlns\" in attrs)) {\n // Canonical form is unprefixed: demote to a default declaration.\n attrs.xmlns = value;\n } else {\n attrs[name] = value;\n }\n if (!nsDeclared) {\n nsPrefixes = new Map(nsPrefixes ?? []);\n nsDeclared = true;\n }\n // Unknown URI keeps the source prefix bound to itself, which the\n // rewrite step treats as \"leave names as written\". Non-null: either\n // just assigned above or already a Map from a prior nsDeclared pass.\n nsPrefixes!.set(prefix, canonical ?? prefix);\n }\n continue;\n }\n if (attrs === undefined) attrs = {};\n attrs[name] = value;\n }\n\n if (attrs && nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n\n // Namespace-normalize the element name and the non-declaration attribute\n // names onto this element's effective bindings (its own declarations\n // included — attributes see the element's in-scope bindings).\n let finalName = tagName;\n if (nsTable !== undefined) {\n const colon = tagName.indexOf(\":\");\n if (colon > 0) {\n const p = tagName.slice(0, colon);\n if (p !== \"xml\") {\n const c = nsPrefixes?.get(p);\n if (c !== undefined && c !== p) {\n finalName = c === \"\" ? tagName.slice(colon + 1) : c + tagName.slice(colon);\n }\n }\n } else if (colon < 0 && nsDefault !== undefined && nsDefault !== \"\") {\n finalName = nsDefault + \":\" + tagName;\n }\n if (attrs !== undefined && nsPrefixes !== undefined) {\n for (const key of Object.keys(attrs)) {\n if (key === \"xmlns\" || key.startsWith(\"xmlns:\")) continue;\n const kcolon = key.indexOf(\":\");\n if (kcolon <= 0) continue;\n const p = key.slice(0, kcolon);\n if (p === \"xml\") continue;\n const c = nsPrefixes.get(p);\n if (c !== undefined && c !== p) {\n const renamed = c === \"\" ? key.slice(kcolon + 1) : c + key.slice(kcolon);\n const attrValue = attrs[key];\n if (attrValue !== undefined) attrs[renamed] = attrValue;\n delete attrs[key];\n }\n }\n }\n }\n\n const isSelfClosing = xmlString.charCodeAt(pos) === 0x2f; /* / */\n if (isSelfClosing) pos += 2;\n else pos++;\n\n // Constant shape: every element node carries all four fields (undefined\n // placeholders included), so attribute/children assignment writes an\n // existing slot instead of walking a hidden-class transition — element\n // access sites stay monomorphic instead of 2-3 shapes deep.\n const element: Element = {\n type: \"element\",\n name: finalName,\n attributes: attrs,\n elements: undefined,\n };\n\n const parent = peek(stack);\n if (!parent.elements) {\n parent.elements = [];\n }\n parent.elements.push(element);\n\n if (!isSelfClosing) {\n if (deferSet !== undefined && deferSet.has(tagName)) {\n // Deferred container: capture inner XML verbatim instead of parsing\n // children. Scan to the matching close tag, counting same-name opens\n // so nested occurrences (if any) don't end the capture early.\n const closeTag = `</${tagName}>`;\n let depth = 1;\n let scan = pos;\n let closeIdx = -1;\n for (;;) {\n closeIdx = xmlString.indexOf(closeTag, scan);\n if (closeIdx === -1) break;\n let p = scan;\n for (;;) {\n const openIdx = xmlString.indexOf(`<${tagName}`, p);\n if (openIdx === -1 || openIdx >= closeIdx) break;\n // Boundary check so `<rowx>` doesn't count as `<row`.\n const after = xmlString.charCodeAt(openIdx + tagName.length + 1);\n if (\n after === 0x20 ||\n after === 0x09 ||\n after === 0x0a ||\n after === 0x0d ||\n after === 0x2f ||\n after === 0x3e\n ) {\n depth++;\n }\n p = openIdx + tagName.length + 1;\n }\n scan = closeIdx + closeTag.length;\n depth--;\n if (depth === 0) break;\n }\n if (closeIdx === -1) {\n element.raw = xmlString.slice(pos);\n i = len;\n } else {\n element.raw = xmlString.slice(pos, closeIdx);\n i = scan;\n }\n continue;\n }\n stack.push(element);\n if (nsTable !== undefined) {\n // Share the parent layer unless this element declared anything (a\n // cloned prefix map, or a default declaration that can shadow one).\n const layerChanged = nsDeclared || nsDefault !== nsParent!.defaultCanonical;\n nsStack.push(\n layerChanged ? { prefixes: nsPrefixes, defaultCanonical: nsDefault } : nsParent!,\n );\n }\n }\n\n i = pos;\n }\n\n if (result.elements) {\n const temp = result.elements;\n delete result.elements;\n result.elements = temp;\n delete result.text;\n }\n\n return result;\n}\n\nfunction findTagNameEnd(str: string, start: number): number {\n let i = start;\n const len = str.length;\n while (i < len) {\n const ch = str.charCodeAt(i);\n if (ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d || ch === 0x2f || ch === 0x3e) {\n return i;\n }\n i++;\n }\n return i;\n}\n\nexport function parseAttributes(str: string): Record<string, string> {\n const result: Record<string, string> = {};\n let i = 0;\n const len = str.length;\n\n while (i < len) {\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len) break;\n\n const nameStart = i;\n while (i < len && str.charCodeAt(i) !== 0x3d) {\n if (isWhitespace(str.charCodeAt(i))) break;\n i++;\n }\n const name = str.slice(nameStart, i);\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len || str.charCodeAt(i) !== 0x3d) break;\n i++;\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n\n const quote = str.charCodeAt(i);\n if (quote !== 0x22 && quote !== 0x27) break;\n i++;\n const valueStart = i;\n while (i < len && str.charCodeAt(i) !== quote) i++;\n result[name] = unescapeXml(str.slice(valueStart, i));\n i++;\n }\n return result;\n}\n\n/**\n * Top of the parse stack. The stack is guaranteed non-empty — the result root\n * is pushed at init and push/pop stay balanced across well-formed input — so\n * this is a compile-time narrow (one non-null assertion) rather than a runtime\n * check: it must not add a throw path that changes how `parse` surfaces\n * malformed documents. Centralising the access keeps that single `!` off the\n * read sites, matching the \"wrap indexed access behind a helper\" pattern.\n */\nfunction peek(stack: Element[]): Element {\n return stack[stack.length - 1]!;\n}\n\nfunction addField(parent: Element, type: string, value: string) {\n if (!parent.elements) {\n parent.elements = [];\n }\n // Merge adjacent text/cdata nodes: a CDATA section containing the literal\n // `]]>` is serialized as two adjacent CDATA sections and must reassemble\n // into a single node on parse. Adjacent text nodes likewise merge.\n if (type === \"text\" || type === \"cdata\") {\n const last = parent.elements[parent.elements.length - 1];\n if (last && last.type === type) {\n const key = type as \"text\" | \"cdata\";\n last[key] = (last[key] as string) + value;\n return;\n }\n }\n const element: Element = { type };\n (element as Record<string, unknown>)[type] = value;\n parent.elements.push(element);\n}\n\n/** True when the nearest ancestor with an explicit xml:space sets \"preserve\". */\nfunction isPreserveContext(stack: Element[]): boolean {\n for (let i = stack.length - 1; i >= 0; i--) {\n const node = stack[i];\n if (!node) continue;\n const space = node.attributes?.[\"xml:space\"];\n if (space !== undefined) return space === \"preserve\";\n }\n return false;\n}\n\nfunction isWhitespace(ch: number): boolean {\n return ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d;\n}\n","// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\nconst XML_SPECIALS = /[&\"'<>]/;\n\n/** Escape text content for XML. Fast path returns original string when no special chars. */\nexport function escapeXml(str: string): string {\n // Fast path: most text content doesn't contain XML-special characters.\n // A character-class regex test beats a manual charCodeAt loop by ~10× (V8\n // compiles it to a native SIMD scan); returning the original string\n // reference means zero allocation for the common case.\n if (!XML_SPECIALS.test(str)) return str;\n\n // Chained native replaces: specials are sparse, so each pass after the\n // first usually finds nothing and exits quickly. Raw /g literals carry no\n // lastIndex state.\n return str\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Build an XML attribute string fragment from a record.\n * `undefined` values are automatically skipped.\n * String values are escaped via `escapeXml`. Booleans serialize as 0/1 —\n * the spelling Office itself writes for xsd:boolean attributes and\n * ST_OnOff unions alike.\n *\n * @example\n * attrs({ id: 1, name: \"foo\", hidden: undefined })\n * // => ' id=\"1\" name=\"foo\"'\n */\nexport function attrs(record: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, v] of Object.entries(record)) {\n if (v !== undefined) {\n const value = typeof v === \"string\" ? escapeXml(v) : typeof v === \"boolean\" ? (v ? 1 : 0) : v;\n parts.push(` ${key}=\"${value}\"`);\n }\n }\n return parts.join(\"\");\n}\n\n/**\n * Build an XML attribute string without escaping.\n *\n * Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when\n * all values are known-safe (numbers, booleans, or strings free of `& \" ' < >`).\n * Avoids per-call array and `Object.keys()` allocation in hot loops.\n *\n * @example\n * attrsRaw({ r: \"A1\", s: 5 })\n * // => ' r=\"A1\" s=\"5\"'\n */\nexport function attrsRaw(record: Record<string, string | number | boolean | undefined>): string {\n let s = \"\";\n for (const key in record) {\n const v = record[key];\n if (v !== undefined) {\n s += ` ${key}=\"${v}\"`;\n }\n }\n return s;\n}\n\n/**\n * Build a self-closing XML element: `<tag attrStr/>`.\n * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.\n */\nexport function selfCloseElement(tag: string, attrStr?: string): string {\n return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;\n}\n\n/**\n * Build a complete XML element string from name, optional attributes, and string children.\n *\n * Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a\n * single function call returning a string — zero object allocation.\n *\n * @param name Element tag name (e.g. `\"a:srgbClr\"`)\n * @param attrRecord Optional flat attribute map; `undefined` values are skipped\n * @param children Optional pre-serialized child XML strings\n *\n * @example\n * ```ts\n * element(\"a:solidFill\", undefined, [element(\"a:srgbClr\", { val: \"FF0000\" })])\n * // => '<a:solidFill><a:srgbClr val=\"FF0000\"/></a:solidFill>'\n * ```\n */\nexport function element(\n name: string,\n attrRecord?: Readonly<Record<string, string | number | boolean | undefined>>,\n children?: readonly string[],\n): string {\n const attrStr = attrRecord ? attrs(attrRecord) : undefined;\n if (!children || children.length === 0) return selfCloseElement(name, attrStr);\n const body = children.join(\"\");\n return body.length === 0\n ? selfCloseElement(name, attrStr)\n : `<${name}${attrStr ?? \"\"}>${body}</${name}>`;\n}\n","import { escapeXml } from \"./escape\";\nimport type { Element, StringifyOptions } from \"./types\";\n\n// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\n// Text content needs only the three markup delimiters — quotes are legal as-is.\nconst TEXT_SPECIALS = /[&<>]/;\n\nexport function stringify(js: Element, options?: StringifyOptions): string {\n const opts = normalizeOptions(options);\n const parts: string[] = [];\n\n if (js.declaration && !opts.ignoreDeclaration) {\n parts.push(writeDeclaration(js.declaration));\n }\n\n if (js.elements?.length) {\n parts.push(writeElements(js.elements, opts, 0, !parts.length));\n }\n\n return parts.join(\"\");\n}\n\ninterface NormalizedOptions {\n spaces: string;\n ignoreDeclaration: boolean;\n ignoreText: boolean;\n ignoreComment: boolean;\n ignoreCdata: boolean;\n ignoreDoctype: boolean;\n fullTagEmptyElement: boolean;\n indentText: boolean;\n indentCdata: boolean;\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"];\n}\n\n// Shared frozen defaults — the no-options call (the hot path) skips the\n// per-call object construction entirely.\nconst DEFAULT_OPTIONS: NormalizedOptions = {\n spaces: \"\",\n ignoreDeclaration: false,\n ignoreText: false,\n ignoreComment: false,\n ignoreCdata: false,\n ignoreDoctype: false,\n fullTagEmptyElement: false,\n indentText: false,\n indentCdata: false,\n};\n\nfunction normalizeOptions(options?: StringifyOptions): NormalizedOptions {\n if (!options) return DEFAULT_OPTIONS;\n let spaces = \"\";\n if (options.spaces != null) {\n spaces = typeof options.spaces === \"number\" ? \" \".repeat(options.spaces) : options.spaces;\n }\n return {\n spaces,\n ignoreDeclaration: options.ignoreDeclaration ?? false,\n ignoreText: options.ignoreText ?? false,\n ignoreComment: options.ignoreComment ?? false,\n ignoreCdata: options.ignoreCdata ?? false,\n ignoreDoctype: options.ignoreDoctype ?? false,\n fullTagEmptyElement: options.fullTagEmptyElement ?? false,\n indentText: options.indentText ?? false,\n indentCdata: options.indentCdata ?? false,\n attributeValueFn: options.attributeValueFn,\n };\n}\n\nfunction writeIndentation(spaces: string, depth: number, firstLine: boolean): string {\n if (!spaces) return \"\";\n return (!firstLine ? \"\\n\" : \"\") + spaces.repeat(depth);\n}\n\nfunction writeDeclaration(declaration: NonNullable<Element[\"declaration\"]>): string {\n const attrs = declaration.attributes;\n if (!attrs) return '<?xml version=\"1.0\"?>';\n\n const parts: string[] = [`<?xml version=\"1.0\"`];\n if (attrs.encoding) parts.push(` encoding=\"${attrs.encoding}\"`);\n if (attrs.standalone) parts.push(` standalone=\"${attrs.standalone}\"`);\n return parts.join(\"\") + \"?>\";\n}\n\nfunction writeAttributes(\n attributes: Record<string, string | number | undefined>,\n elementName: string,\n element: Element,\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"],\n): string {\n // Rope accumulation: attribute counts are small (1-3), a parts array + join\n // would cost more than V8's cons-string +=. for-in over plain data records\n // skips the Object.keys array allocation JSC penalizes (~1.3× on bun).\n let s = \"\";\n for (const key in attributes) {\n const value = attributes[key];\n if (value === null || value === undefined) continue;\n\n // attributeValueFn (xml-js hook) owns escaping when provided; otherwise\n // we escape all XML-special characters ourselves.\n const raw = String(value);\n const attr = attributeValueFn\n ? attributeValueFn(raw, key, elementName, element)\n : escapeXml(raw);\n s += ` ${key}=\"${attr}\"`;\n }\n return s;\n}\n\nfunction writeElements(\n elements: Element[],\n opts: NormalizedOptions,\n depth: number,\n firstLine: boolean,\n): string {\n // Rope accumulation — V8 cons-strings make += O(1) and skip the parts-array\n // allocation the join-based form pays per level. The element body is inlined\n // (no writeElement/writeElements mutual recursion): an ordered if-chain over\n // the node type benchmarks ~1.6× faster than a switch plus a helper call\n // per element under JSC, with V8 unchanged.\n let s = \"\";\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (!element) continue;\n const isFirst = firstLine && i === 0;\n const type = element.type;\n if (type === \"element\") {\n const name = element.name;\n if (!name) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n const attributes = element.attributes;\n const attrStr = attributes\n ? writeAttributes(attributes, name, element, opts.attributeValueFn)\n : \"\";\n // Deferred content: re-emit the captured inner XML verbatim — children\n // were never parsed, and the bytes must survive a set/save round-trip.\n if (element.raw !== undefined) {\n s += `<${name}${attrStr}>${element.raw}</${name}>`;\n continue;\n }\n const children = element.elements;\n // The xml:space dictionary probe is deliberately last: it only matters\n // for empty leaf elements, so child-bearing elements skip it.\n const withClosingTag =\n (children !== undefined && children.length > 0) ||\n opts.fullTagEmptyElement ||\n attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) {\n s += `<${name}${attrStr}/>`;\n continue;\n }\n const open = `<${name}${attrStr}>`;\n if (children !== undefined && children.length > 0) {\n const inner = writeElements(children, opts, depth + 1, false);\n // The child-element scan is only needed to pretty-print the closing\n // tag — skip it entirely when not indenting.\n if (opts.spaces && children.some((e) => e.type === \"element\")) {\n s += open + inner + \"\\n\" + opts.spaces.repeat(depth) + `</${name}>`;\n } else {\n s += open + inner + `</${name}>`;\n }\n } else {\n s += open + `</${name}>`;\n }\n } else if (type === \"text\") {\n if (opts.ignoreText) continue;\n if (opts.indentText && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n // Text escaping inline — fast path: most text content contains no\n // markup delimiters, so a regex test (a native scan in V8/JSC, ~10× a\n // charCodeAt loop) returns the original string reference with zero\n // allocation. Chained replaces afterwards: specials are sparse, so each\n // pass after the first usually finds nothing and exits quickly.\n const text = element.text;\n if (text == null) continue;\n const str = String(text);\n s += TEXT_SPECIALS.test(str)\n ? str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\")\n : str;\n } else if (type === \"cdata\") {\n if (opts.ignoreCdata) continue;\n if (opts.indentCdata && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeCdata(element.cdata);\n } else if (type === \"comment\") {\n if (opts.ignoreComment) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeComment(element.comment);\n } else if (type === \"doctype\") {\n if (opts.ignoreDoctype) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeDoctype(element.doctype);\n }\n }\n return s;\n}\n\nfunction writeCdata(cdata: string | undefined | null): string {\n if (cdata == null) return \"\";\n const escaped = cdata.replace(/\\]\\]>/g, \"]]]]><![CDATA[>\");\n return `<![CDATA[${escaped}]]>`;\n}\n\nfunction writeComment(comment: string | undefined | null): string {\n if (comment == null) return \"\";\n return `<!--${comment}-->`;\n}\n\nfunction writeDoctype(doctype: string | undefined | null): string {\n if (doctype == null) return \"\";\n return `<!DOCTYPE ${doctype}>`;\n}\n\ntype NonNullable<T> = T extends null | undefined ? never : T;\n","/**\n * Serialize an Element including its own opening/closing tag.\n *\n * `stringify` serializes only an element's children (it treats its input as\n * a document root). Raw-XML round-trip of whole elements needs the element's\n * own tag wrapped around its serialized children.\n */\nimport { escapeXml } from \"./escape\";\nimport { stringify as stringifyChildren } from \"./stringify\";\nimport type { Element } from \"./types\";\n\nexport function stringifyElement(el: Element): string {\n if (!el.name) return \"\";\n let attrStr = \"\";\n if (el.attributes) {\n for (const key of Object.keys(el.attributes)) {\n const v = el.attributes[key];\n if (v === null || v === undefined) continue;\n attrStr += ` ${key}=\"${escapeXml(String(v))}\"`;\n }\n }\n const withClosingTag =\n (el.elements?.length ?? 0) > 0 || el.attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) return `<${el.name}${attrStr}/>`;\n return `<${el.name}${attrStr}>${stringifyChildren(el)}</${el.name}>`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["stringifyChildren"],"sources":["../src/parse.ts","../src/escape.ts","../src/stringify.ts","../src/stringify-element.ts"],"sourcesContent":["import type { Element, ParseOptions } from \"./types\";\n\nconst ENTITY_MAP: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \""\": '\"',\n \"'\": \"'\",\n};\n// Matches the five named entities plus numeric character references\n// (A decimal, B hex).\nconst ENTITY_PATTERN = /&(?:amp|lt|gt|quot|apos|#x[0-9a-fA-F]+|#[0-9]+);/g;\n// \"Contains a non-whitespace character\" — same predicate as\n// `text.trim().length > 0` without allocating the trimmed copy.\nconst HAS_CONTENT = /\\S/;\n\nexport function unescapeXml(str: string): string {\n // Fast path: entities all start with '&', and OOXML parts overwhelmingly\n // contain none (a 63 MB worksheet measured zero occurrences). The regex\n // scan + replace setup per call showed up to ~12% of large-file parse\n // profiles, so gate it on the sentinel byte.\n if (str.indexOf(\"&\") === -1) return str;\n return str.replace(ENTITY_PATTERN, (match) => {\n if (ENTITY_MAP[match] !== undefined) return ENTITY_MAP[match];\n // Numeric character reference: strip \"&#\" prefix and \";\" suffix.\n const body = match.slice(2, -1);\n const code =\n body[0] === \"x\" || body[0] === \"X\" ? parseInt(body.slice(1), 16) : parseInt(body, 10);\n return Number.isFinite(code) && code >= 0 ? String.fromCodePoint(code) : match;\n });\n}\n\nexport function nativeTypeValue(value: string): string | number | boolean {\n if (value === \"\") return value;\n // Digit-only fast path — plain integers are the most common numeric shape\n // in OOXML (row/column indexes, sizes, ids). At most 15 digits is always\n // exact in float64, so the scan replaces the Number() + String(n)\n // round-trip (the String(n) side allocates) without re-checking losslessness.\n const neg = value.charCodeAt(0) === 0x2d; /* - */\n const start = neg ? 1 : 0;\n const digits = value.length - start;\n if (digits > 0 && digits <= 15) {\n // Leading zeros (\"00992297\") must stay strings; only a lone \"0\" passes.\n // \"-0\" falls through too — Number coerces it to -0 whose String() is \"0\",\n // so the slow path must keep it a string.\n const head = value.charCodeAt(start);\n if (head !== 0x30 || (!neg && digits === 1)) {\n let n = 0;\n let allDigits = true;\n for (let i = start; i < value.length; i++) {\n const c = value.charCodeAt(i);\n if (c < 0x30 || c > 0x39) {\n allDigits = false;\n break;\n }\n n = n * 10 + (c - 0x30);\n }\n if (allDigits) return neg ? -n : n;\n }\n }\n const n = Number(value);\n // Only coerce when lossless: leading zeros (\"00992297\"), exponential\n // notation (\"1e5\"), and a leading sign (\"+5\") must stay strings so hex-like\n // values (rsid, color) survive parse → stringify round-trips byte-exact.\n if (!isNaN(n) && String(n) === value) return n;\n // Length gate before toLowerCase: every non-numeric attribute value paid\n // two throwaway lowercase strings (cell refs, format names, ids …), which\n // parse profiles attributed to GC pressure.\n if (value.length === 4 || value.length === 5) {\n const lower = value.toLowerCase();\n if (lower === \"true\") return true;\n if (lower === \"false\") return false;\n }\n return value;\n}\n\nexport function parse(xmlString: string, options?: ParseOptions): Element {\n const captureSpaces = options?.captureSpacesBetweenElements ?? false;\n const trim = options?.trim ?? false;\n const ignoreDeclaration = options?.ignoreDeclaration ?? false;\n const ignoreText = options?.ignoreText ?? false;\n const ignoreComment = options?.ignoreComment ?? false;\n const ignoreCdata = options?.ignoreCdata ?? false;\n const ignoreDoctype = options?.ignoreDoctype ?? false;\n const nativeTypeAttributes = options?.nativeTypeAttributes ?? false;\n // Lookup set for deferred elements (raw inner-XML capture). Undefined when\n // the option is absent so the common path pays one truthiness check.\n const deferSet =\n options?.deferElements !== undefined && options.deferElements.length > 0\n ? new Set(options.deferElements)\n : undefined;\n\n // Namespace normalization (see ParseOptions.normalizeNamespaces): in-scope\n // prefix → canonical-prefix bindings, copy-on-write per element that carries\n // xmlns declarations. `prefixes === undefined` means nothing is bound yet\n // (the common subtree shares the root layer object untouched).\n const nsTable =\n options?.normalizeNamespaces !== undefined\n ? new Map(Object.entries(options.normalizeNamespaces))\n : undefined;\n interface NsLayer {\n prefixes: Map<string, string> | undefined;\n /** Canonical form of the default namespace; undefined = none/unknown. */\n defaultCanonical: string | undefined;\n }\n const nsRootLayer: NsLayer = { prefixes: undefined, defaultCanonical: undefined };\n const nsStack: NsLayer[] = [nsRootLayer];\n\n const result: Element = {};\n const stack: Element[] = [result];\n\n let i = 0;\n const len = xmlString.length;\n\n while (i < len) {\n // Text node: read up to the next '<'. Pure-whitespace nodes (indentation)\n // are dropped below unless captureSpaces is on, but leading/trailing\n // spaces of nodes that have content are preserved.\n if (xmlString.charCodeAt(i) !== 0x3c /* < */) {\n const start = i;\n // Single-char indexOf runs as a native memchr scan — far faster than a\n // JS byte-by-byte loop (the outer check already guarantees i is not '<').\n const lt = xmlString.indexOf(\"<\", i);\n i = lt === -1 ? len : lt;\n let text = unescapeXml(xmlString.slice(start, i));\n if (trim) text = text.trim();\n if (ignoreText) continue;\n if (text.length > 0) {\n // trim mode already guarantees non-whitespace past the length gate;\n // otherwise test natively instead of allocating a trimmed copy.\n if (captureSpaces || (trim ? true : HAS_CONTENT.test(text)) || isPreserveContext(stack)) {\n // Text-node hot path, inlined from addField(\"text\"): one lookup of\n // the last child covers both the adjacent-merge case (same shape as\n // addField — a split CDATA/text run must reassemble) and the fresh\n // push. addField stays for the cold node types.\n const parent = stack[stack.length - 1]!;\n const elements = parent.elements;\n const last = elements === undefined ? undefined : elements[elements.length - 1];\n if (last !== undefined && last.type === \"text\") {\n last.text = (last.text as string) + text;\n } else {\n const node: Element = { type: \"text\", text };\n if (elements === undefined) {\n parent.elements = [node];\n } else {\n elements.push(node);\n }\n }\n }\n }\n continue;\n }\n\n i++;\n\n // <? processing instruction / declaration\n if (xmlString.charCodeAt(i) === 0x3f /* ? */) {\n const end = xmlString.indexOf(\"?>\", i + 1);\n if (end === -1) break;\n const body = xmlString.slice(i + 1, end);\n i = end + 2;\n\n const xmlMatch = body.match(/^xml\\s+(.*)$/s);\n if (xmlMatch) {\n if (!ignoreDeclaration) {\n if (!result.declaration) {\n result.declaration = {};\n }\n const attrs = parseAttributes(xmlMatch[1] ?? \"\");\n if (nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n result.declaration.attributes = attrs;\n }\n }\n continue;\n }\n\n // !-- comment\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 3) === \"!--\") {\n const end = xmlString.indexOf(\"-->\", i + 3);\n if (end === -1) break;\n const comment = xmlString.slice(i + 3, end);\n i = end + 3;\n if (!ignoreComment) {\n if (trim) addField(peek(stack), \"comment\", comment.trim());\n else addField(peek(stack), \"comment\", comment);\n }\n continue;\n }\n\n // ![CDATA[\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 8) === \"![CDATA[\") {\n const end = xmlString.indexOf(\"]]>\", i + 8);\n if (end === -1) break;\n const cdata = xmlString.slice(i + 8, end);\n i = end + 3;\n if (!ignoreCdata) {\n if (trim) addField(peek(stack), \"cdata\", cdata.trim());\n else addField(peek(stack), \"cdata\", cdata);\n }\n continue;\n }\n\n // <!DOCTYPE\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 9) === \"!DOCTYPE\") {\n const end = xmlString.indexOf(\">\", i + 9);\n if (end === -1) break;\n const doctype = xmlString.slice(i + 9, end).trim();\n i = end + 1;\n if (!ignoreDoctype) {\n addField(peek(stack), \"doctype\", doctype);\n }\n continue;\n }\n\n // </ closing tag\n if (xmlString.charCodeAt(i) === 0x2f /* / */) {\n const end = xmlString.indexOf(\">\", i + 1);\n if (end === -1) break;\n i = end + 1;\n stack.pop();\n if (nsTable !== undefined) nsStack.pop();\n continue;\n }\n\n // < opening tag\n const tagNameEnd = findTagNameEnd(xmlString, i);\n const tagName = xmlString.slice(i, tagNameEnd);\n let pos = tagNameEnd;\n\n // Namespace bindings for this element: inherit the parent layer; the\n // attribute scan clones it on the first xmlns declaration it meets.\n const nsParent = nsTable !== undefined ? nsStack[nsStack.length - 1]! : undefined;\n let nsPrefixes = nsParent?.prefixes;\n let nsDefault = nsParent?.defaultCanonical;\n let nsDeclared = false;\n\n // Attribute scan, inlined from parseAttributesFromXml: called once per\n // opening tag, the `{ attrs, pos }` wrapper was one allocation per element.\n // `attrs` is allocated lazily on the first attribute — tags without\n // attributes (the majority in data-heavy parts) must not pay a record\n // allocation.\n let attrs: Record<string, string> | undefined;\n while (pos < len) {\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n if (pos >= len || xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) {\n break;\n }\n\n const nameStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== 0x3d) {\n if (xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) break;\n pos++;\n }\n // XML's Eq production allows whitespace around '='; trim it off the\n // name so `Target = \"…\"` resolves like `Target=\"…\"`.\n const name = xmlString.slice(nameStart, pos).trim();\n\n if (xmlString.charCodeAt(pos) !== 0x3d) break;\n pos++;\n\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n\n const quote = xmlString.charCodeAt(pos);\n if (quote !== 0x22 && quote !== 0x27) break;\n pos++;\n const valueStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;\n const value = unescapeXml(xmlString.slice(valueStart, pos));\n pos++;\n\n if (nsTable !== undefined && (name === \"xmlns\" || name.startsWith(\"xmlns:\"))) {\n if (attrs === undefined) attrs = {};\n const canonical = nsTable.get(value);\n if (name === \"xmlns\") {\n nsDefault = canonical;\n if (canonical !== undefined && canonical !== \"\") {\n // Known non-default canonical form: upgrade the declaration to a\n // prefixed one (xmlns=\"uri\" → xmlns:w=\"uri\") so the rewritten\n // element names stay declared.\n attrs[`xmlns:${canonical}`] = value;\n } else {\n attrs[name] = value;\n }\n } else {\n const prefix = name.slice(6);\n if (canonical !== undefined && canonical !== \"\") {\n attrs[`xmlns:${canonical}`] = value;\n } else if (canonical === \"\" && !(\"xmlns\" in attrs)) {\n // Canonical form is unprefixed: demote to a default declaration.\n attrs.xmlns = value;\n } else {\n attrs[name] = value;\n }\n if (!nsDeclared) {\n nsPrefixes = new Map(nsPrefixes ?? []);\n nsDeclared = true;\n }\n // Unknown URI keeps the source prefix bound to itself, which the\n // rewrite step treats as \"leave names as written\". Non-null: either\n // just assigned above or already a Map from a prior nsDeclared pass.\n nsPrefixes!.set(prefix, canonical ?? prefix);\n }\n continue;\n }\n if (attrs === undefined) attrs = {};\n attrs[name] = value;\n }\n\n if (attrs && nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n\n // Namespace-normalize the element name and the non-declaration attribute\n // names onto this element's effective bindings (its own declarations\n // included — attributes see the element's in-scope bindings).\n let finalName = tagName;\n if (nsTable !== undefined) {\n const colon = tagName.indexOf(\":\");\n if (colon > 0) {\n const p = tagName.slice(0, colon);\n if (p !== \"xml\") {\n const c = nsPrefixes?.get(p);\n if (c !== undefined && c !== p) {\n finalName = c === \"\" ? tagName.slice(colon + 1) : c + tagName.slice(colon);\n }\n }\n } else if (colon < 0 && nsDefault !== undefined && nsDefault !== \"\") {\n finalName = nsDefault + \":\" + tagName;\n }\n if (attrs !== undefined && nsPrefixes !== undefined) {\n for (const key of Object.keys(attrs)) {\n if (key === \"xmlns\" || key.startsWith(\"xmlns:\")) continue;\n const kcolon = key.indexOf(\":\");\n if (kcolon <= 0) continue;\n const p = key.slice(0, kcolon);\n if (p === \"xml\") continue;\n const c = nsPrefixes.get(p);\n if (c !== undefined && c !== p) {\n const renamed = c === \"\" ? key.slice(kcolon + 1) : c + key.slice(kcolon);\n const attrValue = attrs[key];\n if (attrValue !== undefined) attrs[renamed] = attrValue;\n delete attrs[key];\n }\n }\n }\n }\n\n const isSelfClosing = xmlString.charCodeAt(pos) === 0x2f; /* / */\n if (isSelfClosing) pos += 2;\n else pos++;\n\n // Constant shape: every element node carries all four fields (undefined\n // placeholders included), so attribute/children assignment writes an\n // existing slot instead of walking a hidden-class transition — element\n // access sites stay monomorphic instead of 2-3 shapes deep.\n const element: Element = {\n type: \"element\",\n name: finalName,\n attributes: attrs,\n elements: undefined,\n };\n\n const parent = peek(stack);\n if (!parent.elements) {\n parent.elements = [];\n }\n parent.elements.push(element);\n\n if (!isSelfClosing) {\n if (deferSet !== undefined && deferSet.has(tagName)) {\n // Deferred container: capture inner XML verbatim instead of parsing\n // children. Scan to the matching close tag, counting same-name opens\n // so nested occurrences (if any) don't end the capture early.\n const closeTag = `</${tagName}>`;\n let depth = 1;\n let scan = pos;\n let closeIdx = -1;\n for (;;) {\n closeIdx = xmlString.indexOf(closeTag, scan);\n if (closeIdx === -1) break;\n let p = scan;\n for (;;) {\n const openIdx = xmlString.indexOf(`<${tagName}`, p);\n if (openIdx === -1 || openIdx >= closeIdx) break;\n // Boundary check so `<rowx>` doesn't count as `<row`.\n const after = xmlString.charCodeAt(openIdx + tagName.length + 1);\n if (\n after === 0x20 ||\n after === 0x09 ||\n after === 0x0a ||\n after === 0x0d ||\n after === 0x2f ||\n after === 0x3e\n ) {\n depth++;\n }\n p = openIdx + tagName.length + 1;\n }\n scan = closeIdx + closeTag.length;\n depth--;\n if (depth === 0) break;\n }\n if (closeIdx === -1) {\n element.raw = xmlString.slice(pos);\n i = len;\n } else {\n element.raw = xmlString.slice(pos, closeIdx);\n i = scan;\n }\n continue;\n }\n stack.push(element);\n if (nsTable !== undefined) {\n // Share the parent layer unless this element declared anything (a\n // cloned prefix map, or a default declaration that can shadow one).\n const layerChanged = nsDeclared || nsDefault !== nsParent!.defaultCanonical;\n nsStack.push(\n layerChanged ? { prefixes: nsPrefixes, defaultCanonical: nsDefault } : nsParent!,\n );\n }\n }\n\n i = pos;\n }\n\n if (result.elements) {\n const temp = result.elements;\n delete result.elements;\n result.elements = temp;\n delete result.text;\n }\n\n return result;\n}\n\nfunction findTagNameEnd(str: string, start: number): number {\n let i = start;\n const len = str.length;\n while (i < len) {\n const ch = str.charCodeAt(i);\n if (ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d || ch === 0x2f || ch === 0x3e) {\n return i;\n }\n i++;\n }\n return i;\n}\n\nexport function parseAttributes(str: string): Record<string, string> {\n const result: Record<string, string> = {};\n let i = 0;\n const len = str.length;\n\n while (i < len) {\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len) break;\n\n const nameStart = i;\n while (i < len && str.charCodeAt(i) !== 0x3d) {\n if (isWhitespace(str.charCodeAt(i))) break;\n i++;\n }\n const name = str.slice(nameStart, i);\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len || str.charCodeAt(i) !== 0x3d) break;\n i++;\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n\n const quote = str.charCodeAt(i);\n if (quote !== 0x22 && quote !== 0x27) break;\n i++;\n const valueStart = i;\n while (i < len && str.charCodeAt(i) !== quote) i++;\n result[name] = unescapeXml(str.slice(valueStart, i));\n i++;\n }\n return result;\n}\n\n/**\n * Top of the parse stack. The stack is guaranteed non-empty — the result root\n * is pushed at init and push/pop stay balanced across well-formed input — so\n * this is a compile-time narrow (one non-null assertion) rather than a runtime\n * check: it must not add a throw path that changes how `parse` surfaces\n * malformed documents. Centralising the access keeps that single `!` off the\n * read sites, matching the \"wrap indexed access behind a helper\" pattern.\n */\nfunction peek(stack: Element[]): Element {\n return stack[stack.length - 1]!;\n}\n\nfunction addField(parent: Element, type: string, value: string) {\n if (!parent.elements) {\n parent.elements = [];\n }\n // Merge adjacent text/cdata nodes: a CDATA section containing the literal\n // `]]>` is serialized as two adjacent CDATA sections and must reassemble\n // into a single node on parse. Adjacent text nodes likewise merge.\n if (type === \"text\" || type === \"cdata\") {\n const last = parent.elements[parent.elements.length - 1];\n if (last && last.type === type) {\n const key = type as \"text\" | \"cdata\";\n last[key] = (last[key] as string) + value;\n return;\n }\n }\n const element: Element = { type };\n (element as Record<string, unknown>)[type] = value;\n parent.elements.push(element);\n}\n\n/** True when the nearest ancestor with an explicit xml:space sets \"preserve\". */\nfunction isPreserveContext(stack: Element[]): boolean {\n for (let i = stack.length - 1; i >= 0; i--) {\n const node = stack[i];\n if (!node) continue;\n const space = node.attributes?.[\"xml:space\"];\n if (space !== undefined) return space === \"preserve\";\n }\n return false;\n}\n\nfunction isWhitespace(ch: number): boolean {\n return ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d;\n}\n","// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\nconst XML_SPECIALS = /[&\"'<>]/;\n\n/** Escape text content for XML. Fast path returns original string when no special chars. */\nexport function escapeXml(str: string): string {\n // Fast path: most text content doesn't contain XML-special characters.\n // A character-class regex test beats a manual charCodeAt loop by ~10× (V8\n // compiles it to a native SIMD scan); returning the original string\n // reference means zero allocation for the common case.\n if (!XML_SPECIALS.test(str)) return str;\n\n // Chained native replaces: specials are sparse, so each pass after the\n // first usually finds nothing and exits quickly. Raw /g literals carry no\n // lastIndex state.\n return str\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\n/**\n * Build an XML attribute string fragment from a record.\n * `undefined` values are automatically skipped.\n * String values are escaped via `escapeXml`. Booleans serialize as 0/1 —\n * the spelling Office itself writes for xsd:boolean attributes and\n * ST_OnOff unions alike.\n *\n * @example\n * attrs({ id: 1, name: \"foo\", hidden: undefined })\n * // => ' id=\"1\" name=\"foo\"'\n */\nexport function attrs(record: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, v] of Object.entries(record)) {\n if (v !== undefined) {\n const value = typeof v === \"string\" ? escapeXml(v) : typeof v === \"boolean\" ? (v ? 1 : 0) : v;\n parts.push(` ${key}=\"${value}\"`);\n }\n }\n return parts.join(\"\");\n}\n\n/**\n * Build an XML attribute string without escaping.\n *\n * Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when\n * all values are known-safe (numbers, booleans, or strings free of `& \" ' < >`).\n * Avoids per-call array and `Object.keys()` allocation in hot loops.\n *\n * @example\n * attrsRaw({ r: \"A1\", s: 5 })\n * // => ' r=\"A1\" s=\"5\"'\n */\nexport function attrsRaw(record: Record<string, string | number | boolean | undefined>): string {\n let s = \"\";\n for (const key in record) {\n const v = record[key];\n if (v !== undefined) {\n s += ` ${key}=\"${v}\"`;\n }\n }\n return s;\n}\n\n/**\n * Build a self-closing XML element: `<tag attrStr/>`.\n * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.\n */\nexport function selfCloseElement(tag: string, attrStr?: string): string {\n return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;\n}\n\n/**\n * Build a complete XML element string from name, optional attributes, and string children.\n *\n * Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a\n * single function call returning a string — zero object allocation.\n *\n * @param name Element tag name (e.g. `\"a:srgbClr\"`)\n * @param attrRecord Optional flat attribute map; `undefined` values are skipped\n * @param children Optional pre-serialized child XML strings\n *\n * @example\n * ```ts\n * element(\"a:solidFill\", undefined, [element(\"a:srgbClr\", { val: \"FF0000\" })])\n * // => '<a:solidFill><a:srgbClr val=\"FF0000\"/></a:solidFill>'\n * ```\n */\nexport function element(\n name: string,\n attrRecord?: Readonly<Record<string, string | number | boolean | undefined>>,\n children?: readonly string[],\n): string {\n const attrStr = attrRecord ? attrs(attrRecord) : undefined;\n if (!children || children.length === 0) return selfCloseElement(name, attrStr);\n const body = children.join(\"\");\n return body.length === 0\n ? selfCloseElement(name, attrStr)\n : `<${name}${attrStr ?? \"\"}>${body}</${name}>`;\n}\n","import { escapeXml } from \"./escape\";\nimport type { Element, StringifyOptions } from \"./types\";\n\n// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\n// Text content needs only the three markup delimiters — quotes are legal as-is.\nconst TEXT_SPECIALS = /[&<>]/;\n\nexport function stringify(js: Element, options?: StringifyOptions): string {\n const opts = normalizeOptions(options);\n const parts: string[] = [];\n\n if (js.declaration && !opts.ignoreDeclaration) {\n parts.push(writeDeclaration(js.declaration));\n }\n\n if (js.elements?.length) {\n parts.push(writeElements(js.elements, opts, 0, !parts.length));\n }\n\n return parts.join(\"\");\n}\n\ninterface NormalizedOptions {\n spaces: string;\n ignoreDeclaration: boolean;\n ignoreText: boolean;\n ignoreComment: boolean;\n ignoreCdata: boolean;\n ignoreDoctype: boolean;\n fullTagEmptyElement: boolean;\n indentText: boolean;\n indentCdata: boolean;\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"];\n}\n\n// Shared frozen defaults — the no-options call (the hot path) skips the\n// per-call object construction entirely.\nconst DEFAULT_OPTIONS: NormalizedOptions = {\n spaces: \"\",\n ignoreDeclaration: false,\n ignoreText: false,\n ignoreComment: false,\n ignoreCdata: false,\n ignoreDoctype: false,\n fullTagEmptyElement: false,\n indentText: false,\n indentCdata: false,\n};\n\nfunction normalizeOptions(options?: StringifyOptions): NormalizedOptions {\n if (!options) return DEFAULT_OPTIONS;\n let spaces = \"\";\n if (options.spaces != null) {\n spaces = typeof options.spaces === \"number\" ? \" \".repeat(options.spaces) : options.spaces;\n }\n return {\n spaces,\n ignoreDeclaration: options.ignoreDeclaration ?? false,\n ignoreText: options.ignoreText ?? false,\n ignoreComment: options.ignoreComment ?? false,\n ignoreCdata: options.ignoreCdata ?? false,\n ignoreDoctype: options.ignoreDoctype ?? false,\n fullTagEmptyElement: options.fullTagEmptyElement ?? false,\n indentText: options.indentText ?? false,\n indentCdata: options.indentCdata ?? false,\n attributeValueFn: options.attributeValueFn,\n };\n}\n\nfunction writeIndentation(spaces: string, depth: number, firstLine: boolean): string {\n if (!spaces) return \"\";\n return (!firstLine ? \"\\n\" : \"\") + spaces.repeat(depth);\n}\n\nfunction writeDeclaration(declaration: NonNullable<Element[\"declaration\"]>): string {\n const attrs = declaration.attributes;\n if (!attrs) return '<?xml version=\"1.0\"?>';\n\n const parts: string[] = [`<?xml version=\"1.0\"`];\n if (attrs.encoding) parts.push(` encoding=\"${attrs.encoding}\"`);\n if (attrs.standalone) parts.push(` standalone=\"${attrs.standalone}\"`);\n return parts.join(\"\") + \"?>\";\n}\n\nfunction writeAttributes(\n attributes: Record<string, string | number | undefined>,\n elementName: string,\n element: Element,\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"],\n): string {\n // Rope accumulation: attribute counts are small (1-3), a parts array + join\n // would cost more than V8's cons-string +=. for-in over plain data records\n // skips the Object.keys array allocation JSC penalizes (~1.3× on bun).\n let s = \"\";\n for (const key in attributes) {\n const value = attributes[key];\n if (value === null || value === undefined) continue;\n\n // attributeValueFn (xml-js hook) owns escaping when provided; otherwise\n // we escape all XML-special characters ourselves.\n const raw = String(value);\n const attr = attributeValueFn\n ? attributeValueFn(raw, key, elementName, element)\n : escapeXml(raw);\n s += ` ${key}=\"${attr}\"`;\n }\n return s;\n}\n\nfunction writeElements(\n elements: Element[],\n opts: NormalizedOptions,\n depth: number,\n firstLine: boolean,\n): string {\n // Rope accumulation — V8 cons-strings make += O(1) and skip the parts-array\n // allocation the join-based form pays per level. The element body is inlined\n // (no writeElement/writeElements mutual recursion): an ordered if-chain over\n // the node type benchmarks ~1.6× faster than a switch plus a helper call\n // per element under JSC, with V8 unchanged.\n let s = \"\";\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (!element) continue;\n const isFirst = firstLine && i === 0;\n const type = element.type;\n if (type === \"element\") {\n const name = element.name;\n if (!name) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n const attributes = element.attributes;\n const attrStr = attributes\n ? writeAttributes(attributes, name, element, opts.attributeValueFn)\n : \"\";\n // Deferred content: re-emit the captured inner XML verbatim — children\n // were never parsed, and the bytes must survive a set/save round-trip.\n if (element.raw !== undefined) {\n s += `<${name}${attrStr}>${element.raw}</${name}>`;\n continue;\n }\n const children = element.elements;\n // The xml:space dictionary probe is deliberately last: it only matters\n // for empty leaf elements, so child-bearing elements skip it.\n const withClosingTag =\n (children !== undefined && children.length > 0) ||\n opts.fullTagEmptyElement ||\n attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) {\n s += `<${name}${attrStr}/>`;\n continue;\n }\n const open = `<${name}${attrStr}>`;\n if (children !== undefined && children.length > 0) {\n const inner = writeElements(children, opts, depth + 1, false);\n // The child-element scan is only needed to pretty-print the closing\n // tag — skip it entirely when not indenting.\n if (opts.spaces && children.some((e) => e.type === \"element\")) {\n s += open + inner + \"\\n\" + opts.spaces.repeat(depth) + `</${name}>`;\n } else {\n s += open + inner + `</${name}>`;\n }\n } else {\n s += open + `</${name}>`;\n }\n } else if (type === \"text\") {\n if (opts.ignoreText) continue;\n if (opts.indentText && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n // Text escaping inline — fast path: most text content contains no\n // markup delimiters, so a regex test (a native scan in V8/JSC, ~10× a\n // charCodeAt loop) returns the original string reference with zero\n // allocation. Chained replaces afterwards: specials are sparse, so each\n // pass after the first usually finds nothing and exits quickly.\n const text = element.text;\n if (text == null) continue;\n const str = String(text);\n s += TEXT_SPECIALS.test(str)\n ? str.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\")\n : str;\n } else if (type === \"cdata\") {\n if (opts.ignoreCdata) continue;\n if (opts.indentCdata && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeCdata(element.cdata);\n } else if (type === \"comment\") {\n if (opts.ignoreComment) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeComment(element.comment);\n } else if (type === \"doctype\") {\n if (opts.ignoreDoctype) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeDoctype(element.doctype);\n }\n }\n return s;\n}\n\nfunction writeCdata(cdata: string | undefined | null): string {\n if (cdata == null) return \"\";\n const escaped = cdata.replace(/\\]\\]>/g, \"]]]]><![CDATA[>\");\n return `<![CDATA[${escaped}]]>`;\n}\n\nfunction writeComment(comment: string | undefined | null): string {\n if (comment == null) return \"\";\n return `<!--${comment}-->`;\n}\n\nfunction writeDoctype(doctype: string | undefined | null): string {\n if (doctype == null) return \"\";\n return `<!DOCTYPE ${doctype}>`;\n}\n\ntype NonNullable<T> = T extends null | undefined ? never : T;\n","/**\n * Serialize an Element including its own opening/closing tag.\n *\n * `stringify` serializes only an element's children (it treats its input as\n * a document root). Raw-XML round-trip of whole elements needs the element's\n * own tag wrapped around its serialized children.\n */\nimport { escapeXml } from \"./escape\";\nimport { stringify as stringifyChildren } from \"./stringify\";\nimport type { Element } from \"./types\";\n\nexport function stringifyElement(el: Element): string {\n if (!el.name) return \"\";\n let attrStr = \"\";\n if (el.attributes) {\n for (const key of Object.keys(el.attributes)) {\n const v = el.attributes[key];\n if (v === null || v === undefined) continue;\n attrStr += ` ${key}=\"${escapeXml(String(v))}\"`;\n }\n }\n const withClosingTag =\n (el.elements?.length ?? 0) > 0 || el.attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) return `<${el.name}${attrStr}/>`;\n return `<${el.name}${attrStr}>${stringifyChildren(el)}</${el.name}>`;\n}\n"],"mappings":"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"}
|