@office-open/xml 0.1.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ![npm license](https://img.shields.io/npm/l/@office-open/xml)
6
6
  ![zero dependencies](https://img.shields.io/badge/dependencies-0-green)
7
7
 
8
- > XML parsing and serialization for Office Open XML. Zero dependencies, drop-in replacement for `xml` + `xml-js`.
8
+ > XML parsing and serialization for Office Open XML. Zero dependencies, drop-in replacement for xml + xml-js.
9
9
 
10
10
  ## Features
11
11
 
@@ -13,7 +13,7 @@
13
13
  - **xml() Serialization** - Drop-in replacement for the `xml` package
14
14
  - **xml2js() Parsing** - Drop-in replacement for `xml-js` XML parsing
15
15
  - **js2xml() Stringifying** - Drop-in replacement for `xml-js` JS-to-XML conversion
16
- - **toElement() Direct Convert** - Direct conversion from xml object format to xml-js Element, 16-33x faster than the xml→xml2js bridge
16
+ - **toElement() Direct Convert** - Direct conversion from xml object format to xml-js Element, 10-19x faster than the xml→xml2js bridge
17
17
  - **Complete Type Definitions** - Full type compatibility with `xml` and `xml-js`, import without changes
18
18
  - **OOXML Optimized** - Implements all options needed for Office Open XML document generation
19
19
 
@@ -103,31 +103,31 @@ Performance comparison against original `xml` (1.0.1) and `xml-js` (1.6.11) pack
103
103
 
104
104
  | Scenario | @office-open/xml | xml | Speedup |
105
105
  | ----------------------- | ---------------: | ---------: | --------: |
106
- | Simple element | 4,103,029 hz | 734,685 hz | **5.58x** |
107
- | Nested element | 940,333 hz | 321,791 hz | **2.92x** |
108
- | Nested with declaration | 800,517 hz | 262,125 hz | **3.05x** |
106
+ | Simple element | 4,023,269 hz | 742,728 hz | **5.42x** |
107
+ | Nested element | 1,030,612 hz | 322,801 hz | **3.19x** |
108
+ | Nested with declaration | 963,725 hz | 276,547 hz | **3.49x** |
109
109
 
110
110
  ### Parsing (xml2js)
111
111
 
112
112
  | Scenario | @office-open/xml | xml-js | Speedup |
113
113
  | ------------------ | ---------------: | --------: | ---------: |
114
- | Simple XML | 1,002,659 hz | 83,057 hz | **12.07x** |
115
- | Complex OOXML | 357,081 hz | 48,687 hz | **7.34x** |
116
- | With captureSpaces | 361,555 hz | 44,868 hz | **8.06x** |
114
+ | Simple XML | 1,147,347 hz | 95,579 hz | **12.00x** |
115
+ | Complex OOXML | 401,553 hz | 52,327 hz | **7.67x** |
116
+ | With captureSpaces | 387,082 hz | 52,317 hz | **7.40x** |
117
117
 
118
118
  ### Stringifying (js2xml)
119
119
 
120
120
  | Scenario | @office-open/xml | xml-js | Speedup |
121
121
  | -------------- | ---------------: | ---------: | --------: |
122
- | Simple element | 780,070 hz | 161,521 hz | **4.83x** |
123
- | Complex OOXML | 276,878 hz | 106,527 hz | **2.60x** |
122
+ | Simple element | 778,368 hz | 197,520 hz | **3.94x** |
123
+ | Complex OOXML | 344,376 hz | 127,493 hz | **2.70x** |
124
124
 
125
125
  ### Direct Conversion (toElement vs bridge)
126
126
 
127
127
  | Scenario | toElement() | xml() + xml2js() bridge | Speedup |
128
128
  | -------- | ------------: | ----------------------: | ---------: |
129
- | Simple | 14,119,071 hz | 853,290 hz | **16.55x** |
130
- | Nested | 3,745,934 hz | 422,217 hz | **8.87x** |
129
+ | Simple | 14,953,390 hz | 803,917 hz | **18.61x** |
130
+ | Nested | 4,530,626 hz | 466,126 hz | **9.72x** |
131
131
 
132
132
  ## Bundle Size
133
133
 
@@ -0,0 +1,181 @@
1
+ //#region src/types.d.ts
2
+ interface Attributes {
3
+ [key: string]: string | number | undefined;
4
+ }
5
+ interface DeclarationAttributes {
6
+ version?: string | number;
7
+ encoding?: string;
8
+ standalone?: string;
9
+ }
10
+ interface Element {
11
+ declaration?: {
12
+ attributes?: DeclarationAttributes;
13
+ };
14
+ instruction?: string;
15
+ attributes?: Attributes;
16
+ cdata?: string;
17
+ doctype?: string;
18
+ comment?: string;
19
+ text?: string | number | boolean;
20
+ type?: string;
21
+ name?: string;
22
+ elements?: Element[];
23
+ parent?: Element;
24
+ }
25
+ interface ElementCompact {
26
+ [key: string]: any;
27
+ _declaration?: {
28
+ _attributes?: DeclarationAttributes;
29
+ };
30
+ _instruction?: {
31
+ [key: string]: string;
32
+ };
33
+ _attributes?: Attributes;
34
+ _cdata?: string;
35
+ _doctype?: string;
36
+ _comment?: string;
37
+ _text?: string | number;
38
+ }
39
+ interface IgnoreOptions {
40
+ ignoreDeclaration?: boolean;
41
+ ignoreInstruction?: boolean;
42
+ ignoreAttributes?: boolean;
43
+ ignoreComment?: boolean;
44
+ ignoreCdata?: boolean;
45
+ ignoreDoctype?: boolean;
46
+ ignoreText?: boolean;
47
+ }
48
+ interface Xml2JsOptions extends IgnoreOptions {
49
+ compact?: boolean;
50
+ trim?: boolean;
51
+ sanitize?: boolean;
52
+ nativeType?: boolean;
53
+ nativeTypeAttributes?: boolean;
54
+ addParent?: boolean;
55
+ alwaysArray?: boolean | string[];
56
+ alwaysChildren?: boolean;
57
+ instructionHasAttributes?: boolean;
58
+ captureSpacesBetweenElements?: boolean;
59
+ doctypeFn?: (value: string, parentElement: object) => string;
60
+ instructionFn?: (value: string, instructionName: string, parentElement: string) => string;
61
+ cdataFn?: (value: string, parentElement: object) => string;
62
+ commentFn?: (value: string, parentElement: object) => string;
63
+ textFn?: (value: string, parentElement: object) => string;
64
+ instructionNameFn?: (instructionName: string, instructionValue: string, parentElement: string) => string;
65
+ elementNameFn?: (value: string, parentElement: object) => string;
66
+ attributeNameFn?: (attributeName: string, attributeValue: string, parentElement: string) => string;
67
+ attributeValueFn?: (attributeValue: string, attributeName: string, parentElement: string) => string;
68
+ attributesFn?: (value: Attributes, parentElement: string) => Attributes;
69
+ }
70
+ interface Js2XmlOptions extends IgnoreOptions {
71
+ spaces?: number | string;
72
+ compact?: boolean;
73
+ indentText?: boolean;
74
+ indentCdata?: boolean;
75
+ indentAttributes?: boolean;
76
+ indentInstruction?: boolean;
77
+ fullTagEmptyElement?: boolean;
78
+ noQuotesForNativeAttributes?: boolean;
79
+ doctypeFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
80
+ instructionFn?: (instructionValue: string, instructionName: string, currentElementName: string, currentElementObj: object) => string;
81
+ cdataFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
82
+ commentFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
83
+ textFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
84
+ instructionNameFn?: (instructionName: string, instructionValue: string, currentElementName: string, currentElementObj: object) => string;
85
+ elementNameFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
86
+ attributeNameFn?: (attributeName: string, attributeValue: string, currentElementName: string, currentElementObj: object) => string;
87
+ attributeValueFn?: (attributeValue: string, attributeName: string, currentElementName: string, currentElementObj: object) => string;
88
+ attributesFn?: (value: Attributes, currentElementName: string, currentElementObj: object) => Attributes;
89
+ fullTagEmptyElementFn?: (currentElementName: string, currentElementObj: object) => boolean;
90
+ }
91
+ interface XmlOption {
92
+ indent?: string;
93
+ stream?: boolean;
94
+ declaration?: boolean | {
95
+ encoding?: string;
96
+ standalone?: string;
97
+ };
98
+ }
99
+ interface XmlAttrs {
100
+ [attr: string]: XmlAtom;
101
+ }
102
+ type XmlAtom = string | number | boolean | null;
103
+ interface ElementObject {
104
+ push(xmlObject: XmlObject): void;
105
+ close(xmlObject?: XmlObject): void;
106
+ }
107
+ type XmlDesc = {
108
+ _attr: XmlAttrs;
109
+ } | {
110
+ _cdata: string;
111
+ } | {
112
+ _attr: XmlAttrs;
113
+ _cdata: string;
114
+ } | XmlAtom | XmlAtom[] | XmlDescArray;
115
+ interface XmlDescArray {
116
+ [index: number]: {
117
+ _attr: XmlAttrs;
118
+ } | XmlObject;
119
+ }
120
+ type XmlObject = {
121
+ [tag: string]: ElementObject | XmlDesc;
122
+ } | XmlDesc;
123
+ //#endregion
124
+ //#region src/utils.d.ts
125
+ /**
126
+ * Find the first direct child element with the given name.
127
+ */
128
+ declare function findChild(parent: Element | undefined, name: string): Element | undefined;
129
+ /**
130
+ * Get all direct child elements matching the given name.
131
+ */
132
+ declare function children(parent: Element | undefined, name: string): Element[];
133
+ /**
134
+ * Get all direct child elements.
135
+ */
136
+ declare function allChildren(parent: Element | undefined): Element[];
137
+ /**
138
+ * Get text content of the first child element with the given name.
139
+ */
140
+ declare function childText(parent: Element | undefined, name: string): string;
141
+ /**
142
+ * Get text content of an element.
143
+ * Handles cases where text may be directly on .text or in a child element.
144
+ */
145
+ declare function textOf(element: Element | undefined): string;
146
+ /**
147
+ * Collect text from all direct text nodes within an element.
148
+ */
149
+ declare function collectText(element: Element | undefined): string;
150
+ /**
151
+ * Get an attribute value as a string.
152
+ */
153
+ declare function attr(element: Element | undefined, name: string): string | undefined;
154
+ /**
155
+ * Get an attribute value as a number.
156
+ */
157
+ declare function attrNum(element: Element | undefined, name: string): number | undefined;
158
+ /**
159
+ * Get an attribute value as a boolean.
160
+ */
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;
168
+ /**
169
+ * Check if an element has a specific child element.
170
+ */
171
+ declare function hasChild(parent: Element | undefined, name: string): boolean;
172
+ /**
173
+ * Find deep descendant elements matching the given name.
174
+ */
175
+ declare function findDeep(parent: Element | undefined, name: string): Element[];
176
+ /**
177
+ * Get the number of direct child elements.
178
+ */
179
+ declare function childCount(parent: Element | undefined): number;
180
+ //#endregion
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,3 +1,5 @@
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
+
1
3
  //#region src/serialize.d.ts
2
4
  declare function xml(input: Record<string, any> | Record<string, any>[], options?: boolean | string | {
3
5
  indent?: boolean | string;
@@ -7,129 +9,6 @@ declare function xml(input: Record<string, any> | Record<string, any>[], options
7
9
  };
8
10
  }): string;
9
11
  //#endregion
10
- //#region src/types.d.ts
11
- interface Attributes {
12
- [key: string]: string | number | undefined;
13
- }
14
- interface DeclarationAttributes {
15
- version?: string | number;
16
- encoding?: string;
17
- standalone?: string;
18
- }
19
- interface Element {
20
- declaration?: {
21
- attributes?: DeclarationAttributes;
22
- };
23
- instruction?: string;
24
- attributes?: Attributes;
25
- cdata?: string;
26
- doctype?: string;
27
- comment?: string;
28
- text?: string | number | boolean;
29
- type?: string;
30
- name?: string;
31
- elements?: Element[];
32
- parent?: Element;
33
- }
34
- interface ElementCompact {
35
- [key: string]: any;
36
- _declaration?: {
37
- _attributes?: DeclarationAttributes;
38
- };
39
- _instruction?: {
40
- [key: string]: string;
41
- };
42
- _attributes?: Attributes;
43
- _cdata?: string;
44
- _doctype?: string;
45
- _comment?: string;
46
- _text?: string | number;
47
- }
48
- interface IgnoreOptions {
49
- ignoreDeclaration?: boolean;
50
- ignoreInstruction?: boolean;
51
- ignoreAttributes?: boolean;
52
- ignoreComment?: boolean;
53
- ignoreCdata?: boolean;
54
- ignoreDoctype?: boolean;
55
- ignoreText?: boolean;
56
- }
57
- interface Xml2JsOptions extends IgnoreOptions {
58
- compact?: boolean;
59
- trim?: boolean;
60
- sanitize?: boolean;
61
- nativeType?: boolean;
62
- nativeTypeAttributes?: boolean;
63
- addParent?: boolean;
64
- alwaysArray?: boolean | string[];
65
- alwaysChildren?: boolean;
66
- instructionHasAttributes?: boolean;
67
- captureSpacesBetweenElements?: boolean;
68
- doctypeFn?: (value: string, parentElement: object) => string;
69
- instructionFn?: (value: string, instructionName: string, parentElement: string) => string;
70
- cdataFn?: (value: string, parentElement: object) => string;
71
- commentFn?: (value: string, parentElement: object) => string;
72
- textFn?: (value: string, parentElement: object) => string;
73
- instructionNameFn?: (instructionName: string, instructionValue: string, parentElement: string) => string;
74
- elementNameFn?: (value: string, parentElement: object) => string;
75
- attributeNameFn?: (attributeName: string, attributeValue: string, parentElement: string) => string;
76
- attributeValueFn?: (attributeValue: string, attributeName: string, parentElement: string) => string;
77
- attributesFn?: (value: Attributes, parentElement: string) => Attributes;
78
- }
79
- interface Js2XmlOptions extends IgnoreOptions {
80
- spaces?: number | string;
81
- compact?: boolean;
82
- indentText?: boolean;
83
- indentCdata?: boolean;
84
- indentAttributes?: boolean;
85
- indentInstruction?: boolean;
86
- fullTagEmptyElement?: boolean;
87
- noQuotesForNativeAttributes?: boolean;
88
- doctypeFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
89
- instructionFn?: (instructionValue: string, instructionName: string, currentElementName: string, currentElementObj: object) => string;
90
- cdataFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
91
- commentFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
92
- textFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
93
- instructionNameFn?: (instructionName: string, instructionValue: string, currentElementName: string, currentElementObj: object) => string;
94
- elementNameFn?: (value: string, currentElementName: string, currentElementObj: object) => string;
95
- attributeNameFn?: (attributeName: string, attributeValue: string, currentElementName: string, currentElementObj: object) => string;
96
- attributeValueFn?: (attributeValue: string, attributeName: string, currentElementName: string, currentElementObj: object) => string;
97
- attributesFn?: (value: Attributes, currentElementName: string, currentElementObj: object) => Attributes;
98
- fullTagEmptyElementFn?: (currentElementName: string, currentElementObj: object) => boolean;
99
- }
100
- interface XmlOption {
101
- indent?: string;
102
- stream?: boolean;
103
- declaration?: boolean | {
104
- encoding?: string;
105
- standalone?: string;
106
- };
107
- }
108
- interface XmlAttrs {
109
- [attr: string]: XmlAtom;
110
- }
111
- type XmlAtom = string | number | boolean | null;
112
- interface ElementObject {
113
- push(xmlObject: XmlObject): void;
114
- close(xmlObject?: XmlObject): void;
115
- }
116
- type XmlDesc = {
117
- _attr: XmlAttrs;
118
- } | {
119
- _cdata: string;
120
- } | {
121
- _attr: XmlAttrs;
122
- _cdata: string;
123
- } | XmlAtom | XmlAtom[] | XmlDescArray;
124
- interface XmlDescArray {
125
- [index: number]: {
126
- _attr: XmlAttrs;
127
- } | XmlObject;
128
- }
129
- type XmlObject = {
130
- [tag: string]: ElementObject | XmlDesc;
131
- } | XmlDesc;
132
- //#endregion
133
12
  //#region src/parse.d.ts
134
13
  declare function xml2js(xmlString: string, options?: Xml2JsOptions): Element;
135
14
  //#endregion
@@ -158,4 +37,4 @@ declare function escapeAttributeValue(str: string): string;
158
37
  /** Convert XML string to JSON string — xml-js compatible export */
159
38
  declare function xml2json(xml: string, options?: Xml2JsOptions): string;
160
39
  //#endregion
161
- export { Attributes, DeclarationAttributes, Element, ElementCompact, ElementObject, IgnoreOptions, Js2XmlOptions, Xml2JsOptions, XmlAtom, XmlAttrs, XmlDesc, XmlDescArray, XmlObject, XmlOption, escapeAttributeValue, escapeXml, js2xml, json2xml, toElement, xml, xml2js, xml2json };
40
+ 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 };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf } from "./utils.mjs";
1
2
  //#region src/escape.ts
