@office-open/xml
Advanced tools
| 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 "./utils-DTU54qj_.mjs"; | ||
| //#region src/serialize.d.ts | ||
| declare function xml(input: Record<string, any> | Record<string, any>[], options?: boolean | string | { | ||
| indent?: boolean | string; | ||
| declaration?: boolean | { | ||
| encoding?: string; | ||
| standalone?: string; | ||
| }; | ||
| }): string; | ||
| //#endregion | ||
| //#region src/parse.d.ts | ||
| declare function unescapeXml(str: string): string; | ||
| declare function nativeTypeValue(value: string): string | number | boolean; | ||
| declare function xml2js(xmlString: string, options?: Xml2JsOptions): Element; | ||
| declare function parseAttributes(str: string): Record<string, string>; | ||
| //#endregion | ||
| //#region src/stringify.d.ts | ||
| declare function js2xml(js: Element, options?: Js2XmlOptions): string; | ||
| /** Alias for js2xml — xml-js compatible export */ | ||
| declare function json2xml(json: Element, options?: Js2XmlOptions): string; | ||
| //#endregion | ||
| //#region src/convert.d.ts | ||
| /** | ||
| * Convert XmlObject (node-xml format) directly to Element (xml-js format). | ||
| * Eliminates the redundant xml() → xml2js() bridge path. | ||
| */ | ||
| declare function toElement(xmlObject: Record<string, any>): Element; | ||
| //#endregion | ||
| //#region src/escape.d.ts | ||
| /** Escape text content for XML. Fast path returns original string when no special chars. */ | ||
| declare function escapeXml(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; | ||
| /** | ||
| * Build a self-closing XML element: `<tag attrStr/>`. | ||
| * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined. | ||
| */ | ||
| declare function selfCloseElement(tag: string, attrStr?: string): string; | ||
| //#endregion | ||
| //#region src/json.d.ts | ||
| /** Convert XML string to JSON string — xml-js compatible export */ | ||
| declare function xml2json(xml: string, options?: Xml2JsOptions): string; | ||
| //#endregion | ||
| 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, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, nativeTypeValue, parseAttributes, selfCloseElement, textOf, toElement, unescapeXml, xml, xml2js, xml2json }; |
+545
| import { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf } from "./utils.mjs"; | ||
| //#region src/escape.ts | ||
| /** Escape text content for XML. Fast path returns original string when no special chars. */ | ||
| function escapeXml(str) { | ||
| for (let i = 0; i < str.length; i++) { | ||
| const c = str.charCodeAt(i); | ||
| if (c === 38 || c === 34 || c === 39 || c === 60 || c === 62) { | ||
| let s = ""; | ||
| let last = 0; | ||
| for (let j = i; j < str.length; j++) { | ||
| const cj = str.charCodeAt(j); | ||
| if (cj === 38) { | ||
| s += str.slice(last, j) + "&"; | ||
| last = j + 1; | ||
| } else if (cj === 34) { | ||
| s += str.slice(last, j) + """; | ||
| last = j + 1; | ||
| } else if (cj === 39) { | ||
| s += str.slice(last, j) + "'"; | ||
| last = j + 1; | ||
| } else if (cj === 60) { | ||
| s += str.slice(last, j) + "<"; | ||
| last = j + 1; | ||
| } else if (cj === 62) { | ||
| s += str.slice(last, j) + ">"; | ||
| last = j + 1; | ||
| } | ||
| } | ||
| return s + str.slice(last); | ||
| } | ||
| } | ||
| return str; | ||
| } | ||
| /** | ||
| * 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 = ""; | ||
| const keys = Object.keys(record); | ||
| for (let i = 0; i < keys.length; i++) { | ||
| const v = record[keys[i]]; | ||
| if (v !== void 0) s += ` ${keys[i]}="${typeof v === "string" ? escapeXml(v) : v}"`; | ||
| } | ||
| return s; | ||
| } | ||
| /** | ||
| * Build a self-closing XML element: `<tag attrStr/>`. | ||
| * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined. | ||
| */ | ||
| function selfCloseElement(tag, attrStr) { | ||
| return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`; | ||
| } | ||
| //#endregion | ||
| //#region src/serialize.ts | ||
| const DEFAULT_INDENT = " "; | ||
| function xml(input, options) { | ||
| const opts = normalizeOptions$1(options); | ||
| const parts = []; | ||
| if (opts.declaration) { | ||
| const declOpts = opts.declaration === true ? {} : opts.declaration; | ||
| const enc = declOpts.encoding || "UTF-8"; | ||
| const sa = declOpts.standalone; | ||
| let decl = "<?xml version=\"1.0\" encoding=\"" + enc + "\""; | ||
| if (sa) decl += " standalone=\"" + sa + "\""; | ||
| decl += "?>"; | ||
| parts.push(decl); | ||
| if (opts.indent) parts.push("\n"); | ||
| } | ||
| const items = Array.isArray(input) ? input : [input]; | ||
| for (let i = 0; i < items.length; i++) { | ||
| 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"); | ||
| } | ||
| return parts.join(""); | ||
| } | ||
| function normalizeOptions$1(options) { | ||
| const opts = typeof options === "object" && !Array.isArray(options) ? options : { indent: options }; | ||
| let indent = ""; | ||
| if (opts.indent) indent = opts.indent === true ? DEFAULT_INDENT : String(opts.indent); | ||
| return { | ||
| indent, | ||
| declaration: opts.declaration | ||
| }; | ||
| } | ||
| /** | ||
| * 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}/>`; | ||
| } | ||
| 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 = 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"); | ||
| 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}>`); | ||
| return parts.join(""); | ||
| } | ||
| //#endregion | ||
| //#region src/parse.ts | ||
| const ENTITY_MAP = { | ||
| "&": "&", | ||
| "<": "<", | ||
| ">": ">", | ||
| """: "\"", | ||
| "'": "'" | ||
| }; | ||
| const ENTITY_PATTERN = /&(amp|lt|gt|quot|apos);/g; | ||
| function unescapeXml(str) { | ||
| return str.replace(ENTITY_PATTERN, (match) => ENTITY_MAP[match]); | ||
| } | ||
| function nativeTypeValue(value) { | ||
| if (value === "") return value; | ||
| const n = Number(value); | ||
| if (!isNaN(n)) return n; | ||
| const lower = value.toLowerCase(); | ||
| if (lower === "true") return true; | ||
| if (lower === "false") return false; | ||
| return value; | ||
| } | ||
| function xml2js(xmlString, options) { | ||
| const captureSpaces = options?.captureSpacesBetweenElements ?? false; | ||
| const trim = options?.trim ?? false; | ||
| const ignoreDeclaration = options?.ignoreDeclaration ?? false; | ||
| const ignoreText = options?.ignoreText ?? false; | ||
| const ignoreComment = options?.ignoreComment ?? false; | ||
| const ignoreCdata = options?.ignoreCdata ?? false; | ||
| const ignoreDoctype = options?.ignoreDoctype ?? false; | ||
| const nativeTypeAttributes = options?.nativeTypeAttributes ?? false; | ||
| const result = {}; | ||
| const stack = [result]; | ||
| let i = 0; | ||
| const len = xmlString.length; | ||
| while (i < len) { | ||
| if (!captureSpaces && isWhitespace(xmlString.charCodeAt(i))) { | ||
| i++; | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) !== 60) { | ||
| const start = i; | ||
| while (i < len && xmlString.charCodeAt(i) !== 60) i++; | ||
| let text = unescapeXml(xmlString.slice(start, i)); | ||
| if (trim) text = text.trim(); | ||
| if (ignoreText) continue; | ||
| if (text.length > 0) { | ||
| if (captureSpaces || text.trim().length > 0) addField(stack[stack.length - 1], "text", text); | ||
| } | ||
| continue; | ||
| } | ||
| i++; | ||
| if (xmlString.charCodeAt(i) === 63) { | ||
| const end = xmlString.indexOf("?>", i + 1); | ||
| if (end === -1) break; | ||
| const body = xmlString.slice(i + 1, end); | ||
| i = end + 2; | ||
| const xmlMatch = body.match(/^xml\s+(.*)$/s); | ||
| if (xmlMatch) { | ||
| if (!ignoreDeclaration) { | ||
| if (!result.declaration) result.declaration = {}; | ||
| const attrs = parseAttributes(xmlMatch[1]); | ||
| if (nativeTypeAttributes) for (const key of Object.keys(attrs)) attrs[key] = nativeTypeValue(attrs[key]); | ||
| result.declaration.attributes = attrs; | ||
| } | ||
| } | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 3) === "!--") { | ||
| const end = xmlString.indexOf("-->", i + 3); | ||
| if (end === -1) break; | ||
| const comment = xmlString.slice(i + 3, end); | ||
| i = end + 3; | ||
| if (!ignoreComment) if (trim) addField(stack[stack.length - 1], "comment", comment.trim()); | ||
| else addField(stack[stack.length - 1], "comment", comment); | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 8) === "![CDATA[") { | ||
| const end = xmlString.indexOf("]]>", i + 8); | ||
| if (end === -1) break; | ||
| const cdata = xmlString.slice(i + 8, end); | ||
| i = end + 3; | ||
| if (!ignoreCdata) if (trim) addField(stack[stack.length - 1], "cdata", cdata.trim()); | ||
| else addField(stack[stack.length - 1], "cdata", cdata); | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 9) === "!DOCTYPE") { | ||
| const end = xmlString.indexOf(">", i + 9); | ||
| if (end === -1) break; | ||
| const doctype = xmlString.slice(i + 9, end).trim(); | ||
| i = end + 1; | ||
| if (!ignoreDoctype) addField(stack[stack.length - 1], "doctype", doctype); | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 47) { | ||
| const end = xmlString.indexOf(">", i + 1); | ||
| if (end === -1) break; | ||
| i = end + 1; | ||
| stack.pop(); | ||
| continue; | ||
| } | ||
| const tagNameEnd = findTagNameEnd(xmlString, i); | ||
| const tagName = xmlString.slice(i, tagNameEnd); | ||
| let pos = tagNameEnd; | ||
| const attributes = parseAttributesFromXml(xmlString, pos); | ||
| pos = attributes.pos; | ||
| if (nativeTypeAttributes) for (const key of Object.keys(attributes.attrs)) attributes.attrs[key] = nativeTypeValue(attributes.attrs[key]); | ||
| const isSelfClosing = xmlString.charCodeAt(pos) === 47; | ||
| if (isSelfClosing) pos += 2; | ||
| else pos++; | ||
| const element = { | ||
| type: "element", | ||
| name: tagName | ||
| }; | ||
| if (Object.keys(attributes.attrs).length > 0) element.attributes = attributes.attrs; | ||
| const parent = stack[stack.length - 1]; | ||
| if (!parent.elements) parent.elements = []; | ||
| parent.elements.push(element); | ||
| if (!isSelfClosing) stack.push(element); | ||
| i = pos; | ||
| } | ||
| if (result.elements) { | ||
| const temp = result.elements; | ||
| delete result.elements; | ||
| result.elements = temp; | ||
| delete result.text; | ||
| } | ||
| return result; | ||
| } | ||
| function findTagNameEnd(str, start) { | ||
| let i = start; | ||
| const len = str.length; | ||
| while (i < len) { | ||
| const ch = str.charCodeAt(i); | ||
| if (ch === 32 || ch === 9 || ch === 10 || ch === 13 || ch === 47 || ch === 62) return i; | ||
| i++; | ||
| } | ||
| return i; | ||
| } | ||
| function parseAttributesFromXml(str, start) { | ||
| const attrs = {}; | ||
| let i = start; | ||
| const len = str.length; | ||
| while (i < len) { | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| if (i >= len || str.charCodeAt(i) === 62 || str.charCodeAt(i) === 47) break; | ||
| const nameStart = i; | ||
| while (i < len && str.charCodeAt(i) !== 61) { | ||
| if (str.charCodeAt(i) === 62 || str.charCodeAt(i) === 47) break; | ||
| i++; | ||
| } | ||
| const name = str.slice(nameStart, i); | ||
| if (str.charCodeAt(i) !== 61) break; | ||
| i++; | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| const quote = str.charCodeAt(i); | ||
| if (quote !== 34 && quote !== 39) break; | ||
| i++; | ||
| const valueStart = i; | ||
| while (i < len && str.charCodeAt(i) !== quote) i++; | ||
| attrs[name] = str.slice(valueStart, i); | ||
| i++; | ||
| } | ||
| return { | ||
| attrs, | ||
| pos: i | ||
| }; | ||
| } | ||
| function parseAttributes(str) { | ||
| const result = {}; | ||
| let i = 0; | ||
| const len = str.length; | ||
| while (i < len) { | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| if (i >= len) break; | ||
| const nameStart = i; | ||
| while (i < len && str.charCodeAt(i) !== 61) { | ||
| if (isWhitespace(str.charCodeAt(i))) break; | ||
| i++; | ||
| } | ||
| const name = str.slice(nameStart, i); | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| if (i >= len || str.charCodeAt(i) !== 61) break; | ||
| i++; | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| const quote = str.charCodeAt(i); | ||
| if (quote !== 34 && quote !== 39) break; | ||
| i++; | ||
| const valueStart = i; | ||
| while (i < len && str.charCodeAt(i) !== quote) i++; | ||
| result[name] = str.slice(valueStart, i); | ||
| i++; | ||
| } | ||
| return result; | ||
| } | ||
| function addField(parent, type, value) { | ||
| if (!parent.elements) parent.elements = []; | ||
| const element = { type }; | ||
| element[type] = value; | ||
| parent.elements.push(element); | ||
| } | ||
| function isWhitespace(ch) { | ||
| return ch === 32 || ch === 9 || ch === 10 || ch === 13; | ||
| } | ||
| //#endregion | ||
| //#region src/stringify.ts | ||
| function js2xml(js, options) { | ||
| const opts = normalizeOptions(options); | ||
| const parts = []; | ||
| if (js.declaration && !opts.ignoreDeclaration) parts.push(writeDeclaration(js.declaration)); | ||
| if (js.elements?.length) parts.push(writeElements(js.elements, opts, 0, !parts.length)); | ||
| return parts.join(""); | ||
| } | ||
| /** Alias for js2xml — xml-js compatible export */ | ||
| function json2xml(json, options) { | ||
| return js2xml(json, options); | ||
| } | ||
| function normalizeOptions(options) { | ||
| if (!options) return { | ||
| spaces: "", | ||
| ignoreDeclaration: false, | ||
| ignoreText: false, | ||
| ignoreComment: false, | ||
| ignoreCdata: false, | ||
| ignoreDoctype: false, | ||
| fullTagEmptyElement: false, | ||
| indentText: false, | ||
| indentCdata: false | ||
| }; | ||
| let spaces = ""; | ||
| if (options.spaces != null) spaces = typeof options.spaces === "number" ? " ".repeat(options.spaces) : options.spaces; | ||
| return { | ||
| spaces, | ||
| ignoreDeclaration: options.ignoreDeclaration ?? false, | ||
| ignoreText: options.ignoreText ?? false, | ||
| ignoreComment: options.ignoreComment ?? false, | ||
| ignoreCdata: options.ignoreCdata ?? false, | ||
| ignoreDoctype: options.ignoreDoctype ?? false, | ||
| fullTagEmptyElement: options.fullTagEmptyElement ?? false, | ||
| indentText: options.indentText ?? false, | ||
| indentCdata: options.indentCdata ?? false, | ||
| attributeValueFn: options.attributeValueFn | ||
| }; | ||
| } | ||
| function writeIndentation(spaces, depth, firstLine) { | ||
| return (!firstLine && spaces ? "\n" : "") + spaces.repeat(depth); | ||
| } | ||
| function writeDeclaration(declaration) { | ||
| const attrs = declaration.attributes; | ||
| if (!attrs) return "<?xml version=\"1.0\"?>"; | ||
| let result = "<?xml version=\"1.0\""; | ||
| if (attrs.encoding) result += ` encoding="${attrs.encoding}"`; | ||
| if (attrs.standalone) result += ` standalone="${attrs.standalone}"`; | ||
| return result + "?>"; | ||
| } | ||
| function writeAttributes(attributes, elementName, element, attributeValueFn) { | ||
| const parts = []; | ||
| for (const key of Object.keys(attributes)) { | ||
| const value = attributes[key]; | ||
| if (value === null || value === void 0) continue; | ||
| let attr = String(value).replace(/"/g, """); | ||
| if (attributeValueFn) attr = attributeValueFn(attr, key, elementName, element); | ||
| parts.push(` ${key}="${attr}"`); | ||
| } | ||
| return parts.join(""); | ||
| } | ||
| function writeElement(element, opts, depth) { | ||
| if (!element.name) return ""; | ||
| const name = element.name; | ||
| const attrStr = element.attributes ? writeAttributes(element.attributes, name, element, opts.attributeValueFn) : ""; | ||
| if (!((element.elements?.length ?? 0) > 0 || element.attributes?.["xml:space"] === "preserve" || opts.fullTagEmptyElement)) return `<${name}${attrStr}/>`; | ||
| const parts = []; | ||
| parts.push(`<${name}${attrStr}>`); | ||
| const hasChildElements = element.elements?.some((e) => e.type === "element") ?? false; | ||
| if (element.elements?.length) parts.push(writeElements(element.elements, opts, depth + 1, false)); | ||
| if (opts.spaces && hasChildElements) parts.push("\n" + opts.spaces.repeat(depth)); | ||
| parts.push(`</${name}>`); | ||
| return parts.join(""); | ||
| } | ||
| function writeElements(elements, opts, depth, firstLine) { | ||
| let result = ""; | ||
| for (let i = 0; i < elements.length; i++) { | ||
| const element = elements[i]; | ||
| const isFirst = firstLine && i === 0; | ||
| switch (element.type) { | ||
| case "element": | ||
| result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeElement(element, opts, depth); | ||
| break; | ||
| case "text": | ||
| if (opts.ignoreText) continue; | ||
| if (opts.indentText) result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeText(element.text); | ||
| break; | ||
| case "cdata": | ||
| if (opts.ignoreCdata) continue; | ||
| if (opts.indentCdata) result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeCdata(element.cdata); | ||
| break; | ||
| case "comment": | ||
| if (opts.ignoreComment) continue; | ||
| result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeComment(element.comment); | ||
| break; | ||
| case "doctype": | ||
| if (opts.ignoreDoctype) continue; | ||
| result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeDoctype(element.doctype); | ||
| break; | ||
| default: break; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function writeText(text) { | ||
| if (text == null) return ""; | ||
| const str = String(text); | ||
| for (let i = 0; i < str.length; i++) { | ||
| const c = str.charCodeAt(i); | ||
| if (c === 38 || c === 60 || c === 62) { | ||
| let s = ""; | ||
| let last = 0; | ||
| for (let j = i; j < str.length; j++) { | ||
| const cj = str.charCodeAt(j); | ||
| if (cj === 38) { | ||
| s += str.slice(last, j) + "&"; | ||
| last = j + 1; | ||
| } else if (cj === 60) { | ||
| s += str.slice(last, j) + "<"; | ||
| last = j + 1; | ||
| } else if (cj === 62) { | ||
| s += str.slice(last, j) + ">"; | ||
| last = j + 1; | ||
| } | ||
| } | ||
| return s + str.slice(last); | ||
| } | ||
| } | ||
| return str; | ||
| } | ||
| function writeCdata(cdata) { | ||
| if (cdata == null) return ""; | ||
| return `<![CDATA[${cdata.replace(/\]\]>/g, "]]]]><![CDATA[>")}]]>`; | ||
| } | ||
| function writeComment(comment) { | ||
| if (comment == null) return ""; | ||
| return `<!--${comment}-->`; | ||
| } | ||
| function writeDoctype(doctype) { | ||
| if (doctype == null) return ""; | ||
| return `<!DOCTYPE ${doctype}>`; | ||
| } | ||
| //#endregion | ||
| //#region src/convert.ts | ||
| /** | ||
| * Convert XmlObject (node-xml format) directly to Element (xml-js format). | ||
| * Eliminates the redundant xml() → xml2js() bridge path. | ||
| */ | ||
| function toElement(xmlObject) { | ||
| const tagName = Object.keys(xmlObject)[0]; | ||
| const value = xmlObject[tagName]; | ||
| const element = { | ||
| type: "element", | ||
| name: tagName | ||
| }; | ||
| if (value == null) return element; | ||
| if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { | ||
| element.elements = [{ | ||
| type: "text", | ||
| text: String(value) | ||
| }]; | ||
| return element; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| const children = []; | ||
| for (const item of value) if (item && typeof item === "object" && "_attr" in item) element.attributes = item._attr; | ||
| else if (item && typeof item === "object") if (Object.keys(item)[0] === "_cdata") children.push({ | ||
| type: "cdata", | ||
| cdata: String(item._cdata) | ||
| }); | ||
| else children.push(toElement(item)); | ||
| else if (item != null) children.push({ | ||
| type: "text", | ||
| text: String(item) | ||
| }); | ||
| if (children.length > 0) element.elements = children; | ||
| return element; | ||
| } | ||
| if (typeof value === "object") { | ||
| if (value._attr) element.attributes = value._attr; | ||
| if (value._cdata) element.elements = [{ | ||
| type: "cdata", | ||
| cdata: String(value._cdata) | ||
| }]; | ||
| } | ||
| return element; | ||
| } | ||
| //#endregion | ||
| //#region src/json.ts | ||
| /** Convert XML string to JSON string — xml-js compatible export */ | ||
| function xml2json(xml, options) { | ||
| return JSON.stringify(xml2js(xml, options)); | ||
| } | ||
| //#endregion | ||
| export { allChildren, attr, attrBool, attrNum, attrs, childCount, childText, children, collectText, colorAttr, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, nativeTypeValue, parseAttributes, selfCloseElement, textOf, toElement, unescapeXml, xml, xml2js, xml2json }; |
| //#region src/types.d.ts | ||
| interface Attributes { | ||
| [key: string]: string | number | undefined; | ||
| } | ||
| interface DeclarationAttributes { | ||
| version?: string | number; | ||
| encoding?: string; | ||
| standalone?: string; | ||
| } | ||
| interface Element { | ||
| declaration?: { | ||
| attributes?: DeclarationAttributes; | ||
| }; | ||
| instruction?: string; | ||
| attributes?: Attributes; | ||
| cdata?: string; | ||
| doctype?: string; | ||
| comment?: string; | ||
| text?: string | number | boolean; | ||
| type?: string; | ||
| name?: string; | ||
| elements?: Element[]; | ||
| parent?: Element; | ||
| } | ||
| interface ElementCompact { | ||
| [key: string]: any; | ||
| _declaration?: { | ||
| _attributes?: DeclarationAttributes; | ||
| }; | ||
| _instruction?: { | ||
| [key: string]: string; | ||
| }; | ||
| _attributes?: Attributes; | ||
| _cdata?: string; | ||
| _doctype?: string; | ||
| _comment?: string; | ||
| _text?: string | number; | ||
| } | ||
| interface IgnoreOptions { | ||
| ignoreDeclaration?: boolean; | ||
| ignoreInstruction?: boolean; | ||
| ignoreAttributes?: boolean; | ||
| ignoreComment?: boolean; | ||
| ignoreCdata?: boolean; | ||
| ignoreDoctype?: boolean; | ||
| ignoreText?: boolean; | ||
| } | ||
| interface Xml2JsOptions extends IgnoreOptions { | ||
| compact?: boolean; | ||
| trim?: boolean; | ||
| sanitize?: boolean; | ||
| nativeType?: boolean; | ||
| nativeTypeAttributes?: boolean; | ||
| addParent?: boolean; | ||
| alwaysArray?: boolean | string[]; | ||
| alwaysChildren?: boolean; | ||
| instructionHasAttributes?: boolean; | ||
| captureSpacesBetweenElements?: boolean; | ||
| doctypeFn?: (value: string, parentElement: object) => string; | ||
| instructionFn?: (value: string, instructionName: string, parentElement: string) => string; | ||
| cdataFn?: (value: string, parentElement: object) => string; | ||
| commentFn?: (value: string, parentElement: object) => string; | ||
| textFn?: (value: string, parentElement: object) => string; | ||
| instructionNameFn?: (instructionName: string, instructionValue: string, parentElement: string) => string; | ||
| elementNameFn?: (value: string, parentElement: object) => string; | ||
| attributeNameFn?: (attributeName: string, attributeValue: string, parentElement: string) => string; | ||
| attributeValueFn?: (attributeValue: string, attributeName: string, parentElement: string) => string; | ||
| attributesFn?: (value: Attributes, parentElement: string) => Attributes; | ||
| } | ||
| interface Js2XmlOptions extends IgnoreOptions { | ||
| spaces?: number | string; | ||
| compact?: boolean; | ||
| indentText?: boolean; | ||
| indentCdata?: boolean; | ||
| indentAttributes?: boolean; | ||
| indentInstruction?: boolean; | ||
| fullTagEmptyElement?: boolean; | ||
| noQuotesForNativeAttributes?: boolean; | ||
| doctypeFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| instructionFn?: (instructionValue: string, instructionName: string, currentElementName: string, currentElementObj: object) => string; | ||
| cdataFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| commentFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| textFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| instructionNameFn?: (instructionName: string, instructionValue: string, currentElementName: string, currentElementObj: object) => string; | ||
| elementNameFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| attributeNameFn?: (attributeName: string, attributeValue: string, currentElementName: string, currentElementObj: object) => string; | ||
| attributeValueFn?: (attributeValue: string, attributeName: string, currentElementName: string, currentElementObj: object) => string; | ||
| attributesFn?: (value: Attributes, currentElementName: string, currentElementObj: object) => Attributes; | ||
| fullTagEmptyElementFn?: (currentElementName: string, currentElementObj: object) => boolean; | ||
| } | ||
| interface XmlOption { | ||
| indent?: string; | ||
| stream?: boolean; | ||
| declaration?: boolean | { | ||
| encoding?: string; | ||
| standalone?: string; | ||
| }; | ||
| } | ||
| interface XmlAttrs { | ||
| [attr: string]: XmlAtom; | ||
| } | ||
| type XmlAtom = string | number | boolean | null; | ||
| interface ElementObject { | ||
| push(xmlObject: XmlObject): void; | ||
| close(xmlObject?: XmlObject): void; | ||
| } | ||
| type XmlDesc = { | ||
| _attr: XmlAttrs; | ||
| } | { | ||
| _cdata: string; | ||
| } | { | ||
| _attr: XmlAttrs; | ||
| _cdata: string; | ||
| } | XmlAtom | XmlAtom[] | XmlDescArray; | ||
| interface XmlDescArray { | ||
| [index: number]: { | ||
| _attr: XmlAttrs; | ||
| } | XmlObject; | ||
| } | ||
| type XmlObject = { | ||
| [tag: string]: ElementObject | XmlDesc; | ||
| } | XmlDesc; | ||
| //#endregion | ||
| //#region src/utils.d.ts | ||
| /** | ||
| * Find the first direct child element with the given name. | ||
| */ | ||
| declare function findChild(parent: Element | undefined, name: string): Element | undefined; | ||
| /** | ||
| * Get all direct child elements matching the given name. | ||
| */ | ||
| declare function children(parent: Element | undefined, name: string): Element[]; | ||
| /** | ||
| * Get all direct child elements. | ||
| */ | ||
| declare function allChildren(parent: Element | undefined): Element[]; | ||
| /** | ||
| * Get text content of the first child element with the given name. | ||
| */ | ||
| declare function childText(parent: Element | undefined, name: string): string; | ||
| /** | ||
| * Get text content of an element. | ||
| * Handles cases where text may be directly on .text or in a child element. | ||
| */ | ||
| declare function textOf(element: Element | undefined): string; | ||
| /** | ||
| * Collect text from all direct text nodes within an element. | ||
| */ | ||
| declare function collectText(element: Element | undefined): string; | ||
| /** | ||
| * Get an attribute value as a string. | ||
| */ | ||
| declare function attr(element: Element | undefined, name: string): string | undefined; | ||
| /** | ||
| * Get an attribute value as a number. | ||
| */ | ||
| declare function attrNum(element: Element | undefined, name: string): number | undefined; | ||
| /** | ||
| * Get an attribute value as a boolean. | ||
| */ | ||
| declare function attrBool(element: Element | undefined, name: string): boolean | undefined; | ||
| /** | ||
| * Get a hex color attribute, handling nativeTypeValue coercion. | ||
| * nativeTypeAttributes converts "000000" → 0 (number); this recovers | ||
| * the original 6-digit hex string by zero-padding numeric values. | ||
| */ | ||
| declare function colorAttr(element: Element | undefined, name: string): string | undefined; | ||
| /** | ||
| * Check if an element has a specific child element. | ||
| */ | ||
| declare function hasChild(parent: Element | undefined, name: string): boolean; | ||
| /** | ||
| * Find deep descendant elements matching the given name. | ||
| */ | ||
| declare function findDeep(parent: Element | undefined, name: string): Element[]; | ||
| /** | ||
| * Get the number of direct child elements. | ||
| */ | ||
| declare function childCount(parent: Element | undefined): number; | ||
| //#endregion | ||
| export { XmlAttrs as C, XmlOption as D, XmlObject as E, XmlAtom as S, XmlDescArray as T, ElementCompact as _, childCount as a, Js2XmlOptions as b, collectText as c, findDeep as d, hasChild as f, Element as g, DeclarationAttributes as h, attrNum as i, colorAttr as l, Attributes as m, attr as n, childText as o, textOf as p, attrBool as r, children as s, allChildren as t, findChild as u, ElementObject as v, XmlDesc as w, Xml2JsOptions as x, IgnoreOptions as y }; |
| import { a as childCount, c as collectText, d as findDeep, f as hasChild, i as attrNum, l as colorAttr, n as attr, o as childText, p as textOf, r as attrBool, s as children, t as allChildren, u as findChild } from "./utils-DTU54qj_.mjs"; | ||
| export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf }; |
+116
| //#region src/utils.ts | ||
| /** | ||
| * Find the first direct child element with the given name. | ||
| */ | ||
| function findChild(parent, name) { | ||
| return parent?.elements?.find((e) => e.name === name); | ||
| } | ||
| /** | ||
| * Get all direct child elements matching the given name. | ||
| */ | ||
| function children(parent, name) { | ||
| return parent?.elements?.filter((e) => e.name === name) ?? []; | ||
| } | ||
| /** | ||
| * Get all direct child elements. | ||
| */ | ||
| function allChildren(parent) { | ||
| return parent?.elements ?? []; | ||
| } | ||
| /** | ||
| * Get text content of the first child element with the given name. | ||
| */ | ||
| function childText(parent, name) { | ||
| return textOf(findChild(parent, name)); | ||
| } | ||
| /** | ||
| * Get text content of an element. | ||
| * Handles cases where text may be directly on .text or in a child element. | ||
| */ | ||
| function textOf(element) { | ||
| if (!element) return ""; | ||
| if (element.text !== void 0 && typeof element.text === "string") return element.text; | ||
| if (element.elements && element.elements.length > 0) return element.elements.map((e) => typeof e.text === "string" ? e.text : "").join(""); | ||
| return ""; | ||
| } | ||
| /** | ||
| * Collect text from all direct text nodes within an element. | ||
| */ | ||
| function collectText(element) { | ||
| if (!element) return ""; | ||
| const parts = []; | ||
| collectTextRecursive(element, parts); | ||
| return parts.join(""); | ||
| } | ||
| function collectTextRecursive(element, parts) { | ||
| if (!element) return; | ||
| if (element.text !== void 0 && typeof element.text === "string") parts.push(element.text); | ||
| if (element.elements) for (const child of element.elements) collectTextRecursive(child, parts); | ||
| } | ||
| /** | ||
| * Get an attribute value as a string. | ||
| */ | ||
| function attr(element, name) { | ||
| const v = element?.attributes?.[name]; | ||
| return v !== void 0 ? String(v) : void 0; | ||
| } | ||
| /** | ||
| * Get an attribute value as a number. | ||
| */ | ||
| function attrNum(element, name) { | ||
| const v = element?.attributes?.[name]; | ||
| if (v === void 0) return void 0; | ||
| const n = Number(v); | ||
| return isNaN(n) ? void 0 : n; | ||
| } | ||
| /** | ||
| * Get an attribute value as a boolean. | ||
| */ | ||
| function attrBool(element, name) { | ||
| const v = element?.attributes?.[name]; | ||
| if (v === void 0) return void 0; | ||
| if (typeof v === "boolean") return v; | ||
| const lower = String(v).toLowerCase(); | ||
| if (lower === "true" || lower === "1") return true; | ||
| if (lower === "false" || lower === "0") return false; | ||
| } | ||
| /** | ||
| * Get a hex color attribute, handling nativeTypeValue coercion. | ||
| * nativeTypeAttributes converts "000000" → 0 (number); this recovers | ||
| * the original 6-digit hex string by zero-padding numeric values. | ||
| */ | ||
| function colorAttr(element, name) { | ||
| const raw = element?.attributes?.[name]; | ||
| if (raw === void 0 || raw === "") return void 0; | ||
| if (typeof raw === "boolean") return void 0; | ||
| if (typeof raw === "number") return String(raw).padStart(6, "0"); | ||
| if (raw === "auto") return "auto"; | ||
| if (/^[0-9A-Fa-f]{6}$/.test(raw)) return raw; | ||
| return raw; | ||
| } | ||
| /** | ||
| * Check if an element has a specific child element. | ||
| */ | ||
| function hasChild(parent, name) { | ||
| return parent?.elements?.some((e) => e.name === name) ?? false; | ||
| } | ||
| /** | ||
| * Find deep descendant elements matching the given name. | ||
| */ | ||
| function findDeep(parent, name) { | ||
| const result = []; | ||
| if (!parent) return result; | ||
| for (const child of parent.elements ?? []) { | ||
| if (child.name === name) result.push(child); | ||
| result.push(...findDeep(child, name)); | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Get the number of direct child elements. | ||
| */ | ||
| function childCount(parent) { | ||
| return parent?.elements?.length ?? 0; | ||
| } | ||
| //#endregion | ||
| export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf }; |
+7
-7
| { | ||
| "name": "@office-open/xml", | ||
| "version": "0.6.6", | ||
| "version": "0.6.7", | ||
| "description": "XML parsing and serialization for Office Open XML. Zero dependencies, drop-in replacement for xml + xml-js.", | ||
@@ -31,12 +31,12 @@ "keywords": [ | ||
| "type": "module", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "main": "dist/index.mjs", | ||
| "types": "dist/index.d.mts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js" | ||
| "types": "./dist/index.d.mts", | ||
| "import": "./dist/index.mjs" | ||
| }, | ||
| "./utils": { | ||
| "types": "./dist/utils.d.ts", | ||
| "import": "./dist/utils.js" | ||
| "types": "./dist/utils.d.mts", | ||
| "import": "./dist/utils.mjs" | ||
| } | ||
@@ -43,0 +43,0 @@ }, |
| 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 "./utils-Dh-Jl3ng.js"; | ||
| //#region src/serialize.d.ts | ||
| declare function xml(input: Record<string, any> | Record<string, any>[], options?: boolean | string | { | ||
| indent?: boolean | string; | ||
| declaration?: boolean | { | ||
| encoding?: string; | ||
| standalone?: string; | ||
| }; | ||
| }): string; | ||
| //#endregion | ||
| //#region src/parse.d.ts | ||
| declare function unescapeXml(str: string): string; | ||
| declare function nativeTypeValue(value: string): string | number | boolean; | ||
| declare function xml2js(xmlString: string, options?: Xml2JsOptions): Element; | ||
| declare function parseAttributes(str: string): Record<string, string>; | ||
| //#endregion | ||
| //#region src/stringify.d.ts | ||
| declare function js2xml(js: Element, options?: Js2XmlOptions): string; | ||
| /** Alias for js2xml — xml-js compatible export */ | ||
| declare function json2xml(json: Element, options?: Js2XmlOptions): string; | ||
| //#endregion | ||
| //#region src/convert.d.ts | ||
| /** | ||
| * Convert XmlObject (node-xml format) directly to Element (xml-js format). | ||
| * Eliminates the redundant xml() → xml2js() bridge path. | ||
| */ | ||
| declare function toElement(xmlObject: Record<string, any>): Element; | ||
| //#endregion | ||
| //#region src/escape.d.ts | ||
| /** Escape text content for XML. Fast path returns original string when no special chars. */ | ||
| declare function escapeXml(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; | ||
| /** | ||
| * Build a self-closing XML element: `<tag attrStr/>`. | ||
| * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined. | ||
| */ | ||
| declare function selfCloseElement(tag: string, attrStr?: string): string; | ||
| //#endregion | ||
| //#region src/json.d.ts | ||
| /** Convert XML string to JSON string — xml-js compatible export */ | ||
| declare function xml2json(xml: string, options?: Xml2JsOptions): string; | ||
| //#endregion | ||
| 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, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, nativeTypeValue, parseAttributes, selfCloseElement, textOf, toElement, unescapeXml, xml, xml2js, xml2json }; |
-523
| import { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf } from "./utils.js"; | ||
| //#region src/escape.ts | ||
| /** Escape text content for XML. Fast path returns original string when no special chars. */ | ||
| function escapeXml(str) { | ||
| for (let i = 0; i < str.length; i++) { | ||
| const c = str.charCodeAt(i); | ||
| if (c === 38 || c === 34 || c === 39 || c === 60 || c === 62) { | ||
| let s = ""; | ||
| let last = 0; | ||
| for (let j = i; j < str.length; j++) { | ||
| const cj = str.charCodeAt(j); | ||
| if (cj === 38) { | ||
| s += str.slice(last, j) + "&"; | ||
| last = j + 1; | ||
| } else if (cj === 34) { | ||
| s += str.slice(last, j) + """; | ||
| last = j + 1; | ||
| } else if (cj === 39) { | ||
| s += str.slice(last, j) + "'"; | ||
| last = j + 1; | ||
| } else if (cj === 60) { | ||
| s += str.slice(last, j) + "<"; | ||
| last = j + 1; | ||
| } else if (cj === 62) { | ||
| s += str.slice(last, j) + ">"; | ||
| last = j + 1; | ||
| } | ||
| } | ||
| return s + str.slice(last); | ||
| } | ||
| } | ||
| return str; | ||
| } | ||
| /** | ||
| * 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 = ""; | ||
| const keys = Object.keys(record); | ||
| for (let i = 0; i < keys.length; i++) { | ||
| const v = record[keys[i]]; | ||
| if (v !== void 0) s += ` ${keys[i]}="${typeof v === "string" ? escapeXml(v) : v}"`; | ||
| } | ||
| return s; | ||
| } | ||
| /** | ||
| * Build a self-closing XML element: `<tag attrStr/>`. | ||
| * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined. | ||
| */ | ||
| function selfCloseElement(tag, attrStr) { | ||
| return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`; | ||
| } | ||
| //#endregion | ||
| //#region src/serialize.ts | ||
| const DEFAULT_INDENT = " "; | ||
| function xml(input, options) { | ||
| const opts = normalizeOptions$1(options); | ||
| const parts = []; | ||
| if (opts.declaration) { | ||
| const declOpts = opts.declaration === true ? {} : opts.declaration; | ||
| const enc = declOpts.encoding || "UTF-8"; | ||
| const sa = declOpts.standalone; | ||
| let decl = "<?xml version=\"1.0\" encoding=\"" + enc + "\""; | ||
| if (sa) decl += " standalone=\"" + sa + "\""; | ||
| decl += "?>"; | ||
| parts.push(decl); | ||
| if (opts.indent) parts.push("\n"); | ||
| } | ||
| const items = Array.isArray(input) ? input : [input]; | ||
| for (let i = 0; i < items.length; i++) { | ||
| 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"); | ||
| } | ||
| return parts.join(""); | ||
| } | ||
| function normalizeOptions$1(options) { | ||
| const opts = typeof options === "object" && !Array.isArray(options) ? options : { indent: options }; | ||
| let indent = ""; | ||
| if (opts.indent) indent = opts.indent === true ? DEFAULT_INDENT : String(opts.indent); | ||
| return { | ||
| indent, | ||
| declaration: opts.declaration | ||
| }; | ||
| } | ||
| /** | ||
| * 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}/>`; | ||
| } | ||
| 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 = 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"); | ||
| 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}>`); | ||
| return parts.join(""); | ||
| } | ||
| //#endregion | ||
| //#region src/parse.ts | ||
| const ENTITY_MAP = { | ||
| "&": "&", | ||
| "<": "<", | ||
| ">": ">", | ||
| """: "\"", | ||
| "'": "'" | ||
| }; | ||
| const ENTITY_PATTERN = /&(amp|lt|gt|quot|apos);/g; | ||
| function unescapeXml(str) { | ||
| return str.replace(ENTITY_PATTERN, (match) => ENTITY_MAP[match]); | ||
| } | ||
| function nativeTypeValue(value) { | ||
| if (value === "") return value; | ||
| const n = Number(value); | ||
| if (!isNaN(n)) return n; | ||
| const lower = value.toLowerCase(); | ||
| if (lower === "true") return true; | ||
| if (lower === "false") return false; | ||
| return value; | ||
| } | ||
| function xml2js(xmlString, options) { | ||
| const captureSpaces = options?.captureSpacesBetweenElements ?? false; | ||
| const trim = options?.trim ?? false; | ||
| const ignoreDeclaration = options?.ignoreDeclaration ?? false; | ||
| const ignoreText = options?.ignoreText ?? false; | ||
| const ignoreComment = options?.ignoreComment ?? false; | ||
| const ignoreCdata = options?.ignoreCdata ?? false; | ||
| const ignoreDoctype = options?.ignoreDoctype ?? false; | ||
| const nativeTypeAttributes = options?.nativeTypeAttributes ?? false; | ||
| const result = {}; | ||
| const stack = [result]; | ||
| let i = 0; | ||
| const len = xmlString.length; | ||
| while (i < len) { | ||
| if (!captureSpaces && isWhitespace(xmlString.charCodeAt(i))) { | ||
| i++; | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) !== 60) { | ||
| const start = i; | ||
| while (i < len && xmlString.charCodeAt(i) !== 60) i++; | ||
| let text = unescapeXml(xmlString.slice(start, i)); | ||
| if (trim) text = text.trim(); | ||
| if (ignoreText) continue; | ||
| if (text.length > 0) { | ||
| if (captureSpaces || text.trim().length > 0) addField(stack[stack.length - 1], "text", text); | ||
| } | ||
| continue; | ||
| } | ||
| i++; | ||
| if (xmlString.charCodeAt(i) === 63) { | ||
| const end = xmlString.indexOf("?>", i + 1); | ||
| if (end === -1) break; | ||
| const body = xmlString.slice(i + 1, end); | ||
| i = end + 2; | ||
| const xmlMatch = body.match(/^xml\s+(.*)$/s); | ||
| if (xmlMatch) { | ||
| if (!ignoreDeclaration) { | ||
| if (!result.declaration) result.declaration = {}; | ||
| const attrs = parseAttributes(xmlMatch[1]); | ||
| if (nativeTypeAttributes) for (const key of Object.keys(attrs)) attrs[key] = nativeTypeValue(attrs[key]); | ||
| result.declaration.attributes = attrs; | ||
| } | ||
| } | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 3) === "!--") { | ||
| const end = xmlString.indexOf("-->", i + 3); | ||
| if (end === -1) break; | ||
| const comment = xmlString.slice(i + 3, end); | ||
| i = end + 3; | ||
| if (!ignoreComment) if (trim) addField(stack[stack.length - 1], "comment", comment.trim()); | ||
| else addField(stack[stack.length - 1], "comment", comment); | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 8) === "![CDATA[") { | ||
| const end = xmlString.indexOf("]]>", i + 8); | ||
| if (end === -1) break; | ||
| const cdata = xmlString.slice(i + 8, end); | ||
| i = end + 3; | ||
| if (!ignoreCdata) if (trim) addField(stack[stack.length - 1], "cdata", cdata.trim()); | ||
| else addField(stack[stack.length - 1], "cdata", cdata); | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 9) === "!DOCTYPE") { | ||
| const end = xmlString.indexOf(">", i + 9); | ||
| if (end === -1) break; | ||
| const doctype = xmlString.slice(i + 9, end).trim(); | ||
| i = end + 1; | ||
| if (!ignoreDoctype) addField(stack[stack.length - 1], "doctype", doctype); | ||
| continue; | ||
| } | ||
| if (xmlString.charCodeAt(i) === 47) { | ||
| const end = xmlString.indexOf(">", i + 1); | ||
| if (end === -1) break; | ||
| i = end + 1; | ||
| stack.pop(); | ||
| continue; | ||
| } | ||
| const tagNameEnd = findTagNameEnd(xmlString, i); | ||
| const tagName = xmlString.slice(i, tagNameEnd); | ||
| let pos = tagNameEnd; | ||
| const attributes = parseAttributesFromXml(xmlString, pos); | ||
| pos = attributes.pos; | ||
| if (nativeTypeAttributes) for (const key of Object.keys(attributes.attrs)) attributes.attrs[key] = nativeTypeValue(attributes.attrs[key]); | ||
| const isSelfClosing = xmlString.charCodeAt(pos) === 47; | ||
| if (isSelfClosing) pos += 2; | ||
| else pos++; | ||
| const element = { | ||
| type: "element", | ||
| name: tagName | ||
| }; | ||
| if (Object.keys(attributes.attrs).length > 0) element.attributes = attributes.attrs; | ||
| const parent = stack[stack.length - 1]; | ||
| if (!parent.elements) parent.elements = []; | ||
| parent.elements.push(element); | ||
| if (!isSelfClosing) stack.push(element); | ||
| i = pos; | ||
| } | ||
| if (result.elements) { | ||
| const temp = result.elements; | ||
| delete result.elements; | ||
| result.elements = temp; | ||
| delete result.text; | ||
| } | ||
| return result; | ||
| } | ||
| function findTagNameEnd(str, start) { | ||
| let i = start; | ||
| const len = str.length; | ||
| while (i < len) { | ||
| const ch = str.charCodeAt(i); | ||
| if (ch === 32 || ch === 9 || ch === 10 || ch === 13 || ch === 47 || ch === 62) return i; | ||
| i++; | ||
| } | ||
| return i; | ||
| } | ||
| function parseAttributesFromXml(str, start) { | ||
| const attrs = {}; | ||
| let i = start; | ||
| const len = str.length; | ||
| while (i < len) { | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| if (i >= len || str.charCodeAt(i) === 62 || str.charCodeAt(i) === 47) break; | ||
| const nameStart = i; | ||
| while (i < len && str.charCodeAt(i) !== 61) { | ||
| if (str.charCodeAt(i) === 62 || str.charCodeAt(i) === 47) break; | ||
| i++; | ||
| } | ||
| const name = str.slice(nameStart, i); | ||
| if (str.charCodeAt(i) !== 61) break; | ||
| i++; | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| const quote = str.charCodeAt(i); | ||
| if (quote !== 34 && quote !== 39) break; | ||
| i++; | ||
| const valueStart = i; | ||
| while (i < len && str.charCodeAt(i) !== quote) i++; | ||
| attrs[name] = str.slice(valueStart, i); | ||
| i++; | ||
| } | ||
| return { | ||
| attrs, | ||
| pos: i | ||
| }; | ||
| } | ||
| function parseAttributes(str) { | ||
| const result = {}; | ||
| let i = 0; | ||
| const len = str.length; | ||
| while (i < len) { | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| if (i >= len) break; | ||
| const nameStart = i; | ||
| while (i < len && str.charCodeAt(i) !== 61) { | ||
| if (isWhitespace(str.charCodeAt(i))) break; | ||
| i++; | ||
| } | ||
| const name = str.slice(nameStart, i); | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| if (i >= len || str.charCodeAt(i) !== 61) break; | ||
| i++; | ||
| while (i < len && isWhitespace(str.charCodeAt(i))) i++; | ||
| const quote = str.charCodeAt(i); | ||
| if (quote !== 34 && quote !== 39) break; | ||
| i++; | ||
| const valueStart = i; | ||
| while (i < len && str.charCodeAt(i) !== quote) i++; | ||
| result[name] = str.slice(valueStart, i); | ||
| i++; | ||
| } | ||
| return result; | ||
| } | ||
| function addField(parent, type, value) { | ||
| if (!parent.elements) parent.elements = []; | ||
| const element = { type }; | ||
| element[type] = value; | ||
| parent.elements.push(element); | ||
| } | ||
| function isWhitespace(ch) { | ||
| return ch === 32 || ch === 9 || ch === 10 || ch === 13; | ||
| } | ||
| //#endregion | ||
| //#region src/stringify.ts | ||
| function js2xml(js, options) { | ||
| const opts = normalizeOptions(options); | ||
| const parts = []; | ||
| if (js.declaration && !opts.ignoreDeclaration) parts.push(writeDeclaration(js.declaration)); | ||
| if (js.elements?.length) parts.push(writeElements(js.elements, opts, 0, !parts.length)); | ||
| return parts.join(""); | ||
| } | ||
| /** Alias for js2xml — xml-js compatible export */ | ||
| function json2xml(json, options) { | ||
| return js2xml(json, options); | ||
| } | ||
| function normalizeOptions(options) { | ||
| if (!options) return { | ||
| spaces: "", | ||
| ignoreDeclaration: false, | ||
| ignoreText: false, | ||
| ignoreComment: false, | ||
| ignoreCdata: false, | ||
| ignoreDoctype: false, | ||
| fullTagEmptyElement: false, | ||
| indentText: false, | ||
| indentCdata: false | ||
| }; | ||
| let spaces = ""; | ||
| if (options.spaces != null) spaces = typeof options.spaces === "number" ? " ".repeat(options.spaces) : options.spaces; | ||
| return { | ||
| spaces, | ||
| ignoreDeclaration: options.ignoreDeclaration ?? false, | ||
| ignoreText: options.ignoreText ?? false, | ||
| ignoreComment: options.ignoreComment ?? false, | ||
| ignoreCdata: options.ignoreCdata ?? false, | ||
| ignoreDoctype: options.ignoreDoctype ?? false, | ||
| fullTagEmptyElement: options.fullTagEmptyElement ?? false, | ||
| indentText: options.indentText ?? false, | ||
| indentCdata: options.indentCdata ?? false, | ||
| attributeValueFn: options.attributeValueFn | ||
| }; | ||
| } | ||
| function writeIndentation(spaces, depth, firstLine) { | ||
| return (!firstLine && spaces ? "\n" : "") + spaces.repeat(depth); | ||
| } | ||
| function writeDeclaration(declaration) { | ||
| const attrs = declaration.attributes; | ||
| if (!attrs) return "<?xml version=\"1.0\"?>"; | ||
| let result = "<?xml version=\"1.0\""; | ||
| if (attrs.encoding) result += ` encoding="${attrs.encoding}"`; | ||
| if (attrs.standalone) result += ` standalone="${attrs.standalone}"`; | ||
| return result + "?>"; | ||
| } | ||
| function writeAttributes(attributes, elementName, element, attributeValueFn) { | ||
| const parts = []; | ||
| for (const key of Object.keys(attributes)) { | ||
| const value = attributes[key]; | ||
| if (value === null || value === void 0) continue; | ||
| let attr = String(value).replace(/"/g, """); | ||
| if (attributeValueFn) attr = attributeValueFn(attr, key, elementName, element); | ||
| parts.push(` ${key}="${attr}"`); | ||
| } | ||
| return parts.join(""); | ||
| } | ||
| function writeElement(element, opts, depth) { | ||
| if (!element.name) return ""; | ||
| const name = element.name; | ||
| const attrStr = element.attributes ? writeAttributes(element.attributes, name, element, opts.attributeValueFn) : ""; | ||
| if (!((element.elements?.length ?? 0) > 0 || element.attributes?.["xml:space"] === "preserve" || opts.fullTagEmptyElement)) return `<${name}${attrStr}/>`; | ||
| const parts = []; | ||
| parts.push(`<${name}${attrStr}>`); | ||
| const hasChildElements = element.elements?.some((e) => e.type === "element") ?? false; | ||
| if (element.elements?.length) parts.push(writeElements(element.elements, opts, depth + 1, false)); | ||
| if (opts.spaces && hasChildElements) parts.push("\n" + opts.spaces.repeat(depth)); | ||
| parts.push(`</${name}>`); | ||
| return parts.join(""); | ||
| } | ||
| function writeElements(elements, opts, depth, firstLine) { | ||
| let result = ""; | ||
| for (let i = 0; i < elements.length; i++) { | ||
| const element = elements[i]; | ||
| const isFirst = firstLine && i === 0; | ||
| switch (element.type) { | ||
| case "element": | ||
| result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeElement(element, opts, depth); | ||
| break; | ||
| case "text": | ||
| if (opts.ignoreText) continue; | ||
| if (opts.indentText) result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeText(element.text); | ||
| break; | ||
| case "cdata": | ||
| if (opts.ignoreCdata) continue; | ||
| if (opts.indentCdata) result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeCdata(element.cdata); | ||
| break; | ||
| case "comment": | ||
| if (opts.ignoreComment) continue; | ||
| result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeComment(element.comment); | ||
| break; | ||
| case "doctype": | ||
| if (opts.ignoreDoctype) continue; | ||
| result += writeIndentation(opts.spaces, depth, isFirst); | ||
| result += writeDoctype(element.doctype); | ||
| break; | ||
| default: break; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| function writeText(text) { | ||
| if (text == null) return ""; | ||
| return String(text).replace(/&/g, "&").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); | ||
| } | ||
| function writeCdata(cdata) { | ||
| if (cdata == null) return ""; | ||
| return `<![CDATA[${cdata.replace(/\]\]>/g, "]]]]><![CDATA[>")}]]>`; | ||
| } | ||
| function writeComment(comment) { | ||
| if (comment == null) return ""; | ||
| return `<!--${comment}-->`; | ||
| } | ||
| function writeDoctype(doctype) { | ||
| if (doctype == null) return ""; | ||
| return `<!DOCTYPE ${doctype}>`; | ||
| } | ||
| //#endregion | ||
| //#region src/convert.ts | ||
| /** | ||
| * Convert XmlObject (node-xml format) directly to Element (xml-js format). | ||
| * Eliminates the redundant xml() → xml2js() bridge path. | ||
| */ | ||
| function toElement(xmlObject) { | ||
| const tagName = Object.keys(xmlObject)[0]; | ||
| const value = xmlObject[tagName]; | ||
| const element = { | ||
| type: "element", | ||
| name: tagName | ||
| }; | ||
| if (value == null) return element; | ||
| if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { | ||
| element.elements = [{ | ||
| type: "text", | ||
| text: String(value) | ||
| }]; | ||
| return element; | ||
| } | ||
| if (Array.isArray(value)) { | ||
| const children = []; | ||
| for (const item of value) if (item && typeof item === "object" && "_attr" in item) element.attributes = item._attr; | ||
| else if (item && typeof item === "object") if (Object.keys(item)[0] === "_cdata") children.push({ | ||
| type: "cdata", | ||
| cdata: String(item._cdata) | ||
| }); | ||
| else children.push(toElement(item)); | ||
| else if (item != null) children.push({ | ||
| type: "text", | ||
| text: String(item) | ||
| }); | ||
| if (children.length > 0) element.elements = children; | ||
| return element; | ||
| } | ||
| if (typeof value === "object") { | ||
| if (value._attr) element.attributes = value._attr; | ||
| if (value._cdata) element.elements = [{ | ||
| type: "cdata", | ||
| cdata: String(value._cdata) | ||
| }]; | ||
| } | ||
| return element; | ||
| } | ||
| //#endregion | ||
| //#region src/json.ts | ||
| /** Convert XML string to JSON string — xml-js compatible export */ | ||
| function xml2json(xml, options) { | ||
| return JSON.stringify(xml2js(xml, options)); | ||
| } | ||
| //#endregion | ||
| export { allChildren, attr, attrBool, attrNum, attrs, childCount, childText, children, collectText, colorAttr, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, nativeTypeValue, parseAttributes, selfCloseElement, textOf, toElement, unescapeXml, xml, xml2js, xml2json }; |
| //#region src/types.d.ts | ||
| interface Attributes { | ||
| [key: string]: string | number | undefined; | ||
| } | ||
| interface DeclarationAttributes { | ||
| version?: string | number; | ||
| encoding?: string; | ||
| standalone?: string; | ||
| } | ||
| interface Element { | ||
| declaration?: { | ||
| attributes?: DeclarationAttributes; | ||
| }; | ||
| instruction?: string; | ||
| attributes?: Attributes; | ||
| cdata?: string; | ||
| doctype?: string; | ||
| comment?: string; | ||
| text?: string | number | boolean; | ||
| type?: string; | ||
| name?: string; | ||
| elements?: Element[]; | ||
| parent?: Element; | ||
| } | ||
| interface ElementCompact { | ||
| [key: string]: any; | ||
| _declaration?: { | ||
| _attributes?: DeclarationAttributes; | ||
| }; | ||
| _instruction?: { | ||
| [key: string]: string; | ||
| }; | ||
| _attributes?: Attributes; | ||
| _cdata?: string; | ||
| _doctype?: string; | ||
| _comment?: string; | ||
| _text?: string | number; | ||
| } | ||
| interface IgnoreOptions { | ||
| ignoreDeclaration?: boolean; | ||
| ignoreInstruction?: boolean; | ||
| ignoreAttributes?: boolean; | ||
| ignoreComment?: boolean; | ||
| ignoreCdata?: boolean; | ||
| ignoreDoctype?: boolean; | ||
| ignoreText?: boolean; | ||
| } | ||
| interface Xml2JsOptions extends IgnoreOptions { | ||
| compact?: boolean; | ||
| trim?: boolean; | ||
| sanitize?: boolean; | ||
| nativeType?: boolean; | ||
| nativeTypeAttributes?: boolean; | ||
| addParent?: boolean; | ||
| alwaysArray?: boolean | string[]; | ||
| alwaysChildren?: boolean; | ||
| instructionHasAttributes?: boolean; | ||
| captureSpacesBetweenElements?: boolean; | ||
| doctypeFn?: (value: string, parentElement: object) => string; | ||
| instructionFn?: (value: string, instructionName: string, parentElement: string) => string; | ||
| cdataFn?: (value: string, parentElement: object) => string; | ||
| commentFn?: (value: string, parentElement: object) => string; | ||
| textFn?: (value: string, parentElement: object) => string; | ||
| instructionNameFn?: (instructionName: string, instructionValue: string, parentElement: string) => string; | ||
| elementNameFn?: (value: string, parentElement: object) => string; | ||
| attributeNameFn?: (attributeName: string, attributeValue: string, parentElement: string) => string; | ||
| attributeValueFn?: (attributeValue: string, attributeName: string, parentElement: string) => string; | ||
| attributesFn?: (value: Attributes, parentElement: string) => Attributes; | ||
| } | ||
| interface Js2XmlOptions extends IgnoreOptions { | ||
| spaces?: number | string; | ||
| compact?: boolean; | ||
| indentText?: boolean; | ||
| indentCdata?: boolean; | ||
| indentAttributes?: boolean; | ||
| indentInstruction?: boolean; | ||
| fullTagEmptyElement?: boolean; | ||
| noQuotesForNativeAttributes?: boolean; | ||
| doctypeFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| instructionFn?: (instructionValue: string, instructionName: string, currentElementName: string, currentElementObj: object) => string; | ||
| cdataFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| commentFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| textFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| instructionNameFn?: (instructionName: string, instructionValue: string, currentElementName: string, currentElementObj: object) => string; | ||
| elementNameFn?: (value: string, currentElementName: string, currentElementObj: object) => string; | ||
| attributeNameFn?: (attributeName: string, attributeValue: string, currentElementName: string, currentElementObj: object) => string; | ||
| attributeValueFn?: (attributeValue: string, attributeName: string, currentElementName: string, currentElementObj: object) => string; | ||
| attributesFn?: (value: Attributes, currentElementName: string, currentElementObj: object) => Attributes; | ||
| fullTagEmptyElementFn?: (currentElementName: string, currentElementObj: object) => boolean; | ||
| } | ||
| interface XmlOption { | ||
| indent?: string; | ||
| stream?: boolean; | ||
| declaration?: boolean | { | ||
| encoding?: string; | ||
| standalone?: string; | ||
| }; | ||
| } | ||
| interface XmlAttrs { | ||
| [attr: string]: XmlAtom; | ||
| } | ||
| type XmlAtom = string | number | boolean | null; | ||
| interface ElementObject { | ||
| push(xmlObject: XmlObject): void; | ||
| close(xmlObject?: XmlObject): void; | ||
| } | ||
| type XmlDesc = { | ||
| _attr: XmlAttrs; | ||
| } | { | ||
| _cdata: string; | ||
| } | { | ||
| _attr: XmlAttrs; | ||
| _cdata: string; | ||
| } | XmlAtom | XmlAtom[] | XmlDescArray; | ||
| interface XmlDescArray { | ||
| [index: number]: { | ||
| _attr: XmlAttrs; | ||
| } | XmlObject; | ||
| } | ||
| type XmlObject = { | ||
| [tag: string]: ElementObject | XmlDesc; | ||
| } | XmlDesc; | ||
| //#endregion | ||
| //#region src/utils.d.ts | ||
| /** | ||
| * Find the first direct child element with the given name. | ||
| */ | ||
| declare function findChild(parent: Element | undefined, name: string): Element | undefined; | ||
| /** | ||
| * Get all direct child elements matching the given name. | ||
| */ | ||
| declare function children(parent: Element | undefined, name: string): Element[]; | ||
| /** | ||
| * Get all direct child elements. | ||
| */ | ||
| declare function allChildren(parent: Element | undefined): Element[]; | ||
| /** | ||
| * Get text content of the first child element with the given name. | ||
| */ | ||
| declare function childText(parent: Element | undefined, name: string): string; | ||
| /** | ||
| * Get text content of an element. | ||
| * Handles cases where text may be directly on .text or in a child element. | ||
| */ | ||
| declare function textOf(element: Element | undefined): string; | ||
| /** | ||
| * Collect text from all direct text nodes within an element. | ||
| */ | ||
| declare function collectText(element: Element | undefined): string; | ||
| /** | ||
| * Get an attribute value as a string. | ||
| */ | ||
| declare function attr(element: Element | undefined, name: string): string | undefined; | ||
| /** | ||
| * Get an attribute value as a number. | ||
| */ | ||
| declare function attrNum(element: Element | undefined, name: string): number | undefined; | ||
| /** | ||
| * Get an attribute value as a boolean. | ||
| */ | ||
| declare function attrBool(element: Element | undefined, name: string): boolean | undefined; | ||
| /** | ||
| * Get a hex color attribute, handling nativeTypeValue coercion. | ||
| * nativeTypeAttributes converts "000000" → 0 (number); this recovers | ||
| * the original 6-digit hex string by zero-padding numeric values. | ||
| */ | ||
| declare function colorAttr(element: Element | undefined, name: string): string | undefined; | ||
| /** | ||
| * Check if an element has a specific child element. | ||
| */ | ||
| declare function hasChild(parent: Element | undefined, name: string): boolean; | ||
| /** | ||
| * Find deep descendant elements matching the given name. | ||
| */ | ||
| declare function findDeep(parent: Element | undefined, name: string): Element[]; | ||
| /** | ||
| * Get the number of direct child elements. | ||
| */ | ||
| declare function childCount(parent: Element | undefined): number; | ||
| //#endregion | ||
| export { XmlAttrs as C, XmlOption as D, XmlObject as E, XmlAtom as S, XmlDescArray as T, ElementCompact as _, childCount as a, Js2XmlOptions as b, collectText as c, findDeep as d, hasChild as f, Element as g, DeclarationAttributes as h, attrNum as i, colorAttr as l, Attributes as m, attr as n, childText as o, textOf as p, attrBool as r, children as s, allChildren as t, findChild as u, ElementObject as v, XmlDesc as w, Xml2JsOptions as x, IgnoreOptions as y }; |
| import { a as childCount, c as collectText, d as findDeep, f as hasChild, i as attrNum, l as colorAttr, n as attr, o as childText, p as textOf, r as attrBool, s as children, t as allChildren, u as findChild } from "./utils-Dh-Jl3ng.js"; | ||
| export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf }; |
-116
| //#region src/utils.ts | ||
| /** | ||
| * Find the first direct child element with the given name. | ||
| */ | ||
| function findChild(parent, name) { | ||
| return parent?.elements?.find((e) => e.name === name); | ||
| } | ||
| /** | ||
| * Get all direct child elements matching the given name. | ||
| */ | ||
| function children(parent, name) { | ||
| return parent?.elements?.filter((e) => e.name === name) ?? []; | ||
| } | ||
| /** | ||
| * Get all direct child elements. | ||
| */ | ||
| function allChildren(parent) { | ||
| return parent?.elements ?? []; | ||
| } | ||
| /** | ||
| * Get text content of the first child element with the given name. | ||
| */ | ||
| function childText(parent, name) { | ||
| return textOf(findChild(parent, name)); | ||
| } | ||
| /** | ||
| * Get text content of an element. | ||
| * Handles cases where text may be directly on .text or in a child element. | ||
| */ | ||
| function textOf(element) { | ||
| if (!element) return ""; | ||
| if (element.text !== void 0 && typeof element.text === "string") return element.text; | ||
| if (element.elements && element.elements.length > 0) return element.elements.map((e) => typeof e.text === "string" ? e.text : "").join(""); | ||
| return ""; | ||
| } | ||
| /** | ||
| * Collect text from all direct text nodes within an element. | ||
| */ | ||
| function collectText(element) { | ||
| if (!element) return ""; | ||
| const parts = []; | ||
| collectTextRecursive(element, parts); | ||
| return parts.join(""); | ||
| } | ||
| function collectTextRecursive(element, parts) { | ||
| if (!element) return; | ||
| if (element.text !== void 0 && typeof element.text === "string") parts.push(element.text); | ||
| if (element.elements) for (const child of element.elements) collectTextRecursive(child, parts); | ||
| } | ||
| /** | ||
| * Get an attribute value as a string. | ||
| */ | ||
| function attr(element, name) { | ||
| const v = element?.attributes?.[name]; | ||
| return v !== void 0 ? String(v) : void 0; | ||
| } | ||
| /** | ||
| * Get an attribute value as a number. | ||
| */ | ||
| function attrNum(element, name) { | ||
| const v = element?.attributes?.[name]; | ||
| if (v === void 0) return void 0; | ||
| const n = Number(v); | ||
| return isNaN(n) ? void 0 : n; | ||
| } | ||
| /** | ||
| * Get an attribute value as a boolean. | ||
| */ | ||
| function attrBool(element, name) { | ||
| const v = element?.attributes?.[name]; | ||
| if (v === void 0) return void 0; | ||
| if (typeof v === "boolean") return v; | ||
| const lower = String(v).toLowerCase(); | ||
| if (lower === "true" || lower === "1") return true; | ||
| if (lower === "false" || lower === "0") return false; | ||
| } | ||
| /** | ||
| * Get a hex color attribute, handling nativeTypeValue coercion. | ||
| * nativeTypeAttributes converts "000000" → 0 (number); this recovers | ||
| * the original 6-digit hex string by zero-padding numeric values. | ||
| */ | ||
| function colorAttr(element, name) { | ||
| const raw = element?.attributes?.[name]; | ||
| if (raw === void 0 || raw === "") return void 0; | ||
| if (typeof raw === "boolean") return void 0; | ||
| if (typeof raw === "number") return String(raw).padStart(6, "0"); | ||
| if (raw === "auto") return "auto"; | ||
| if (/^[0-9A-Fa-f]{6}$/.test(raw)) return raw; | ||
| return raw; | ||
| } | ||
| /** | ||
| * Check if an element has a specific child element. | ||
| */ | ||
| function hasChild(parent, name) { | ||
| return parent?.elements?.some((e) => e.name === name) ?? false; | ||
| } | ||
| /** | ||
| * Find deep descendant elements matching the given name. | ||
| */ | ||
| function findDeep(parent, name) { | ||
| const result = []; | ||
| if (!parent) return result; | ||
| for (const child of parent.elements ?? []) { | ||
| if (child.name === name) result.push(child); | ||
| result.push(...findDeep(child, name)); | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Get the number of direct child elements. | ||
| */ | ||
| function childCount(parent) { | ||
| return parent?.elements?.length ?? 0; | ||
| } | ||
| //#endregion | ||
| export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
39145
1.19%661
-24.2%