@office-open/xml 0.10.15 → 0.12.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 +46 -89
- package/dist/index.d.mts +6 -20
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +323 -379
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-BFKTfRa8.d.mts → utils-qhk6IlD8.d.mts} +8 -36
- package/dist/utils-qhk6IlD8.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 +8 -4
- package/dist/utils-BFKTfRa8.d.mts.map +0 -1
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,12 @@ 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;
|
|
59
|
+
const nsTable = options?.normalizeNamespaces !== void 0 ? new Map(Object.entries(options.normalizeNamespaces)) : void 0;
|
|
60
|
+
const nsStack = [{
|
|
61
|
+
prefixes: void 0,
|
|
62
|
+
defaultCanonical: void 0
|
|
63
|
+
}];
|
|
233
64
|
const result = {};
|
|
234
65
|
const stack = [result];
|
|
235
66
|
let i = 0;
|
|
@@ -237,12 +68,26 @@ function parse(xmlString, options) {
|
|
|
237
68
|
while (i < len) {
|
|
238
69
|
if (xmlString.charCodeAt(i) !== 60) {
|
|
239
70
|
const start = i;
|
|
240
|
-
|
|
71
|
+
const lt = xmlString.indexOf("<", i);
|
|
72
|
+
i = lt === -1 ? len : lt;
|
|
241
73
|
let text = unescapeXml(xmlString.slice(start, i));
|
|
242
74
|
if (trim) text = text.trim();
|
|
243
75
|
if (ignoreText) continue;
|
|
244
76
|
if (text.length > 0) {
|
|
245
|
-
if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack))
|
|
77
|
+
if (captureSpaces || text.trim().length > 0 || isPreserveContext(stack)) {
|
|
78
|
+
const parent = stack[stack.length - 1];
|
|
79
|
+
const elements = parent.elements;
|
|
80
|
+
const last = elements === void 0 ? void 0 : elements[elements.length - 1];
|
|
81
|
+
if (last !== void 0 && last.type === "text") last.text = last.text + text;
|
|
82
|
+
else {
|
|
83
|
+
const node = {
|
|
84
|
+
type: "text",
|
|
85
|
+
text
|
|
86
|
+
};
|
|
87
|
+
if (elements === void 0) parent.elements = [node];
|
|
88
|
+
else elements.push(node);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
246
91
|
}
|
|
247
92
|
continue;
|
|
248
93
|
}
|
|
@@ -268,8 +113,10 @@ function parse(xmlString, options) {
|
|
|
268
113
|
if (end === -1) break;
|
|
269
114
|
const comment = xmlString.slice(i + 3, end);
|
|
270
115
|
i = end + 3;
|
|
271
|
-
if (!ignoreComment)
|
|
272
|
-
|
|
116
|
+
if (!ignoreComment) {
|
|
117
|
+
if (trim) addField(peek(stack), "comment", comment.trim());
|
|
118
|
+
else addField(peek(stack), "comment", comment);
|
|
119
|
+
}
|
|
273
120
|
continue;
|
|
274
121
|
}
|
|
275
122
|
if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 8) === "![CDATA[") {
|
|
@@ -277,8 +124,10 @@ function parse(xmlString, options) {
|
|
|
277
124
|
if (end === -1) break;
|
|
278
125
|
const cdata = xmlString.slice(i + 8, end);
|
|
279
126
|
i = end + 3;
|
|
280
|
-
if (!ignoreCdata)
|
|
281
|
-
|
|
127
|
+
if (!ignoreCdata) {
|
|
128
|
+
if (trim) addField(peek(stack), "cdata", cdata.trim());
|
|
129
|
+
else addField(peek(stack), "cdata", cdata);
|
|
130
|
+
}
|
|
282
131
|
continue;
|
|
283
132
|
}
|
|
284
133
|
if (xmlString.charCodeAt(i) === 33 && xmlString.slice(i, i + 9) === "!DOCTYPE") {
|
|
@@ -294,26 +143,136 @@ function parse(xmlString, options) {
|
|
|
294
143
|
if (end === -1) break;
|
|
295
144
|
i = end + 1;
|
|
296
145
|
stack.pop();
|
|
146
|
+
if (nsTable !== void 0) nsStack.pop();
|
|
297
147
|
continue;
|
|
298
148
|
}
|
|
299
149
|
const tagNameEnd = findTagNameEnd(xmlString, i);
|
|
300
150
|
const tagName = xmlString.slice(i, tagNameEnd);
|
|
301
151
|
let pos = tagNameEnd;
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
152
|
+
const nsParent = nsTable !== void 0 ? nsStack[nsStack.length - 1] : void 0;
|
|
153
|
+
let nsPrefixes = nsParent?.prefixes;
|
|
154
|
+
let nsDefault = nsParent?.defaultCanonical;
|
|
155
|
+
let nsDeclared = false;
|
|
156
|
+
let attrs;
|
|
157
|
+
while (pos < len) {
|
|
158
|
+
while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;
|
|
159
|
+
if (pos >= len || xmlString.charCodeAt(pos) === 62 || xmlString.charCodeAt(pos) === 47) break;
|
|
160
|
+
const nameStart = pos;
|
|
161
|
+
while (pos < len && xmlString.charCodeAt(pos) !== 61) {
|
|
162
|
+
if (xmlString.charCodeAt(pos) === 62 || xmlString.charCodeAt(pos) === 47) break;
|
|
163
|
+
pos++;
|
|
164
|
+
}
|
|
165
|
+
const name = xmlString.slice(nameStart, pos).trim();
|
|
166
|
+
if (xmlString.charCodeAt(pos) !== 61) break;
|
|
167
|
+
pos++;
|
|
168
|
+
while (pos < len && isWhitespace(xmlString.charCodeAt(pos))) pos++;
|
|
169
|
+
const quote = xmlString.charCodeAt(pos);
|
|
170
|
+
if (quote !== 34 && quote !== 39) break;
|
|
171
|
+
pos++;
|
|
172
|
+
const valueStart = pos;
|
|
173
|
+
while (pos < len && xmlString.charCodeAt(pos) !== quote) pos++;
|
|
174
|
+
const value = unescapeXml(xmlString.slice(valueStart, pos));
|
|
175
|
+
pos++;
|
|
176
|
+
if (nsTable !== void 0 && (name === "xmlns" || name.startsWith("xmlns:"))) {
|
|
177
|
+
if (attrs === void 0) attrs = {};
|
|
178
|
+
const canonical = nsTable.get(value);
|
|
179
|
+
if (name === "xmlns") {
|
|
180
|
+
nsDefault = canonical;
|
|
181
|
+
if (canonical !== void 0 && canonical !== "") attrs[`xmlns:${canonical}`] = value;
|
|
182
|
+
else attrs[name] = value;
|
|
183
|
+
} else {
|
|
184
|
+
const prefix = name.slice(6);
|
|
185
|
+
if (canonical !== void 0 && canonical !== "") attrs[`xmlns:${canonical}`] = value;
|
|
186
|
+
else if (canonical === "" && !("xmlns" in attrs)) attrs.xmlns = value;
|
|
187
|
+
else attrs[name] = value;
|
|
188
|
+
if (!nsDeclared) {
|
|
189
|
+
nsPrefixes = new Map(nsPrefixes ?? []);
|
|
190
|
+
nsDeclared = true;
|
|
191
|
+
}
|
|
192
|
+
nsPrefixes.set(prefix, canonical ?? prefix);
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (attrs === void 0) attrs = {};
|
|
197
|
+
attrs[name] = value;
|
|
198
|
+
}
|
|
199
|
+
if (attrs && nativeTypeAttributes) for (const key in attrs) attrs[key] = nativeTypeValue(attrs[key]);
|
|
200
|
+
let finalName = tagName;
|
|
201
|
+
if (nsTable !== void 0) {
|
|
202
|
+
const colon = tagName.indexOf(":");
|
|
203
|
+
if (colon > 0) {
|
|
204
|
+
const p = tagName.slice(0, colon);
|
|
205
|
+
if (p !== "xml") {
|
|
206
|
+
const c = nsPrefixes?.get(p);
|
|
207
|
+
if (c !== void 0 && c !== p) finalName = c === "" ? tagName.slice(colon + 1) : c + tagName.slice(colon);
|
|
208
|
+
}
|
|
209
|
+
} else if (colon < 0 && nsDefault !== void 0 && nsDefault !== "") finalName = nsDefault + ":" + tagName;
|
|
210
|
+
if (attrs !== void 0 && nsPrefixes !== void 0) for (const key of Object.keys(attrs)) {
|
|
211
|
+
if (key === "xmlns" || key.startsWith("xmlns:")) continue;
|
|
212
|
+
const kcolon = key.indexOf(":");
|
|
213
|
+
if (kcolon <= 0) continue;
|
|
214
|
+
const p = key.slice(0, kcolon);
|
|
215
|
+
if (p === "xml") continue;
|
|
216
|
+
const c = nsPrefixes.get(p);
|
|
217
|
+
if (c !== void 0 && c !== p) {
|
|
218
|
+
const renamed = c === "" ? key.slice(kcolon + 1) : c + key.slice(kcolon);
|
|
219
|
+
const attrValue = attrs[key];
|
|
220
|
+
if (attrValue !== void 0) attrs[renamed] = attrValue;
|
|
221
|
+
delete attrs[key];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
305
225
|
const isSelfClosing = xmlString.charCodeAt(pos) === 47;
|
|
306
226
|
if (isSelfClosing) pos += 2;
|
|
307
227
|
else pos++;
|
|
308
228
|
const element = {
|
|
309
229
|
type: "element",
|
|
310
|
-
name:
|
|
230
|
+
name: finalName,
|
|
231
|
+
attributes: attrs,
|
|
232
|
+
elements: void 0
|
|
311
233
|
};
|
|
312
|
-
if (Object.keys(attributes.attrs).length > 0) element.attributes = attributes.attrs;
|
|
313
234
|
const parent = peek(stack);
|
|
314
235
|
if (!parent.elements) parent.elements = [];
|
|
315
236
|
parent.elements.push(element);
|
|
316
|
-
if (!isSelfClosing)
|
|
237
|
+
if (!isSelfClosing) {
|
|
238
|
+
if (deferSet !== void 0 && deferSet.has(tagName)) {
|
|
239
|
+
const closeTag = `</${tagName}>`;
|
|
240
|
+
let depth = 1;
|
|
241
|
+
let scan = pos;
|
|
242
|
+
let closeIdx = -1;
|
|
243
|
+
for (;;) {
|
|
244
|
+
closeIdx = xmlString.indexOf(closeTag, scan);
|
|
245
|
+
if (closeIdx === -1) break;
|
|
246
|
+
let p = scan;
|
|
247
|
+
for (;;) {
|
|
248
|
+
const openIdx = xmlString.indexOf(`<${tagName}`, p);
|
|
249
|
+
if (openIdx === -1 || openIdx >= closeIdx) break;
|
|
250
|
+
const after = xmlString.charCodeAt(openIdx + tagName.length + 1);
|
|
251
|
+
if (after === 32 || after === 9 || after === 10 || after === 13 || after === 47 || after === 62) depth++;
|
|
252
|
+
p = openIdx + tagName.length + 1;
|
|
253
|
+
}
|
|
254
|
+
scan = closeIdx + closeTag.length;
|
|
255
|
+
depth--;
|
|
256
|
+
if (depth === 0) break;
|
|
257
|
+
}
|
|
258
|
+
if (closeIdx === -1) {
|
|
259
|
+
element.raw = xmlString.slice(pos);
|
|
260
|
+
i = len;
|
|
261
|
+
} else {
|
|
262
|
+
element.raw = xmlString.slice(pos, closeIdx);
|
|
263
|
+
i = scan;
|
|
264
|
+
}
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
stack.push(element);
|
|
268
|
+
if (nsTable !== void 0) {
|
|
269
|
+
const layerChanged = nsDeclared || nsDefault !== nsParent.defaultCanonical;
|
|
270
|
+
nsStack.push(layerChanged ? {
|
|
271
|
+
prefixes: nsPrefixes,
|
|
272
|
+
defaultCanonical: nsDefault
|
|
273
|
+
} : nsParent);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
317
276
|
i = pos;
|
|
318
277
|
}
|
|
319
278
|
if (result.elements) {
|
|
@@ -334,35 +293,6 @@ function findTagNameEnd(str, start) {
|
|
|
334
293
|
}
|
|
335
294
|
return i;
|
|
336
295
|
}
|
|
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
296
|
function parseAttributes(str) {
|
|
367
297
|
const result = {};
|
|
368
298
|
let i = 0;
|
|
@@ -429,7 +359,83 @@ function isWhitespace(ch) {
|
|
|
429
359
|
return ch === 32 || ch === 9 || ch === 10 || ch === 13;
|
|
430
360
|
}
|
|
431
361
|
//#endregion
|
|
362
|
+
//#region src/escape.ts
|
|
363
|
+
const XML_SPECIALS = /[&"'<>]/;
|
|
364
|
+
/** Escape text content for XML. Fast path returns original string when no special chars. */
|
|
365
|
+
function escapeXml(str) {
|
|
366
|
+
if (!XML_SPECIALS.test(str)) return str;
|
|
367
|
+
return str.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Build an XML attribute string fragment from a record.
|
|
371
|
+
* `undefined` values are automatically skipped.
|
|
372
|
+
* String values are escaped via `escapeXml`. Booleans serialize as 0/1 —
|
|
373
|
+
* the spelling Office itself writes for xsd:boolean attributes and
|
|
374
|
+
* ST_OnOff unions alike.
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* attrs({ id: 1, name: "foo", hidden: undefined })
|
|
378
|
+
* // => ' id="1" name="foo"'
|
|
379
|
+
*/
|
|
380
|
+
function attrs(record) {
|
|
381
|
+
const parts = [];
|
|
382
|
+
for (const [key, v] of Object.entries(record)) if (v !== void 0) {
|
|
383
|
+
const value = typeof v === "string" ? escapeXml(v) : typeof v === "boolean" ? v ? 1 : 0 : v;
|
|
384
|
+
parts.push(` ${key}="${value}"`);
|
|
385
|
+
}
|
|
386
|
+
return parts.join("");
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Build an XML attribute string without escaping.
|
|
390
|
+
*
|
|
391
|
+
* Same as `attrs()` but skips `typeof` checks and `escapeXml` — use only when
|
|
392
|
+
* all values are known-safe (numbers, booleans, or strings free of `& " ' < >`).
|
|
393
|
+
* Avoids per-call array and `Object.keys()` allocation in hot loops.
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* attrsRaw({ r: "A1", s: 5 })
|
|
397
|
+
* // => ' r="A1" s="5"'
|
|
398
|
+
*/
|
|
399
|
+
function attrsRaw(record) {
|
|
400
|
+
let s = "";
|
|
401
|
+
for (const key in record) {
|
|
402
|
+
const v = record[key];
|
|
403
|
+
if (v !== void 0) s += ` ${key}="${v}"`;
|
|
404
|
+
}
|
|
405
|
+
return s;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Build a self-closing XML element: `<tag attrStr/>`.
|
|
409
|
+
* `attrStr` is a pre-serialized attribute string (from `attrs()`) or undefined.
|
|
410
|
+
*/
|
|
411
|
+
function selfCloseElement(tag, attrStr) {
|
|
412
|
+
return attrStr ? `<${tag}${attrStr}/>` : `<${tag}/>`;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Build a complete XML element string from name, optional attributes, and string children.
|
|
416
|
+
*
|
|
417
|
+
* Replaces `new BuilderElement({...})` + `.toXml()` / `.serialize()` with a
|
|
418
|
+
* single function call returning a string — zero object allocation.
|
|
419
|
+
*
|
|
420
|
+
* @param name Element tag name (e.g. `"a:srgbClr"`)
|
|
421
|
+
* @param attrRecord Optional flat attribute map; `undefined` values are skipped
|
|
422
|
+
* @param children Optional pre-serialized child XML strings
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```ts
|
|
426
|
+
* element("a:solidFill", undefined, [element("a:srgbClr", { val: "FF0000" })])
|
|
427
|
+
* // => '<a:solidFill><a:srgbClr val="FF0000"/></a:solidFill>'
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
function element(name, attrRecord, children) {
|
|
431
|
+
const attrStr = attrRecord ? attrs(attrRecord) : void 0;
|
|
432
|
+
if (!children || children.length === 0) return selfCloseElement(name, attrStr);
|
|
433
|
+
const body = children.join("");
|
|
434
|
+
return body.length === 0 ? selfCloseElement(name, attrStr) : `<${name}${attrStr ?? ""}>${body}</${name}>`;
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
432
437
|
//#region src/stringify.ts
|
|
438
|
+
const TEXT_SPECIALS = /[&<>]/;
|
|
433
439
|
function stringify(js, options) {
|
|
434
440
|
const opts = normalizeOptions(options);
|
|
435
441
|
const parts = [];
|
|
@@ -437,22 +443,19 @@ function stringify(js, options) {
|
|
|
437
443
|
if (js.elements?.length) parts.push(writeElements(js.elements, opts, 0, !parts.length));
|
|
438
444
|
return parts.join("");
|
|
439
445
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
446
|
+
const DEFAULT_OPTIONS = {
|
|
447
|
+
spaces: "",
|
|
448
|
+
ignoreDeclaration: false,
|
|
449
|
+
ignoreText: false,
|
|
450
|
+
ignoreComment: false,
|
|
451
|
+
ignoreCdata: false,
|
|
452
|
+
ignoreDoctype: false,
|
|
453
|
+
fullTagEmptyElement: false,
|
|
454
|
+
indentText: false,
|
|
455
|
+
indentCdata: false
|
|
456
|
+
};
|
|
444
457
|
function normalizeOptions(options) {
|
|
445
|
-
if (!options) return
|
|
446
|
-
spaces: "",
|
|
447
|
-
ignoreDeclaration: false,
|
|
448
|
-
ignoreText: false,
|
|
449
|
-
ignoreComment: false,
|
|
450
|
-
ignoreCdata: false,
|
|
451
|
-
ignoreDoctype: false,
|
|
452
|
-
fullTagEmptyElement: false,
|
|
453
|
-
indentText: false,
|
|
454
|
-
indentCdata: false
|
|
455
|
-
};
|
|
458
|
+
if (!options) return DEFAULT_OPTIONS;
|
|
456
459
|
let spaces = "";
|
|
457
460
|
if (options.spaces != null) spaces = typeof options.spaces === "number" ? " ".repeat(options.spaces) : options.spaces;
|
|
458
461
|
return {
|
|
@@ -469,7 +472,8 @@ function normalizeOptions(options) {
|
|
|
469
472
|
};
|
|
470
473
|
}
|
|
471
474
|
function writeIndentation(spaces, depth, firstLine) {
|
|
472
|
-
|
|
475
|
+
if (!spaces) return "";
|
|
476
|
+
return (!firstLine ? "\n" : "") + spaces.repeat(depth);
|
|
473
477
|
}
|
|
474
478
|
function writeDeclaration(declaration) {
|
|
475
479
|
const attrs = declaration.attributes;
|
|
@@ -480,90 +484,66 @@ function writeDeclaration(declaration) {
|
|
|
480
484
|
return parts.join("") + "?>";
|
|
481
485
|
}
|
|
482
486
|
function writeAttributes(attributes, elementName, element, attributeValueFn) {
|
|
483
|
-
|
|
484
|
-
for (const key
|
|
487
|
+
let s = "";
|
|
488
|
+
for (const key in attributes) {
|
|
485
489
|
const value = attributes[key];
|
|
486
490
|
if (value === null || value === void 0) continue;
|
|
487
491
|
const raw = String(value);
|
|
488
492
|
const attr = attributeValueFn ? attributeValueFn(raw, key, elementName, element) : escapeXml(raw);
|
|
489
|
-
|
|
493
|
+
s += ` ${key}="${attr}"`;
|
|
490
494
|
}
|
|
491
|
-
return
|
|
492
|
-
}
|
|
493
|
-
function writeElement(element, opts, depth) {
|
|
494
|
-
if (!element.name) return "";
|
|
495
|
-
const name = element.name;
|
|
496
|
-
const attrStr = element.attributes ? writeAttributes(element.attributes, name, element, opts.attributeValueFn) : "";
|
|
497
|
-
if (!((element.elements?.length ?? 0) > 0 || element.attributes?.["xml:space"] === "preserve" || opts.fullTagEmptyElement)) return `<${name}${attrStr}/>`;
|
|
498
|
-
const parts = [];
|
|
499
|
-
parts.push(`<${name}${attrStr}>`);
|
|
500
|
-
const hasChildElements = element.elements?.some((e) => e.type === "element") ?? false;
|
|
501
|
-
if (element.elements?.length) parts.push(writeElements(element.elements, opts, depth + 1, false));
|
|
502
|
-
if (opts.spaces && hasChildElements) parts.push("\n" + opts.spaces.repeat(depth));
|
|
503
|
-
parts.push(`</${name}>`);
|
|
504
|
-
return parts.join("");
|
|
495
|
+
return s;
|
|
505
496
|
}
|
|
506
497
|
function writeElements(elements, opts, depth, firstLine) {
|
|
507
|
-
|
|
498
|
+
let s = "";
|
|
508
499
|
for (let i = 0; i < elements.length; i++) {
|
|
509
500
|
const element = elements[i];
|
|
510
501
|
if (!element) continue;
|
|
511
502
|
const isFirst = firstLine && i === 0;
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
case "comment":
|
|
528
|
-
if (opts.ignoreComment) continue;
|
|
529
|
-
parts.push(writeIndentation(opts.spaces, depth, isFirst));
|
|
530
|
-
parts.push(writeComment(element.comment));
|
|
531
|
-
break;
|
|
532
|
-
case "doctype":
|
|
533
|
-
if (opts.ignoreDoctype) continue;
|
|
534
|
-
parts.push(writeIndentation(opts.spaces, depth, isFirst));
|
|
535
|
-
parts.push(writeDoctype(element.doctype));
|
|
536
|
-
break;
|
|
537
|
-
default: break;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return parts.join("");
|
|
541
|
-
}
|
|
542
|
-
function writeText(text) {
|
|
543
|
-
if (text == null) return "";
|
|
544
|
-
const str = String(text);
|
|
545
|
-
for (let i = 0; i < str.length; i++) {
|
|
546
|
-
const c = str.charCodeAt(i);
|
|
547
|
-
if (c === 38 || c === 60 || c === 62) {
|
|
548
|
-
let s = "";
|
|
549
|
-
let last = 0;
|
|
550
|
-
for (let j = i; j < str.length; j++) {
|
|
551
|
-
const cj = str.charCodeAt(j);
|
|
552
|
-
if (cj === 38) {
|
|
553
|
-
s += str.slice(last, j) + "&";
|
|
554
|
-
last = j + 1;
|
|
555
|
-
} else if (cj === 60) {
|
|
556
|
-
s += str.slice(last, j) + "<";
|
|
557
|
-
last = j + 1;
|
|
558
|
-
} else if (cj === 62) {
|
|
559
|
-
s += str.slice(last, j) + ">";
|
|
560
|
-
last = j + 1;
|
|
561
|
-
}
|
|
503
|
+
const type = element.type;
|
|
504
|
+
if (type === "element") {
|
|
505
|
+
const name = element.name;
|
|
506
|
+
if (!name) continue;
|
|
507
|
+
if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
|
|
508
|
+
const attributes = element.attributes;
|
|
509
|
+
const attrStr = attributes ? writeAttributes(attributes, name, element, opts.attributeValueFn) : "";
|
|
510
|
+
if (element.raw !== void 0) {
|
|
511
|
+
s += `<${name}${attrStr}>${element.raw}</${name}>`;
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
const children = element.elements;
|
|
515
|
+
if (!(children !== void 0 && children.length > 0 || opts.fullTagEmptyElement || attributes?.["xml:space"] === "preserve")) {
|
|
516
|
+
s += `<${name}${attrStr}/>`;
|
|
517
|
+
continue;
|
|
562
518
|
}
|
|
563
|
-
|
|
519
|
+
const open = `<${name}${attrStr}>`;
|
|
520
|
+
if (children !== void 0 && children.length > 0) {
|
|
521
|
+
const inner = writeElements(children, opts, depth + 1, false);
|
|
522
|
+
if (opts.spaces && children.some((e) => e.type === "element")) s += open + inner + "\n" + opts.spaces.repeat(depth) + `</${name}>`;
|
|
523
|
+
else s += open + inner + `</${name}>`;
|
|
524
|
+
} else s += open + `</${name}>`;
|
|
525
|
+
} else if (type === "text") {
|
|
526
|
+
if (opts.ignoreText) continue;
|
|
527
|
+
if (opts.indentText && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
|
|
528
|
+
const text = element.text;
|
|
529
|
+
if (text == null) continue;
|
|
530
|
+
const str = String(text);
|
|
531
|
+
s += TEXT_SPECIALS.test(str) ? str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") : str;
|
|
532
|
+
} else if (type === "cdata") {
|
|
533
|
+
if (opts.ignoreCdata) continue;
|
|
534
|
+
if (opts.indentCdata && opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
|
|
535
|
+
s += writeCdata(element.cdata);
|
|
536
|
+
} else if (type === "comment") {
|
|
537
|
+
if (opts.ignoreComment) continue;
|
|
538
|
+
if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
|
|
539
|
+
s += writeComment(element.comment);
|
|
540
|
+
} else if (type === "doctype") {
|
|
541
|
+
if (opts.ignoreDoctype) continue;
|
|
542
|
+
if (opts.spaces) s += writeIndentation(opts.spaces, depth, isFirst);
|
|
543
|
+
s += writeDoctype(element.doctype);
|
|
564
544
|
}
|
|
565
545
|
}
|
|
566
|
-
return
|
|
546
|
+
return s;
|
|
567
547
|
}
|
|
568
548
|
function writeCdata(cdata) {
|
|
569
549
|
if (cdata == null) return "";
|
|
@@ -578,62 +558,26 @@ function writeDoctype(doctype) {
|
|
|
578
558
|
return `<!DOCTYPE ${doctype}>`;
|
|
579
559
|
}
|
|
580
560
|
//#endregion
|
|
581
|
-
//#region src/
|
|
561
|
+
//#region src/stringify-element.ts
|
|
582
562
|
/**
|
|
583
|
-
*
|
|
584
|
-
*
|
|
563
|
+
* Serialize an Element including its own opening/closing tag.
|
|
564
|
+
*
|
|
565
|
+
* `stringify` serializes only an element's children (it treats its input as
|
|
566
|
+
* a document root). Raw-XML round-trip of whole elements needs the element's
|
|
567
|
+
* own tag wrapped around its serialized children.
|
|
585
568
|
*/
|
|
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;
|
|
569
|
+
function stringifyElement(el) {
|
|
570
|
+
if (!el.name) return "";
|
|
571
|
+
let attrStr = "";
|
|
572
|
+
if (el.attributes) for (const key of Object.keys(el.attributes)) {
|
|
573
|
+
const v = el.attributes[key];
|
|
574
|
+
if (v === null || v === void 0) continue;
|
|
575
|
+
attrStr += ` ${key}="${escapeXml(String(v))}"`;
|
|
619
576
|
}
|
|
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));
|
|
577
|
+
if (!((el.elements?.length ?? 0) > 0 || el.attributes?.["xml:space"] === "preserve")) return `<${el.name}${attrStr}/>`;
|
|
578
|
+
return `<${el.name}${attrStr}>${stringify(el)}</${el.name}>`;
|
|
635
579
|
}
|
|
636
580
|
//#endregion
|
|
637
|
-
export { allChildren, attr, attrBool, attrMeasure, attrNum, attrs, attrsRaw, childCount, childText, children, collectText, colorAttr, element, escapeXml, findChild, findDeep, findFirst, hasChild, isNonEmpty,
|
|
581
|
+
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
582
|
|
|
639
583
|
//# sourceMappingURL=index.mjs.map
|