@office-open/xml 0.3.0 → 0.3.2

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.
@@ -159,6 +159,12 @@ declare function attrNum(element: Element | undefined, name: string): number | u
159
159
  * Get an attribute value as a boolean.
160
160
  */
161
161
  declare function attrBool(element: Element | undefined, name: string): boolean | undefined;
162
+ /**
163
+ * Get a hex color attribute, handling nativeTypeValue coercion.
164
+ * nativeTypeAttributes converts "000000" → 0 (number); this recovers
165
+ * the original 6-digit hex string by zero-padding numeric values.
166
+ */
167
+ declare function colorAttr(element: Element | undefined, name: string): string | undefined;
162
168
  /**
163
169
  * Check if an element has a specific child element.
164
170
  */
@@ -172,4 +178,4 @@ declare function findDeep(parent: Element | undefined, name: string): Element[];
172
178
  */
173
179
  declare function childCount(parent: Element | undefined): number;
174
180
  //#endregion
175
- export { XmlDesc as C, XmlOption as E, XmlAttrs as S, XmlObject as T, ElementObject as _, childCount as a, Xml2JsOptions as b, collectText as c, hasChild as d, textOf as f, ElementCompact as g, Element as h, attrNum as i, findChild as l, DeclarationAttributes as m, attr as n, childText as o, Attributes as p, attrBool as r, children as s, allChildren as t, findDeep as u, IgnoreOptions as v, XmlDescArray as w, XmlAtom as x, Js2XmlOptions as y };
181
+ 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 };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as XmlDesc, E as XmlOption, S as XmlAttrs, T as XmlObject, _ as ElementObject, a as childCount, b as Xml2JsOptions, c as collectText, d as hasChild, f as textOf, g as ElementCompact, h as Element, i as attrNum, l as findChild, m as DeclarationAttributes, n as attr, o as childText, p as Attributes, r as attrBool, s as children, t as allChildren, u as findDeep, v as IgnoreOptions, w as XmlDescArray, x as XmlAtom, y as Js2XmlOptions } from "./_chunks/utils-DlOOMr6B.mjs";
1
+ 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";
2
2
 
3
3
  //#region src/serialize.d.ts
4
4
  declare function xml(input: Record<string, any> | Record<string, any>[], options?: boolean | string | {
@@ -32,9 +32,19 @@ declare function escapeXml(str: string): string;
32
32
  * Handles already-escaped entities to prevent double-escaping.
33
33
  */
34
34
  declare function escapeAttributeValue(str: string): string;
35
+ /**
36
+ * Build an XML attribute string fragment from a record.
37
+ * `undefined` values are automatically skipped.
38
+ * String values are escaped via `escapeXml`.
39
+ *
40
+ * @example
41
+ * attrs({ id: 1, name: "foo", hidden: undefined })
42
+ * // => ' id="1" name="foo"'
43
+ */
44
+ declare function attrs(record: Record<string, string | number | boolean | undefined>): string;
35
45
  //#endregion
36
46
  //#region src/json.d.ts
37
47
  /** Convert XML string to JSON string — xml-js compatible export */
38
48
  declare function xml2json(xml: string, options?: Xml2JsOptions): string;
39
49
  //#endregion
40
- export { Attributes, DeclarationAttributes, Element, ElementCompact, ElementObject, IgnoreOptions, Js2XmlOptions, Xml2JsOptions, XmlAtom, XmlAttrs, XmlDesc, XmlDescArray, XmlObject, XmlOption, allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json };
50
+ 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 };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, findChild, findDeep, hasChild, textOf } from "./utils.mjs";
1
+ import { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf } from "./utils.mjs";
2
2
  //#region src/escape.ts
