@office-open/xml 0.10.15 → 0.11.0
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 +41 -90
- package/dist/index.d.mts +6 -19
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +194 -291
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-BFKTfRa8.d.mts → utils-CVZp5dCd.d.mts} +7 -36
- package/dist/utils-CVZp5dCd.d.mts.map +1 -0
- package/dist/utils.d.mts +2 -2
- package/dist/utils.mjs +11 -3
- package/dist/utils.mjs.map +1 -1
- package/package.json +7 -4
- package/dist/utils-BFKTfRa8.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -5,17 +5,15 @@
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
> XML parsing and serialization for Office Open XML. Zero dependencies,
|
|
8
|
+
> XML parsing and serialization for Office Open XML. Zero dependencies, pure TypeScript.
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
12
|
- **Zero Dependencies** - No external runtime dependencies, pure TypeScript implementation
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **Complete Type Definitions** - Full type compatibility with `xml` and `xml-js`, import without changes
|
|
18
|
-
- **OOXML Optimized** - Implements all options needed for Office Open XML document generation
|
|
13
|
+
- **parse() / stringify()** - XML string ↔ Element tree, OOXML-optimized
|
|
14
|
+
- **Element Type** - Tolerant element model for round-tripping Office Open XML parts
|
|
15
|
+
- **escapeXml() / unescapeXml()** - Low-level XML entity escaping
|
|
16
|
+
- **OOXML Optimized** - Implements the options needed for Office Open XML document generation and parsing
|
|
19
17
|
|
|
20
18
|
## Installation
|
|
21
19
|
|
|
@@ -33,113 +31,66 @@ yarn add @office-open/xml
|
|
|
33
31
|
bun add @office-open/xml
|
|
34
32
|
```
|
|
35
33
|
|
|
36
|
-
## Migration from xml + xml-js
|
|
37
|
-
|
|
38
|
-
Replace your existing imports:
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
// Before
|
|
42
|
-
import xml from "xml";
|
|
43
|
-
import { xml2js, js2xml } from "xml-js";
|
|
44
|
-
import type { Element } from "xml-js";
|
|
45
|
-
|
|
46
|
-
// After
|
|
47
|
-
import { xml, xml2js, js2xml } from "@office-open/xml";
|
|
48
|
-
import type { Element } from "@office-open/xml";
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
No other code changes needed. All options and output formats are compatible.
|
|
52
|
-
|
|
53
34
|
## Quick Start
|
|
54
35
|
|
|
55
36
|
```typescript
|
|
56
|
-
import {
|
|
37
|
+
import { parse, stringify } from "@office-open/xml";
|
|
57
38
|
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
// <w:p w:val="1"><w:r><w:t>Hello</w:t></w:r></w:p>
|
|
39
|
+
// Parse XML to an Element tree
|
|
40
|
+
const doc = parse("<w:t>Hello</w:t>");
|
|
61
41
|
|
|
62
|
-
//
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
// Convert JS objects back to XML
|
|
66
|
-
const output = js2xml(parsed);
|
|
67
|
-
|
|
68
|
-
// Direct conversion (faster than xml → xml2js bridge)
|
|
69
|
-
const element = toElement({
|
|
70
|
-
"w:p": [{ _attr: { "w:val": "1" } }, { "w:r": [{ "w:t": "Hello" }] }],
|
|
71
|
-
});
|
|
42
|
+
// Serialize an Element tree back to XML
|
|
43
|
+
const xml = stringify(doc);
|
|
72
44
|
```
|
|
73
45
|
|
|
74
46
|
## API
|
|
75
47
|
|
|
76
|
-
###
|
|
77
|
-
|
|
78
|
-
Serialize JavaScript objects to XML string. Compatible with the `xml` package.
|
|
79
|
-
|
|
80
|
-
### xml2js(xmlString, options?)
|
|
81
|
-
|
|
82
|
-
Parse XML string to JavaScript object. Compatible with `xml-js`.
|
|
48
|
+
### parse(xmlString, options?)
|
|
83
49
|
|
|
84
|
-
|
|
50
|
+
Parse an XML string into an `Element` tree. Options include `compact`, `trim`, `nativeType`, `captureSpacesBetweenElements`, the `ignore*` flags, and the `*Fn` transformation hooks.
|
|
85
51
|
|
|
86
|
-
|
|
52
|
+
### stringify(element, options?)
|
|
87
53
|
|
|
88
|
-
|
|
54
|
+
Serialize an `Element` tree to an XML string. Options include `spaces` (indentation), the `ignore*` flags, and the `*Fn` hooks.
|
|
89
55
|
|
|
90
|
-
|
|
56
|
+
### escapeXml(str) / unescapeXml(str)
|
|
91
57
|
|
|
92
|
-
|
|
58
|
+
Low-level XML entity escaping and unescaping.
|
|
93
59
|
|
|
94
|
-
|
|
60
|
+
### Element
|
|
95
61
|
|
|
96
|
-
|
|
62
|
+
The tolerant element type used across all office-open packages:
|
|
97
63
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
64
|
+
```typescript
|
|
65
|
+
interface Element {
|
|
66
|
+
declaration?: { attributes?: DeclarationAttributes };
|
|
67
|
+
attributes?: Attributes;
|
|
68
|
+
type?: string;
|
|
69
|
+
name?: string;
|
|
70
|
+
text?: string | number | boolean;
|
|
71
|
+
cdata?: string;
|
|
72
|
+
comment?: string;
|
|
73
|
+
elements?: Element[];
|
|
74
|
+
}
|
|
75
|
+
```
|
|
103
76
|
|
|
104
77
|
## Benchmark
|
|
105
78
|
|
|
106
|
-
Performance
|
|
107
|
-
|
|
108
|
-
### Serialization (xml)
|
|
109
|
-
|
|
110
|
-
| Scenario | @office-open/xml | xml | Speedup |
|
|
111
|
-
| ----------------------- | ---------------: | ---------: | --------: |
|
|
112
|
-
| Simple element | 5,440,771 hz | 805,545 hz | **6.75x** |
|
|
113
|
-
| Nested element | 1,050,272 hz | 315,184 hz | **3.33x** |
|
|
114
|
-
| Nested with declaration | 967,945 hz | 275,684 hz | **3.51x** |
|
|
115
|
-
|
|
116
|
-
### Parsing (xml2js)
|
|
117
|
-
|
|
118
|
-
| Scenario | @office-open/xml | xml-js | Speedup |
|
|
119
|
-
| ------------------ | ---------------: | ---------: | --------: |
|
|
120
|
-
| Simple XML | 869,965 hz | 100,507 hz | **8.66x** |
|
|
121
|
-
| Complex OOXML | 346,440 hz | 53,621 hz | **6.46x** |
|
|
122
|
-
| With captureSpaces | 344,586 hz | 52,414 hz | **6.57x** |
|
|
123
|
-
|
|
124
|
-
### Stringifying (js2xml)
|
|
125
|
-
|
|
126
|
-
| Scenario | @office-open/xml | xml-js | Speedup |
|
|
127
|
-
| -------------- | ---------------: | ---------: | --------: |
|
|
128
|
-
| Simple element | 793,710 hz | 207,730 hz | **3.82x** |
|
|
129
|
-
| Complex OOXML | 366,515 hz | 135,815 hz | **2.70x** |
|
|
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.
|
|
130
80
|
|
|
131
|
-
|
|
81
|
+
**parse() — XML string → Element tree**
|
|
132
82
|
|
|
133
|
-
| Scenario
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
|
|
|
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 |
|
|
137
87
|
|
|
138
|
-
|
|
88
|
+
**stringify() — Element tree → XML string**
|
|
139
89
|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
142
|
-
|
|
|
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 |
|
|
143
94
|
|
|
144
95
|
## License
|
|
145
96
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
|
-
import {
|
|
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
2
|
|
|
3
|
-
//#region src/serialize.d.ts
|
|
4
|
-
declare function xml(input: Record<string, unknown> | Record<string, unknown>[], options?: boolean | string | {
|
|
5
|
-
indent?: boolean | string;
|
|
6
|
-
declaration?: boolean | {
|
|
7
|
-
encoding?: string;
|
|
8
|
-
standalone?: string;
|
|
9
|
-
};
|
|
10
|
-
}): string;
|
|
11
|
-
//#endregion
|
|
12
3
|
//#region src/parse.d.ts
|
|
13
4
|
declare function unescapeXml(str: string): string;
|
|
14
5
|
declare function nativeTypeValue(value: string): string | number | boolean;
|
|
15
|
-
declare function parse(xmlString: string, options?:
|
|
6
|
+
declare function parse(xmlString: string, options?: ParseOptions): Element;
|
|
16
7
|
declare function parseAttributes(str: string): Record<string, string>;
|
|
17
8
|
//#endregion
|
|
18
9
|
//#region src/stringify.d.ts
|
|
19
|
-
declare function stringify(js: Element, options?:
|
|
20
|
-
declare function json2xml(json: Element, options?: Js2XmlOptions): string;
|
|
10
|
+
declare function stringify(js: Element, options?: StringifyOptions): string;
|
|
21
11
|
//#endregion
|
|
22
|
-
//#region src/
|
|
23
|
-
declare function
|
|
12
|
+
//#region src/stringify-element.d.ts
|
|
13
|
+
declare function stringifyElement(el: Element): string;
|
|
24
14
|
//#endregion
|
|
25
15
|
//#region src/escape.d.ts
|
|
26
16
|
declare function escapeXml(str: string): string;
|
|
@@ -29,8 +19,5 @@ declare function attrsRaw(record: Record<string, string | number | boolean | und
|
|
|
29
19
|
declare function selfCloseElement(tag: string, attrStr?: string): string;
|
|
30
20
|
declare function element(name: string, attrRecord?: Readonly<Record<string, string | number | boolean | undefined>>, children?: readonly string[]): string;
|
|
31
21
|
//#endregion
|
|
32
|
-
|
|
33
|
-
declare function xml2json(xml: string, options?: Xml2JsOptions): string;
|
|
34
|
-
//#endregion
|
|
35
|
-
export { type Attributes, type DeclarationAttributes, type Element, type ElementCompact, type ElementObject, type IgnoreOptions, type Js2XmlOptions, NonEmptyArray, type Xml2JsOptions, type XmlAtom, type XmlAttrs, type XmlDesc, type XmlDescArray, type XmlObject, type XmlOption, allChildren, attr, attrBool, attrMeasure, attrNum, attrs, attrsRaw, childCount, childText, children, collectText, colorAttr, element, escapeXml, findChild, findDeep, findFirst, hasChild, isNonEmpty, stringify as js2xml, stringify, json2xml, nativeTypeValue, parse, parse as xml2js, parseAttributes, selfCloseElement, textOf, toElement, unescapeXml, xml, xml2json };
|
|
22
|
+
export { type Attributes, type DeclarationAttributes, type Element, type ElementCompact, type IgnoreOptions, NonEmptyArray, OOXML_XML_DECLARATION, type ParseOptions, type StringifyOptions, allChildren, attr, attrBool, attrMeasure, attrNum, attrs, attrsRaw, childCount, childText, children, collectText, colorAttr, element, escapeXml, findChild, findDeep, findFirst, hasChild, isNonEmpty, nativeTypeValue, parse, parseAttributes, selfCloseElement, stringify, stringifyElement, textOf, unescapeXml };
|
|
36
23
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/
|
|
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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,200 +1,4 @@
|
|
|
1
|
-
import { allChildren, attr, attrBool, attrMeasure, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, findFirst, hasChild, isNonEmpty, textOf } from "./utils.mjs";
|
|
2
|
-
//#region src/escape.ts
|
|
3
|
-
/** Escape text content for XML. Fast path returns original string when no special chars. */
|
|
4
|
-
function escapeXml(str) {
|
|
5
|
-
for (let i = 0; i < str.length; i++) {
|
|
6
|
-
const c = str.charCodeAt(i);
|
|
7
|
-
if (c === 38 || c === 34 || c === 39 || c === 60 || c === 62) {
|
|
8
|
-
let s = "";
|
|
9
|
-
let last = 0;
|
|
10
|
-
for (let j = i; j < str.length; j++) {
|
|
11
|
-
const cj = str.charCodeAt(j);
|
|
12
|
-
if (cj === 38) {
|
|
13
|
-
s += str.slice(last, j) + "&";
|
|
14
|
-
last = j + 1;
|
|
15
|
-
} else if (cj === 34) {
|
|
16
|
-
s += str.slice(last, j) + """;
|
|
17
|
-
last = j + 1;
|
|
18
|
-
} else if (cj === 39) {
|
|
19
|
-
s += str.slice(last, j) + "'";
|
|
20
|
-
last = j + 1;
|
|
21
|
-
} else if (cj === 60) {
|
|
22
|
-
s += str.slice(last, j) + "<";
|
|
23
|
-
last = j + 1;
|
|
24
|
-
} else if (cj === 62) {
|
|
25
|
-
s += str.slice(last, j) + ">";
|
|
26
|
-
last = j + 1;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return s + str.slice(last);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return str;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Build an XML attribute string fragment from a record.
|
|
36
|
-
* `undefined` values are automatically skipped.
|
|
37
|
-
* String values are escaped via `escapeXml`.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* attrs({ id: 1, name: "foo", hidden: undefined })
|
|
41
|
-
* // => ' id="1" name="foo"'
|
|
42
|
-
*/
|
|
43
|
-
function attrs(record) {
|
|
44
|
-
const parts = [];
|
|
45
|
-
for (const [key, v] of Object.entries(record)) if (v !== void 0) parts.push(` ${key}="${typeof v === "string" ? escapeXml(v) : v}"`);
|
|
46
|
-
return parts.join("");
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Build an XML attribute string without escaping.
|
|
50
|
-
*
|
|
51
|
-
* Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when
|
|
52
|
-
* all values are known-safe (numbers, booleans, or strings free of `& " ' < >`).
|
|
53
|
-
* Avoids per-call array and `Object.keys()` allocation in hot loops.
|
|
54
|
-
*
|
|
55
|
-
* @example
|
|
56
|
-
* attrsRaw({ r: "A1", s: 5 })
|
|
57
|
-
* // => ' r="A1" s="5"'
|
|
58
|
-
*/
|
|
59
|
-
function attrsRaw(record) {
|
|
60
|
-
let s = "";
|
|
61
|
-
for (const key in record) {
|
|
62
|
-
const v = record[key];
|
|
63
|
-
if (v !== void 0) s += ` ${key}="${v}"`;
|
|
64
|
-
}
|
|
65
|
-
return s;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Build a self-closing XML element: `<tag attrStr/>`.
|
|
69
|
-
* `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.
|
|
70
|
-
*/
|
|
71
|
-
function selfCloseElement(tag, attrStr) {
|
|
72
|
-
return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Build a complete XML element string from name, optional attributes, and string children.
|
|
76
|
-
*
|
|
77
|
-
* Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a
|
|
78
|
-
* single function call returning a string — zero object allocation.
|
|
79
|
-
*
|
|
80
|
-
* @param name Element tag name (e.g. `"a:srgbClr"`)
|
|
81
|
-
* @param attrRecord Optional flat attribute map; `undefined` values are skipped
|
|
82
|
-
* @param children Optional pre-serialized child XML strings
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* ```ts
|
|
86
|
-
* element("a:solidFill", undefined, [element("a:srgbClr", { val: "FF0000" })])
|
|
87
|
-
* // => '<a:solidFill><a:srgbClr val="FF0000"/></a:solidFill>'
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
function element(name, attrRecord, children) {
|
|
91
|
-
const attrStr = attrRecord ? attrs(attrRecord) : void 0;
|
|
92
|
-
if (!children || children.length === 0) return selfCloseElement(name, attrStr);
|
|
93
|
-
const body = children.join("");
|
|
94
|
-
return body.length === 0 ? selfCloseElement(name, attrStr) : `<${name}${attrStr ?? ""}>${body}</${name}>`;
|
|
95
|
-
}
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/serialize.ts
|
|
98
|
-
const DEFAULT_INDENT = " ";
|
|
99
|
-
/**
|
|
100
|
-
* Serialize a Record-based XML object tree to an XML string.
|
|
101
|
-
* @deprecated Use `stringify` (Element → string) instead. This object-tree path
|
|
102
|
-
* will be removed once the Descriptor migration is complete.
|
|
103
|
-
*/
|
|
104
|
-
function xml(input, options) {
|
|
105
|
-
const opts = normalizeOptions$1(options);
|
|
106
|
-
const parts = [];
|
|
107
|
-
if (opts.declaration) {
|
|
108
|
-
const declOpts = opts.declaration === true ? {} : opts.declaration;
|
|
109
|
-
const enc = declOpts.encoding || "UTF-8";
|
|
110
|
-
const sa = declOpts.standalone;
|
|
111
|
-
const declParts = [`<?xml version="1.0" encoding="${enc}"`];
|
|
112
|
-
if (sa) declParts.push(` standalone="${sa}"`);
|
|
113
|
-
declParts.push("?>");
|
|
114
|
-
parts.push(declParts.join(""));
|
|
115
|
-
if (opts.indent) parts.push("\n");
|
|
116
|
-
}
|
|
117
|
-
const items = Array.isArray(input) ? input : [input];
|
|
118
|
-
for (let i = 0; i < items.length; i++) {
|
|
119
|
-
const item = items[i];
|
|
120
|
-
if (!item) continue;
|
|
121
|
-
const key = Object.keys(item)[0];
|
|
122
|
-
if (!key) continue;
|
|
123
|
-
parts.push(formatElement(key, item[key], opts.indent, 0));
|
|
124
|
-
if (opts.indent && i < items.length - 1) parts.push("\n");
|
|
125
|
-
}
|
|
126
|
-
return parts.join("");
|
|
127
|
-
}
|
|
128
|
-
function normalizeOptions$1(options) {
|
|
129
|
-
const opts = typeof options === "object" && !Array.isArray(options) ? options : { indent: options };
|
|
130
|
-
let indent = "";
|
|
131
|
-
if (opts.indent) indent = opts.indent === true ? DEFAULT_INDENT : String(opts.indent);
|
|
132
|
-
return {
|
|
133
|
-
indent,
|
|
134
|
-
declaration: opts.declaration
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Single-pass XML formatter: directly converts a Record-based XML object to string,
|
|
139
|
-
* eliminating the intermediate ResolvedElement tree.
|
|
140
|
-
*/
|
|
141
|
-
function formatElement(name, values, indent, depth) {
|
|
142
|
-
const attrParts = [];
|
|
143
|
-
const textParts = [];
|
|
144
|
-
const elemParts = [];
|
|
145
|
-
let emptyArray = false;
|
|
146
|
-
if (values == null) {
|
|
147
|
-
const attrStr = attrParts.length ? " " + attrParts.join(" ") : "";
|
|
148
|
-
return `${indent ? indent.repeat(depth) : ""}<${name}${attrStr}/>`;
|
|
149
|
-
}
|
|
150
|
-
if (typeof values === "object") {
|
|
151
|
-
const obj = values;
|
|
152
|
-
if (obj._attr) {
|
|
153
|
-
const attr = obj._attr;
|
|
154
|
-
for (const key of Object.keys(attr)) attrParts.push(`${key}="${escapeXml(String(attr[key]))}"`);
|
|
155
|
-
}
|
|
156
|
-
if (obj._attributes) {
|
|
157
|
-
const attr = obj._attributes;
|
|
158
|
-
for (const key of Object.keys(attr)) attrParts.push(`${key}="${escapeXml(String(attr[key]))}"`);
|
|
159
|
-
}
|
|
160
|
-
if (obj._cdata) {
|
|
161
|
-
const escaped = String(obj._cdata).replace(/\]\]>/g, "]]]]><![CDATA[>");
|
|
162
|
-
textParts.push(`<![CDATA[${escaped}]]>`);
|
|
163
|
-
}
|
|
164
|
-
if (Array.isArray(values)) {
|
|
165
|
-
if (values.length === 0) emptyArray = true;
|
|
166
|
-
else for (const value of values) if (value && typeof value === "object" && "_attr" in value) {
|
|
167
|
-
const attr = value._attr;
|
|
168
|
-
for (const key of Object.keys(attr)) attrParts.push(`${key}="${escapeXml(String(attr[key]))}"`);
|
|
169
|
-
} else if (value && typeof value === "object" && "_attributes" in value) {
|
|
170
|
-
const attr = value._attributes;
|
|
171
|
-
for (const key of Object.keys(attr)) attrParts.push(`${key}="${escapeXml(String(attr[key]))}"`);
|
|
172
|
-
} else if (value && typeof value === "object") {
|
|
173
|
-
const childKey = Object.keys(value)[0];
|
|
174
|
-
if (childKey) elemParts.push(formatElement(childKey, value[childKey], indent, depth + 1));
|
|
175
|
-
} else if (value != null) textParts.push(escapeXml(String(value)));
|
|
176
|
-
}
|
|
177
|
-
} else textParts.push(escapeXml(String(values)));
|
|
178
|
-
const ind = indent ? indent.repeat(depth) : "";
|
|
179
|
-
const attrStr = attrParts.length ? " " + attrParts.join(" ") : "";
|
|
180
|
-
if (textParts.length + elemParts.length === 0) return emptyArray ? `${ind}<${name}${attrStr}></${name}>` : `${ind}<${name}${attrStr}/>`;
|
|
181
|
-
if (elemParts.length === 0 && textParts.length === 1) return indent ? `${ind}<${name}${attrStr}>${textParts[0]}</${name}>` : `<${name}${attrStr}>${textParts[0]}</${name}>`;
|
|
182
|
-
const parts = [];
|
|
183
|
-
parts.push(`${ind}<${name}${attrStr}>`);
|
|
184
|
-
if (indent) parts.push("\n");
|
|
185
|
-
const childIndent = indent ? indent.repeat(depth + 1) : "";
|
|
186
|
-
for (const t of textParts) {
|
|
187
|
-
parts.push(`${childIndent}${t}`);
|
|
188
|
-
if (indent) parts.push("\n");
|
|
189
|
-
}
|
|
190
|
-
for (const e of elemParts) {
|
|
191
|
-
parts.push(e);
|
|
192
|
-
if (indent) parts.push("\n");
|
|
193
|
-
}
|
|
194
|
-
parts.push(`${ind}</${name}>`);
|
|
195
|
-
return parts.join("");
|
|
196
|
-
}
|
|
197
|
-
//#endregion
|
|
1
|
+
import { OOXML_XML_DECLARATION, allChildren, attr, attrBool, attrMeasure, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, findFirst, hasChild, isNonEmpty, textOf } from "./utils.mjs";
|
|
198
2
|
//#region src/parse.ts
|
|
199
3
|
const ENTITY_MAP = {
|
|
200
4
|
"&": "&",
|
|
@@ -205,6 +9,7 @@ const ENTITY_MAP = {
|
|
|
205
9
|
};
|
|
206
10
|
const ENTITY_PATTERN = /&(?:amp|lt|gt|quot|apos|#x[0-9a-fA-F]+|#[0-9]+);/g;
|
|
207
11
|
function unescapeXml(str) {
|
|
12
|
+
if (str.indexOf("&") === -1) return str;
|
|
208
13
|
return str.replace(ENTITY_PATTERN, (match) => {
|
|
209
14
|
if (ENTITY_MAP[match] !== void 0) return ENTITY_MAP[match];
|
|
210
15
|
const body = match.slice(2, -1);
|
|
@@ -214,11 +19,31 @@ function unescapeXml(str) {
|
|
|
214
19
|
}
|
|
215
20
|
function nativeTypeValue(value) {
|
|
216
21
|
if (value === "") return value;
|
|
22
|
+
const neg = value.charCodeAt(0) === 45;
|
|
23
|
+
const start = neg ? 1 : 0;
|
|
24
|
+
const digits = value.length - start;
|
|
25
|
+
if (digits > 0 && digits <= 15) {
|
|
26
|
+
if (value.charCodeAt(start) !== 48 || !neg && digits === 1) {
|
|
27
|
+
let n = 0;
|
|
28
|
+
let allDigits = true;
|
|
29
|
+
for (let i = start; i < value.length; i++) {
|
|
30
|
+
const c = value.charCodeAt(i);
|
|
31
|
+
if (c < 48 || c > 57) {
|
|
32
|
+
allDigits = false;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
n = n * 10 + (c - 48);
|
|
36
|
+
}
|
|
37
|
+
if (allDigits) return neg ? -n : n;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
217
40
|
const n = Number(value);
|
|
218
41
|
if (!isNaN(n) && String(n) === value) return n;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
42
|
+
if (value.length === 4 || value.length === 5) {
|
|
43
|
+
const lower = value.toLowerCase();
|
|
44
|
+
if (lower === "true") return true;
|
|
45
|
+
if (lower === "false") return false;
|
|
46
|
+
}
|
|
222
47
|
return value;
|
|
223
48
|
}
|
|
224
49
|
function parse(xmlString, options) {
|
|
@@ -230,6 +55,7 @@ function parse(xmlString, options) {
|
|
|
230
55
|
const ignoreCdata = options?.ignoreCdata ?? false;
|
|
231
56
|
const ignoreDoctype = options?.ignoreDoctype ?? false;
|
|
232
57
|
const nativeTypeAttributes = options?.nativeTypeAttributes ?? false;
|
|
58
|
+
const deferSet = options?.deferElements !== void 0 && options.deferElements.length > 0 ? new Set(options.deferElements) : void 0;
|
|
233
59
|
const result = {};
|
|
234
60
|
const stack = [result];
|
|
235
61
|
let i = 0;
|
|
@@ -242,7 +68,20 @@ function parse(xmlString, options) {
|
|
|
242
68
|
if (trim) text = text.trim();
|
|
243
69
|
if (ignoreText) continue;
|
|
244
70
|
if (text.length > 0) {
|
|
245
|
-
if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack))
|
|
71
|
+
if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack)) {
|
|
72
|
+
const parent = stack[stack.length - 1];
|
|
73
|
+
const elements = parent.elements;
|
|
74
|
+
const last = elements === void 0 ? void 0 : elements[elements.length - 1];
|
|
75
|
+
if (last !== void 0 && last.type === "text") last.text = last.text + text;
|
|
76
|
+
else {
|
|
77
|
+
const node = {
|
|
78
|
+
type: "text",
|
|
79
|
+
text
|
|
80
|
+
};
|
|
81
|
+
if (elements === void 0) parent.elements = [node];
|
|
82
|
+
else elements.push(node);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
246
85
|
}
|
|
247
86
|
continue;
|
|
248
87
|
}
|
|
@@ -299,9 +138,29 @@ function parse(xmlString, options) {
|
|
|
299
138
|
const tagNameEnd = findTagNameEnd(xmlString, i);
|
|
300
139
|
const tagName = xmlString.slice(i, tagNameEnd);
|
|
301
140
|
let pos = tagNameEnd;
|
|
302
|
-
|
|
303
|
-
pos
|
|
304
|
-
|
|
141
|
+
let attrs;
|
|
142
|
+
while (pos < len) {
|
|
143
|
+
while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;
|
|
144
|
+
if (pos >= len || xmlString.charCodeAt(pos) === 62 || xmlString.charCodeAt(pos) === 47) break;
|
|
145
|
+
const nameStart = pos;
|
|
146
|
+
while (pos < len && xmlString.charCodeAt(pos) !== 61) {
|
|
147
|
+
if (xmlString.charCodeAt(pos) === 62 || xmlString.charCodeAt(pos) === 47) break;
|
|
148
|
+
pos++;
|
|
149
|
+
}
|
|
150
|
+
const name = xmlString.slice(nameStart, pos);
|
|
151
|
+
if (xmlString.charCodeAt(pos) !== 61) break;
|
|
152
|
+
pos++;
|
|
153
|
+
while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;
|
|
154
|
+
const quote = xmlString.charCodeAt(pos);
|
|
155
|
+
if (quote !== 34 && quote !== 39) break;
|
|
156
|
+
pos++;
|
|
157
|
+
const valueStart = pos;
|
|
158
|
+
while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;
|
|
159
|
+
if (attrs === void 0) attrs = {};
|
|
160
|
+
attrs[name] = unescapeXml(xmlString.slice(valueStart, pos));
|
|
161
|
+
pos++;
|
|
162
|
+
}
|
|
163
|
+
if (attrs && nativeTypeAttributes) for (const key in attrs) attrs[key] = nativeTypeValue(attrs[key]);
|
|
305
164
|
const isSelfClosing = xmlString.charCodeAt(pos) === 47;
|
|
306
165
|
if (isSelfClosing) pos += 2;
|
|
307
166
|
else pos++;
|
|
@@ -309,11 +168,42 @@ function parse(xmlString, options) {
|
|
|
309
168
|
type: "element",
|
|
310
169
|
name: tagName
|
|
311
170
|
};
|
|
312
|
-
if (
|
|
171
|
+
if (attrs) element.attributes = attrs;
|
|
313
172
|
const parent = peek(stack);
|
|
314
173
|
if (!parent.elements) parent.elements = [];
|
|
315
174
|
parent.elements.push(element);
|
|
316
|
-
if (!isSelfClosing)
|
|
175
|
+
if (!isSelfClosing) {
|
|
176
|
+
if (deferSet !== void 0 && deferSet.has(tagName)) {
|
|
177
|
+
const closeTag = `</${tagName}>`;
|
|
178
|
+
let depth = 1;
|
|
179
|
+
let scan = pos;
|
|
180
|
+
let closeIdx = -1;
|
|
181
|
+
for (;;) {
|
|
182
|
+
closeIdx = xmlString.indexOf(closeTag, scan);
|
|
183
|
+
if (closeIdx === -1) break;
|
|
184
|
+
let p = scan;
|
|
185
|
+
for (;;) {
|
|
186
|
+
const openIdx = xmlString.indexOf(`<${tagName}`, p);
|
|
187
|
+
if (openIdx === -1 || openIdx >= closeIdx) break;
|
|
188
|
+
const after = xmlString.charCodeAt(openIdx + tagName.length + 1);
|
|
189
|
+
if (after === 32 || after === 9 || after === 10 || after === 13 || after === 47 || after === 62) depth++;
|
|
190
|
+
p = openIdx + tagName.length + 1;
|
|
191
|
+
}
|
|
192
|
+
scan = closeIdx + closeTag.length;
|
|
193
|
+
depth--;
|
|
194
|
+
if (depth === 0) break;
|
|
195
|
+
}
|
|
196
|
+
if (closeIdx === -1) {
|
|
197
|
+
element.raw = xmlString.slice(pos);
|
|
198
|
+
i = len;
|
|
199
|
+
} else {
|
|
200
|
+
element.raw = xmlString.slice(pos, closeIdx);
|
|
201
|
+
i = scan;
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
stack.push(element);
|
|
206
|
+
}
|
|
317
207
|
i = pos;
|
|
318
208
|
}
|
|
319
209
|
if (result.elements) {
|
|
@@ -334,35 +224,6 @@ function findTagNameEnd(str, start) {
|
|
|
334
224
|
}
|
|
335
225
|
return i;
|
|
336
226
|
}
|
|
337
|
-
function parseAttributesFromXml(str, start) {
|
|
338
|
-
const attrs = {};
|
|
339
|
-
let i = start;
|
|
340
|
-
const len = str.length;
|
|
341
|
-
while (i < len) {
|
|
342
|
-
while (i < len && isWhitespace(str.charCodeAt(i))) i++;
|
|
343
|
-
if (i >= len || str.charCodeAt(i) === 62 || str.charCodeAt(i) === 47) break;
|
|
344
|
-
const nameStart = i;
|
|
345
|
-
while (i < len && str.charCodeAt(i) !== 61) {
|
|
346
|
-
if (str.charCodeAt(i) === 62 || str.charCodeAt(i) === 47) break;
|
|
347
|
-
i++;
|
|
348
|
-
}
|
|
349
|
-
const name = str.slice(nameStart, i);
|
|
350
|
-
if (str.charCodeAt(i) !== 61) break;
|
|
351
|
-
i++;
|
|
352
|
-
while (i < len && isWhitespace(str.charCodeAt(i))) i++;
|
|
353
|
-
const quote = str.charCodeAt(i);
|
|
354
|
-
if (quote !== 34 && quote !== 39) break;
|
|
355
|
-
i++;
|
|
356
|
-
const valueStart = i;
|
|
357
|
-
while (i < len && str.charCodeAt(i) !== quote) i++;
|
|
358
|
-
attrs[name] = unescapeXml(str.slice(valueStart, i));
|
|
359
|
-
i++;
|
|
360
|
-
}
|
|
361
|
-
return {
|
|
362
|
-
attrs,
|
|
363
|
-
pos: i
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
227
|
function parseAttributes(str) {
|
|
367
228
|
const result = {};
|
|
368
229
|
let i = 0;
|
|
@@ -429,6 +290,87 @@ function isWhitespace(ch) {
|
|
|
429
290
|
return ch === 32 || ch === 9 || ch === 10 || ch === 13;
|
|
430
291
|
}
|
|
431
292
|
//#endregion
|
|
293
|
+
//#region src/escape.ts
|
|
294
|
+
const XML_SPECIALS = /[&"'<>]/;
|
|
295
|
+
/** Escape text content for XML. Fast path returns original string when no special chars. */
|
|
296
|
+
function escapeXml(str) {
|
|
297
|
+
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("&");
|
|
303
|
+
else if (c === 34) parts.push(""");
|
|
304
|
+
else if (c === 39) parts.push("'");
|
|
305
|
+
else if (c === 60) parts.push("<");
|
|
306
|
+
else if (c === 62) parts.push(">");
|
|
307
|
+
else parts.push(str.charAt(i));
|
|
308
|
+
}
|
|
309
|
+
return parts.join("");
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Build an XML attribute string fragment from a record.
|
|
313
|
+
* `undefined` values are automatically skipped.
|
|
314
|
+
* String values are escaped via `escapeXml`.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* attrs({ id: 1, name: "foo", hidden: undefined })
|
|
318
|
+
* // => ' id="1" name="foo"'
|
|
319
|
+
*/
|
|
320
|
+
function attrs(record) {
|
|
321
|
+
const parts = [];
|
|
322
|
+
for (const [key, v] of Object.entries(record)) if (v !== void 0) parts.push(` ${key}="${typeof v === "string" ? escapeXml(v) : v}"`);
|
|
323
|
+
return parts.join("");
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Build an XML attribute string without escaping.
|
|
327
|
+
*
|
|
328
|
+
* Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when
|
|
329
|
+
* all values are known-safe (numbers, booleans, or strings free of `& " ' < >`).
|
|
330
|
+
* Avoids per-call array and `Object.keys()` allocation in hot loops.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* attrsRaw({ r: "A1", s: 5 })
|
|
334
|
+
* // => ' r="A1" s="5"'
|
|
335
|
+
*/
|
|
336
|
+
function attrsRaw(record) {
|
|
337
|
+
let s = "";
|
|
338
|
+
for (const key in record) {
|
|
339
|
+
const v = record[key];
|
|
340
|
+
if (v !== void 0) s += ` ${key}="${v}"`;
|
|
341
|
+
}
|
|
342
|
+
return s;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Build a self-closing XML element: `<tag attrStr/>`.
|
|
346
|
+
* `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.
|
|
347
|
+
*/
|
|
348
|
+
function selfCloseElement(tag, attrStr) {
|
|
349
|
+
return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Build a complete XML element string from name, optional attributes, and string children.
|
|
353
|
+
*
|
|
354
|
+
* Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a
|
|
355
|
+
* single function call returning a string — zero object allocation.
|
|
356
|
+
*
|
|
357
|
+
* @param name Element tag name (e.g. `"a:srgbClr"`)
|
|
358
|
+
* @param attrRecord Optional flat attribute map; `undefined` values are skipped
|
|
359
|
+
* @param children Optional pre-serialized child XML strings
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* element("a:solidFill", undefined, [element("a:srgbClr", { val: "FF0000" })])
|
|
364
|
+
* // => '<a:solidFill><a:srgbClr val="FF0000"/></a:solidFill>'
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
function element(name, attrRecord, children) {
|
|
368
|
+
const attrStr = attrRecord ? attrs(attrRecord) : void 0;
|
|
369
|
+
if (!children || children.length === 0) return selfCloseElement(name, attrStr);
|
|
370
|
+
const body = children.join("");
|
|
371
|
+
return body.length === 0 ? selfCloseElement(name, attrStr) : `<${name}${attrStr ?? ""}>${body}</${name}>`;
|
|
372
|
+
}
|
|
373
|
+
//#endregion
|
|
432
374
|
//#region src/stringify.ts
|
|
433
375
|
function stringify(js, options) {
|
|
434
376
|
const opts = normalizeOptions(options);
|
|
@@ -437,10 +379,6 @@ function stringify(js, options) {
|
|
|
437
379
|
if (js.elements?.length) parts.push(writeElements(js.elements, opts, 0, !parts.length));
|
|
438
380
|
return parts.join("");
|
|
439
381
|
}
|
|
440
|
-
/** @deprecated Use `stringify` instead. xml-js compatible alias. */
|
|
441
|
-
function json2xml(json, options) {
|
|
442
|
-
return stringify(json, options);
|
|
443
|
-
}
|
|
444
382
|
function normalizeOptions(options) {
|
|
445
383
|
if (!options) return {
|
|
446
384
|
spaces: "",
|
|
@@ -494,6 +432,7 @@ function writeElement(element, opts, depth) {
|
|
|
494
432
|
if (!element.name) return "";
|
|
495
433
|
const name = element.name;
|
|
496
434
|
const attrStr = element.attributes ? writeAttributes(element.attributes, name, element, opts.attributeValueFn) : "";
|
|
435
|
+
if (element.raw !== void 0) return `<${name}${attrStr}>${element.raw}</${name}>`;
|
|
497
436
|
if (!((element.elements?.length ?? 0) > 0 || element.attributes?.["xml:space"] === "preserve" || opts.fullTagEmptyElement)) return `<${name}${attrStr}/>`;
|
|
498
437
|
const parts = [];
|
|
499
438
|
parts.push(`<${name}${attrStr}>`);
|
|
@@ -578,62 +517,26 @@ function writeDoctype(doctype) {
|
|
|
578
517
|
return `<!DOCTYPE ${doctype}>`;
|
|
579
518
|
}
|
|
580
519
|
//#endregion
|
|
581
|
-
//#region src/
|
|
520
|
+
//#region src/stringify-element.ts
|
|
582
521
|
/**
|
|
583
|
-
*
|
|
584
|
-
*
|
|
522
|
+
* Serialize an Element including its own opening/closing tag.
|
|
523
|
+
*
|
|
524
|
+
* `stringify` serializes only an element's children (it treats its input as
|
|
525
|
+
* a document root). Raw-XML round-trip of whole elements needs the element's
|
|
526
|
+
* own tag wrapped around its serialized children.
|
|
585
527
|
*/
|
|
586
|
-
function
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const element = {
|
|
594
|
-
type: "element",
|
|
595
|
-
name: tagName
|
|
596
|
-
};
|
|
597
|
-
if (value == null) return element;
|
|
598
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
599
|
-
element.elements = [{
|
|
600
|
-
type: "text",
|
|
601
|
-
text: String(value)
|
|
602
|
-
}];
|
|
603
|
-
return element;
|
|
604
|
-
}
|
|
605
|
-
if (Array.isArray(value)) {
|
|
606
|
-
const children = [];
|
|
607
|
-
for (const item of value) if (item && typeof item === "object" && "_attr" in item) element.attributes = item._attr;
|
|
608
|
-
else if (item && typeof item === "object") if (Object.keys(item)[0] === "_cdata") children.push({
|
|
609
|
-
type: "cdata",
|
|
610
|
-
cdata: String(item._cdata)
|
|
611
|
-
});
|
|
612
|
-
else children.push(toElement(item));
|
|
613
|
-
else if (item != null) children.push({
|
|
614
|
-
type: "text",
|
|
615
|
-
text: String(item)
|
|
616
|
-
});
|
|
617
|
-
if (children.length > 0) element.elements = children;
|
|
618
|
-
return element;
|
|
528
|
+
function stringifyElement(el) {
|
|
529
|
+
if (!el.name) return "";
|
|
530
|
+
let attrStr = "";
|
|
531
|
+
if (el.attributes) for (const key of Object.keys(el.attributes)) {
|
|
532
|
+
const v = el.attributes[key];
|
|
533
|
+
if (v === null || v === void 0) continue;
|
|
534
|
+
attrStr += ` ${key}="${escapeXml(String(v))}"`;
|
|
619
535
|
}
|
|
620
|
-
if (
|
|
621
|
-
|
|
622
|
-
if (obj._attr) element.attributes = obj._attr;
|
|
623
|
-
if (obj._cdata) element.elements = [{
|
|
624
|
-
type: "cdata",
|
|
625
|
-
cdata: String(obj._cdata)
|
|
626
|
-
}];
|
|
627
|
-
}
|
|
628
|
-
return element;
|
|
629
|
-
}
|
|
630
|
-
//#endregion
|
|
631
|
-
//#region src/json.ts
|
|
632
|
-
/** Convert XML string to JSON string — xml-js compatible export */
|
|
633
|
-
function xml2json(xml, options) {
|
|
634
|
-
return JSON.stringify(parse(xml, options));
|
|
536
|
+
if (!((el.elements?.length ?? 0) > 0 || el.attributes?.["xml:space"] === "preserve")) return `<${el.name}${attrStr}/>`;
|
|
537
|
+
return `<${el.name}${attrStr}>${stringify(el)}</${el.name}>`;
|
|
635
538
|
}
|
|
636
539
|
//#endregion
|
|
637
|
-
export { allChildren, attr, attrBool, attrMeasure, attrNum, attrs, attrsRaw, childCount, childText, children, collectText, colorAttr, element, escapeXml, findChild, findDeep, findFirst, hasChild, isNonEmpty,
|
|
540
|
+
export { OOXML_XML_DECLARATION, allChildren, attr, attrBool, attrMeasure, attrNum, attrs, attrsRaw, childCount, childText, children, collectText, colorAttr, element, escapeXml, findChild, findDeep, findFirst, hasChild, isNonEmpty, nativeTypeValue, parse, parseAttributes, selfCloseElement, stringify, stringifyElement, textOf, unescapeXml };
|
|
638
541
|
|
|
639
542
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["normalizeOptions","xml2js"],"sources":["../src/escape.ts","../src/serialize.ts","../src/parse.ts","../src/stringify.ts","../src/convert.ts","../src/json.ts"],"sourcesContent":["/** 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 // Manual scan avoids regex overhead; returning the original string reference\n // means zero allocation for the common case.\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c === 38 || c === 34 || c === 39 || c === 60 || c === 62) {\n // & \" ' < >\n // Slow path: slice-and-append avoids regex + temporary match objects.\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) + \"&\";\n last = j + 1;\n } else if (cj === 34) {\n s += str.slice(last, j) + \""\";\n last = j + 1;\n } else if (cj === 39) {\n s += str.slice(last, j) + \"'\";\n last = j + 1;\n } else if (cj === 60) {\n s += str.slice(last, j) + \"<\";\n last = j + 1;\n } else if (cj === 62) {\n s += str.slice(last, j) + \">\";\n last = j + 1;\n }\n }\n return s + str.slice(last);\n }\n }\n return str;\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\";\n\nconst DEFAULT_INDENT = \" \";\n\n/**\n * Serialize a Record-based XML object tree to an XML string.\n * @deprecated Use `stringify` (Element → string) instead. This object-tree path\n * will be removed once the Descriptor migration is complete.\n */\nexport function xml(\n input: Record<string, unknown> | Record<string, unknown>[],\n options?:\n | boolean\n | string\n | {\n indent?: boolean | string;\n declaration?: boolean | { encoding?: string; standalone?: string };\n },\n): string {\n const opts = normalizeOptions(options);\n const parts: string[] = [];\n\n if (opts.declaration) {\n const declOpts = opts.declaration === true ? {} : opts.declaration;\n const enc = declOpts.encoding || \"UTF-8\";\n const sa = declOpts.standalone;\n const declParts: string[] = [`<?xml version=\"1.0\" encoding=\"${enc}\"`];\n if (sa) declParts.push(` standalone=\"${sa}\"`);\n declParts.push(\"?>\");\n parts.push(declParts.join(\"\"));\n if (opts.indent) parts.push(\"\\n\");\n }\n\n const items = Array.isArray(input) ? input : [input];\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (!item) continue;\n const key = Object.keys(item)[0];\n if (!key) continue;\n parts.push(formatElement(key, item[key], opts.indent, 0));\n if (opts.indent && i < items.length - 1) parts.push(\"\\n\");\n }\n\n return parts.join(\"\");\n}\n\ntype XmlInputOptions = {\n indent?: boolean | string;\n declaration?: boolean | { encoding?: string; standalone?: string };\n};\n\nfunction normalizeOptions(options?: boolean | string | XmlInputOptions): {\n indent: string;\n declaration: XmlInputOptions[\"declaration\"];\n} {\n const opts =\n typeof options === \"object\" && !Array.isArray(options)\n ? options\n : { indent: options as boolean | string };\n let indent = \"\";\n if (opts.indent) {\n indent = opts.indent === true ? DEFAULT_INDENT : String(opts.indent);\n }\n return { indent, declaration: opts.declaration };\n}\n\n/**\n * Single-pass XML formatter: directly converts a Record-based XML object to string,\n * eliminating the intermediate ResolvedElement tree.\n */\nfunction formatElement(name: string, values: unknown, indent: string, depth: number): string {\n const attrParts: string[] = [];\n const textParts: string[] = [];\n const elemParts: string[] = [];\n let emptyArray = false;\n\n if (values == null) {\n const attrStr = attrParts.length ? \" \" + attrParts.join(\" \") : \"\";\n const ind = indent ? indent.repeat(depth) : \"\";\n return `${ind}<${name}${attrStr}/>`;\n }\n\n if (typeof values === \"object\") {\n const obj = values as Record<string, unknown>;\n if (obj._attr) {\n const attr = obj._attr as Record<string, unknown>;\n for (const key of Object.keys(attr)) {\n attrParts.push(`${key}=\"${escapeXml(String(attr[key]))}\"`);\n }\n }\n if (obj._attributes) {\n const attr = obj._attributes as Record<string, unknown>;\n for (const key of Object.keys(attr)) {\n attrParts.push(`${key}=\"${escapeXml(String(attr[key]))}\"`);\n }\n }\n if (obj._cdata) {\n const escaped = String(obj._cdata as string).replace(/\\]\\]>/g, \"]]]]><![CDATA[>\");\n textParts.push(`<![CDATA[${escaped}]]>`);\n }\n if (Array.isArray(values)) {\n if (values.length === 0) {\n emptyArray = true;\n } else {\n for (const value of values) {\n if (value && typeof value === \"object\" && \"_attr\" in value) {\n const attr = (value as Record<string, unknown>)._attr as Record<string, unknown>;\n for (const key of Object.keys(attr)) {\n attrParts.push(`${key}=\"${escapeXml(String(attr[key]))}\"`);\n }\n } else if (value && typeof value === \"object\" && \"_attributes\" in value) {\n const attr = (value as Record<string, unknown>)._attributes as Record<string, unknown>;\n for (const key of Object.keys(attr)) {\n attrParts.push(`${key}=\"${escapeXml(String(attr[key]))}\"`);\n }\n } else if (value && typeof value === \"object\") {\n const childKeys = Object.keys(value);\n const childKey = childKeys[0];\n if (childKey) {\n elemParts.push(\n formatElement(\n childKey,\n (value as Record<string, unknown>)[childKey],\n indent,\n depth + 1,\n ),\n );\n }\n } else if (value != null) {\n textParts.push(escapeXml(String(value)));\n }\n }\n }\n }\n } else {\n textParts.push(escapeXml(String(values as string | number | boolean)));\n }\n\n const ind = indent ? indent.repeat(depth) : \"\";\n const attrStr = attrParts.length ? \" \" + attrParts.join(\" \") : \"\";\n const totalParts = textParts.length + elemParts.length;\n\n if (totalParts === 0) {\n return emptyArray ? `${ind}<${name}${attrStr}></${name}>` : `${ind}<${name}${attrStr}/>`;\n }\n\n // Text-only optimization: single text child, no element children\n if (elemParts.length === 0 && textParts.length === 1) {\n return indent\n ? `${ind}<${name}${attrStr}>${textParts[0]}</${name}>`\n : `<${name}${attrStr}>${textParts[0]}</${name}>`;\n }\n\n // Mixed content\n const parts: string[] = [];\n parts.push(`${ind}<${name}${attrStr}>`);\n if (indent) parts.push(\"\\n\");\n const childIndent = indent ? indent.repeat(depth + 1) : \"\";\n for (const t of textParts) {\n parts.push(`${childIndent}${t}`);\n if (indent) parts.push(\"\\n\");\n }\n for (const e of elemParts) {\n parts.push(e);\n if (indent) parts.push(\"\\n\");\n }\n parts.push(`${ind}</${name}>`);\n return parts.join(\"\");\n}\n","import type { Element, Xml2JsOptions } from \"./types\";\n\nconst ENTITY_MAP: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \""\": '\"',\n \"'\": \"'\",\n};\n// Matches the five named entities plus numeric character references\n// (A decimal, B 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 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 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 const lower = value.toLowerCase();\n if (lower === \"true\") return true;\n if (lower === \"false\") return false;\n return value;\n}\n\nexport function parse(xmlString: string, options?: Xml2JsOptions): 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\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 addField(peek(stack), \"text\", text);\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 const attributes = parseAttributesFromXml(xmlString, pos);\n pos = attributes.pos;\n\n if (nativeTypeAttributes) {\n for (const key in attributes.attrs) {\n attributes.attrs[key] = nativeTypeValue(attributes.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 (Object.keys(attributes.attrs).length > 0) {\n element.attributes = 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 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\nfunction parseAttributesFromXml(\n str: string,\n start: number,\n): { attrs: Record<string, string>; pos: number } {\n const attrs: Record<string, string> = {};\n let i = start;\n const len = str.length;\n\n while (i < len) {\n while (i < len && isWhitespace(str.charCodeAt(i))) i++;\n if (i >= len || str.charCodeAt(i) === 0x3e || str.charCodeAt(i) === 0x2f) {\n break;\n }\n\n const nameStart = i;\n while (i < len && str.charCodeAt(i) !== 0x3d) {\n if (str.charCodeAt(i) === 0x3e || str.charCodeAt(i) === 0x2f) break;\n i++;\n }\n const name = str.slice(nameStart, i);\n\n if (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 attrs[name] = unescapeXml(str.slice(valueStart, i));\n i++;\n }\n\n return { attrs, pos: i };\n}\n\n/** @deprecated Use `parse` instead. xml-js compatible alias. */\nexport { parse as xml2js };\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","import { escapeXml } from \"./escape\";\nimport type { Element, Js2XmlOptions } from \"./types\";\n\nexport function stringify(js: Element, options?: Js2XmlOptions): 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\n/** @deprecated Use `stringify` instead. xml-js compatible alias. */\nexport { stringify as js2xml };\n\n/** @deprecated Use `stringify` instead. xml-js compatible alias. */\nexport function json2xml(json: Element, options?: Js2XmlOptions): string {\n return stringify(json, options);\n}\n\nfunction normalizeOptions(options?: Js2XmlOptions): {\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?: Js2XmlOptions[\"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?: Js2XmlOptions[\"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 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) + \"&\";\n last = j + 1;\n } else if (cj === 60) {\n s += str.slice(last, j) + \"<\";\n last = j + 1;\n } else if (cj === 62) {\n s += str.slice(last, j) + \">\";\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","import type { Element, Attributes } from \"./types\";\n\n/**\n * Convert XmlObject (node-xml format) directly to Element (xml-js format).\n * Eliminates the redundant xml() → xml2js() bridge path.\n */\nexport function toElement(xmlObject: Record<string, unknown>): Element {\n const tagName = Object.keys(xmlObject)[0];\n if (!tagName) {\n return { type: \"element\", name: \"\" };\n }\n const value = xmlObject[tagName];\n\n const element: Element = {\n type: \"element\",\n name: tagName,\n };\n\n if (value == null) {\n return element;\n }\n\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n element.elements = [{ type: \"text\", text: String(value) }];\n return element;\n }\n\n if (Array.isArray(value)) {\n const children: Element[] = [];\n for (const item of value) {\n if (item && typeof item === \"object\" && \"_attr\" in item) {\n element.attributes = item._attr as Attributes;\n } else if (item && typeof item === \"object\") {\n const childKeys = Object.keys(item);\n if (childKeys[0] === \"_cdata\") {\n children.push({ type: \"cdata\", cdata: String(item._cdata) });\n } else {\n children.push(toElement(item));\n }\n } else if (item != null) {\n children.push({ type: \"text\", text: String(item) });\n }\n }\n if (children.length > 0) {\n element.elements = children;\n }\n return element;\n }\n\n if (typeof value === \"object\" && value !== null) {\n const obj = value as { _attr?: Attributes; _cdata?: string };\n if (obj._attr) {\n element.attributes = obj._attr;\n }\n if (obj._cdata) {\n element.elements = [{ type: \"cdata\", cdata: String(obj._cdata) }];\n }\n }\n\n return element;\n}\n","import { xml2js } from \"./parse\";\nimport type { Xml2JsOptions } from \"./types\";\n\n/** Convert XML string to JSON string — xml-js compatible export */\nexport function xml2json(xml: string, options?: Xml2JsOptions): string {\n return JSON.stringify(xml2js(xml, options));\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 \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n \""\": '\"',\n \"'\": \"'\",\n};\n// Matches the five named entities plus numeric character references\n// (A decimal, B 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(\"&\");\n } else if (c === 34) {\n parts.push(\""\");\n } else if (c === 39) {\n parts.push(\"'\");\n } else if (c === 60) {\n parts.push(\"<\");\n } else if (c === 62) {\n parts.push(\">\");\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) + \"&\";\n last = j + 1;\n } else if (cj === 60) {\n s += str.slice(last, j) + \"<\";\n last = j + 1;\n } else if (cj === 62) {\n s += str.slice(last, j) + \">\";\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":";;AAEA,MAAM,aAAqC;CACzC,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,UAAU;AACZ;AAGA,MAAM,iBAAiB;AAEvB,SAAgB,YAAY,KAAqB;CAK/C,IAAI,IAAI,QAAQ,GAAG,MAAM,IAAI,OAAO;CACpC,OAAO,IAAI,QAAQ,iBAAiB,UAAU;EAC5C,IAAI,WAAW,WAAW,KAAA,GAAW,OAAO,WAAW;EAEvD,MAAM,OAAO,MAAM,MAAM,GAAG,EAAE;EAC9B,MAAM,OACJ,KAAK,OAAO,OAAO,KAAK,OAAO,MAAM,SAAS,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,SAAS,MAAM,EAAE;EACtF,OAAO,OAAO,SAAS,IAAI,KAAK,QAAQ,IAAI,OAAO,cAAc,IAAI,IAAI;CAC3E,CAAC;AACH;AAEA,SAAgB,gBAAgB,OAA0C;CACxE,IAAI,UAAU,IAAI,OAAO;CAKzB,MAAM,MAAM,MAAM,WAAW,CAAC,MAAM;CACpC,MAAM,QAAQ,MAAM,IAAI;CACxB,MAAM,SAAS,MAAM,SAAS;CAC9B,IAAI,SAAS,KAAK,UAAU;MAIb,MAAM,WAAW,KACvB,MAAM,MAAS,CAAC,OAAO,WAAW,GAAI;GAC3C,IAAI,IAAI;GACR,IAAI,YAAY;GAChB,KAAK,IAAI,IAAI,OAAO,IAAI,MAAM,QAAQ,KAAK;IACzC,MAAM,IAAI,MAAM,WAAW,CAAC;IAC5B,IAAI,IAAI,MAAQ,IAAI,IAAM;KACxB,YAAY;KACZ;IACF;IACA,IAAI,IAAI,MAAM,IAAI;GACpB;GACA,IAAI,WAAW,OAAO,MAAM,CAAC,IAAI;EACnC;;CAEF,MAAM,IAAI,OAAO,KAAK;CAItB,IAAI,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,OAAO,OAAO;CAI7C,IAAI,MAAM,WAAW,KAAK,MAAM,WAAW,GAAG;EAC5C,MAAM,QAAQ,MAAM,YAAY;EAChC,IAAI,UAAU,QAAQ,OAAO;EAC7B,IAAI,UAAU,SAAS,OAAO;CAChC;CACA,OAAO;AACT;AAEA,SAAgB,MAAM,WAAmB,SAAiC;CACxE,MAAM,gBAAgB,SAAS,gCAAgC;CAC/D,MAAM,OAAO,SAAS,QAAQ;CAC9B,MAAM,oBAAoB,SAAS,qBAAqB;CACxD,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,gBAAgB,SAAS,iBAAiB;CAChD,MAAM,cAAc,SAAS,eAAe;CAC5C,MAAM,gBAAgB,SAAS,iBAAiB;CAChD,MAAM,uBAAuB,SAAS,wBAAwB;CAG9D,MAAM,WACJ,SAAS,kBAAkB,KAAA,KAAa,QAAQ,cAAc,SAAS,IACnE,IAAI,IAAI,QAAQ,aAAa,IAC7B,KAAA;CAEN,MAAM,SAAkB,CAAC;CACzB,MAAM,QAAmB,CAAC,MAAM;CAEhC,IAAI,IAAI;CACR,MAAM,MAAM,UAAU;CAEtB,OAAO,IAAI,KAAK;EAId,IAAI,UAAU,WAAW,CAAC,MAAM,IAAc;GAC5C,MAAM,QAAQ;GACd,OAAO,IAAI,OAAO,UAAU,WAAW,CAAC,MAAM,IAAM;GACpD,IAAI,OAAO,YAAY,UAAU,MAAM,OAAO,CAAC,CAAC;GAChD,IAAI,MAAM,OAAO,KAAK,KAAK;GAC3B,IAAI,YAAY;GAChB,IAAI,KAAK,SAAS;QACZ,iBAAiB,KAAK,KAAK,CAAC,CAAC,SAAS,KAAK,kBAAkB,KAAK,GAAG;KAKvE,MAAM,SAAS,MAAM,MAAM,SAAS;KACpC,MAAM,WAAW,OAAO;KACxB,MAAM,OAAO,aAAa,KAAA,IAAY,KAAA,IAAY,SAAS,SAAS,SAAS;KAC7E,IAAI,SAAS,KAAA,KAAa,KAAK,SAAS,QACtC,KAAK,OAAQ,KAAK,OAAkB;UAC/B;MACL,MAAM,OAAgB;OAAE,MAAM;OAAQ;MAAK;MAC3C,IAAI,aAAa,KAAA,GACf,OAAO,WAAW,CAAC,IAAI;WAEvB,SAAS,KAAK,IAAI;KAEtB;IACF;;GAEF;EACF;EAEA;EAGA,IAAI,UAAU,WAAW,CAAC,MAAM,IAAc;GAC5C,MAAM,MAAM,UAAU,QAAQ,MAAM,IAAI,CAAC;GACzC,IAAI,QAAQ,IAAI;GAChB,MAAM,OAAO,UAAU,MAAM,IAAI,GAAG,GAAG;GACvC,IAAI,MAAM;GAEV,MAAM,WAAW,KAAK,MAAM,eAAe;GAC3C,IAAI;QACE,CAAC,mBAAmB;KACtB,IAAI,CAAC,OAAO,aACV,OAAO,cAAc,CAAC;KAExB,MAAM,QAAQ,gBAAgB,SAAS,MAAM,EAAE;KAC/C,IAAI,sBACF,KAAK,MAAM,OAAO,OAChB,MAAM,OAAO,gBAAgB,MAAM,IAAc;KAGrD,OAAO,YAAY,aAAa;IAClC;;GAEF;EACF;EAGA,IAAI,UAAU,WAAW,CAAC,MAAM,MAAQ,UAAU,MAAM,GAAG,IAAI,CAAC,MAAM,OAAO;GAC3E,MAAM,MAAM,UAAU,QAAQ,OAAO,IAAI,CAAC;GAC1C,IAAI,QAAQ,IAAI;GAChB,MAAM,UAAU,UAAU,MAAM,IAAI,GAAG,GAAG;GAC1C,IAAI,MAAM;GACV,IAAI,CAAC,eACH,IAAI,MAAM,SAAS,KAAK,KAAK,GAAG,WAAW,QAAQ,KAAK,CAAC;QACpD,SAAS,KAAK,KAAK,GAAG,WAAW,OAAO;GAE/C;EACF;EAGA,IAAI,UAAU,WAAW,CAAC,MAAM,MAAQ,UAAU,MAAM,GAAG,IAAI,CAAC,MAAM,YAAY;GAChF,MAAM,MAAM,UAAU,QAAQ,OAAO,IAAI,CAAC;GAC1C,IAAI,QAAQ,IAAI;GAChB,MAAM,QAAQ,UAAU,MAAM,IAAI,GAAG,GAAG;GACxC,IAAI,MAAM;GACV,IAAI,CAAC,aACH,IAAI,MAAM,SAAS,KAAK,KAAK,GAAG,SAAS,MAAM,KAAK,CAAC;QAChD,SAAS,KAAK,KAAK,GAAG,SAAS,KAAK;GAE3C;EACF;EAGA,IAAI,UAAU,WAAW,CAAC,MAAM,MAAQ,UAAU,MAAM,GAAG,IAAI,CAAC,MAAM,YAAY;GAChF,MAAM,MAAM,UAAU,QAAQ,KAAK,IAAI,CAAC;GACxC,IAAI,QAAQ,IAAI;GAChB,MAAM,UAAU,UAAU,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,KAAK;GACjD,IAAI,MAAM;GACV,IAAI,CAAC,eACH,SAAS,KAAK,KAAK,GAAG,WAAW,OAAO;GAE1C;EACF;EAGA,IAAI,UAAU,WAAW,CAAC,MAAM,IAAc;GAC5C,MAAM,MAAM,UAAU,QAAQ,KAAK,IAAI,CAAC;GACxC,IAAI,QAAQ,IAAI;GAChB,IAAI,MAAM;GACV,MAAM,IAAI;GACV;EACF;EAGA,MAAM,aAAa,eAAe,WAAW,CAAC;EAC9C,MAAM,UAAU,UAAU,MAAM,GAAG,UAAU;EAC7C,IAAI,MAAM;EAOV,IAAI;EACJ,OAAO,MAAM,KAAK;GAChB,OAAO,MAAM,OAAO,aAAa,UAAU,WAAW,GAAG,CAAC,GAAG;GAC7D,IAAI,OAAO,OAAO,UAAU,WAAW,GAAG,MAAM,MAAQ,UAAU,WAAW,GAAG,MAAM,IACpF;GAGF,MAAM,YAAY;GAClB,OAAO,MAAM,OAAO,UAAU,WAAW,GAAG,MAAM,IAAM;IACtD,IAAI,UAAU,WAAW,GAAG,MAAM,MAAQ,UAAU,WAAW,GAAG,MAAM,IAAM;IAC9E;GACF;GACA,MAAM,OAAO,UAAU,MAAM,WAAW,GAAG;GAE3C,IAAI,UAAU,WAAW,GAAG,MAAM,IAAM;GACxC;GAEA,OAAO,MAAM,OAAO,aAAa,UAAU,WAAW,GAAG,CAAC,GAAG;GAE7D,MAAM,QAAQ,UAAU,WAAW,GAAG;GACtC,IAAI,UAAU,MAAQ,UAAU,IAAM;GACtC;GACA,MAAM,aAAa;GACnB,OAAO,MAAM,OAAO,UAAU,WAAW,GAAG,MAAM,OAAO;GACzD,IAAI,UAAU,KAAA,GAAW,QAAQ,CAAC;GAClC,MAAM,QAAQ,YAAY,UAAU,MAAM,YAAY,GAAG,CAAC;GAC1D;EACF;EAEA,IAAI,SAAS,sBACX,KAAK,MAAM,OAAO,OAChB,MAAM,OAAO,gBAAgB,MAAM,IAAc;EAIrD,MAAM,gBAAgB,UAAU,WAAW,GAAG,MAAM;EACpD,IAAI,eAAe,OAAO;OACrB;EAEL,MAAM,UAAmB;GACvB,MAAM;GACN,MAAM;EACR;EACA,IAAI,OACF,QAAQ,aAAa;EAGvB,MAAM,SAAS,KAAK,KAAK;EACzB,IAAI,CAAC,OAAO,UACV,OAAO,WAAW,CAAC;EAErB,OAAO,SAAS,KAAK,OAAO;EAE5B,IAAI,CAAC,eAAe;GAClB,IAAI,aAAa,KAAA,KAAa,SAAS,IAAI,OAAO,GAAG;IAInD,MAAM,WAAW,KAAK,QAAQ;IAC9B,IAAI,QAAQ;IACZ,IAAI,OAAO;IACX,IAAI,WAAW;IACf,SAAS;KACP,WAAW,UAAU,QAAQ,UAAU,IAAI;KAC3C,IAAI,aAAa,IAAI;KACrB,IAAI,IAAI;KACR,SAAS;MACP,MAAM,UAAU,UAAU,QAAQ,IAAI,WAAW,CAAC;MAClD,IAAI,YAAY,MAAM,WAAW,UAAU;MAE3C,MAAM,QAAQ,UAAU,WAAW,UAAU,QAAQ,SAAS,CAAC;MAC/D,IACE,UAAU,MACV,UAAU,KACV,UAAU,MACV,UAAU,MACV,UAAU,MACV,UAAU,IAEV;MAEF,IAAI,UAAU,QAAQ,SAAS;KACjC;KACA,OAAO,WAAW,SAAS;KAC3B;KACA,IAAI,UAAU,GAAG;IACnB;IACA,IAAI,aAAa,IAAI;KACnB,QAAQ,MAAM,UAAU,MAAM,GAAG;KACjC,IAAI;IACN,OAAO;KACL,QAAQ,MAAM,UAAU,MAAM,KAAK,QAAQ;KAC3C,IAAI;IACN;IACA;GACF;GACA,MAAM,KAAK,OAAO;EACpB;EAEA,IAAI;CACN;CAEA,IAAI,OAAO,UAAU;EACnB,MAAM,OAAO,OAAO;EACpB,OAAO,OAAO;EACd,OAAO,WAAW;EAClB,OAAO,OAAO;CAChB;CAEA,OAAO;AACT;AAEA,SAAS,eAAe,KAAa,OAAuB;CAC1D,IAAI,IAAI;CACR,MAAM,MAAM,IAAI;CAChB,OAAO,IAAI,KAAK;EACd,MAAM,KAAK,IAAI,WAAW,CAAC;EAC3B,IAAI,OAAO,MAAQ,OAAO,KAAQ,OAAO,MAAQ,OAAO,MAAQ,OAAO,MAAQ,OAAO,IACpF,OAAO;EAET;CACF;CACA,OAAO;AACT;AAEA,SAAgB,gBAAgB,KAAqC;CACnE,MAAM,SAAiC,CAAC;CACxC,IAAI,IAAI;CACR,MAAM,MAAM,IAAI;CAEhB,OAAO,IAAI,KAAK;EACd,OAAO,IAAI,OAAO,aAAa,IAAI,WAAW,CAAC,CAAC,GAAG;EACnD,IAAI,KAAK,KAAK;EAEd,MAAM,YAAY;EAClB,OAAO,IAAI,OAAO,IAAI,WAAW,CAAC,MAAM,IAAM;GAC5C,IAAI,aAAa,IAAI,WAAW,CAAC,CAAC,GAAG;GACrC;EACF;EACA,MAAM,OAAO,IAAI,MAAM,WAAW,CAAC;EAEnC,OAAO,IAAI,OAAO,aAAa,IAAI,WAAW,CAAC,CAAC,GAAG;EACnD,IAAI,KAAK,OAAO,IAAI,WAAW,CAAC,MAAM,IAAM;EAC5C;EAEA,OAAO,IAAI,OAAO,aAAa,IAAI,WAAW,CAAC,CAAC,GAAG;EAEnD,MAAM,QAAQ,IAAI,WAAW,CAAC;EAC9B,IAAI,UAAU,MAAQ,UAAU,IAAM;EACtC;EACA,MAAM,aAAa;EACnB,OAAO,IAAI,OAAO,IAAI,WAAW,CAAC,MAAM,OAAO;EAC/C,OAAO,QAAQ,YAAY,IAAI,MAAM,YAAY,CAAC,CAAC;EACnD;CACF;CACA,OAAO;AACT;;;;;;;;;AAUA,SAAS,KAAK,OAA2B;CACvC,OAAO,MAAM,MAAM,SAAS;AAC9B;AAEA,SAAS,SAAS,QAAiB,MAAc,OAAe;CAC9D,IAAI,CAAC,OAAO,UACV,OAAO,WAAW,CAAC;CAKrB,IAAI,SAAS,UAAU,SAAS,SAAS;EACvC,MAAM,OAAO,OAAO,SAAS,OAAO,SAAS,SAAS;EACtD,IAAI,QAAQ,KAAK,SAAS,MAAM;GAC9B,MAAM,MAAM;GACZ,KAAK,OAAQ,KAAK,OAAkB;GACpC;EACF;CACF;CACA,MAAM,UAAmB,EAAE,KAAK;CAChC,QAAqC,QAAQ;CAC7C,OAAO,SAAS,KAAK,OAAO;AAC9B;;AAGA,SAAS,kBAAkB,OAA2B;CACpD,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;EAC1C,MAAM,OAAO,MAAM;EACnB,IAAI,CAAC,MAAM;EACX,MAAM,QAAQ,KAAK,aAAa;EAChC,IAAI,UAAU,KAAA,GAAW,OAAO,UAAU;CAC5C;CACA,OAAO;AACT;AAEA,SAAS,aAAa,IAAqB;CACzC,OAAO,OAAO,MAAQ,OAAO,KAAQ,OAAO,MAAQ,OAAO;AAC7D;;;AC/ZA,MAAM,eAAe;;AAGrB,SAAgB,UAAU,KAAqB;CAK7C,IAAI,CAAC,aAAa,KAAK,GAAG,GAAG,OAAO;CAEpC,MAAM,eAAe,IAAI,OAAO,YAAY;CAE5C,MAAM,QAAkB,CAAC,IAAI,MAAM,GAAG,YAAY,CAAC;CACnD,KAAK,IAAI,IAAI,cAAc,IAAI,IAAI,QAAQ,KAAK;EAC9C,MAAM,IAAI,IAAI,WAAW,CAAC;EAC1B,IAAI,MAAM,IACR,MAAM,KAAK,OAAO;OACb,IAAI,MAAM,IACf,MAAM,KAAK,QAAQ;OACd,IAAI,MAAM,IACf,MAAM,KAAK,QAAQ;OACd,IAAI,MAAM,IACf,MAAM,KAAK,MAAM;OACZ,IAAI,MAAM,IACf,MAAM,KAAK,MAAM;OAEjB,MAAM,KAAK,IAAI,OAAO,CAAC,CAAC;CAE5B;CACA,OAAO,MAAM,KAAK,EAAE;AACtB;;;;;;;;;;AAWA,SAAgB,MAAM,QAAuE;CAC3F,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,KAAK,MAAM,OAAO,QAAQ,MAAM,GAC1C,IAAI,MAAM,KAAA,GACR,MAAM,KAAK,IAAI,IAAI,IAAI,OAAO,MAAM,WAAW,UAAU,CAAC,IAAI,EAAE,EAAE;CAGtE,OAAO,MAAM,KAAK,EAAE;AACtB;;;;;;;;;;;;AAaA,SAAgB,SAAS,QAAuE;CAC9F,IAAI,IAAI;CACR,KAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,IAAI,OAAO;EACjB,IAAI,MAAM,KAAA,GACR,KAAK,IAAI,IAAI,IAAI,EAAE;CAEvB;CACA,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,KAAa,SAA0B;CACtE,OAAO,UAAU,IAAI,MAAM,QAAQ,MAAM,IAAI,IAAI;AACnD;;;;;;;;;;;;;;;;;AAkBA,SAAgB,QACd,MACA,YACA,UACQ;CACR,MAAM,UAAU,aAAa,MAAM,UAAU,IAAI,KAAA;CACjD,IAAI,CAAC,YAAY,SAAS,WAAW,GAAG,OAAO,iBAAiB,MAAM,OAAO;CAC7E,MAAM,OAAO,SAAS,KAAK,EAAE;CAC7B,OAAO,KAAK,WAAW,IACnB,iBAAiB,MAAM,OAAO,IAC9B,IAAI,OAAO,WAAW,GAAG,GAAG,KAAK,IAAI,KAAK;AAChD;;;AC1GA,SAAgB,UAAU,IAAa,SAAoC;CACzE,MAAM,OAAO,iBAAiB,OAAO;CACrC,MAAM,QAAkB,CAAC;CAEzB,IAAI,GAAG,eAAe,CAAC,KAAK,mBAC1B,MAAM,KAAK,iBAAiB,GAAG,WAAW,CAAC;CAG7C,IAAI,GAAG,UAAU,QACf,MAAM,KAAK,cAAc,GAAG,UAAU,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC;CAG/D,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,iBAAiB,SAWxB;CACA,IAAI,CAAC,SACH,OAAO;EACL,QAAQ;EACR,mBAAmB;EACnB,YAAY;EACZ,eAAe;EACf,aAAa;EACb,eAAe;EACf,qBAAqB;EACrB,YAAY;EACZ,aAAa;CACf;CAEF,IAAI,SAAS;CACb,IAAI,QAAQ,UAAU,MACpB,SAAS,OAAO,QAAQ,WAAW,WAAW,IAAI,OAAO,QAAQ,MAAM,IAAI,QAAQ;CAErF,OAAO;EACL;EACA,mBAAmB,QAAQ,qBAAqB;EAChD,YAAY,QAAQ,cAAc;EAClC,eAAe,QAAQ,iBAAiB;EACxC,aAAa,QAAQ,eAAe;EACpC,eAAe,QAAQ,iBAAiB;EACxC,qBAAqB,QAAQ,uBAAuB;EACpD,YAAY,QAAQ,cAAc;EAClC,aAAa,QAAQ,eAAe;EACpC,kBAAkB,QAAQ;CAC5B;AACF;AAEA,SAAS,iBAAiB,QAAgB,OAAe,WAA4B;CACnF,QAAQ,CAAC,aAAa,SAAS,OAAO,MAAM,OAAO,OAAO,KAAK;AACjE;AAEA,SAAS,iBAAiB,aAA0D;CAClF,MAAM,QAAQ,YAAY;CAC1B,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,QAAkB,CAAC,qBAAqB;CAC9C,IAAI,MAAM,UAAU,MAAM,KAAK,cAAc,MAAM,SAAS,EAAE;CAC9D,IAAI,MAAM,YAAY,MAAM,KAAK,gBAAgB,MAAM,WAAW,EAAE;CACpE,OAAO,MAAM,KAAK,EAAE,IAAI;AAC1B;AAEA,SAAS,gBACP,YACA,aACA,SACA,kBACQ;CACR,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,OAAO,OAAO,KAAK,UAAU,GAAG;EACzC,MAAM,QAAQ,WAAW;EACzB,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW;EAI3C,MAAM,MAAM,OAAO,KAAK;EACxB,MAAM,OAAO,mBACT,iBAAiB,KAAK,KAAK,aAAa,OAAO,IAC/C,UAAU,GAAG;EACjB,MAAM,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE;CAChC;CACA,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,aACP,SACA,MACA,OACQ;CACR,IAAI,CAAC,QAAQ,MAAM,OAAO;CAC1B,MAAM,OAAO,QAAQ;CACrB,MAAM,UAAU,QAAQ,aACpB,gBAAgB,QAAQ,YAAY,MAAM,SAAS,KAAK,gBAAgB,IACxE;CAGJ,IAAI,QAAQ,QAAQ,KAAA,GAClB,OAAO,IAAI,OAAO,QAAQ,GAAG,QAAQ,IAAI,IAAI,KAAK;CAOpD,IAAI,GAJD,QAAQ,UAAU,UAAU,KAAK,KAClC,QAAQ,aAAa,iBAAiB,cACtC,KAAK,sBAGL,OAAO,IAAI,OAAO,QAAQ;CAG5B,MAAM,QAAkB,CAAC;CACzB,MAAM,KAAK,IAAI,OAAO,QAAQ,EAAE;CAChC,MAAM,mBAAmB,QAAQ,UAAU,MAAM,MAAM,EAAE,SAAS,SAAS,KAAK;CAChF,IAAI,QAAQ,UAAU,QACpB,MAAM,KAAK,cAAc,QAAQ,UAAU,MAAM,QAAQ,GAAG,KAAK,CAAC;CAEpE,IAAI,KAAK,UAAU,kBACjB,MAAM,KAAK,OAAO,KAAK,OAAO,OAAO,KAAK,CAAC;CAE7C,MAAM,KAAK,KAAK,KAAK,EAAE;CACvB,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,cACP,UACA,MACA,OACA,WACQ;CACR,MAAM,QAAkB,CAAC;CACzB,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,SAAS;EACd,MAAM,UAAU,aAAa,MAAM;EACnC,QAAQ,QAAQ,MAAhB;GACE,KAAK;IACH,MAAM,KAAK,iBAAiB,KAAK,QAAQ,OAAO,OAAO,CAAC;IACxD,MAAM,KAAK,aAAa,SAAS,MAAM,KAAK,CAAC;IAC7C;GACF,KAAK;IACH,IAAI,KAAK,YAAY;IACrB,IAAI,KAAK,YAAY,MAAM,KAAK,iBAAiB,KAAK,QAAQ,OAAO,OAAO,CAAC;IAC7E,MAAM,KAAK,UAAU,QAAQ,IAAI,CAAC;IAClC;GACF,KAAK;IACH,IAAI,KAAK,aAAa;IACtB,IAAI,KAAK,aAAa,MAAM,KAAK,iBAAiB,KAAK,QAAQ,OAAO,OAAO,CAAC;IAC9E,MAAM,KAAK,WAAW,QAAQ,KAAK,CAAC;IACpC;GACF,KAAK;IACH,IAAI,KAAK,eAAe;IACxB,MAAM,KAAK,iBAAiB,KAAK,QAAQ,OAAO,OAAO,CAAC;IACxD,MAAM,KAAK,aAAa,QAAQ,OAAO,CAAC;IACxC;GACF,KAAK;IACH,IAAI,KAAK,eAAe;IACxB,MAAM,KAAK,iBAAiB,KAAK,QAAQ,OAAO,OAAO,CAAC;IACxD,MAAM,KAAK,aAAa,QAAQ,OAAO,CAAC;IACxC;GACF,SACE;EACJ;CACF;CACA,OAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,UAAU,MAA4D;CAC7E,IAAI,QAAQ,MAAM,OAAO;CACzB,MAAM,MAAM,OAAO,IAAI;CAEvB,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;EACnC,MAAM,IAAI,IAAI,WAAW,CAAC;EAC1B,IAAI,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI;GAEpC,IAAI,IAAI;GACR,IAAI,OAAO;GACX,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;IACnC,MAAM,KAAK,IAAI,WAAW,CAAC;IAC3B,IAAI,OAAO,IAAI;KACb,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI;KAC1B,OAAO,IAAI;IACb,OAAO,IAAI,OAAO,IAAI;KACpB,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI;KAC1B,OAAO,IAAI;IACb,OAAO,IAAI,OAAO,IAAI;KACpB,KAAK,IAAI,MAAM,MAAM,CAAC,IAAI;KAC1B,OAAO,IAAI;IACb;GACF;GACA,OAAO,IAAI,IAAI,MAAM,IAAI;EAC3B;CACF;CACA,OAAO;AACT;AAEA,SAAS,WAAW,OAA0C;CAC5D,IAAI,SAAS,MAAM,OAAO;CAE1B,OAAO,YADS,MAAM,QAAQ,UAAU,iBACf,EAAE;AAC7B;AAEA,SAAS,aAAa,SAA4C;CAChE,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO,OAAO,QAAQ;AACxB;AAEA,SAAS,aAAa,SAA4C;CAChE,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO,aAAa,QAAQ;AAC9B;;;;;;;;;;ACjNA,SAAgB,iBAAiB,IAAqB;CACpD,IAAI,CAAC,GAAG,MAAM,OAAO;CACrB,IAAI,UAAU;CACd,IAAI,GAAG,YACL,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,UAAU,GAAG;EAC5C,MAAM,IAAI,GAAG,WAAW;EACxB,IAAI,MAAM,QAAQ,MAAM,KAAA,GAAW;EACnC,WAAW,IAAI,IAAI,IAAI,UAAU,OAAO,CAAC,CAAC,EAAE;CAC9C;CAIF,IAAI,GADD,GAAG,UAAU,UAAU,KAAK,KAAK,GAAG,aAAa,iBAAiB,aAChD,OAAO,IAAI,GAAG,OAAO,QAAQ;CAClD,OAAO,IAAI,GAAG,OAAO,QAAQ,GAAGA,UAAkB,EAAE,EAAE,IAAI,GAAG,KAAK;AACpE"}
|
|
@@ -20,6 +20,7 @@ interface Element {
|
|
|
20
20
|
type?: string;
|
|
21
21
|
name?: string;
|
|
22
22
|
elements?: Element[];
|
|
23
|
+
raw?: string;
|
|
23
24
|
parent?: Element;
|
|
24
25
|
}
|
|
25
26
|
interface ElementCompact {
|
|
@@ -45,7 +46,7 @@ interface IgnoreOptions {
|
|
|
45
46
|
ignoreDoctype?: boolean;
|
|
46
47
|
ignoreText?: boolean;
|
|
47
48
|
}
|
|
48
|
-
interface
|
|
49
|
+
interface ParseOptions extends IgnoreOptions {
|
|
49
50
|
compact?: boolean;
|
|
50
51
|
trim?: boolean;
|
|
51
52
|
sanitize?: boolean;
|
|
@@ -56,6 +57,7 @@ interface Xml2JsOptions extends IgnoreOptions {
|
|
|
56
57
|
alwaysChildren?: boolean;
|
|
57
58
|
instructionHasAttributes?: boolean;
|
|
58
59
|
captureSpacesBetweenElements?: boolean;
|
|
60
|
+
deferElements?: string[];
|
|
59
61
|
doctypeFn?: (value: string, parentElement: object) => string;
|
|
60
62
|
instructionFn?: (value: string, instructionName: string, parentElement: string) => string;
|
|
61
63
|
cdataFn?: (value: string, parentElement: object) => string;
|
|
@@ -67,7 +69,7 @@ interface Xml2JsOptions extends IgnoreOptions {
|
|
|
67
69
|
attributeValueFn?: (attributeValue: string, attributeName: string, parentElement: string) => string;
|
|
68
70
|
attributesFn?: (value: Attributes, parentElement: string) => Attributes;
|
|
69
71
|
}
|
|
70
|
-
interface
|
|
72
|
+
interface StringifyOptions extends IgnoreOptions {
|
|
71
73
|
spaces?: number | string;
|
|
72
74
|
compact?: boolean;
|
|
73
75
|
indentText?: boolean;
|
|
@@ -88,40 +90,9 @@ interface Js2XmlOptions extends IgnoreOptions {
|
|
|
88
90
|
attributesFn?: (value: Attributes, currentElementName: string, currentElementObj: object) => Attributes;
|
|
89
91
|
fullTagEmptyElementFn?: (currentElementName: string, currentElementObj: object) => boolean;
|
|
90
92
|
}
|
|
91
|
-
interface XmlOption {
|
|
92
|
-
indent?: string;
|
|
93
|
-
stream?: boolean;
|
|
94
|
-
declaration?: boolean | {
|
|
95
|
-
encoding?: string;
|
|
96
|
-
standalone?: string;
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
interface XmlAttrs {
|
|
100
|
-
[attr: string]: XmlAtom;
|
|
101
|
-
}
|
|
102
|
-
type XmlAtom = string | number | boolean | null;
|
|
103
|
-
interface ElementObject {
|
|
104
|
-
push(xmlObject: XmlObject): void;
|
|
105
|
-
close(xmlObject?: XmlObject): void;
|
|
106
|
-
}
|
|
107
|
-
type XmlDesc = {
|
|
108
|
-
_attr: XmlAttrs;
|
|
109
|
-
} | {
|
|
110
|
-
_cdata: string;
|
|
111
|
-
} | {
|
|
112
|
-
_attr: XmlAttrs;
|
|
113
|
-
_cdata: string;
|
|
114
|
-
} | XmlAtom | XmlAtom[] | XmlDescArray;
|
|
115
|
-
interface XmlDescArray {
|
|
116
|
-
[index: number]: {
|
|
117
|
-
_attr: XmlAttrs;
|
|
118
|
-
} | XmlObject;
|
|
119
|
-
}
|
|
120
|
-
type XmlObject = {
|
|
121
|
-
[tag: string]: ElementObject | XmlDesc;
|
|
122
|
-
} | XmlDesc;
|
|
123
93
|
//#endregion
|
|
124
94
|
//#region src/utils.d.ts
|
|
95
|
+
declare const OOXML_XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
125
96
|
type NonEmptyArray<T> = readonly [T, ...ReadonlyArray<T>];
|
|
126
97
|
declare const isNonEmpty: <T>(arr: readonly T[]) => arr is NonEmptyArray<T>;
|
|
127
98
|
declare function findChild(parent: Element | undefined, name: string): Element | undefined;
|
|
@@ -140,5 +111,5 @@ declare function findDeep(parent: Element | undefined, name: string): Element[];
|
|
|
140
111
|
declare function findFirst(parent: Element | undefined, name: string): Element | undefined;
|
|
141
112
|
declare function childCount(parent: Element | undefined): number;
|
|
142
113
|
//#endregion
|
|
143
|
-
export {
|
|
144
|
-
//# sourceMappingURL=utils-
|
|
114
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-CVZp5dCd.d.mts","names":[],"sources":["../src/types.ts","../src/utils.ts"],"mappings":"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"}
|
package/dist/utils.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
export { NonEmptyArray, allChildren, attr, attrBool, attrMeasure, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, findFirst, hasChild, isNonEmpty, textOf };
|
|
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";
|
|
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
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
//#region src/utils.ts
|
|
2
2
|
/**
|
|
3
|
+
* Standard OOXML XML declaration used in all Office Open XML documents.
|
|
4
|
+
*
|
|
5
|
+
* The declaration specifies UTF-8 encoding and standalone="yes" as required
|
|
6
|
+
* by the OOXML specification. All XML parts in .docx, .pptx, and .xlsx files
|
|
7
|
+
* use this declaration.
|
|
8
|
+
*/
|
|
9
|
+
const OOXML_XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
|
|
10
|
+
/**
|
|
3
11
|
* User-defined type guard narrowing an array to {@link NonEmptyArray}.
|
|
4
12
|
* Prefer this over a bare `arr.length > 0` check, which TypeScript does
|
|
5
13
|
* not reliably narrow into a non-empty tuple at the read site.
|
|
@@ -101,8 +109,8 @@ function attrBool(element, name) {
|
|
|
101
109
|
if (v === void 0) return void 0;
|
|
102
110
|
if (typeof v === "boolean") return v;
|
|
103
111
|
const lower = String(v).toLowerCase();
|
|
104
|
-
if (lower === "true" || lower === "1") return true;
|
|
105
|
-
if (lower === "false" || lower === "0") return false;
|
|
112
|
+
if (lower === "true" || lower === "1" || lower === "on") return true;
|
|
113
|
+
if (lower === "false" || lower === "0" || lower === "off") return false;
|
|
106
114
|
}
|
|
107
115
|
/**
|
|
108
116
|
* Get a hex color attribute, handling nativeTypeValue coercion.
|
|
@@ -159,6 +167,6 @@ function childCount(parent) {
|
|
|
159
167
|
return parent?.elements?.length ?? 0;
|
|
160
168
|
}
|
|
161
169
|
//#endregion
|
|
162
|
-
export { allChildren, attr, attrBool, attrMeasure, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, findFirst, hasChild, isNonEmpty, textOf };
|
|
170
|
+
export { OOXML_XML_DECLARATION, allChildren, attr, attrBool, attrMeasure, attrNum, childCount, childText, children, collectText, colorAttr, findChild, findDeep, findFirst, hasChild, isNonEmpty, textOf };
|
|
163
171
|
|
|
164
172
|
//# sourceMappingURL=utils.mjs.map
|
package/dist/utils.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import type { Element } from \"./types\";\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\") return true;\n if (lower === \"false\" || lower === \"0\") 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":";;;;;;
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@office-open/xml",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "XML parsing and serialization for Office Open XML
|
|
3
|
+
"version": "0.11.0",
|
|
4
|
+
"description": "Zero-dependency XML parsing and serialization for Office Open XML — drop-in replacement for xml and xml-js",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"drop-in",
|
|
6
7
|
"office-open",
|
|
7
8
|
"ooxml",
|
|
8
|
-
"
|
|
9
|
-
"serialize",
|
|
9
|
+
"openxml",
|
|
10
10
|
"xml",
|
|
11
|
+
"xml-js",
|
|
12
|
+
"xml-parser",
|
|
13
|
+
"xml-serializer",
|
|
11
14
|
"zero-dependencies"
|
|
12
15
|
],
|
|
13
16
|
"homepage": "https://www.office-open.com",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils-BFKTfRa8.d.mts","names":[],"sources":["../src/types.ts","../src/utils.ts"],"mappings":"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"}
|