@office-open/xml 0.11.0 → 0.12.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
@@ -76,21 +76,27 @@ interface Element {
76
76
 
77
77
  ## Benchmark
78
78
 
79
- Performance vs [xml-js](https://github.com/nashwaan/xml-js) and [xml](https://github.com/dylang/node-xml) (higher ops/s is better, Windows 11 / Node 24). `@office-open/xml` is a drop-in replacement for both. The `xml` (npm) package is generation-only (no parser), so it only appears under stringify.
79
+ Performance vs [txml](https://github.com/TobiasNickel/tXml), [xml-js](https://github.com/nashwaan/xml-js), and [xml](https://github.com/dylang/node-xml) (higher ops/s is better, Windows 11; each scenario runs under both Node 24 and Bun 1.4). `@office-open/xml` is a drop-in replacement for xml-js and xml. The `xml` (npm) package is generation-only (no parser), so it only appears under stringify. `Bun.XML` is Bun-only and its compact parse mode is lossy (`#text` runs concatenate, same-name children collapse into keyed arrays), so it is a throughput reference rather than a drop-in option.
80
+
81
+ txml skips entity encoding by default (`encodeEntities: false`), which emits invalid XML when text contains `&`, `<`, or `>`; the `txml` column shows that raw mode and the `txml (entities)` column is the output-equivalent mode.
80
82
 
81
83
  **parse() — XML string → Element tree**
82
84
 
83
- | Scenario | @office-open/xml | xml-js |
84
- | ------------- | ---------------: | ------------: |
85
- | simple XML | 837,902 ops/s | 100,244 ops/s |
86
- | complex OOXML | 354,836 ops/s | 54,457 ops/s |
85
+ | Scenario | Runtime | @office-open/xml | txml | xml-js | Bun.XML.parse |
86
+ | ------------- | ------- | ---------------- | ------------- | ------------- | ------------- |
87
+ | simple XML | Node 24 | 1,301,742 ops/s | 983,046 ops/s | 93,951 ops/s | |
88
+ | | Bun 1.4 | 1,906,525 ops/s | 616,562 ops/s | 161,894 ops/s | 828,554 ops/s |
89
+ | complex OOXML | Node 24 | 423,536 ops/s | 388,743 ops/s | 49,771 ops/s | — |
90
+ | | Bun 1.4 | 483,512 ops/s | 241,036 ops/s | 63,029 ops/s | 451,177 ops/s |
87
91
 
88
92
  **stringify() — Element tree → XML string**
89
93
 
90
- | Scenario | @office-open/xml | xml-js | xml (npm) |
91
- | -------------- | ---------------: | ------------: | ------------: |
92
- | simple element | 827,864 ops/s | 208,934 ops/s | 379,424 ops/s |
93
- | complex OOXML | 374,933 ops/s | 135,967 ops/s | 203,205 ops/s |
94
+ | Scenario | Runtime | @office-open/xml | txml (entities) | txml | xml-js | xml (npm) | Bun.XML.stringify |
95
+ | -------------- | ------- | ---------------- | --------------- | --------------- | ------------- | ------------- | ----------------- |
96
+ | simple element | Node 24 | 2,049,617 ops/s | 1,255,456 ops/s | 2,765,770 ops/s | 190,783 ops/s | 303,724 ops/s | — |
97
+ | | Bun 1.4 | 2,588,728 ops/s | 1,721,498 ops/s | 4,248,377 ops/s | 438,430 ops/s | 483,526 ops/s | 584,746 ops/s |
98
+ | complex OOXML | Node 24 | 583,811 ops/s | 447,274 ops/s | 1,326,397 ops/s | 130,335 ops/s | 172,258 ops/s | — |
99
+ | | Bun 1.4 | 510,448 ops/s | 528,375 ops/s | 2,303,173 ops/s | 226,645 ops/s | 292,534 ops/s | 251,444 ops/s |
94
100
 
95
101
  ## License
96
102
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,4 @@
1
- import { C as IgnoreOptions, S as ElementCompact, T as StringifyOptions, _ as isNonEmpty, a as attrBool, b as DeclarationAttributes, c as childCount, d as collectText, f as colorAttr, g as hasChild, h as findFirst, i as attr, l as childText, m as findDeep, n as OOXML_XML_DECLARATION, o as attrMeasure, p as findChild, r as allChildren, s as attrNum, t as NonEmptyArray, u as children, v as textOf, w as ParseOptions, x as Element, y as Attributes } from "./utils-CVZp5dCd.mjs";
2
-
1
+ import { C as IgnoreOptions, S as ElementCompact, T as StringifyOptions, _ as isNonEmpty, a as attrBool, b as DeclarationAttributes, c as childCount, d as collectText, f as colorAttr, g as hasChild, h as findFirst, i as attr, l as childText, m as findDeep, n as OOXML_XML_DECLARATION, o as attrMeasure, p as findChild, r as allChildren, s as attrNum, t as NonEmptyArray, u as children, v as textOf, w as ParseOptions, x as Element, y as Attributes } from "./utils-qhk6IlD8.mjs";
3
2
  //#region src/parse.d.ts
4
3
  declare function unescapeXml(str: string): string;
5
4
  declare function nativeTypeValue(value: string): string | number | boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/parse.ts","../src/stringify.ts","../src/stringify-element.ts","../src/escape.ts"],"mappings":";;;iBAagB,WAAA,CAAY,GAAW;AAAA,iBAgBvB,eAAA,CAAgB,KAAa;AAAA,iBA4C7B,KAAA,CAAM,SAAA,UAAmB,OAAA,GAAU,YAAA,GAAe,OAAO;AAAA,iBAyQzD,eAAA,CAAgB,GAAA,WAAc,MAAM;;;iBC/UpC,SAAA,CAAU,EAAA,EAAI,OAAA,EAAS,OAAA,GAAU,gBAAgB;;;iBCQjD,gBAAA,CAAiB,EAAW,EAAP,OAAO;;;iBCP5B,SAAA,CAAU,GAAW;AAAA,iBAsCrB,KAAA,CAAM,MAA6D,EAArD,MAAM;AAAA,iBAqBpB,QAAA,CAAS,MAA6D,EAArD,MAAM;AAAA,iBAevB,gBAAA,CAAiB,GAAA,UAAa,OAAgB;AAAA,iBAoB9C,OAAA,CACd,IAAA,UACA,UAAA,GAAa,QAAQ,CAAC,MAAA,kDACtB,QAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/parse.ts","../src/stringify.ts","../src/stringify-element.ts","../src/escape.ts"],"mappings":";;iBAagB,YAAY;iBAgBZ,gBAAgB;iBA4ChB,MAAM,mBAAmB,UAAU,eAAe;iBAwXlD,gBAAgB,cAAc;;;iBC1b9B,UAAU,IAAI,SAAS,UAAU;;;iBCIjC,iBAAiB,IAAI;;;iBCPrB,UAAU;iBA6BV,MAAM,QAAQ;iBAsBd,SAAS,QAAQ;iBAejB,iBAAiB,aAAa;iBAoB9B,QACd,cACA,aAAa,SAAS,wDACtB"}
package/dist/index.mjs CHANGED
@@ -56,6 +56,11 @@ function parse(xmlString, options) {
56
56
  const ignoreDoctype = options?.ignoreDoctype ?? false;
57
57
  const nativeTypeAttributes = options?.nativeTypeAttributes ?? false;
58
58
  const deferSet = options?.deferElements !== void 0 && options.deferElements.length > 0 ? new Set(options.deferElements) : void 0;
59
+ const nsTable = options?.normalizeNamespaces !== void 0 ? new Map(Object.entries(options.normalizeNamespaces)) : void 0;
60
+ const nsStack = [{
61
+ prefixes: void 0,
62
+ defaultCanonical: void 0
63
+ }];
59
64
  const result = {};
60
65
  const stack = [result];
61
66
  let i = 0;
@@ -63,7 +68,8 @@ function parse(xmlString, options) {
63
68
  while (i < len) {
64
69
  if (xmlString.charCodeAt(i) !== 60) {
65
70
  const start = i;
66
- while (i < len && xmlString.charCodeAt(i) !== 60) i++;
71
+ const lt = xmlString.indexOf("<", i);
72
+ i = lt === -1 ? len : lt;
67
73
  let text = unescapeXml(xmlString.slice(start, i));
68
74
  if (trim) text = text.trim();
69
75
  if (ignoreText) continue;
@@ -107,8 +113,10 @@ function parse(xmlString, options) {
107
113
  if (end === -1) break;
108
114
  const comment = xmlString.slice(i + 3, end);
109
115
  i = end + 3;
110
- if (!ignoreComment) if (trim) addField(peek(stack), "comment", comment.trim());
111
- else addField(peek(stack), "comment", comment);
116
+ if (!ignoreComment) {
117
+ if (trim) addField(peek(stack), "comment", comment.trim());
118
+ else addField(peek(stack), "comment", comment);
119
+ }
112
120
  continue;
113
121
  }
114
122
  if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 8) === "![CDATA[") {
@@ -116,8 +124,10 @@ function parse(xmlString, options) {
116
124
  if (end === -1) break;
117
125
  const cdata = xmlString.slice(i + 8, end);
118
126
  i = end + 3;
119
- if (!ignoreCdata) if (trim) addField(peek(stack), "cdata", cdata.trim());
120
- else addField(peek(stack), "cdata", cdata);
127
+ if (!ignoreCdata) {
128
+ if (trim) addField(peek(stack), "cdata", cdata.trim());
129
+ else addField(peek(stack), "cdata", cdata);
130
+ }
121
131
  continue;
122
132
  }
123
133
  if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 9) === "!DOCTYPE") {
@@ -133,11 +143,16 @@ function parse(xmlString, options) {
133
143
  if (end === -1) break;
134
144
  i = end + 1;
135
145
  stack.pop();
146
+ if (nsTable !== void 0) nsStack.pop();
136
147
  continue;
137
148
  }
138
149
  const tagNameEnd = findTagNameEnd(xmlString, i);
139
150
  const tagName = xmlString.slice(i, tagNameEnd);
140
151
  let pos = tagNameEnd;
152
+ const nsParent = nsTable !== void 0 ? nsStack[nsStack.length - 1] : void 0;
153
+ let nsPrefixes = nsParent?.prefixes;
154
+ let nsDefault = nsParent?.defaultCanonical;
155
+ let nsDeclared = false;
141
156
  let attrs;
142
157
  while (pos < len) {
143
158
  while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;
@@ -147,7 +162,7 @@ function parse(xmlString, options) {
147
162
  if (xmlString.charCodeAt(pos) === 62 || xmlString.charCodeAt(pos) === 47) break;
148
163
  pos++;
149
164
  }
150
- const name = xmlString.slice(nameStart, pos);
165
+ const name = xmlString.slice(nameStart, pos).trim();
151
166
  if (xmlString.charCodeAt(pos) !== 61) break;
152
167
  pos++;
153
168
  while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;
@@ -156,19 +171,66 @@ function parse(xmlString, options) {
156
171
  pos++;
157
172
  const valueStart = pos;
158
173
  while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;
159
- if (attrs === void 0) attrs = {};
160
- attrs[name] = unescapeXml(xmlString.slice(valueStart, pos));
174
+ const value = unescapeXml(xmlString.slice(valueStart, pos));
161
175
  pos++;
176
+ if (nsTable !== void 0 && (name === "xmlns" || name.startsWith("xmlns:"))) {
177
+ if (attrs === void 0) attrs = {};
178
+ const canonical = nsTable.get(value);
179
+ if (name === "xmlns") {
180
+ nsDefault = canonical;
181
+ if (canonical !== void 0 && canonical !== "") attrs[`xmlns:${canonical}`] = value;
182
+ else attrs[name] = value;
183
+ } else {
184
+ const prefix = name.slice(6);
185
+ if (canonical !== void 0 && canonical !== "") attrs[`xmlns:${canonical}`] = value;
186
+ else if (canonical === "" && !("xmlns" in attrs)) attrs.xmlns = value;
187
+ else attrs[name] = value;
188
+ if (!nsDeclared) {
189
+ nsPrefixes = new Map(nsPrefixes ?? []);
190
+ nsDeclared = true;
191
+ }
192
+ nsPrefixes.set(prefix, canonical ?? prefix);
193
+ }
194
+ continue;
195
+ }
196
+ if (attrs === void 0) attrs = {};
197
+ attrs[name] = value;
162
198
  }
163
199
  if (attrs && nativeTypeAttributes) for (const key in attrs) attrs[key] = nativeTypeValue(attrs[key]);
200
+ let finalName = tagName;
201
+ if (nsTable !== void 0) {
202
+ const colon = tagName.indexOf(":");
203
+ if (colon > 0) {
204
+ const p = tagName.slice(0, colon);
205
+ if (p !== "xml") {
206
+ const c = nsPrefixes?.get(p);
207
+ if (c !== void 0 && c !== p) finalName = c === "" ? tagName.slice(colon + 1) : c + tagName.slice(colon);
208
+ }
209
+ } else if (colon < 0 && nsDefault !== void 0 && nsDefault !== "") finalName = nsDefault + ":" + tagName;
210
+ if (attrs !== void 0 && nsPrefixes !== void 0) for (const key of Object.keys(attrs)) {
211
+ if (key === "xmlns" || key.startsWith("xmlns:")) continue;
212
+ const kcolon = key.indexOf(":");
213
+ if (kcolon <= 0) continue;
214
+ const p = key.slice(0, kcolon);
215
+ if (p === "xml") continue;
216
+ const c = nsPrefixes.get(p);
217
+ if (c !== void 0 && c !== p) {
218
+ const renamed = c === "" ? key.slice(kcolon + 1) : c + key.slice(kcolon);
219
+ const attrValue = attrs[key];
220
+ if (attrValue !== void 0) attrs[renamed] = attrValue;
221
+ delete attrs[key];
222
+ }
223
+ }
224
+ }
164
225
  const isSelfClosing = xmlString.charCodeAt(pos) === 47;
165
226
  if (isSelfClosing) pos += 2;
166
227
  else pos++;
167
228
  const element = {
168
229
  type: "element",
169
- name: tagName
230
+ name: finalName,
231
+ attributes: attrs,
232
+ elements: void 0
170
233
  };
171
- if (attrs) element.attributes = attrs;
172
234
  const parent = peek(stack);
173
235
  if (!parent.elements) parent.elements = [];
174
236
  parent.elements.push(element);
@@ -203,6 +265,13 @@ function parse(xmlString, options) {
203
265
  continue;
204
266
  }
205
267
  stack.push(element);
268
+ if (nsTable !== void 0) {
269
+ const layerChanged = nsDeclared || nsDefault !== nsParent.defaultCanonical;
270
+ nsStack.push(layerChanged ? {
271
+ prefixes: nsPrefixes,
272
+ defaultCanonical: nsDefault
273
+ } : nsParent);
274
+ }
206
275
  }
207
276
  i = pos;
208
277
  }
@@ -295,23 +364,14 @@ const XML_SPECIALS = /[&"'<>]/;
295
364
  /** Escape text content for XML. Fast path returns original string when no special chars. */
296
365
  function escapeXml(str) {
297
366
  if (!XML_SPECIALS.test(str)) return str;
298
- const firstSpecial = str.search(XML_SPECIALS);
299
- const parts = [str.slice(0, firstSpecial)];
300
- for (let i = firstSpecial; i < str.length; i++) {
301
- const c = str.charCodeAt(i);
302
- if (c === 38) parts.push("&amp;");
303
- else if (c === 34) parts.push("&quot;");
304
- else if (c === 39) parts.push("&apos;");
305
- else if (c === 60) parts.push("&lt;");
306
- else if (c === 62) parts.push("&gt;");
307
- else parts.push(str.charAt(i));
308
- }
309
- return parts.join("");
367
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
310
368
  }
311
369
  /**
312
370
  * Build an XML attribute string fragment from a record.
313
371
  * `undefined` values are automatically skipped.
314
- * String values are escaped via `escapeXml`.
372
+ * String values are escaped via `escapeXml`. Booleans serialize as 0/1 —
373
+ * the spelling Office itself writes for xsd:boolean attributes and
374
+ * ST_OnOff unions alike.
315
375
  *
316
376
  * @example
317
377
  * attrs({ id: 1, name: "foo", hidden: undefined })
@@ -319,7 +379,10 @@ function escapeXml(str) {
319
379
  */
320
380
  function attrs(record) {
321
381
  const parts = [];
322
- for (const [key, v] of Object.entries(record)) if (v !== void 0) parts.push(` ${key}="${typeof v === "string" ? escapeXml(v) : v}"`);
382
+ for (const [key, v] of Object.entries(record)) if (v !== void 0) {
383
+ const value = typeof v === "string" ? escapeXml(v) : typeof v === "boolean" ? v ? 1 : 0 : v;
384
+ parts.push(` ${key}="${value}"`);
385
+ }
323
386
  return parts.join("");
324
387
  }
325
388
  /**
@@ -372,6 +435,7 @@ function element(name, attrRecord, children) {
372
435
  }
373
436
  //#endregion
374
437
  //#region src/stringify.ts
438
+ const TEXT_SPECIALS = /[&<>]/;
375
439
  function stringify(js, options) {
376
440
  const opts = normalizeOptions(options);
377
441
  const parts = [];
@@ -379,18 +443,19 @@ function stringify(js, options) {
379
443
  if (js.elements?.length) parts.push(writeElements(js.elements, opts, 0, !parts.length));
380
444
  return parts.join("");
381
445
  }
446
+ const DEFAULT_OPTIONS = {
447
+ spaces: "",
448
+ ignoreDeclaration: false,
449
+ ignoreText: false,
450
+ ignoreComment: false,
451
+ ignoreCdata: false,
452
+ ignoreDoctype: false,
453
+ fullTagEmptyElement: false,
454
+ indentText: false,
455
+ indentCdata: false
456
+ };
382
457
  function normalizeOptions(options) {
383
- if (!options) return {
384
- spaces: "",
385
- ignoreDeclaration: false,
386
- ignoreText: false,
387
- ignoreComment: false,
388
- ignoreCdata: false,
389
- ignoreDoctype: false,
390
- fullTagEmptyElement: false,
391
- indentText: false,
392
- indentCdata: false
393
- };
458
+ if (!options) return DEFAULT_OPTIONS;
394
459
  let spaces = "";
395
460
  if (options.spaces != null) spaces = typeof options.spaces === "number" ? " ".repeat(options.spaces) : options.spaces;
396
461
  return {
@@ -407,7 +472,8 @@ function normalizeOptions(options) {
407
472
  };
408
473
  }
409
474
  function writeIndentation(spaces, depth, firstLine) {
410
- return (!firstLine && spaces ? "\n" : "") + spaces.repeat(depth);
475
+ if (!spaces) return "";
476
+ return (!firstLine ? "\n" : "") + spaces.repeat(depth);
411
477
  }
412
478
  function writeDeclaration(declaration) {
413
479
  const attrs = declaration.attributes;
@@ -418,91 +484,66 @@ function writeDeclaration(declaration) {
418
484
  return parts.join("") + "?>";
419
485
  }
420
486
  function writeAttributes(attributes, elementName, element, attributeValueFn) {
421
- const parts = [];
422
- for (const key of Object.keys(attributes)) {
487
+ let s = "";
488
+ for (const key in attributes) {
423
489
  const value = attributes[key];
424
490
  if (value === null || value === void 0) continue;
425
491
  const raw = String(value);
426
492
  const attr = attributeValueFn ? attributeValueFn(raw, key, elementName, element) : escapeXml(raw);
427
- parts.push(` ${key}="${attr}"`);
493
+ s += ` ${key}="${attr}"`;
428
494
  }
429
- return parts.join("");
430
- }
431
- function writeElement(element, opts, depth) {
432
- if (!element.name) return "";
433
- const name = element.name;
434
- const attrStr = element.attributes ? writeAttributes(element.attributes, name, element, opts.attributeValueFn) : "";
435
- if (element.raw !== void 0) return `<${name}${attrStr}>${element.raw}</${name}>`;
436
- if (!((element.elements?.length ?? 0) > 0 || element.attributes?.["xml:space"] === "preserve" || opts.fullTagEmptyElement)) return `<${name}${attrStr}/>`;
437
- const parts = [];
438
- parts.push(`<${name}${attrStr}>`);
439
- const hasChildElements = element.elements?.some((e) => e.type === "element") ?? false;
440
- if (element.elements?.length) parts.push(writeElements(element.elements, opts, depth + 1, false));
441
- if (opts.spaces && hasChildElements) parts.push("\n" + opts.spaces.repeat(depth));
442
- parts.push(`</${name}>`);
443
- return parts.join("");
495
+ return s;
444
496
  }
445
497
  function writeElements(elements, opts, depth, firstLine) {
446
- const parts = [];
498
+ let s = "";
447
499
  for (let i = 0; i < elements.length; i++) {
448
500
  const element = elements[i];
449
501
  if (!element) continue;
450
502
  const isFirst = firstLine && i === 0;
451
- switch (element.type) {
452
- case "element":
453
- parts.push(writeIndentation(opts.spaces, depth, isFirst));
454
- parts.push(writeElement(element, opts, depth));
455
- break;
456
- case "text":
457
- if (opts.ignoreText) continue;
458
- if (opts.indentText) parts.push(writeIndentation(opts.spaces, depth, isFirst));
459
- parts.push(writeText(element.text));
460
- break;
461
- case "cdata":
462
- if (opts.ignoreCdata) continue;
463
- if (opts.indentCdata) parts.push(writeIndentation(opts.spaces, depth, isFirst));
464
- parts.push(writeCdata(element.cdata));
465
- break;
466
- case "comment":
467
- if (opts.ignoreComment) continue;
468
- parts.push(writeIndentation(opts.spaces, depth, isFirst));
469
- parts.push(writeComment(element.comment));
470
- break;
471
- case "doctype":
472
- if (opts.ignoreDoctype) continue;
473
- parts.push(writeIndentation(opts.spaces, depth, isFirst));
474
- parts.push(writeDoctype(element.doctype));
475
- break;
476
- default: break;
477
- }
478
- }
479
- return parts.join("");
480
- }
481
- function writeText(text) {
482
- if (text == null) return "";
483
- const str = String(text);
484
- for (let i = 0; i < str.length; i++) {
485
- const c = str.charCodeAt(i);
486
- if (c === 38 || c === 60 || c === 62) {
487
- let s = "";
488
- let last = 0;
489
- for (let j = i; j < str.length; j++) {
490
- const cj = str.charCodeAt(j);
491
- if (cj === 38) {
492
- s += str.slice(last, j) + "&amp;";
493
- last = j + 1;
494
- } else if (cj === 60) {
495
- s += str.slice(last, j) + "&lt;";
496
- last = j + 1;
497
- } else if (cj === 62) {
498
- s += str.slice(last, j) + "&gt;";
499
- last = j + 1;
500
- }
503
+ const type = element.type;
504
+ if (type === "element") {
505
+ const name = element.name;
506
+ if (!name) continue;
507
+ if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
508
+ const attributes = element.attributes;
509
+ const attrStr = attributes ? writeAttributes(attributes, name, element, opts.attributeValueFn) : "";
510
+ if (element.raw !== void 0) {
511
+ s += `<${name}${attrStr}>${element.raw}</${name}>`;
512
+ continue;
513
+ }
514
+ const children = element.elements;
515
+ if (!(children !== void 0 && children.length > 0 || opts.fullTagEmptyElement || attributes?.["xml:space"] === "preserve")) {
516
+ s += `<${name}${attrStr}/>`;
517
+ continue;
501
518
  }
502
- return s + str.slice(last);
519
+ const open = `<${name}${attrStr}>`;
520
+ if (children !== void 0 && children.length > 0) {
521
+ const inner = writeElements(children, opts, depth + 1, false);
522
+ if (opts.spaces && children.some((e) => e.type === "element")) s += open + inner + "\n" + opts.spaces.repeat(depth) + `</${name}>`;
523
+ else s += open + inner + `</${name}>`;
524
+ } else s += open + `</${name}>`;
525
+ } else if (type === "text") {
526
+ if (opts.ignoreText) continue;
527
+ if (opts.indentText && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
528
+ const text = element.text;
529
+ if (text == null) continue;
530
+ const str = String(text);
531
+ s += TEXT_SPECIALS.test(str) ? str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") : str;
532
+ } else if (type === "cdata") {
533
+ if (opts.ignoreCdata) continue;
534
+ if (opts.indentCdata && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
535
+ s += writeCdata(element.cdata);
536
+ } else if (type === "comment") {
537
+ if (opts.ignoreComment) continue;
538
+ if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
539
+ s += writeComment(element.comment);
540
+ } else if (type === "doctype") {
541
+ if (opts.ignoreDoctype) continue;
542
+ if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
543
+ s += writeDoctype(element.doctype);
503
544
  }
504
545
  }
505
- return str;
546
+ return s;
506
547
  }
507
548
  function writeCdata(cdata) {
508
549
  if (cdata == null) return "";
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["stringifyChildren"],"sources":["../src/parse.ts","../src/escape.ts","../src/stringify.ts","../src/stringify-element.ts"],"sourcesContent":["import type { Element, ParseOptions } from \"./types\";\n\nconst ENTITY_MAP: Record<string, string> = {\n \"&amp;\": \"&\",\n \"&lt;\": \"<\",\n \"&gt;\": \">\",\n \"&quot;\": '\"',\n \"&apos;\": \"'\",\n};\n// Matches the five named entities plus numeric character references\n// (&#65; decimal, &#x42; hex).\nconst ENTITY_PATTERN = /&(?:amp|lt|gt|quot|apos|#x[0-9a-fA-F]+|#[0-9]+);/g;\n\nexport function unescapeXml(str: string): string {\n // Fast path: entities all start with '&', and OOXML parts overwhelmingly\n // contain none (a 63 MB worksheet measured zero occurrences). The regex\n // scan + replace setup per call showed up to ~12% of large-file parse\n // profiles, so gate it on the sentinel byte.\n if (str.indexOf(\"&\") === -1) return str;\n return str.replace(ENTITY_PATTERN, (match) => {\n if (ENTITY_MAP[match] !== undefined) return ENTITY_MAP[match];\n // Numeric character reference: strip \"&#\" prefix and \";\" suffix.\n const body = match.slice(2, -1);\n const code =\n body[0] === \"x\" || body[0] === \"X\" ? parseInt(body.slice(1), 16) : parseInt(body, 10);\n return Number.isFinite(code) && code >= 0 ? String.fromCodePoint(code) : match;\n });\n}\n\nexport function nativeTypeValue(value: string): string | number | boolean {\n if (value === \"\") return value;\n // Digit-only fast path — plain integers are the most common numeric shape\n // in OOXML (row/column indexes, sizes, ids). At most 15 digits is always\n // exact in float64, so the scan replaces the Number() + String(n)\n // round-trip (the String(n) side allocates) without re-checking losslessness.\n const neg = value.charCodeAt(0) === 0x2d /* - */;\n const start = neg ? 1 : 0;\n const digits = value.length - start;\n if (digits > 0 && digits <= 15) {\n // Leading zeros (\"00992297\") must stay strings; only a lone \"0\" passes.\n // \"-0\" falls through too — Number coerces it to -0 whose String() is \"0\",\n // so the slow path must keep it a string.\n const head = value.charCodeAt(start);\n if (head !== 0x30 || (!neg && digits === 1)) {\n let n = 0;\n let allDigits = true;\n for (let i = start; i < value.length; i++) {\n const c = value.charCodeAt(i);\n if (c < 0x30 || c > 0x39) {\n allDigits = false;\n break;\n }\n n = n * 10 + (c - 0x30);\n }\n if (allDigits) return neg ? -n : n;\n }\n }\n const n = Number(value);\n // Only coerce when lossless: leading zeros (\"00992297\"), exponential\n // notation (\"1e5\"), and a leading sign (\"+5\") must stay strings so hex-like\n // values (rsid, color) survive parse → stringify round-trips byte-exact.\n if (!isNaN(n) && String(n) === value) return n;\n // Length gate before toLowerCase: every non-numeric attribute value paid\n // two throwaway lowercase strings (cell refs, format names, ids …), which\n // parse profiles attributed to GC pressure.\n if (value.length === 4 || value.length === 5) {\n const lower = value.toLowerCase();\n if (lower === \"true\") return true;\n if (lower === \"false\") return false;\n }\n return value;\n}\n\nexport function parse(xmlString: string, options?: ParseOptions): Element {\n const captureSpaces = options?.captureSpacesBetweenElements ?? false;\n const trim = options?.trim ?? false;\n const ignoreDeclaration = options?.ignoreDeclaration ?? false;\n const ignoreText = options?.ignoreText ?? false;\n const ignoreComment = options?.ignoreComment ?? false;\n const ignoreCdata = options?.ignoreCdata ?? false;\n const ignoreDoctype = options?.ignoreDoctype ?? false;\n const nativeTypeAttributes = options?.nativeTypeAttributes ?? false;\n // Lookup set for deferred elements (raw inner-XML capture). Undefined when\n // the option is absent so the common path pays one truthiness check.\n const deferSet =\n options?.deferElements !== undefined && options.deferElements.length > 0\n ? new Set(options.deferElements)\n : undefined;\n\n const result: Element = {};\n const stack: Element[] = [result];\n\n let i = 0;\n const len = xmlString.length;\n\n while (i < len) {\n // Text node: read up to the next '<'. Pure-whitespace nodes (indentation)\n // are dropped below unless captureSpaces is on, but leading/trailing\n // spaces of nodes that have content are preserved.\n if (xmlString.charCodeAt(i) !== 0x3c /* < */) {\n const start = i;\n while (i < len && xmlString.charCodeAt(i) !== 0x3c) i++;\n let text = unescapeXml(xmlString.slice(start, i));\n if (trim) text = text.trim();\n if (ignoreText) continue;\n if (text.length > 0) {\n if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack)) {\n // Text-node hot path, inlined from addField(\"text\"): one lookup of\n // the last child covers both the adjacent-merge case (same shape as\n // addField — a split CDATA/text run must reassemble) and the fresh\n // push. addField stays for the cold node types.\n const parent = stack[stack.length - 1]!;\n const elements = parent.elements;\n const last = elements === undefined ? undefined : elements[elements.length - 1];\n if (last !== undefined && last.type === \"text\") {\n last.text = (last.text as string) + text;\n } else {\n const node: Element = { type: \"text\", text };\n if (elements === undefined) {\n parent.elements = [node];\n } else {\n elements.push(node);\n }\n }\n }\n }\n continue;\n }\n\n i++;\n\n // <? processing instruction / declaration\n if (xmlString.charCodeAt(i) === 0x3f /* ? */) {\n const end = xmlString.indexOf(\"?>\", i + 1);\n if (end === -1) break;\n const body = xmlString.slice(i + 1, end);\n i = end + 2;\n\n const xmlMatch = body.match(/^xml\\s+(.*)$/s);\n if (xmlMatch) {\n if (!ignoreDeclaration) {\n if (!result.declaration) {\n result.declaration = {};\n }\n const attrs = parseAttributes(xmlMatch[1] ?? \"\");\n if (nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n result.declaration.attributes = attrs;\n }\n }\n continue;\n }\n\n // !-- comment\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 3) === \"!--\") {\n const end = xmlString.indexOf(\"-->\", i + 3);\n if (end === -1) break;\n const comment = xmlString.slice(i + 3, end);\n i = end + 3;\n if (!ignoreComment) {\n if (trim) addField(peek(stack), \"comment\", comment.trim());\n else addField(peek(stack), \"comment\", comment);\n }\n continue;\n }\n\n // ![CDATA[\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 8) === \"![CDATA[\") {\n const end = xmlString.indexOf(\"]]>\", i + 8);\n if (end === -1) break;\n const cdata = xmlString.slice(i + 8, end);\n i = end + 3;\n if (!ignoreCdata) {\n if (trim) addField(peek(stack), \"cdata\", cdata.trim());\n else addField(peek(stack), \"cdata\", cdata);\n }\n continue;\n }\n\n // <!DOCTYPE\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 9) === \"!DOCTYPE\") {\n const end = xmlString.indexOf(\">\", i + 9);\n if (end === -1) break;\n const doctype = xmlString.slice(i + 9, end).trim();\n i = end + 1;\n if (!ignoreDoctype) {\n addField(peek(stack), \"doctype\", doctype);\n }\n continue;\n }\n\n // </ closing tag\n if (xmlString.charCodeAt(i) === 0x2f /* / */) {\n const end = xmlString.indexOf(\">\", i + 1);\n if (end === -1) break;\n i = end + 1;\n stack.pop();\n continue;\n }\n\n // < opening tag\n const tagNameEnd = findTagNameEnd(xmlString, i);\n const tagName = xmlString.slice(i, tagNameEnd);\n let pos = tagNameEnd;\n\n // Attribute scan, inlined from parseAttributesFromXml: called once per\n // opening tag, the `{ attrs, pos }` wrapper was one allocation per element.\n // `attrs` is allocated lazily on the first attribute — tags without\n // attributes (the majority in data-heavy parts) must not pay a record\n // allocation.\n let attrs: Record<string, string> | undefined;\n while (pos < len) {\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n if (pos >= len || xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) {\n break;\n }\n\n const nameStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== 0x3d) {\n if (xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) break;\n pos++;\n }\n const name = xmlString.slice(nameStart, pos);\n\n if (xmlString.charCodeAt(pos) !== 0x3d) break;\n pos++;\n\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n\n const quote = xmlString.charCodeAt(pos);\n if (quote !== 0x22 && quote !== 0x27) break;\n pos++;\n const valueStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;\n if (attrs === undefined) attrs = {};\n attrs[name] = unescapeXml(xmlString.slice(valueStart, pos));\n pos++;\n }\n\n if (attrs && nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n\n const isSelfClosing = xmlString.charCodeAt(pos) === 0x2f /* / */;\n if (isSelfClosing) pos += 2;\n else pos++;\n\n const element: Element = {\n type: \"element\",\n name: tagName,\n };\n if (attrs) {\n element.attributes = attrs;\n }\n\n const parent = peek(stack);\n if (!parent.elements) {\n parent.elements = [];\n }\n parent.elements.push(element);\n\n if (!isSelfClosing) {\n if (deferSet !== undefined && deferSet.has(tagName)) {\n // Deferred container: capture inner XML verbatim instead of parsing\n // children. Scan to the matching close tag, counting same-name opens\n // so nested occurrences (if any) don't end the capture early.\n const closeTag = `</${tagName}>`;\n let depth = 1;\n let scan = pos;\n let closeIdx = -1;\n for (;;) {\n closeIdx = xmlString.indexOf(closeTag, scan);\n if (closeIdx === -1) break;\n let p = scan;\n for (;;) {\n const openIdx = xmlString.indexOf(`<${tagName}`, p);\n if (openIdx === -1 || openIdx >= closeIdx) break;\n // Boundary check so `<rowx>` doesn't count as `<row`.\n const after = xmlString.charCodeAt(openIdx + tagName.length + 1);\n if (\n after === 0x20 ||\n after === 0x09 ||\n after === 0x0a ||\n after === 0x0d ||\n after === 0x2f ||\n after === 0x3e\n ) {\n depth++;\n }\n p = openIdx + tagName.length + 1;\n }\n scan = closeIdx + closeTag.length;\n depth--;\n if (depth === 0) break;\n }\n if (closeIdx === -1) {\n element.raw = xmlString.slice(pos);\n i = len;\n } else {\n element.raw = xmlString.slice(pos, closeIdx);\n i = scan;\n }\n continue;\n }\n stack.push(element);\n }\n\n i = pos;\n }\n\n if (result.elements) {\n const temp = result.elements;\n delete result.elements;\n result.elements = temp;\n delete result.text;\n }\n\n return result;\n}\n\nfunction findTagNameEnd(str: string, start: number): number {\n let i = start;\n const len = str.length;\n while (i < len) {\n const ch = str.charCodeAt(i);\n if (ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d || ch === 0x2f || ch === 0x3e) {\n return i;\n }\n i++;\n }\n return i;\n}\n\nexport function parseAttributes(str: string): Record<string, string> {\n const result: Record<string, string> = {};\n let i = 0;\n const len = str.length;\n\n while (i < len) {\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len) break;\n\n const nameStart = i;\n while (i < len && str.charCodeAt(i) !== 0x3d) {\n if (isWhitespace(str.charCodeAt(i))) break;\n i++;\n }\n const name = str.slice(nameStart, i);\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len || str.charCodeAt(i) !== 0x3d) break;\n i++;\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n\n const quote = str.charCodeAt(i);\n if (quote !== 0x22 && quote !== 0x27) break;\n i++;\n const valueStart = i;\n while (i < len && str.charCodeAt(i) !== quote) i++;\n result[name] = unescapeXml(str.slice(valueStart, i));\n i++;\n }\n return result;\n}\n\n/**\n * Top of the parse stack. The stack is guaranteed non-empty — the result root\n * is pushed at init and push/pop stay balanced across well-formed input — so\n * this is a compile-time narrow (one non-null assertion) rather than a runtime\n * check: it must not add a throw path that changes how `parse` surfaces\n * malformed documents. Centralising the access keeps that single `!` off the\n * read sites, matching the \"wrap indexed access behind a helper\" pattern.\n */\nfunction peek(stack: Element[]): Element {\n return stack[stack.length - 1]!;\n}\n\nfunction addField(parent: Element, type: string, value: string) {\n if (!parent.elements) {\n parent.elements = [];\n }\n // Merge adjacent text/cdata nodes: a CDATA section containing the literal\n // `]]>` is serialized as two adjacent CDATA sections and must reassemble\n // into a single node on parse. Adjacent text nodes likewise merge.\n if (type === \"text\" || type === \"cdata\") {\n const last = parent.elements[parent.elements.length - 1];\n if (last && last.type === type) {\n const key = type as \"text\" | \"cdata\";\n last[key] = (last[key] as string) + value;\n return;\n }\n }\n const element: Element = { type };\n (element as Record<string, unknown>)[type] = value;\n parent.elements.push(element);\n}\n\n/** True when the nearest ancestor with an explicit xml:space sets \"preserve\". */\nfunction isPreserveContext(stack: Element[]): boolean {\n for (let i = stack.length - 1; i >= 0; i--) {\n const node = stack[i];\n if (!node) continue;\n const space = node.attributes?.[\"xml:space\"];\n if (space !== undefined) return space === \"preserve\";\n }\n return false;\n}\n\nfunction isWhitespace(ch: number): boolean {\n return ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d;\n}\n","// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\nconst XML_SPECIALS = /[&\"'<>]/;\n\n/** Escape text content for XML. Fast path returns original string when no special chars. */\nexport function escapeXml(str: string): string {\n // Fast path: most text content doesn't contain XML-special characters.\n // A character-class regex test beats a manual charCodeAt loop by ~10× (V8\n // compiles it to a native SIMD scan); returning the original string\n // reference means zero allocation for the common case.\n if (!XML_SPECIALS.test(str)) return str;\n\n const firstSpecial = str.search(XML_SPECIALS);\n // Slow path: collect all replacement positions, then batch slice\n const parts: string[] = [str.slice(0, firstSpecial)];\n for (let i = firstSpecial; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c === 38) {\n parts.push(\"&amp;\");\n } else if (c === 34) {\n parts.push(\"&quot;\");\n } else if (c === 39) {\n parts.push(\"&apos;\");\n } else if (c === 60) {\n parts.push(\"&lt;\");\n } else if (c === 62) {\n parts.push(\"&gt;\");\n } else {\n parts.push(str.charAt(i));\n }\n }\n return parts.join(\"\");\n}\n\n/**\n * Build an XML attribute string fragment from a record.\n * `undefined` values are automatically skipped.\n * String values are escaped via `escapeXml`.\n *\n * @example\n * attrs({ id: 1, name: \"foo\", hidden: undefined })\n * // => ' id=\"1\" name=\"foo\"'\n */\nexport function attrs(record: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, v] of Object.entries(record)) {\n if (v !== undefined) {\n parts.push(` ${key}=\"${typeof v === \"string\" ? escapeXml(v) : v}\"`);\n }\n }\n return parts.join(\"\");\n}\n\n/**\n * Build an XML attribute string without escaping.\n *\n * Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when\n * all values are known-safe (numbers, booleans, or strings free of `& \" ' < >`).\n * Avoids per-call array and `Object.keys()` allocation in hot loops.\n *\n * @example\n * attrsRaw({ r: \"A1\", s: 5 })\n * // => ' r=\"A1\" s=\"5\"'\n */\nexport function attrsRaw(record: Record<string, string | number | boolean | undefined>): string {\n let s = \"\";\n for (const key in record) {\n const v = record[key];\n if (v !== undefined) {\n s += ` ${key}=\"${v}\"`;\n }\n }\n return s;\n}\n\n/**\n * Build a self-closing XML element: `<tag attrStr/>`.\n * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.\n */\nexport function selfCloseElement(tag: string, attrStr?: string): string {\n return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;\n}\n\n/**\n * Build a complete XML element string from name, optional attributes, and string children.\n *\n * Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a\n * single function call returning a string — zero object allocation.\n *\n * @param name Element tag name (e.g. `\"a:srgbClr\"`)\n * @param attrRecord Optional flat attribute map; `undefined` values are skipped\n * @param children Optional pre-serialized child XML strings\n *\n * @example\n * ```ts\n * element(\"a:solidFill\", undefined, [element(\"a:srgbClr\", { val: \"FF0000\" })])\n * // => '<a:solidFill><a:srgbClr val=\"FF0000\"/></a:solidFill>'\n * ```\n */\nexport function element(\n name: string,\n attrRecord?: Readonly<Record<string, string | number | boolean | undefined>>,\n children?: readonly string[],\n): string {\n const attrStr = attrRecord ? attrs(attrRecord) : undefined;\n if (!children || children.length === 0) return selfCloseElement(name, attrStr);\n const body = children.join(\"\");\n return body.length === 0\n ? selfCloseElement(name, attrStr)\n : `<${name}${attrStr ?? \"\"}>${body}</${name}>`;\n}\n","import { escapeXml } from \"./escape\";\nimport type { Element, StringifyOptions } from \"./types\";\n\nexport function stringify(js: Element, options?: StringifyOptions): string {\n const opts = normalizeOptions(options);\n const parts: string[] = [];\n\n if (js.declaration && !opts.ignoreDeclaration) {\n parts.push(writeDeclaration(js.declaration));\n }\n\n if (js.elements?.length) {\n parts.push(writeElements(js.elements, opts, 0, !parts.length));\n }\n\n return parts.join(\"\");\n}\n\nfunction normalizeOptions(options?: StringifyOptions): {\n spaces: string;\n ignoreDeclaration: boolean;\n ignoreText: boolean;\n ignoreComment: boolean;\n ignoreCdata: boolean;\n ignoreDoctype: boolean;\n fullTagEmptyElement: boolean;\n indentText: boolean;\n indentCdata: boolean;\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"];\n} {\n if (!options) {\n return {\n spaces: \"\",\n ignoreDeclaration: false,\n ignoreText: false,\n ignoreComment: false,\n ignoreCdata: false,\n ignoreDoctype: false,\n fullTagEmptyElement: false,\n indentText: false,\n indentCdata: false,\n };\n }\n let spaces = \"\";\n if (options.spaces != null) {\n spaces = typeof options.spaces === \"number\" ? \" \".repeat(options.spaces) : options.spaces;\n }\n return {\n spaces,\n ignoreDeclaration: options.ignoreDeclaration ?? false,\n ignoreText: options.ignoreText ?? false,\n ignoreComment: options.ignoreComment ?? false,\n ignoreCdata: options.ignoreCdata ?? false,\n ignoreDoctype: options.ignoreDoctype ?? false,\n fullTagEmptyElement: options.fullTagEmptyElement ?? false,\n indentText: options.indentText ?? false,\n indentCdata: options.indentCdata ?? false,\n attributeValueFn: options.attributeValueFn,\n };\n}\n\nfunction writeIndentation(spaces: string, depth: number, firstLine: boolean): string {\n return (!firstLine && spaces ? \"\\n\" : \"\") + spaces.repeat(depth);\n}\n\nfunction writeDeclaration(declaration: NonNullable<Element[\"declaration\"]>): string {\n const attrs = declaration.attributes;\n if (!attrs) return '<?xml version=\"1.0\"?>';\n\n const parts: string[] = [`<?xml version=\"1.0\"`];\n if (attrs.encoding) parts.push(` encoding=\"${attrs.encoding}\"`);\n if (attrs.standalone) parts.push(` standalone=\"${attrs.standalone}\"`);\n return parts.join(\"\") + \"?>\";\n}\n\nfunction writeAttributes(\n attributes: Record<string, string | number | undefined>,\n elementName: string,\n element: Element,\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"],\n): string {\n const parts: string[] = [];\n for (const key of Object.keys(attributes)) {\n const value = attributes[key];\n if (value === null || value === undefined) continue;\n\n // attributeValueFn (xml-js hook) owns escaping when provided; otherwise\n // we escape all XML-special characters ourselves.\n const raw = String(value);\n const attr = attributeValueFn\n ? attributeValueFn(raw, key, elementName, element)\n : escapeXml(raw);\n parts.push(` ${key}=\"${attr}\"`);\n }\n return parts.join(\"\");\n}\n\nfunction writeElement(\n element: Element,\n opts: ReturnType<typeof normalizeOptions>,\n depth: number,\n): string {\n if (!element.name) return \"\";\n const name = element.name;\n const attrStr = element.attributes\n ? writeAttributes(element.attributes, name, element, opts.attributeValueFn)\n : \"\";\n // Deferred content: re-emit the captured inner XML verbatim — children were\n // never parsed, and the bytes must survive a set/save round-trip.\n if (element.raw !== undefined) {\n return `<${name}${attrStr}>${element.raw}</${name}>`;\n }\n const withClosingTag =\n (element.elements?.length ?? 0) > 0 ||\n element.attributes?.[\"xml:space\"] === \"preserve\" ||\n opts.fullTagEmptyElement;\n\n if (!withClosingTag) {\n return `<${name}${attrStr}/>`;\n }\n\n const parts: string[] = [];\n parts.push(`<${name}${attrStr}>`);\n const hasChildElements = element.elements?.some((e) => e.type === \"element\") ?? false;\n if (element.elements?.length) {\n parts.push(writeElements(element.elements, opts, depth + 1, false));\n }\n if (opts.spaces && hasChildElements) {\n parts.push(\"\\n\" + opts.spaces.repeat(depth));\n }\n parts.push(`</${name}>`);\n return parts.join(\"\");\n}\n\nfunction writeElements(\n elements: Element[],\n opts: ReturnType<typeof normalizeOptions>,\n depth: number,\n firstLine: boolean,\n): string {\n const parts: string[] = [];\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (!element) continue;\n const isFirst = firstLine && i === 0;\n switch (element.type) {\n case \"element\":\n parts.push(writeIndentation(opts.spaces, depth, isFirst));\n parts.push(writeElement(element, opts, depth));\n break;\n case \"text\":\n if (opts.ignoreText) continue;\n if (opts.indentText) parts.push(writeIndentation(opts.spaces, depth, isFirst));\n parts.push(writeText(element.text));\n break;\n case \"cdata\":\n if (opts.ignoreCdata) continue;\n if (opts.indentCdata) parts.push(writeIndentation(opts.spaces, depth, isFirst));\n parts.push(writeCdata(element.cdata));\n break;\n case \"comment\":\n if (opts.ignoreComment) continue;\n parts.push(writeIndentation(opts.spaces, depth, isFirst));\n parts.push(writeComment(element.comment));\n break;\n case \"doctype\":\n if (opts.ignoreDoctype) continue;\n parts.push(writeIndentation(opts.spaces, depth, isFirst));\n parts.push(writeDoctype(element.doctype));\n break;\n default:\n break;\n }\n }\n return parts.join(\"\");\n}\n\nfunction writeText(text: string | number | boolean | undefined | null): string {\n if (text == null) return \"\";\n const str = String(text);\n // Fast path: most text content doesn't contain XML-special characters.\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c === 38 || c === 60 || c === 62) {\n // & < >\n let s = \"\";\n let last = 0;\n for (let j = i; j < str.length; j++) {\n const cj = str.charCodeAt(j);\n if (cj === 38) {\n s += str.slice(last, j) + \"&amp;\";\n last = j + 1;\n } else if (cj === 60) {\n s += str.slice(last, j) + \"&lt;\";\n last = j + 1;\n } else if (cj === 62) {\n s += str.slice(last, j) + \"&gt;\";\n last = j + 1;\n }\n }\n return s + str.slice(last);\n }\n }\n return str;\n}\n\nfunction writeCdata(cdata: string | undefined | null): string {\n if (cdata == null) return \"\";\n const escaped = cdata.replace(/\\]\\]>/g, \"]]]]><![CDATA[>\");\n return `<![CDATA[${escaped}]]>`;\n}\n\nfunction writeComment(comment: string | undefined | null): string {\n if (comment == null) return \"\";\n return `<!--${comment}-->`;\n}\n\nfunction writeDoctype(doctype: string | undefined | null): string {\n if (doctype == null) return \"\";\n return `<!DOCTYPE ${doctype}>`;\n}\n\ntype NonNullable<T> = T extends null | undefined ? never : T;\n","/**\n * Serialize an Element including its own opening/closing tag.\n *\n * `stringify` serializes only an element's children (it treats its input as\n * a document root). Raw-XML round-trip of whole elements needs the element's\n * own tag wrapped around its serialized children.\n */\nimport { escapeXml } from \"./escape\";\nimport { stringify as stringifyChildren } from \"./stringify\";\nimport type { Element } from \"./types\";\n\nexport function stringifyElement(el: Element): string {\n if (!el.name) return \"\";\n let attrStr = \"\";\n if (el.attributes) {\n for (const key of Object.keys(el.attributes)) {\n const v = el.attributes[key];\n if (v === null || v === undefined) continue;\n attrStr += ` ${key}=\"${escapeXml(String(v))}\"`;\n }\n }\n const withClosingTag =\n (el.elements?.length ?? 0) > 0 || el.attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) return `<${el.name}${attrStr}/>`;\n return `<${el.name}${attrStr}>${stringifyChildren(el)}</${el.name}>`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["stringifyChildren"],"sources":["../src/parse.ts","../src/escape.ts","../src/stringify.ts","../src/stringify-element.ts"],"sourcesContent":["import type { Element, ParseOptions } from \"./types\";\n\nconst ENTITY_MAP: Record<string, string> = {\n \"&amp;\": \"&\",\n \"&lt;\": \"<\",\n \"&gt;\": \">\",\n \"&quot;\": '\"',\n \"&apos;\": \"'\",\n};\n// Matches the five named entities plus numeric character references\n// (&#65; decimal, &#x42; hex).\nconst ENTITY_PATTERN = /&(?:amp|lt|gt|quot|apos|#x[0-9a-fA-F]+|#[0-9]+);/g;\n\nexport function unescapeXml(str: string): string {\n // Fast path: entities all start with '&', and OOXML parts overwhelmingly\n // contain none (a 63 MB worksheet measured zero occurrences). The regex\n // scan + replace setup per call showed up to ~12% of large-file parse\n // profiles, so gate it on the sentinel byte.\n if (str.indexOf(\"&\") === -1) return str;\n return str.replace(ENTITY_PATTERN, (match) => {\n if (ENTITY_MAP[match] !== undefined) return ENTITY_MAP[match];\n // Numeric character reference: strip \"&#\" prefix and \";\" suffix.\n const body = match.slice(2, -1);\n const code =\n body[0] === \"x\" || body[0] === \"X\" ? parseInt(body.slice(1), 16) : parseInt(body, 10);\n return Number.isFinite(code) && code >= 0 ? String.fromCodePoint(code) : match;\n });\n}\n\nexport function nativeTypeValue(value: string): string | number | boolean {\n if (value === \"\") return value;\n // Digit-only fast path — plain integers are the most common numeric shape\n // in OOXML (row/column indexes, sizes, ids). At most 15 digits is always\n // exact in float64, so the scan replaces the Number() + String(n)\n // round-trip (the String(n) side allocates) without re-checking losslessness.\n const neg = value.charCodeAt(0) === 0x2d; /* - */\n const start = neg ? 1 : 0;\n const digits = value.length - start;\n if (digits > 0 && digits <= 15) {\n // Leading zeros (\"00992297\") must stay strings; only a lone \"0\" passes.\n // \"-0\" falls through too — Number coerces it to -0 whose String() is \"0\",\n // so the slow path must keep it a string.\n const head = value.charCodeAt(start);\n if (head !== 0x30 || (!neg && digits === 1)) {\n let n = 0;\n let allDigits = true;\n for (let i = start; i < value.length; i++) {\n const c = value.charCodeAt(i);\n if (c < 0x30 || c > 0x39) {\n allDigits = false;\n break;\n }\n n = n * 10 + (c - 0x30);\n }\n if (allDigits) return neg ? -n : n;\n }\n }\n const n = Number(value);\n // Only coerce when lossless: leading zeros (\"00992297\"), exponential\n // notation (\"1e5\"), and a leading sign (\"+5\") must stay strings so hex-like\n // values (rsid, color) survive parse → stringify round-trips byte-exact.\n if (!isNaN(n) && String(n) === value) return n;\n // Length gate before toLowerCase: every non-numeric attribute value paid\n // two throwaway lowercase strings (cell refs, format names, ids …), which\n // parse profiles attributed to GC pressure.\n if (value.length === 4 || value.length === 5) {\n const lower = value.toLowerCase();\n if (lower === \"true\") return true;\n if (lower === \"false\") return false;\n }\n return value;\n}\n\nexport function parse(xmlString: string, options?: ParseOptions): Element {\n const captureSpaces = options?.captureSpacesBetweenElements ?? false;\n const trim = options?.trim ?? false;\n const ignoreDeclaration = options?.ignoreDeclaration ?? false;\n const ignoreText = options?.ignoreText ?? false;\n const ignoreComment = options?.ignoreComment ?? false;\n const ignoreCdata = options?.ignoreCdata ?? false;\n const ignoreDoctype = options?.ignoreDoctype ?? false;\n const nativeTypeAttributes = options?.nativeTypeAttributes ?? false;\n // Lookup set for deferred elements (raw inner-XML capture). Undefined when\n // the option is absent so the common path pays one truthiness check.\n const deferSet =\n options?.deferElements !== undefined && options.deferElements.length > 0\n ? new Set(options.deferElements)\n : undefined;\n\n // Namespace normalization (see ParseOptions.normalizeNamespaces): in-scope\n // prefix → canonical-prefix bindings, copy-on-write per element that carries\n // xmlns declarations. `prefixes === undefined` means nothing is bound yet\n // (the common subtree shares the root layer object untouched).\n const nsTable =\n options?.normalizeNamespaces !== undefined\n ? new Map(Object.entries(options.normalizeNamespaces))\n : undefined;\n interface NsLayer {\n prefixes: Map<string, string> | undefined;\n /** Canonical form of the default namespace; undefined = none/unknown. */\n defaultCanonical: string | undefined;\n }\n const nsRootLayer: NsLayer = { prefixes: undefined, defaultCanonical: undefined };\n const nsStack: NsLayer[] = [nsRootLayer];\n\n const result: Element = {};\n const stack: Element[] = [result];\n\n let i = 0;\n const len = xmlString.length;\n\n while (i < len) {\n // Text node: read up to the next '<'. Pure-whitespace nodes (indentation)\n // are dropped below unless captureSpaces is on, but leading/trailing\n // spaces of nodes that have content are preserved.\n if (xmlString.charCodeAt(i) !== 0x3c /* < */) {\n const start = i;\n // Single-char indexOf runs as a native memchr scan — far faster than a\n // JS byte-by-byte loop (the outer check already guarantees i is not '<').\n const lt = xmlString.indexOf(\"<\", i);\n i = lt === -1 ? len : lt;\n let text = unescapeXml(xmlString.slice(start, i));\n if (trim) text = text.trim();\n if (ignoreText) continue;\n if (text.length > 0) {\n if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack)) {\n // Text-node hot path, inlined from addField(\"text\"): one lookup of\n // the last child covers both the adjacent-merge case (same shape as\n // addField — a split CDATA/text run must reassemble) and the fresh\n // push. addField stays for the cold node types.\n const parent = stack[stack.length - 1]!;\n const elements = parent.elements;\n const last = elements === undefined ? undefined : elements[elements.length - 1];\n if (last !== undefined && last.type === \"text\") {\n last.text = (last.text as string) + text;\n } else {\n const node: Element = { type: \"text\", text };\n if (elements === undefined) {\n parent.elements = [node];\n } else {\n elements.push(node);\n }\n }\n }\n }\n continue;\n }\n\n i++;\n\n // <? processing instruction / declaration\n if (xmlString.charCodeAt(i) === 0x3f /* ? */) {\n const end = xmlString.indexOf(\"?>\", i + 1);\n if (end === -1) break;\n const body = xmlString.slice(i + 1, end);\n i = end + 2;\n\n const xmlMatch = body.match(/^xml\\s+(.*)$/s);\n if (xmlMatch) {\n if (!ignoreDeclaration) {\n if (!result.declaration) {\n result.declaration = {};\n }\n const attrs = parseAttributes(xmlMatch[1] ?? \"\");\n if (nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n result.declaration.attributes = attrs;\n }\n }\n continue;\n }\n\n // !-- comment\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 3) === \"!--\") {\n const end = xmlString.indexOf(\"-->\", i + 3);\n if (end === -1) break;\n const comment = xmlString.slice(i + 3, end);\n i = end + 3;\n if (!ignoreComment) {\n if (trim) addField(peek(stack), \"comment\", comment.trim());\n else addField(peek(stack), \"comment\", comment);\n }\n continue;\n }\n\n // ![CDATA[\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 8) === \"![CDATA[\") {\n const end = xmlString.indexOf(\"]]>\", i + 8);\n if (end === -1) break;\n const cdata = xmlString.slice(i + 8, end);\n i = end + 3;\n if (!ignoreCdata) {\n if (trim) addField(peek(stack), \"cdata\", cdata.trim());\n else addField(peek(stack), \"cdata\", cdata);\n }\n continue;\n }\n\n // <!DOCTYPE\n if (xmlString.charCodeAt(i) === 0x21 && xmlString.slice(i, i + 9) === \"!DOCTYPE\") {\n const end = xmlString.indexOf(\">\", i + 9);\n if (end === -1) break;\n const doctype = xmlString.slice(i + 9, end).trim();\n i = end + 1;\n if (!ignoreDoctype) {\n addField(peek(stack), \"doctype\", doctype);\n }\n continue;\n }\n\n // </ closing tag\n if (xmlString.charCodeAt(i) === 0x2f /* / */) {\n const end = xmlString.indexOf(\">\", i + 1);\n if (end === -1) break;\n i = end + 1;\n stack.pop();\n if (nsTable !== undefined) nsStack.pop();\n continue;\n }\n\n // < opening tag\n const tagNameEnd = findTagNameEnd(xmlString, i);\n const tagName = xmlString.slice(i, tagNameEnd);\n let pos = tagNameEnd;\n\n // Namespace bindings for this element: inherit the parent layer; the\n // attribute scan clones it on the first xmlns declaration it meets.\n const nsParent = nsTable !== undefined ? nsStack[nsStack.length - 1]! : undefined;\n let nsPrefixes = nsParent?.prefixes;\n let nsDefault = nsParent?.defaultCanonical;\n let nsDeclared = false;\n\n // Attribute scan, inlined from parseAttributesFromXml: called once per\n // opening tag, the `{ attrs, pos }` wrapper was one allocation per element.\n // `attrs` is allocated lazily on the first attribute — tags without\n // attributes (the majority in data-heavy parts) must not pay a record\n // allocation.\n let attrs: Record<string, string> | undefined;\n while (pos < len) {\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n if (pos >= len || xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) {\n break;\n }\n\n const nameStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== 0x3d) {\n if (xmlString.charCodeAt(pos) === 0x3e || xmlString.charCodeAt(pos) === 0x2f) break;\n pos++;\n }\n // XML's Eq production allows whitespace around '='; trim it off the\n // name so `Target = \"…\"` resolves like `Target=\"…\"`.\n const name = xmlString.slice(nameStart, pos).trim();\n\n if (xmlString.charCodeAt(pos) !== 0x3d) break;\n pos++;\n\n while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;\n\n const quote = xmlString.charCodeAt(pos);\n if (quote !== 0x22 && quote !== 0x27) break;\n pos++;\n const valueStart = pos;\n while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;\n const value = unescapeXml(xmlString.slice(valueStart, pos));\n pos++;\n\n if (nsTable !== undefined && (name === \"xmlns\" || name.startsWith(\"xmlns:\"))) {\n if (attrs === undefined) attrs = {};\n const canonical = nsTable.get(value);\n if (name === \"xmlns\") {\n nsDefault = canonical;\n if (canonical !== undefined && canonical !== \"\") {\n // Known non-default canonical form: upgrade the declaration to a\n // prefixed one (xmlns=\"uri\" → xmlns:w=\"uri\") so the rewritten\n // element names stay declared.\n attrs[`xmlns:${canonical}`] = value;\n } else {\n attrs[name] = value;\n }\n } else {\n const prefix = name.slice(6);\n if (canonical !== undefined && canonical !== \"\") {\n attrs[`xmlns:${canonical}`] = value;\n } else if (canonical === \"\" && !(\"xmlns\" in attrs)) {\n // Canonical form is unprefixed: demote to a default declaration.\n attrs.xmlns = value;\n } else {\n attrs[name] = value;\n }\n if (!nsDeclared) {\n nsPrefixes = new Map(nsPrefixes ?? []);\n nsDeclared = true;\n }\n // Unknown URI keeps the source prefix bound to itself, which the\n // rewrite step treats as \"leave names as written\". Non-null: either\n // just assigned above or already a Map from a prior nsDeclared pass.\n nsPrefixes!.set(prefix, canonical ?? prefix);\n }\n continue;\n }\n if (attrs === undefined) attrs = {};\n attrs[name] = value;\n }\n\n if (attrs && nativeTypeAttributes) {\n for (const key in attrs) {\n attrs[key] = nativeTypeValue(attrs[key] as string) as string;\n }\n }\n\n // Namespace-normalize the element name and the non-declaration attribute\n // names onto this element's effective bindings (its own declarations\n // included — attributes see the element's in-scope bindings).\n let finalName = tagName;\n if (nsTable !== undefined) {\n const colon = tagName.indexOf(\":\");\n if (colon > 0) {\n const p = tagName.slice(0, colon);\n if (p !== \"xml\") {\n const c = nsPrefixes?.get(p);\n if (c !== undefined && c !== p) {\n finalName = c === \"\" ? tagName.slice(colon + 1) : c + tagName.slice(colon);\n }\n }\n } else if (colon < 0 && nsDefault !== undefined && nsDefault !== \"\") {\n finalName = nsDefault + \":\" + tagName;\n }\n if (attrs !== undefined && nsPrefixes !== undefined) {\n for (const key of Object.keys(attrs)) {\n if (key === \"xmlns\" || key.startsWith(\"xmlns:\")) continue;\n const kcolon = key.indexOf(\":\");\n if (kcolon <= 0) continue;\n const p = key.slice(0, kcolon);\n if (p === \"xml\") continue;\n const c = nsPrefixes.get(p);\n if (c !== undefined && c !== p) {\n const renamed = c === \"\" ? key.slice(kcolon + 1) : c + key.slice(kcolon);\n const attrValue = attrs[key];\n if (attrValue !== undefined) attrs[renamed] = attrValue;\n delete attrs[key];\n }\n }\n }\n }\n\n const isSelfClosing = xmlString.charCodeAt(pos) === 0x2f; /* / */\n if (isSelfClosing) pos += 2;\n else pos++;\n\n // Constant shape: every element node carries all four fields (undefined\n // placeholders included), so attribute/children assignment writes an\n // existing slot instead of walking a hidden-class transition — element\n // access sites stay monomorphic instead of 2-3 shapes deep.\n const element: Element = {\n type: \"element\",\n name: finalName,\n attributes: attrs,\n elements: undefined,\n };\n\n const parent = peek(stack);\n if (!parent.elements) {\n parent.elements = [];\n }\n parent.elements.push(element);\n\n if (!isSelfClosing) {\n if (deferSet !== undefined && deferSet.has(tagName)) {\n // Deferred container: capture inner XML verbatim instead of parsing\n // children. Scan to the matching close tag, counting same-name opens\n // so nested occurrences (if any) don't end the capture early.\n const closeTag = `</${tagName}>`;\n let depth = 1;\n let scan = pos;\n let closeIdx = -1;\n for (;;) {\n closeIdx = xmlString.indexOf(closeTag, scan);\n if (closeIdx === -1) break;\n let p = scan;\n for (;;) {\n const openIdx = xmlString.indexOf(`<${tagName}`, p);\n if (openIdx === -1 || openIdx >= closeIdx) break;\n // Boundary check so `<rowx>` doesn't count as `<row`.\n const after = xmlString.charCodeAt(openIdx + tagName.length + 1);\n if (\n after === 0x20 ||\n after === 0x09 ||\n after === 0x0a ||\n after === 0x0d ||\n after === 0x2f ||\n after === 0x3e\n ) {\n depth++;\n }\n p = openIdx + tagName.length + 1;\n }\n scan = closeIdx + closeTag.length;\n depth--;\n if (depth === 0) break;\n }\n if (closeIdx === -1) {\n element.raw = xmlString.slice(pos);\n i = len;\n } else {\n element.raw = xmlString.slice(pos, closeIdx);\n i = scan;\n }\n continue;\n }\n stack.push(element);\n if (nsTable !== undefined) {\n // Share the parent layer unless this element declared anything (a\n // cloned prefix map, or a default declaration that can shadow one).\n const layerChanged = nsDeclared || nsDefault !== nsParent!.defaultCanonical;\n nsStack.push(\n layerChanged ? { prefixes: nsPrefixes, defaultCanonical: nsDefault } : nsParent!,\n );\n }\n }\n\n i = pos;\n }\n\n if (result.elements) {\n const temp = result.elements;\n delete result.elements;\n result.elements = temp;\n delete result.text;\n }\n\n return result;\n}\n\nfunction findTagNameEnd(str: string, start: number): number {\n let i = start;\n const len = str.length;\n while (i < len) {\n const ch = str.charCodeAt(i);\n if (ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d || ch === 0x2f || ch === 0x3e) {\n return i;\n }\n i++;\n }\n return i;\n}\n\nexport function parseAttributes(str: string): Record<string, string> {\n const result: Record<string, string> = {};\n let i = 0;\n const len = str.length;\n\n while (i < len) {\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len) break;\n\n const nameStart = i;\n while (i < len && str.charCodeAt(i) !== 0x3d) {\n if (isWhitespace(str.charCodeAt(i))) break;\n i++;\n }\n const name = str.slice(nameStart, i);\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len || str.charCodeAt(i) !== 0x3d) break;\n i++;\n\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n\n const quote = str.charCodeAt(i);\n if (quote !== 0x22 && quote !== 0x27) break;\n i++;\n const valueStart = i;\n while (i < len && str.charCodeAt(i) !== quote) i++;\n result[name] = unescapeXml(str.slice(valueStart, i));\n i++;\n }\n return result;\n}\n\n/**\n * Top of the parse stack. The stack is guaranteed non-empty — the result root\n * is pushed at init and push/pop stay balanced across well-formed input — so\n * this is a compile-time narrow (one non-null assertion) rather than a runtime\n * check: it must not add a throw path that changes how `parse` surfaces\n * malformed documents. Centralising the access keeps that single `!` off the\n * read sites, matching the \"wrap indexed access behind a helper\" pattern.\n */\nfunction peek(stack: Element[]): Element {\n return stack[stack.length - 1]!;\n}\n\nfunction addField(parent: Element, type: string, value: string) {\n if (!parent.elements) {\n parent.elements = [];\n }\n // Merge adjacent text/cdata nodes: a CDATA section containing the literal\n // `]]>` is serialized as two adjacent CDATA sections and must reassemble\n // into a single node on parse. Adjacent text nodes likewise merge.\n if (type === \"text\" || type === \"cdata\") {\n const last = parent.elements[parent.elements.length - 1];\n if (last && last.type === type) {\n const key = type as \"text\" | \"cdata\";\n last[key] = (last[key] as string) + value;\n return;\n }\n }\n const element: Element = { type };\n (element as Record<string, unknown>)[type] = value;\n parent.elements.push(element);\n}\n\n/** True when the nearest ancestor with an explicit xml:space sets \"preserve\". */\nfunction isPreserveContext(stack: Element[]): boolean {\n for (let i = stack.length - 1; i >= 0; i--) {\n const node = stack[i];\n if (!node) continue;\n const space = node.attributes?.[\"xml:space\"];\n if (space !== undefined) return space === \"preserve\";\n }\n return false;\n}\n\nfunction isWhitespace(ch: number): boolean {\n return ch === 0x20 || ch === 0x09 || ch === 0x0a || ch === 0x0d;\n}\n","// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\nconst XML_SPECIALS = /[&\"'<>]/;\n\n/** Escape text content for XML. Fast path returns original string when no special chars. */\nexport function escapeXml(str: string): string {\n // Fast path: most text content doesn't contain XML-special characters.\n // A character-class regex test beats a manual charCodeAt loop by ~10× (V8\n // compiles it to a native SIMD scan); returning the original string\n // reference means zero allocation for the common case.\n if (!XML_SPECIALS.test(str)) return str;\n\n // Chained native replaces: specials are sparse, so each pass after the\n // first usually finds nothing and exits quickly. Raw /g literals carry no\n // lastIndex state.\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\n/**\n * Build an XML attribute string fragment from a record.\n * `undefined` values are automatically skipped.\n * String values are escaped via `escapeXml`. Booleans serialize as 0/1 —\n * the spelling Office itself writes for xsd:boolean attributes and\n * ST_OnOff unions alike.\n *\n * @example\n * attrs({ id: 1, name: \"foo\", hidden: undefined })\n * // => ' id=\"1\" name=\"foo\"'\n */\nexport function attrs(record: Record<string, string | number | boolean | undefined>): string {\n const parts: string[] = [];\n for (const [key, v] of Object.entries(record)) {\n if (v !== undefined) {\n const value = typeof v === \"string\" ? escapeXml(v) : typeof v === \"boolean\" ? (v ? 1 : 0) : v;\n parts.push(` ${key}=\"${value}\"`);\n }\n }\n return parts.join(\"\");\n}\n\n/**\n * Build an XML attribute string without escaping.\n *\n * Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when\n * all values are known-safe (numbers, booleans, or strings free of `& \" ' < >`).\n * Avoids per-call array and `Object.keys()` allocation in hot loops.\n *\n * @example\n * attrsRaw({ r: \"A1\", s: 5 })\n * // => ' r=\"A1\" s=\"5\"'\n */\nexport function attrsRaw(record: Record<string, string | number | boolean | undefined>): string {\n let s = \"\";\n for (const key in record) {\n const v = record[key];\n if (v !== undefined) {\n s += ` ${key}=\"${v}\"`;\n }\n }\n return s;\n}\n\n/**\n * Build a self-closing XML element: `<tag attrStr/>`.\n * `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.\n */\nexport function selfCloseElement(tag: string, attrStr?: string): string {\n return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;\n}\n\n/**\n * Build a complete XML element string from name, optional attributes, and string children.\n *\n * Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a\n * single function call returning a string — zero object allocation.\n *\n * @param name Element tag name (e.g. `\"a:srgbClr\"`)\n * @param attrRecord Optional flat attribute map; `undefined` values are skipped\n * @param children Optional pre-serialized child XML strings\n *\n * @example\n * ```ts\n * element(\"a:solidFill\", undefined, [element(\"a:srgbClr\", { val: \"FF0000\" })])\n * // => '<a:solidFill><a:srgbClr val=\"FF0000\"/></a:solidFill>'\n * ```\n */\nexport function element(\n name: string,\n attrRecord?: Readonly<Record<string, string | number | boolean | undefined>>,\n children?: readonly string[],\n): string {\n const attrStr = attrRecord ? attrs(attrRecord) : undefined;\n if (!children || children.length === 0) return selfCloseElement(name, attrStr);\n const body = children.join(\"\");\n return body.length === 0\n ? selfCloseElement(name, attrStr)\n : `<${name}${attrStr ?? \"\"}>${body}</${name}>`;\n}\n","import { escapeXml } from \"./escape\";\nimport type { Element, StringifyOptions } from \"./types\";\n\n// Non-global on purpose: a /g regex would carry stateful lastIndex across calls.\n// Text content needs only the three markup delimiters — quotes are legal as-is.\nconst TEXT_SPECIALS = /[&<>]/;\n\nexport function stringify(js: Element, options?: StringifyOptions): string {\n const opts = normalizeOptions(options);\n const parts: string[] = [];\n\n if (js.declaration && !opts.ignoreDeclaration) {\n parts.push(writeDeclaration(js.declaration));\n }\n\n if (js.elements?.length) {\n parts.push(writeElements(js.elements, opts, 0, !parts.length));\n }\n\n return parts.join(\"\");\n}\n\ninterface NormalizedOptions {\n spaces: string;\n ignoreDeclaration: boolean;\n ignoreText: boolean;\n ignoreComment: boolean;\n ignoreCdata: boolean;\n ignoreDoctype: boolean;\n fullTagEmptyElement: boolean;\n indentText: boolean;\n indentCdata: boolean;\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"];\n}\n\n// Shared frozen defaults — the no-options call (the hot path) skips the\n// per-call object construction entirely.\nconst DEFAULT_OPTIONS: NormalizedOptions = {\n spaces: \"\",\n ignoreDeclaration: false,\n ignoreText: false,\n ignoreComment: false,\n ignoreCdata: false,\n ignoreDoctype: false,\n fullTagEmptyElement: false,\n indentText: false,\n indentCdata: false,\n};\n\nfunction normalizeOptions(options?: StringifyOptions): NormalizedOptions {\n if (!options) return DEFAULT_OPTIONS;\n let spaces = \"\";\n if (options.spaces != null) {\n spaces = typeof options.spaces === \"number\" ? \" \".repeat(options.spaces) : options.spaces;\n }\n return {\n spaces,\n ignoreDeclaration: options.ignoreDeclaration ?? false,\n ignoreText: options.ignoreText ?? false,\n ignoreComment: options.ignoreComment ?? false,\n ignoreCdata: options.ignoreCdata ?? false,\n ignoreDoctype: options.ignoreDoctype ?? false,\n fullTagEmptyElement: options.fullTagEmptyElement ?? false,\n indentText: options.indentText ?? false,\n indentCdata: options.indentCdata ?? false,\n attributeValueFn: options.attributeValueFn,\n };\n}\n\nfunction writeIndentation(spaces: string, depth: number, firstLine: boolean): string {\n if (!spaces) return \"\";\n return (!firstLine ? \"\\n\" : \"\") + spaces.repeat(depth);\n}\n\nfunction writeDeclaration(declaration: NonNullable<Element[\"declaration\"]>): string {\n const attrs = declaration.attributes;\n if (!attrs) return '<?xml version=\"1.0\"?>';\n\n const parts: string[] = [`<?xml version=\"1.0\"`];\n if (attrs.encoding) parts.push(` encoding=\"${attrs.encoding}\"`);\n if (attrs.standalone) parts.push(` standalone=\"${attrs.standalone}\"`);\n return parts.join(\"\") + \"?>\";\n}\n\nfunction writeAttributes(\n attributes: Record<string, string | number | undefined>,\n elementName: string,\n element: Element,\n attributeValueFn?: StringifyOptions[\"attributeValueFn\"],\n): string {\n // Rope accumulation: attribute counts are small (1-3), a parts array + join\n // would cost more than V8's cons-string +=. for-in over plain data records\n // skips the Object.keys array allocation JSC penalizes (~1.3× on bun).\n let s = \"\";\n for (const key in attributes) {\n const value = attributes[key];\n if (value === null || value === undefined) continue;\n\n // attributeValueFn (xml-js hook) owns escaping when provided; otherwise\n // we escape all XML-special characters ourselves.\n const raw = String(value);\n const attr = attributeValueFn\n ? attributeValueFn(raw, key, elementName, element)\n : escapeXml(raw);\n s += ` ${key}=\"${attr}\"`;\n }\n return s;\n}\n\nfunction writeElements(\n elements: Element[],\n opts: NormalizedOptions,\n depth: number,\n firstLine: boolean,\n): string {\n // Rope accumulation — V8 cons-strings make += O(1) and skip the parts-array\n // allocation the join-based form pays per level. The element body is inlined\n // (no writeElement/writeElements mutual recursion): an ordered if-chain over\n // the node type benchmarks ~1.6× faster than a switch plus a helper call\n // per element under JSC, with V8 unchanged.\n let s = \"\";\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i];\n if (!element) continue;\n const isFirst = firstLine && i === 0;\n const type = element.type;\n if (type === \"element\") {\n const name = element.name;\n if (!name) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n const attributes = element.attributes;\n const attrStr = attributes\n ? writeAttributes(attributes, name, element, opts.attributeValueFn)\n : \"\";\n // Deferred content: re-emit the captured inner XML verbatim — children\n // were never parsed, and the bytes must survive a set/save round-trip.\n if (element.raw !== undefined) {\n s += `<${name}${attrStr}>${element.raw}</${name}>`;\n continue;\n }\n const children = element.elements;\n // The xml:space dictionary probe is deliberately last: it only matters\n // for empty leaf elements, so child-bearing elements skip it.\n const withClosingTag =\n (children !== undefined && children.length > 0) ||\n opts.fullTagEmptyElement ||\n attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) {\n s += `<${name}${attrStr}/>`;\n continue;\n }\n const open = `<${name}${attrStr}>`;\n if (children !== undefined && children.length > 0) {\n const inner = writeElements(children, opts, depth + 1, false);\n // The child-element scan is only needed to pretty-print the closing\n // tag — skip it entirely when not indenting.\n if (opts.spaces && children.some((e) => e.type === \"element\")) {\n s += open + inner + \"\\n\" + opts.spaces.repeat(depth) + `</${name}>`;\n } else {\n s += open + inner + `</${name}>`;\n }\n } else {\n s += open + `</${name}>`;\n }\n } else if (type === \"text\") {\n if (opts.ignoreText) continue;\n if (opts.indentText && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n // Text escaping inline — fast path: most text content contains no\n // markup delimiters, so a regex test (a native scan in V8/JSC, ~10× a\n // charCodeAt loop) returns the original string reference with zero\n // allocation. Chained replaces afterwards: specials are sparse, so each\n // pass after the first usually finds nothing and exits quickly.\n const text = element.text;\n if (text == null) continue;\n const str = String(text);\n s += TEXT_SPECIALS.test(str)\n ? str.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")\n : str;\n } else if (type === \"cdata\") {\n if (opts.ignoreCdata) continue;\n if (opts.indentCdata && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeCdata(element.cdata);\n } else if (type === \"comment\") {\n if (opts.ignoreComment) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeComment(element.comment);\n } else if (type === \"doctype\") {\n if (opts.ignoreDoctype) continue;\n if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);\n s += writeDoctype(element.doctype);\n }\n }\n return s;\n}\n\nfunction writeCdata(cdata: string | undefined | null): string {\n if (cdata == null) return \"\";\n const escaped = cdata.replace(/\\]\\]>/g, \"]]]]><![CDATA[>\");\n return `<![CDATA[${escaped}]]>`;\n}\n\nfunction writeComment(comment: string | undefined | null): string {\n if (comment == null) return \"\";\n return `<!--${comment}-->`;\n}\n\nfunction writeDoctype(doctype: string | undefined | null): string {\n if (doctype == null) return \"\";\n return `<!DOCTYPE ${doctype}>`;\n}\n\ntype NonNullable<T> = T extends null | undefined ? never : T;\n","/**\n * Serialize an Element including its own opening/closing tag.\n *\n * `stringify` serializes only an element's children (it treats its input as\n * a document root). Raw-XML round-trip of whole elements needs the element's\n * own tag wrapped around its serialized children.\n */\nimport { escapeXml } from \"./escape\";\nimport { stringify as stringifyChildren } from \"./stringify\";\nimport type { Element } from \"./types\";\n\nexport function stringifyElement(el: Element): string {\n if (!el.name) return \"\";\n let attrStr = \"\";\n if (el.attributes) {\n for (const key of Object.keys(el.attributes)) {\n const v = el.attributes[key];\n if (v === null || v === undefined) continue;\n attrStr += ` ${key}=\"${escapeXml(String(v))}\"`;\n }\n }\n const withClosingTag =\n (el.elements?.length ?? 0) > 0 || el.attributes?.[\"xml:space\"] === \"preserve\";\n if (!withClosingTag) return `<${el.name}${attrStr}/>`;\n return `<${el.name}${attrStr}>${stringifyChildren(el)}</${el.name}>`;\n}\n"],"mappings":"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"}
@@ -58,6 +58,7 @@ interface ParseOptions extends IgnoreOptions {
58
58
  instructionHasAttributes?: boolean;
59
59
  captureSpacesBetweenElements?: boolean;
60
60
  deferElements?: string[];
61
+ normalizeNamespaces?: Readonly<Record<string, string>>;
61
62
  doctypeFn?: (value: string, parentElement: object) => string;
62
63
  instructionFn?: (value: string, instructionName: string, parentElement: string) => string;
63
64
  cdataFn?: (value: string, parentElement: object) => string;
@@ -112,4 +113,4 @@ declare function findFirst(parent: Element | undefined, name: string): Element |
112
113
  declare function childCount(parent: Element | undefined): number;
113
114
  //#endregion
114
115
  export { IgnoreOptions as C, ElementCompact as S, StringifyOptions as T, isNonEmpty as _, attrBool as a, DeclarationAttributes as b, childCount as c, collectText as d, colorAttr as f, hasChild as g, findFirst as h, attr as i, childText as l, findDeep as m, OOXML_XML_DECLARATION as n, attrMeasure as o, findChild as p, allChildren as r, attrNum as s, NonEmptyArray as t, children as u, textOf as v, ParseOptions as w, Element as x, Attributes as y };
115
- //# sourceMappingURL=utils-CVZp5dCd.d.mts.map
116
+ //# sourceMappingURL=utils-qhk6IlD8.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-qhk6IlD8.d.mts","names":[],"sources":["../src/types.ts","../src/utils.ts"],"mappings":";UAEiB;GACd;;UAGc;EACf;EACA;EACA;;UAGe;EACf;IACE,aAAa;;EAEf;EACA,aAAa;EACb;EACA;EACA;EACA;EACA;EACA;EACA,WAAW;EAOX;EACA,SAAS;;UAGM;GACd;EACD;IACE,cAAc;;EAEhB;KACG;;EAEH,cAAc;EACd;EACA;EACA;EACA;;UAKe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;UAKe,qBAAqB;EACpC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAQA;EAYA,sBAAsB,SAAS;EAC/B,aAAa,eAAe;EAC5B,iBAAiB,eAAe,yBAAyB;EACzD,WAAW,eAAe;EAC1B,aAAa,eAAe;EAC5B,UAAU,eAAe;EACzB,qBACE,yBACA,0BACA;EAEF,iBAAiB,eAAe;EAChC,mBACE,uBACA,wBACA;EAEF,oBACE,wBACA,uBACA;EAEF,gBAAgB,OAAO,YAAY,0BAA0B;;UAK9C,yBAAyB;EACxC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,aAAa,eAAe,4BAA4B;EACxD,iBACE,0BACA,yBACA,4BACA;EAEF,WAAW,eAAe,4BAA4B;EACtD,aAAa,eAAe,4BAA4B;EACxD,UAAU,eAAe,4BAA4B;EACrD,qBACE,yBACA,0BACA,4BACA;EAEF,iBAAiB,eAAe,4BAA4B;EAC5D,mBACE,uBACA,wBACA,4BACA;EAEF,oBACE,wBACA,uBACA,4BACA;EAEF,gBACE,OAAO,YACP,4BACA,8BACG;EACL,yBAAyB,4BAA4B;;;;cC3J1C;KAOD,cAAc,eAAe,MAAM,cAAc;cAOhD,aAAc,GAAG,cAAc,QAAM,OAAO,cAAc;iBAKvD,UAAU,QAAQ,qBAAqB,eAAe;iBAOtD,SAAS,QAAQ,qBAAqB,eAAe;iBAOrD,YAAY,QAAQ,sBAAsB;iBAO1C,UAAU,QAAQ,qBAAqB;iBASvC,OAAO,SAAS;iBAgBhB,YAAY,SAAS;iBAsBrB,KAAK,SAAS,qBAAqB;iBAQnC,QAAQ,SAAS,qBAAqB;iBAoBtC,YACd,SAAS,qBACT;iBAYc,SAAS,SAAS,qBAAqB;iBAkBvC,UAAU,SAAS,qBAAqB;iBAexC,SAAS,QAAQ,qBAAqB;iBAOtC,SAAS,QAAQ,qBAAqB,eAAe;iBAmBrD,UAAU,QAAQ,qBAAqB,eAAe;iBAatD,WAAW,QAAQ"}
package/dist/utils.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as isNonEmpty, a as attrBool, c as childCount, d as collectText, f as colorAttr, g as hasChild, h as findFirst, i as attr, l as childText, m as findDeep, n as OOXML_XML_DECLARATION, o as attrMeasure, p as findChild, r as allChildren, s as attrNum, t as NonEmptyArray, u as children, v as textOf } from "./utils-CVZp5dCd.mjs";
1
+ import { _ as isNonEmpty, a as attrBool, c as childCount, d as collectText, f as colorAttr, g as hasChild, h as findFirst, i as attr, l as childText, m as findDeep, n as OOXML_XML_DECLARATION, o as attrMeasure, p as findChild, r as allChildren, s as attrNum, t as NonEmptyArray, u as children, v as textOf } from "./utils-qhk6IlD8.mjs";
2
2
  export { NonEmptyArray, OOXML_XML_DECLARATION, allChildren, attr, attrBool, attrMeasure, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, findFirst, hasChild, isNonEmpty, textOf };
package/dist/utils.mjs CHANGED
@@ -109,8 +109,8 @@ function attrBool(element, name) {
109
109
  if (v === void 0) return void 0;
110
110
  if (typeof v === "boolean") return v;
111
111
  const lower = String(v).toLowerCase();
112
- if (lower === "true" || lower === "1" || lower === "on") return true;
113
- if (lower === "false" || lower === "0" || lower === "off") return false;
112
+ if (lower === "true" || lower === "1" || lower === "on" || lower === "t") return true;
113
+ if (lower === "false" || lower === "0" || lower === "off" || lower === "f") return false;
114
114
  }
115
115
  /**
116
116
  * Get a hex color attribute, handling nativeTypeValue coercion.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import type { Element } from \"./types\";\n\n/**\n * Standard OOXML XML declaration used in all Office Open XML documents.\n *\n * The declaration specifies UTF-8 encoding and standalone=\"yes\" as required\n * by the OOXML specification. All XML parts in .docx, .pptx, and .xlsx files\n * use this declaration.\n */\nexport const OOXML_XML_DECLARATION = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>';\n\n/**\n * A readonly array guaranteed to have at least one element. Use with\n * {@link isNonEmpty} to narrow `T[]` so indexed access (e.g. `arr[0]`)\n * returns `T` instead of `T | undefined` under `noUncheckedIndexedAccess`.\n */\nexport type NonEmptyArray<T> = readonly [T, ...ReadonlyArray<T>];\n\n/**\n * User-defined type guard narrowing an array to {@link NonEmptyArray}.\n * Prefer this over a bare `arr.length > 0` check, which TypeScript does\n * not reliably narrow into a non-empty tuple at the read site.\n */\nexport const isNonEmpty = <T>(arr: readonly T[]): arr is NonEmptyArray<T> => arr.length > 0;\n\n/**\n * Find the first direct child element with the given name.\n */\nexport function findChild(parent: Element | undefined, name: string): Element | undefined {\n return parent?.elements?.find((e) => e.name === name);\n}\n\n/**\n * Get all direct child elements matching the given name.\n */\nexport function children(parent: Element | undefined, name: string): Element[] {\n return parent?.elements?.filter((e) => e.name === name) ?? [];\n}\n\n/**\n * Get all direct child elements.\n */\nexport function allChildren(parent: Element | undefined): Element[] {\n return parent?.elements ?? [];\n}\n\n/**\n * Get text content of the first child element with the given name.\n */\nexport function childText(parent: Element | undefined, name: string): string {\n const child = findChild(parent, name);\n return textOf(child);\n}\n\n/**\n * Get text content of an element.\n * Handles cases where text may be directly on .text or in a child element.\n */\nexport function textOf(element: Element | undefined): string {\n if (!element) return \"\";\n if (element.text !== undefined && typeof element.text === \"string\") return element.text;\n if (element.elements && element.elements.length > 0) {\n let text = \"\";\n for (const e of element.elements) {\n if (typeof e.text === \"string\") text += e.text;\n }\n return text;\n }\n return \"\";\n}\n\n/**\n * Collect text from all direct text nodes within an element.\n */\nexport function collectText(element: Element | undefined): string {\n if (!element) return \"\";\n const parts: string[] = [];\n collectTextRecursive(element, parts);\n return parts.join(\"\");\n}\n\nfunction collectTextRecursive(element: Element | undefined, parts: string[]): void {\n if (!element) return;\n if (element.text !== undefined && typeof element.text === \"string\") {\n parts.push(element.text);\n }\n if (element.elements) {\n for (const child of element.elements) {\n collectTextRecursive(child, parts);\n }\n }\n}\n\n/**\n * Get an attribute value as a string.\n */\nexport function attr(element: Element | undefined, name: string): string | undefined {\n const v = element?.attributes?.[name];\n return v !== undefined ? String(v) : undefined;\n}\n\n/**\n * Get an attribute value as a number.\n */\nexport function attrNum(element: Element | undefined, name: string): number | undefined {\n const v = element?.attributes?.[name];\n if (v === undefined) return undefined;\n const n = Number(v);\n return isNaN(n) ? undefined : n;\n}\n\n/**\n * Get a measurement attribute as a number or a verbatim measure/percent string.\n *\n * Counterpart to {@link attrNum} for XSD attribute unions of a decimal number\n * and UniversalMeasure/Percentage (ST_TwipsMeasure, ST_MeasurementOrPercent,\n * CT_TblWidth/@w, CT_Height/@val): a plain numeric token yields a number, while\n * UniversalMeasure (\"5mm\") and Percentage (\"50%\") stay verbatim so they\n * round-trip with the stringify-side value helpers in @office-open/core.\n *\n * For CT_TblWidth with type=\"pct\", the fiftieths token (\"5000\" = 100%) is a\n * plain numeric token and is returned as the number 5000; the stringify side\n * emits it verbatim (never \"5000%\", which is a different XSD branch).\n */\nexport function attrMeasure(\n element: Element | undefined,\n name: string,\n): number | string | undefined {\n const v = element?.attributes?.[name];\n if (v === undefined) return undefined;\n const raw = String(v);\n const n = Number(raw);\n return Number.isNaN(n) ? raw : n;\n}\n\n/**\n * Get an attribute value as a boolean.\n */\nexport function attrBool(element: Element | undefined, name: string): boolean | undefined {\n const v = element?.attributes?.[name];\n if (v === undefined) return undefined;\n if (typeof v === \"boolean\") return v;\n const lower = String(v).toLowerCase();\n if (lower === \"true\" || lower === \"1\" || lower === \"on\") return true;\n if (lower === \"false\" || lower === \"0\" || lower === \"off\") return false;\n return undefined;\n}\n\n/**\n * Get a hex color attribute, handling nativeTypeValue coercion.\n * nativeTypeAttributes converts \"000000\" → 0 (number); this recovers\n * the original 6-digit hex string by zero-padding numeric values.\n */\nexport function colorAttr(element: Element | undefined, name: string): string | undefined {\n const raw = element?.attributes?.[name];\n if (raw === undefined || raw === \"\") return undefined;\n if (typeof raw === \"boolean\") return undefined;\n if (typeof raw === \"number\") {\n return String(raw).padStart(6, \"0\");\n }\n if (raw === \"auto\") return \"auto\";\n if (/^[0-9A-Fa-f]{6}$/.test(raw)) return raw;\n return raw;\n}\n\n/**\n * Check if an element has a specific child element.\n */\nexport function hasChild(parent: Element | undefined, name: string): boolean {\n return parent?.elements?.some((e) => e.name === name) ?? false;\n}\n\n/**\n * Find deep descendant elements matching the given name.\n */\nexport function findDeep(parent: Element | undefined, name: string): Element[] {\n const result: Element[] = [];\n collectDeep(parent, name, result);\n return result;\n}\n\nfunction collectDeep(parent: Element | undefined, name: string, result: Element[]): void {\n if (!parent) return;\n for (const child of parent.elements ?? []) {\n if (child.name === name) result.push(child);\n collectDeep(child, name, result);\n }\n}\n\n/**\n * Find the first descendant element with the given name (depth-first pre-order).\n * Short-circuits at the first match — prefer this over `findDeep(parent, name)[0]`\n * to avoid traversing the whole subtree and allocating the full results array.\n */\nexport function findFirst(parent: Element | undefined, name: string): Element | undefined {\n if (!parent) return undefined;\n for (const child of parent.elements ?? []) {\n if (child.name === name) return child;\n const found = findFirst(child, name);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * Get the number of direct child elements.\n */\nexport function childCount(parent: Element | undefined): number {\n return parent?.elements?.length ?? 0;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import type { Element } from \"./types\";\n\n/**\n * Standard OOXML XML declaration used in all Office Open XML documents.\n *\n * The declaration specifies UTF-8 encoding and standalone=\"yes\" as required\n * by the OOXML specification. All XML parts in .docx, .pptx, and .xlsx files\n * use this declaration.\n */\nexport const OOXML_XML_DECLARATION = '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>';\n\n/**\n * A readonly array guaranteed to have at least one element. Use with\n * {@link isNonEmpty} to narrow `T[]` so indexed access (e.g. `arr[0]`)\n * returns `T` instead of `T | undefined` under `noUncheckedIndexedAccess`.\n */\nexport type NonEmptyArray<T> = readonly [T, ...ReadonlyArray<T>];\n\n/**\n * User-defined type guard narrowing an array to {@link NonEmptyArray}.\n * Prefer this over a bare `arr.length > 0` check, which TypeScript does\n * not reliably narrow into a non-empty tuple at the read site.\n */\nexport const isNonEmpty = <T>(arr: readonly T[]): arr is NonEmptyArray<T> => arr.length > 0;\n\n/**\n * Find the first direct child element with the given name.\n */\nexport function findChild(parent: Element | undefined, name: string): Element | undefined {\n return parent?.elements?.find((e) => e.name === name);\n}\n\n/**\n * Get all direct child elements matching the given name.\n */\nexport function children(parent: Element | undefined, name: string): Element[] {\n return parent?.elements?.filter((e) => e.name === name) ?? [];\n}\n\n/**\n * Get all direct child elements.\n */\nexport function allChildren(parent: Element | undefined): Element[] {\n return parent?.elements ?? [];\n}\n\n/**\n * Get text content of the first child element with the given name.\n */\nexport function childText(parent: Element | undefined, name: string): string {\n const child = findChild(parent, name);\n return textOf(child);\n}\n\n/**\n * Get text content of an element.\n * Handles cases where text may be directly on .text or in a child element.\n */\nexport function textOf(element: Element | undefined): string {\n if (!element) return \"\";\n if (element.text !== undefined && typeof element.text === \"string\") return element.text;\n if (element.elements && element.elements.length > 0) {\n let text = \"\";\n for (const e of element.elements) {\n if (typeof e.text === \"string\") text += e.text;\n }\n return text;\n }\n return \"\";\n}\n\n/**\n * Collect text from all direct text nodes within an element.\n */\nexport function collectText(element: Element | undefined): string {\n if (!element) return \"\";\n const parts: string[] = [];\n collectTextRecursive(element, parts);\n return parts.join(\"\");\n}\n\nfunction collectTextRecursive(element: Element | undefined, parts: string[]): void {\n if (!element) return;\n if (element.text !== undefined && typeof element.text === \"string\") {\n parts.push(element.text);\n }\n if (element.elements) {\n for (const child of element.elements) {\n collectTextRecursive(child, parts);\n }\n }\n}\n\n/**\n * Get an attribute value as a string.\n */\nexport function attr(element: Element | undefined, name: string): string | undefined {\n const v = element?.attributes?.[name];\n return v !== undefined ? String(v) : undefined;\n}\n\n/**\n * Get an attribute value as a number.\n */\nexport function attrNum(element: Element | undefined, name: string): number | undefined {\n const v = element?.attributes?.[name];\n if (v === undefined) return undefined;\n const n = Number(v);\n return isNaN(n) ? undefined : n;\n}\n\n/**\n * Get a measurement attribute as a number or a verbatim measure/percent string.\n *\n * Counterpart to {@link attrNum} for XSD attribute unions of a decimal number\n * and UniversalMeasure/Percentage (ST_TwipsMeasure, ST_MeasurementOrPercent,\n * CT_TblWidth/@w, CT_Height/@val): a plain numeric token yields a number, while\n * UniversalMeasure (\"5mm\") and Percentage (\"50%\") stay verbatim so they\n * round-trip with the stringify-side value helpers in @office-open/core.\n *\n * For CT_TblWidth with type=\"pct\", the fiftieths token (\"5000\" = 100%) is a\n * plain numeric token and is returned as the number 5000; the stringify side\n * emits it verbatim (never \"5000%\", which is a different XSD branch).\n */\nexport function attrMeasure(\n element: Element | undefined,\n name: string,\n): number | string | undefined {\n const v = element?.attributes?.[name];\n if (v === undefined) return undefined;\n const raw = String(v);\n const n = Number(raw);\n return Number.isNaN(n) ? raw : n;\n}\n\n/**\n * Get an attribute value as a boolean.\n */\nexport function attrBool(element: Element | undefined, name: string): boolean | undefined {\n const v = element?.attributes?.[name];\n if (v === undefined) return undefined;\n if (typeof v === \"boolean\") return v;\n const lower = String(v).toLowerCase();\n // ST_OnOff across editions: transitional uses xsd:boolean (true/false/1/0),\n // the original 2006 enumeration added on/off plus the single-letter t/f\n // Word 2007 wrote — accept the full union so old files parse losslessly.\n if (lower === \"true\" || lower === \"1\" || lower === \"on\" || lower === \"t\") return true;\n if (lower === \"false\" || lower === \"0\" || lower === \"off\" || lower === \"f\") return false;\n return undefined;\n}\n\n/**\n * Get a hex color attribute, handling nativeTypeValue coercion.\n * nativeTypeAttributes converts \"000000\" → 0 (number); this recovers\n * the original 6-digit hex string by zero-padding numeric values.\n */\nexport function colorAttr(element: Element | undefined, name: string): string | undefined {\n const raw = element?.attributes?.[name];\n if (raw === undefined || raw === \"\") return undefined;\n if (typeof raw === \"boolean\") return undefined;\n if (typeof raw === \"number\") {\n return String(raw).padStart(6, \"0\");\n }\n if (raw === \"auto\") return \"auto\";\n if (/^[0-9A-Fa-f]{6}$/.test(raw)) return raw;\n return raw;\n}\n\n/**\n * Check if an element has a specific child element.\n */\nexport function hasChild(parent: Element | undefined, name: string): boolean {\n return parent?.elements?.some((e) => e.name === name) ?? false;\n}\n\n/**\n * Find deep descendant elements matching the given name.\n */\nexport function findDeep(parent: Element | undefined, name: string): Element[] {\n const result: Element[] = [];\n collectDeep(parent, name, result);\n return result;\n}\n\nfunction collectDeep(parent: Element | undefined, name: string, result: Element[]): void {\n if (!parent) return;\n for (const child of parent.elements ?? []) {\n if (child.name === name) result.push(child);\n collectDeep(child, name, result);\n }\n}\n\n/**\n * Find the first descendant element with the given name (depth-first pre-order).\n * Short-circuits at the first match — prefer this over `findDeep(parent, name)[0]`\n * to avoid traversing the whole subtree and allocating the full results array.\n */\nexport function findFirst(parent: Element | undefined, name: string): Element | undefined {\n if (!parent) return undefined;\n for (const child of parent.elements ?? []) {\n if (child.name === name) return child;\n const found = findFirst(child, name);\n if (found) return found;\n }\n return undefined;\n}\n\n/**\n * Get the number of direct child elements.\n */\nexport function childCount(parent: Element | undefined): number {\n return parent?.elements?.length ?? 0;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@office-open/xml",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Zero-dependency XML parsing and serialization for Office Open XML — drop-in replacement for xml and xml-js",
5
5
  "keywords": [
6
6
  "drop-in",
@@ -46,6 +46,7 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/xml": "1.0.11",
49
+ "txml": "6.0.1",
49
50
  "xml": "1.0.1",
50
51
  "xml-js": "1.6.11"
51
52
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils-CVZp5dCd.d.mts","names":[],"sources":["../src/types.ts","../src/utils.ts"],"mappings":"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"}