3
3
  const XML_CHAR_MAP = {
4
4
  "&": "&amp;",
@@ -19,6 +19,20 @@ function escapeXml(str) {
19
19
  function escapeAttributeValue(str) {
20
20
  return String(str).replace(/&(?!amp;|lt;|gt;|quot;|apos;)/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
21
21
  }
22
+ /**
23
+ * Build an XML attribute string fragment from a record.
24
+ * `undefined` values are automatically skipped.
25
+ * String values are escaped via `escapeXml`.
26
+ *
27
+ * @example
28
+ * attrs({ id: 1, name: "foo", hidden: undefined })
29
+ * // => ' id="1" name="foo"'
30
+ */
31
+ function attrs(record) {
32
+ let s = "";
33
+ for (const [k, v] of Object.entries(record)) if (v !== void 0) s += ` ${k}="${typeof v === "string" ? escapeXml(v) : v}"`;
34
+ return s;
35
+ }
22
36
  //#endregion
23
37
  //#region src/serialize.ts
24
38
  const DEFAULT_INDENT = " ";
@@ -37,7 +51,8 @@ function xml(input, options) {
37
51
  }
38
52
  const items = Array.isArray(input) ? input : [input];
39
53
  for (let i = 0; i < items.length; i++) {
40
- parts.push(formatElement(resolve(items[i], opts.indent, 0)));
54
+ const keys = Object.keys(items[i]);
55
+ parts.push(formatElement(keys[0], items[i][keys[0]], opts.indent, 0));
41
56
  if (opts.indent && i < items.length - 1) parts.push("\n");
42
57
  }
43
58
  return parts.join("");
@@ -51,69 +66,50 @@ function normalizeOptions$1(options) {
51
66
  declaration: opts.declaration
52
67
  };
53
68
  }
54
- function resolve(data, indent, depth) {
55
- const name = Object.keys(data)[0];
56
- const values = data[name];
57
- const attributes = [];
58
- const content = [];
59
- if (values == null) return {
60
- name,
61
- attributes,
62
- content,
63
- indent,
64
- depth,
65
- emptyArray: false
66
- };
67
- switch (typeof values) {
68
- case "object":
69
- if (values._attr) for (const key of Object.keys(values._attr)) attributes.push(`${key}="${escapeXml(String(values._attr[key]))}"`);
70
- if (values._cdata) {
71
- const escaped = String(values._cdata).replace(/\]\]>/g, "]]]]><![CDATA[>");
72
- content.push(`<![CDATA[${escaped}]]>`);
73
- }
74
- if (Array.isArray(values)) {
75
- if (values.length === 0) return {
76
- name,
77
- attributes,
78
- content,
79
- indent,
80
- depth,
81
- emptyArray: true
82
- };
83
- 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]))}"`);
84
- else if (value && typeof value === "object") content.push(resolve(value, indent, depth + 1));
85
- else if (value != null) content.push(escapeXml(String(value)));
86
- }
87
- break;
88
- default: content.push(escapeXml(String(values)));
69
+ /**
70
+ * Single-pass XML formatter: directly converts IXmlableObject to string,
71
+ * eliminating the intermediate ResolvedElement tree.
72
+ */
73
+ function formatElement(name, values, indent, depth) {
74
+ const attrParts = [];
75
+ const textParts = [];
76
+ const elemParts = [];
77
+ let emptyArray = false;
78
+ if (values == null) {
79
+ const attrStr = attrParts.length ? " " + attrParts.join(" ") : "";
80
+ return `${indent ? indent.repeat(depth) : ""}<${name}${attrStr}/>`;
89
81
  }
90
- return {
91
- name,
92
- attributes,
93
- content,
94
- indent,
95
- depth,
96
- emptyArray: false
97
- };
98
- }
99
- function formatElement(elem) {
100
- const { name, attributes, content, indent, depth } = elem;
101
- const hasChildren = content.length > 0;
82
+ if (typeof values === "object") {
83
+ if (values._attr) for (const key of Object.keys(values._attr)) attrParts.push(`${key}="${escapeXml(String(values._attr[key]))}"`);
84
+ if (values._attributes) for (const key of Object.keys(values._attributes)) attrParts.push(`${key}="${escapeXml(String(values._attributes[key]))}"`);
85
+ if (values._cdata) {
86
+ const escaped = String(values._cdata).replace(/\]\]>/g, "]]]]><![CDATA[>");
87
+ textParts.push(`<![CDATA[${escaped}]]>`);
88
+ }
89
+ if (Array.isArray(values)) {
90
+ if (values.length === 0) emptyArray = true;
91
+ 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]))}"`);
92
+ 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]))}"`);
93
+ else if (value && typeof value === "object") {
94
+ const childKeys = Object.keys(value);
95
+ elemParts.push(formatElement(childKeys[0], value[childKeys[0]], indent, depth + 1));
96
+ } else if (value != null) textParts.push(escapeXml(String(value)));
97
+ }
98
+ } else textParts.push(escapeXml(String(values)));
102
99
  const ind = indent ? indent.repeat(depth) : "";
