@office-open/xml
Advanced tools
+11
-1
@@ -35,2 +35,12 @@ import { C as XmlAttrs, D as XmlOption, E as XmlObject, S as XmlAtom, T as XmlDescArray, _ as ElementCompact, a as childCount, b as Js2XmlOptions, c as collectText, d as findDeep, f as hasChild, g as Element, h as DeclarationAttributes, i as attrNum, l as colorAttr, m as Attributes, n as attr, o as childText, p as textOf, r as attrBool, s as children, t as allChildren, u as findChild, v as ElementObject, w as XmlDesc, x as Xml2JsOptions, y as IgnoreOptions } from "./_chunks/utils-BHmdH50s.mjs"; | ||
| declare function escapeAttributeValue(str: string): string; | ||
| /** | ||
| * Build an XML attribute string fragment from a record. | ||
| * `undefined` values are automatically skipped. | ||
| * String values are escaped via `escapeXml`. | ||
| * | ||
| * @example | ||
| * attrs({ id: 1, name: "foo", hidden: undefined }) | ||
| * // => ' id="1" name="foo"' | ||
| */ | ||
| declare function attrs(record: Record<string, string | number | boolean | undefined>): string; | ||
| //#endregion | ||
@@ -41,2 +51,2 @@ //#region src/json.d.ts | ||
| //#endregion | ||
| export { Attributes, DeclarationAttributes, Element, ElementCompact, ElementObject, IgnoreOptions, Js2XmlOptions, Xml2JsOptions, XmlAtom, XmlAttrs, XmlDesc, XmlDescArray, XmlObject, XmlOption, allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json }; | ||
| export { Attributes, DeclarationAttributes, Element, ElementCompact, ElementObject, IgnoreOptions, Js2XmlOptions, Xml2JsOptions, XmlAtom, XmlAttrs, XmlDesc, XmlDescArray, XmlObject, XmlOption, allChildren, attr, attrBool, attrNum, attrs, childCount, childText, children, collectText, colorAttr, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json }; |
+56
-62
@@ -22,2 +22,16 @@ import { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf } from "./utils.mjs"; | ||
| } | ||
| /** | ||
| * Build an XML attribute string fragment from a record. | ||
| * `undefined` values are automatically skipped. | ||
| * String values are escaped via `escapeXml`. | ||
| * | ||
| * @example | ||
| * attrs({ id: 1, name: "foo", hidden: undefined }) | ||
| * // => ' id="1" name="foo"' | ||
| */ | ||
| function attrs(record) { | ||
| let s = ""; | ||
| for (const [k, v] of Object.entries(record)) if (v !== void 0) s += ` ${k}="${typeof v === "string" ? escapeXml(v) : v}"`; | ||
| return s; | ||
| } | ||
| //#endregion | ||
@@ -41,3 +55,4 @@ //#region src/serialize.ts | ||
| for (let i = 0; i < items.length; i++) { | ||
| parts.push(formatElement(resolve(items[i], opts.indent, 0))); | ||
| const keys = Object.keys(items[i]); | ||
| parts.push(formatElement(keys[0], items[i][keys[0]], opts.indent, 0)); | ||
| if (opts.indent && i < items.length - 1) parts.push("\n"); | ||
@@ -56,69 +71,48 @@ } | ||
| } | ||
| function resolve(data, indent, depth) { | ||
| const name = Object.keys(data)[0]; | ||
| const values = data[name]; | ||
| const attributes = []; | ||
| const content = []; | ||
| if (values == null) return { | ||
| name, | ||
| attributes, | ||
| content, | ||
| indent, | ||
| depth, | ||
| emptyArray: false | ||
| }; | ||
| switch (typeof values) { | ||
| case "object": | ||
| if (values._attr) for (const key of Object.keys(values._attr)) attributes.push(`${key}="${escapeXml(String(values._attr[key]))}"`); | ||
| if (values._attributes) for (const key of Object.keys(values._attributes)) attributes.push(`${key}="${escapeXml(String(values._attributes[key]))}"`); | ||
| if (values._cdata) { | ||
| const escaped = String(values._cdata).replace(/\]\]>/g, "]]]]><![CDATA[>"); | ||
| content.push(`<![CDATA[${escaped}]]>`); | ||
| } | ||
| if (Array.isArray(values)) { | ||
| if (values.length === 0) return { | ||
| name, | ||
| attributes, | ||
| content, | ||
| indent, | ||
| depth, | ||
| emptyArray: true | ||
| }; | ||
| for (const value of values) if (value && typeof value === "object" && "_attr" in value) for (const key of Object.keys(value._attr)) attributes.push(`${key}="${escapeXml(String(value._attr[key]))}"`); | ||
| else if (value && typeof value === "object" && "_attributes" in value) for (const key of Object.keys(value._attributes)) attributes.push(`${key}="${escapeXml(String(value._attributes[key]))}"`); | ||
| else if (value && typeof value === "object") content.push(resolve(value, indent, depth + 1)); | ||
| else if (value != null) content.push(escapeXml(String(value))); | ||
| } | ||
| break; | ||
| default: content.push(escapeXml(String(values))); | ||
| /** | ||
| * Single-pass XML formatter: directly converts IXmlableObject to string, | ||
| * eliminating the intermediate ResolvedElement tree. | ||
| */ | ||
| function formatElement(name, values, indent, depth) { | ||
| const attrParts = []; | ||
| const textParts = []; | ||
| const elemParts = []; | ||
| let emptyArray = false; | ||
| if (values == null) { | ||
| const attrStr = attrParts.length ? " " + attrParts.join(" ") : ""; | ||
| return `${indent ? indent.repeat(depth) : ""}<${name}${attrStr}/>`; | ||
| } | ||
| return { | ||
| name, | ||
| attributes, | ||
| content, | ||
| indent, | ||
| depth, | ||
| emptyArray: false | ||
| }; | ||
| } | ||
| function formatElement(elem) { | ||
| const { name, attributes, content, indent, depth } = elem; | ||
| const hasChildren = content.length > 0; | ||
| if (typeof values === "object") { | ||
| if (values._attr) for (const key of Object.keys(values._attr)) attrParts.push(`${key}="${escapeXml(String(values._attr[key]))}"`); | ||
| if (values._attributes) for (const key of Object.keys(values._attributes)) attrParts.push(`${key}="${escapeXml(String(values._attributes[key]))}"`); | ||
| if (values._cdata) { | ||
| const escaped = String(values._cdata).replace(/\]\]>/g, "]]]]><![CDATA[>"); | ||
| textParts.push(`<![CDATA[${escaped}]]>`); | ||
| } | ||
| if (Array.isArray(values)) { | ||
| if (values.length === 0) emptyArray = true; | ||
| else for (const value of values) if (value && typeof value === "object" && "_attr" in value) for (const key of Object.keys(value._attr)) attrParts.push(`${key}="${escapeXml(String(value._attr[key]))}"`); | ||
| else if (value && typeof value === "object" && "_attributes" in value) for (const key of Object.keys(value._attributes)) attrParts.push(`${key}="${escapeXml(String(value._attributes[key]))}"`); | ||
| else if (value && typeof value === "object") { | ||
| const childKeys = Object.keys(value); | ||
| elemParts.push(formatElement(childKeys[0], value[childKeys[0]], indent, depth + 1)); | ||
| } else if (value != null) textParts.push(escapeXml(String(value))); | ||
| } | ||
| } else textParts.push(escapeXml(String(values))); | ||
| const ind = indent ? indent.repeat(depth) : ""; | ||
| const attrStr = attributes.length ? " " + attributes.join(" ") : ""; | ||
| if (!hasChildren) { | ||
| if (elem.emptyArray) return `${ind}<${name}${attrStr}></${name}>`; | ||
| return `${ind}<${name}${attrStr}/>`; | ||
| } | ||
| const textContent = content.length === 1 && typeof content[0] === "string" ? content[0] : null; | ||
| if (textContent !== null && !indent) return `<${name}${attrStr}>${textContent}</${name}>`; | ||
| if (textContent !== null) return `${ind}<${name}${attrStr}>${textContent}</${name}>`; | ||
| const attrStr = attrParts.length ? " " + attrParts.join(" ") : ""; | ||
| if (textParts.length + elemParts.length === 0) return emptyArray ? `${ind}<${name}${attrStr}></${name}>` : `${ind}<${name}${attrStr}/>`; | ||
| if (elemParts.length === 0 && textParts.length === 1) return indent ? `${ind}<${name}${attrStr}>${textParts[0]}</${name}>` : `<${name}${attrStr}>${textParts[0]}</${name}>`; | ||
| const parts = []; | ||
| parts.push(`${ind}<${name}${attrStr}>`); | ||
| if (indent) parts.push("\n"); | ||
| for (const child of content) { | ||
| if (typeof child === "string") parts.push(`${indent.repeat(depth + 1)}${child}`); | ||
| else parts.push(formatElement(child)); | ||
| const childIndent = indent ? indent.repeat(depth + 1) : ""; | ||
| for (const t of textParts) { | ||
| parts.push(`${childIndent}${t}`); | ||
| if (indent) parts.push("\n"); | ||
| } | ||
| for (const e of elemParts) { | ||
| parts.push(e); | ||
| if (indent) parts.push("\n"); | ||
| } | ||
| parts.push(`${ind}</${name}>`); | ||
@@ -508,2 +502,2 @@ return parts.join(""); | ||
| //#endregion | ||
| export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json }; | ||
| export { allChildren, attr, attrBool, attrNum, attrs, childCount, childText, children, collectText, colorAttr, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json }; |
+1
-1
| { | ||
| "name": "@office-open/xml", | ||
| "version": "0.3.1", | ||
| "version": "0.3.2", | ||
| "description": "XML parsing and serialization for Office Open XML. Zero dependencies, drop-in replacement for xml + xml-js.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
37772
1.98%615
-0.97%