2
3
  const XML_CHAR_MAP = {
3
4
  "&": "&amp;",
@@ -66,6 +67,7 @@ function resolve(data, indent, depth) {
66
67
  switch (typeof values) {
67
68
  case "object":
68
69
  if (values._attr) for (const key of Object.keys(values._attr)) attributes.push(`${key}="${escapeXml(String(values._attr[key]))}"`);
70
+ if (values._attributes) for (const key of Object.keys(values._attributes)) attributes.push(`${key}="${escapeXml(String(values._attributes[key]))}"`);
69
71
  if (values._cdata) {
70
72
  const escaped = String(values._cdata).replace(/\]\]>/g, "]]]]><![CDATA[>");
71
73
  content.push(`<![CDATA[${escaped}]]>`);
@@ -80,6 +82,7 @@ function resolve(data, indent, depth) {
80
82
  emptyArray: true
81
83
  };
82
84
  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]))}"`);
85
+ 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]))}"`);
83
86
  else if (value && typeof value === "object") content.push(resolve(value, indent, depth + 1));
84
87
  else if (value != null) content.push(escapeXml(String(value)));
85
88
  }
@@ -499,4 +502,4 @@ function xml2json(xml, options) {
499
502
  return JSON.stringify(xml2js(xml, options));
500
503
  }