103
- const attrStr = attributes.length ? " " + attributes.join(" ") : "";
104
- if (!hasChildren) {
105
- if (elem.emptyArray) return `${ind}<${name}${attrStr}></${name}>`;
106
- return `${ind}<${name}${attrStr}/>`;
107
- }
108
- const textContent = content.length === 1 && typeof content[0] === "string" ? content[0] : null;
109
- if (textContent !== null && !indent) return `<${name}${attrStr}>${textContent}</${name}>`;
110
- if (textContent !== null) return `${ind}<${name}${attrStr}>${textContent}</${name}>`;
100
+ const attrStr = attrParts.length ? " " + attrParts.join(" ") : "";
101
+ if (textParts.length + elemParts.length === 0) return emptyArray ? `${ind}<${name}${attrStr}></${name}>` : `${ind}<${name}${attrStr}/>`;
102
+ if (elemParts.length === 0 && textParts.length === 1) return indent ? `${ind}<${name}${attrStr}>${textParts[0]}</${name}>` : `<${name}${attrStr}>${textParts[0]}</${name}>`;
111
103
  const parts = [];
112
104
  parts.push(`${ind}<${name}${attrStr}>`);
113
105
  if (indent) parts.push("\n");
114
- for (const child of content) {
115
- if (typeof child === "string") parts.push(`${indent.repeat(depth + 1)}${child}`);
116
- else parts.push(formatElement(child));
106
+ const childIndent = indent ? indent.repeat(depth + 1) : "";
107
+ for (const t of textParts) {
108
+ parts.push(`${childIndent}${t}`);
109
+ if (indent) parts.push("\n");
110
+ }
111
+ for (const e of elemParts) {
112
+ parts.push(e);
117
113
  if (indent) parts.push("\n");
118
114
  }
119
115
  parts.push(`${ind}</${name}>`);
@@ -500,4 +496,4 @@ function xml2json(xml, options) {
500
496
  return JSON.stringify(xml2js(xml, options));
501
497
  }
502
498
  //#endregion
503
- export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json };
499
+ export { allChildren, attr, attrBool, attrNum, attrs, childCount, childText, children, collectText, colorAttr, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json };
package/dist/utils.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as childCount, c as collectText, d as hasChild, f as textOf, i as attrNum, l as findChild, n as attr, o as childText, r as attrBool, s as children, t as allChildren, u as findDeep } from "./_chunks/utils-DlOOMr6B.mjs";
2
- export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, findChild, findDeep, hasChild, textOf };
1
+ 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 "./_chunks/utils-BHmdH50s.mjs";
2
+ export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf };
package/dist/utils.mjs CHANGED
@@ -75,6 +75,20 @@ function attrBool(element, name) {
75
75
  if (lower === "false" || lower === "0") return false;
76
76
  }
77
77
  /**
78
+ * Get a hex color attribute, handling nativeTypeValue coercion.
79
+ * nativeTypeAttributes converts "000000" → 0 (number); this recovers
80
+ * the original 6-digit hex string by zero-padding numeric values.
81
+ */
82
+ function colorAttr(element, name) {
83
+ const raw = element?.attributes?.[name];
84
+ if (raw === void 0 || raw === "") return void 0;
85
+ if (typeof raw === "boolean") return void 0;
86
+ if (typeof raw === "number") return String(raw).padStart(6, "0");
87
+ if (raw === "auto") return "auto";
88
+ if (/^[0-9A-Fa-f]{6}$/.test(raw)) return raw;
89
+ return raw;
90
+ }
91
+ /**
78
92
  * Check if an element has a specific child element.
79
93
  */
80
94
  function hasChild(parent, name) {
@@ -99,4 +113,4 @@ function childCount(parent) {
99
113
  return parent?.elements?.length ?? 0;
100
114
  }
101
115
  //#endregion
102
- export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, findChild, findDeep, hasChild, textOf };
116
+ export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@office-open/xml",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "XML parsing and serialization for Office Open XML. Zero dependencies, drop-in replacement for xml + xml-js.",
5
5
  "keywords": [
6
6
  "office-open",