501
504
  //#endregion
502
- export { escapeAttributeValue, escapeXml, js2xml, json2xml, toElement, xml, xml2js, xml2json };
505
+ export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, escapeAttributeValue, escapeXml, findChild, findDeep, hasChild, js2xml, json2xml, textOf, toElement, xml, xml2js, xml2json };
@@ -0,0 +1,2 @@
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 ADDED
@@ -0,0 +1,116 @@
1
+ //#region src/utils.ts
2
+ /**
3
+ * Find the first direct child element with the given name.
4
+ */
5
+ function findChild(parent, name) {
6
+ return parent?.elements?.find((e) => e.name === name);
7
+ }
8
+ /**
9
+ * Get all direct child elements matching the given name.
10
+ */
11
+ function children(parent, name) {
12
+ return parent?.elements?.filter((e) => e.name === name) ?? [];
13
+ }
14
+ /**
15
+ * Get all direct child elements.
16
+ */
17
+ function allChildren(parent) {
18
+ return parent?.elements ?? [];
19
+ }
20
+ /**
21
+ * Get text content of the first child element with the given name.
22
+ */
23
+ function childText(parent, name) {
24
+ return textOf(findChild(parent, name));
25
+ }
26
+ /**
27
+ * Get text content of an element.
28
+ * Handles cases where text may be directly on .text or in a child element.
29
+ */
30
+ function textOf(element) {
31
+ if (!element) return "";
32
+ if (element.text !== void 0 && typeof element.text === "string") return element.text;
33
+ if (element.elements && element.elements.length > 0) return element.elements.map((e) => typeof e.text === "string" ? e.text : "").join("");
34
+ return "";
35
+ }
36
+ /**
37
+ * Collect text from all direct text nodes within an element.
38
+ */
39
+ function collectText(element) {
40
+ if (!element) return "";
41
+ const parts = [];
42
+ collectTextRecursive(element, parts);
43
+ return parts.join("");
44
+ }
45
+ function collectTextRecursive(element, parts) {
46
+ if (!element) return;
47
+ if (element.text !== void 0 && typeof element.text === "string") parts.push(element.text);
48
+ if (element.elements) for (const child of element.elements) collectTextRecursive(child, parts);
49
+ }
50
+ /**
51
+ * Get an attribute value as a string.
52
+ */
53
+ function attr(element, name) {
54
+ const v = element?.attributes?.[name];
55
+ return v !== void 0 ? String(v) : void 0;
56
+ }
57
+ /**
58
+ * Get an attribute value as a number.
59
+ */
60
+ function attrNum(element, name) {
61
+ const v = element?.attributes?.[name];
62
+ if (v === void 0) return void 0;
63
+ const n = Number(v);
64
+ return isNaN(n) ? void 0 : n;
65
+ }
66
+ /**
67
+ * Get an attribute value as a boolean.
68
+ */
69
+ function attrBool(element, name) {
70
+ const v = element?.attributes?.[name];
71
+ if (v === void 0) return void 0;
72
+ if (typeof v === "boolean") return v;
73
+ const lower = String(v).toLowerCase();
74
+ if (lower === "true" || lower === "1") return true;
75
+ if (lower === "false" || lower === "0") return false;
76
+ }
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
+ /**
92
+ * Check if an element has a specific child element.
93
+ */
94
+ function hasChild(parent, name) {
95
+ return parent?.elements?.some((e) => e.name === name) ?? false;
96
+ }
97
+ /**
98
+ * Find deep descendant elements matching the given name.
99
+ */
100
+ function findDeep(parent, name) {
101
+ const result = [];
102
+ if (!parent) return result;
103
+ for (const child of parent.elements ?? []) {
104
+ if (child.name === name) result.push(child);
105
+ result.push(...findDeep(child, name));
106
+ }
107
+ return result;
108
+ }
109
+ /**
110
+ * Get the number of direct child elements.
111
+ */
112
+ function childCount(parent) {
113
+ return parent?.elements?.length ?? 0;
114
+ }
115
+ //#endregion
116
+ export { allChildren, attr, attrBool, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, hasChild, textOf };
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@office-open/xml",
3
- "version": "0.1.0",
4
- "description": "XML parsing and serialization, replacing xml + xml-js",
3
+ "version": "0.3.1",
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",
7
7
  "ooxml",
8
8
  "parse",
9
9
  "serialize",
10
- "xml"
10
+ "xml",
11
+ "zero-dependencies"
11
12
  ],
12
13
  "homepage": "https://github.com/DemoMacro/office-open#readme",
13
14
  "bugs": {
@@ -33,6 +34,10 @@
33
34
  ".": {
34
35
  "types": "./dist/index.d.mts",
35
36
  "import": "./dist/index.mjs"
37
+ },
38
+ "./utils": {
39
+ "types": "./dist/utils.d.mts",
40
+ "import": "./dist/utils.mjs"
36
41
  }
37
42
  },
38
43
  "devDependencies": {