fast-xml-parser 4.5.3 → 4.5.5
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/CHANGELOG.md +17 -0
- package/package.json +5 -6
- package/src/fxp.d.ts +126 -18
- package/src/util.js +26 -9
- package/src/xmlbuilder/orderedJs2Xml.js +14 -3
- package/src/xmlparser/DocTypeReader.js +387 -131
- package/src/xmlparser/OptionsBuilder.js +139 -41
- package/src/xmlparser/OrderedObjParser.js +346 -191
- package/src/v6/CharsSymbol.js +0 -16
- package/src/v6/EntitiesParser.js +0 -104
- package/src/v6/OptionsBuilder.js +0 -61
- package/src/v6/OutputBuilders/BaseOutputBuilder.js +0 -69
- package/src/v6/OutputBuilders/JsArrBuilder.js +0 -103
- package/src/v6/OutputBuilders/JsMinArrBuilder.js +0 -100
- package/src/v6/OutputBuilders/JsObjBuilder.js +0 -154
- package/src/v6/OutputBuilders/ParserOptionsBuilder.js +0 -94
- package/src/v6/Report.js +0 -0
- package/src/v6/TagPath.js +0 -81
- package/src/v6/TagPathMatcher.js +0 -13
- package/src/v6/XMLParser.js +0 -83
- package/src/v6/Xml2JsParser.js +0 -235
- package/src/v6/XmlPartReader.js +0 -210
- package/src/v6/XmlSpecialTagsReader.js +0 -111
- package/src/v6/inputSource/BufferSource.js +0 -116
- package/src/v6/inputSource/StringSource.js +0 -121
- package/src/v6/valueParsers/EntitiesParser.js +0 -105
- package/src/v6/valueParsers/booleanParser.js +0 -22
- package/src/v6/valueParsers/booleanParserExt.js +0 -19
- package/src/v6/valueParsers/currency.js +0 -38
- package/src/v6/valueParsers/join.js +0 -13
- package/src/v6/valueParsers/number.js +0 -14
- package/src/v6/valueParsers/trim.js +0 -6
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
const util = require('../util');
|
|
5
5
|
const xmlNode = require('./xmlNode');
|
|
6
|
-
const
|
|
6
|
+
const DocTypeReader = require('./DocTypeReader');
|
|
7
7
|
const toNumber = require("strnum");
|
|
8
8
|
const getIgnoreAttributesFn = require('../ignoreAttributes')
|
|
9
9
|
|
|
@@ -14,19 +14,19 @@ const getIgnoreAttributesFn = require('../ignoreAttributes')
|
|
|
14
14
|
//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
|
|
15
15
|
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
|
|
16
16
|
|
|
17
|
-
class OrderedObjParser{
|
|
18
|
-
constructor(options){
|
|
17
|
+
class OrderedObjParser {
|
|
18
|
+
constructor(options) {
|
|
19
19
|
this.options = options;
|
|
20
20
|
this.currentNode = null;
|
|
21
21
|
this.tagsNodeStack = [];
|
|
22
22
|
this.docTypeEntities = {};
|
|
23
23
|
this.lastEntities = {
|
|
24
|
-
"apos"
|
|
25
|
-
"gt"
|
|
26
|
-
"lt"
|
|
27
|
-
"quot"
|
|
24
|
+
"apos": { regex: /&(apos|#39|#x27);/g, val: "'" },
|
|
25
|
+
"gt": { regex: /&(gt|#62|#x3E);/g, val: ">" },
|
|
26
|
+
"lt": { regex: /&(lt|#60|#x3C);/g, val: "<" },
|
|
27
|
+
"quot": { regex: /&(quot|#34|#x22);/g, val: "\"" },
|
|
28
28
|
};
|
|
29
|
-
this.ampEntity = { regex: /&(amp|#38|#x26);/g, val
|
|
29
|
+
this.ampEntity = { regex: /&(amp|#38|#x26);/g, val: "&" };
|
|
30
30
|
this.htmlEntities = {
|
|
31
31
|
"space": { regex: /&(nbsp|#160);/g, val: " " },
|
|
32
32
|
// "lt" : { regex: /&(lt|#60);/g, val: "<" },
|
|
@@ -34,15 +34,15 @@ class OrderedObjParser{
|
|
|
34
34
|
// "amp" : { regex: /&(amp|#38);/g, val: "&" },
|
|
35
35
|
// "quot" : { regex: /&(quot|#34);/g, val: "\"" },
|
|
36
36
|
// "apos" : { regex: /&(apos|#39);/g, val: "'" },
|
|
37
|
-
"cent"
|
|
38
|
-
"pound"
|
|
39
|
-
"yen"
|
|
40
|
-
"euro"
|
|
41
|
-
"copyright"
|
|
42
|
-
"reg"
|
|
43
|
-
"inr"
|
|
44
|
-
"num_dec": { regex: /&#([0-9]{1,7});/g, val
|
|
45
|
-
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val
|
|
37
|
+
"cent": { regex: /&(cent|#162);/g, val: "¢" },
|
|
38
|
+
"pound": { regex: /&(pound|#163);/g, val: "£" },
|
|
39
|
+
"yen": { regex: /&(yen|#165);/g, val: "¥" },
|
|
40
|
+
"euro": { regex: /&(euro|#8364);/g, val: "€" },
|
|
41
|
+
"copyright": { regex: /&(copy|#169);/g, val: "©" },
|
|
42
|
+
"reg": { regex: /&(reg|#174);/g, val: "®" },
|
|
43
|
+
"inr": { regex: /&(inr|#8377);/g, val: "₹" },
|
|
44
|
+
"num_dec": { regex: /&#([0-9]{1,7});/g, val: (_, str) => fromCodePoint(str, 10, "&#") },
|
|
45
|
+
"num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val: (_, str) => fromCodePoint(str, 16, "&#x") },
|
|
46
46
|
};
|
|
47
47
|
this.addExternalEntities = addExternalEntities;
|
|
48
48
|
this.parseXml = parseXml;
|
|
@@ -55,17 +55,34 @@ class OrderedObjParser{
|
|
|
55
55
|
this.saveTextToParentTag = saveTextToParentTag;
|
|
56
56
|
this.addChild = addChild;
|
|
57
57
|
this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
|
|
58
|
+
this.entityExpansionCount = 0;
|
|
59
|
+
this.currentExpandedLength = 0;
|
|
60
|
+
|
|
61
|
+
if (this.options.stopNodes && this.options.stopNodes.length > 0) {
|
|
62
|
+
this.stopNodesExact = new Set();
|
|
63
|
+
this.stopNodesWildcard = new Set();
|
|
64
|
+
for (let i = 0; i < this.options.stopNodes.length; i++) {
|
|
65
|
+
const stopNodeExp = this.options.stopNodes[i];
|
|
66
|
+
if (typeof stopNodeExp !== 'string') continue;
|
|
67
|
+
if (stopNodeExp.startsWith("*.")) {
|
|
68
|
+
this.stopNodesWildcard.add(stopNodeExp.substring(2));
|
|
69
|
+
} else {
|
|
70
|
+
this.stopNodesExact.add(stopNodeExp);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
}
|
|
61
77
|
|
|
62
|
-
function addExternalEntities(externalEntities){
|
|
78
|
+
function addExternalEntities(externalEntities) {
|
|
63
79
|
const entKeys = Object.keys(externalEntities);
|
|
64
80
|
for (let i = 0; i < entKeys.length; i++) {
|
|
65
81
|
const ent = entKeys[i];
|
|
82
|
+
const escaped = ent.replace(/[.\-+*:]/g, '\\.');
|
|
66
83
|
this.lastEntities[ent] = {
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
regex: new RegExp("&" + escaped + ";", "g"),
|
|
85
|
+
val: externalEntities[ent]
|
|
69
86
|
}
|
|
70
87
|
}
|
|
71
88
|
}
|
|
@@ -84,23 +101,23 @@ function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode,
|
|
|
84
101
|
if (this.options.trimValues && !dontTrim) {
|
|
85
102
|
val = val.trim();
|
|
86
103
|
}
|
|
87
|
-
if(val.length > 0){
|
|
88
|
-
if(!escapeEntities) val = this.replaceEntitiesValue(val);
|
|
89
|
-
|
|
104
|
+
if (val.length > 0) {
|
|
105
|
+
if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);
|
|
106
|
+
|
|
90
107
|
const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
|
|
91
|
-
if(newval === null || newval === undefined){
|
|
108
|
+
if (newval === null || newval === undefined) {
|
|
92
109
|
//don't parse
|
|
93
110
|
return val;
|
|
94
|
-
}else if(typeof newval !== typeof val || newval !== val){
|
|
111
|
+
} else if (typeof newval !== typeof val || newval !== val) {
|
|
95
112
|
//overwrite
|
|
96
113
|
return newval;
|
|
97
|
-
}else if(this.options.trimValues){
|
|
114
|
+
} else if (this.options.trimValues) {
|
|
98
115
|
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
|
99
|
-
}else{
|
|
116
|
+
} else {
|
|
100
117
|
const trimmedVal = val.trim();
|
|
101
|
-
if(trimmedVal === val){
|
|
118
|
+
if (trimmedVal === val) {
|
|
102
119
|
return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
|
|
103
|
-
}else{
|
|
120
|
+
} else {
|
|
104
121
|
return val;
|
|
105
122
|
}
|
|
106
123
|
}
|
|
@@ -145,20 +162,20 @@ function buildAttributesMap(attrStr, jPath, tagName) {
|
|
|
145
162
|
if (this.options.transformAttributeName) {
|
|
146
163
|
aName = this.options.transformAttributeName(aName);
|
|
147
164
|
}
|
|
148
|
-
|
|
165
|
+
aName = sanitizeName(aName, this.options);
|
|
149
166
|
if (oldVal !== undefined) {
|
|
150
167
|
if (this.options.trimValues) {
|
|
151
168
|
oldVal = oldVal.trim();
|
|
152
169
|
}
|
|
153
|
-
oldVal = this.replaceEntitiesValue(oldVal);
|
|
170
|
+
oldVal = this.replaceEntitiesValue(oldVal, tagName, jPath);
|
|
154
171
|
const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
|
|
155
|
-
if(newVal === null || newVal === undefined){
|
|
172
|
+
if (newVal === null || newVal === undefined) {
|
|
156
173
|
//don't parse
|
|
157
174
|
attrs[aName] = oldVal;
|
|
158
|
-
}else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
|
|
175
|
+
} else if (typeof newVal !== typeof oldVal || newVal !== oldVal) {
|
|
159
176
|
//overwrite
|
|
160
177
|
attrs[aName] = newVal;
|
|
161
|
-
}else{
|
|
178
|
+
} else {
|
|
162
179
|
//parse
|
|
163
180
|
attrs[aName] = parseValue(
|
|
164
181
|
oldVal,
|
|
@@ -183,46 +200,52 @@ function buildAttributesMap(attrStr, jPath, tagName) {
|
|
|
183
200
|
}
|
|
184
201
|
}
|
|
185
202
|
|
|
186
|
-
const parseXml = function(xmlData) {
|
|
203
|
+
const parseXml = function (xmlData) {
|
|
187
204
|
xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
|
|
188
205
|
const xmlObj = new xmlNode('!xml');
|
|
189
206
|
let currentNode = xmlObj;
|
|
190
207
|
let textData = "";
|
|
191
208
|
let jPath = "";
|
|
192
|
-
|
|
209
|
+
|
|
210
|
+
// Reset entity expansion counters for this document
|
|
211
|
+
this.entityExpansionCount = 0;
|
|
212
|
+
this.currentExpandedLength = 0;
|
|
213
|
+
|
|
214
|
+
const docTypeReader = new DocTypeReader(this.options.processEntities);
|
|
215
|
+
for (let i = 0; i < xmlData.length; i++) {//for each char in XML data
|
|
193
216
|
const ch = xmlData[i];
|
|
194
|
-
if(ch === '<'){
|
|
217
|
+
if (ch === '<') {
|
|
195
218
|
// const nextIndex = i+1;
|
|
196
219
|
// const _2ndChar = xmlData[nextIndex];
|
|
197
|
-
if(
|
|
220
|
+
if (xmlData[i + 1] === '/') {//Closing Tag
|
|
198
221
|
const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
|
|
199
|
-
let tagName = xmlData.substring(i+2,closeIndex).trim();
|
|
222
|
+
let tagName = xmlData.substring(i + 2, closeIndex).trim();
|
|
200
223
|
|
|
201
|
-
if(this.options.removeNSPrefix){
|
|
224
|
+
if (this.options.removeNSPrefix) {
|
|
202
225
|
const colonIndex = tagName.indexOf(":");
|
|
203
|
-
if(colonIndex !== -1){
|
|
204
|
-
tagName = tagName.substr(colonIndex+1);
|
|
226
|
+
if (colonIndex !== -1) {
|
|
227
|
+
tagName = tagName.substr(colonIndex + 1);
|
|
205
228
|
}
|
|
206
229
|
}
|
|
207
230
|
|
|
208
|
-
if(this.options.transformTagName) {
|
|
231
|
+
if (this.options.transformTagName) {
|
|
209
232
|
tagName = this.options.transformTagName(tagName);
|
|
210
233
|
}
|
|
211
234
|
|
|
212
|
-
if(currentNode){
|
|
235
|
+
if (currentNode) {
|
|
213
236
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
214
237
|
}
|
|
215
238
|
|
|
216
239
|
//check if last tag of nested tag was unpaired tag
|
|
217
|
-
const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1);
|
|
218
|
-
if(tagName && this.options.unpairedTags.indexOf(tagName) !== -1
|
|
240
|
+
const lastTagName = jPath.substring(jPath.lastIndexOf(".") + 1);
|
|
241
|
+
if (tagName && this.options.unpairedTags.indexOf(tagName) !== -1) {
|
|
219
242
|
throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
|
|
220
243
|
}
|
|
221
244
|
let propIndex = 0
|
|
222
|
-
if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1
|
|
223
|
-
propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1)
|
|
245
|
+
if (lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1) {
|
|
246
|
+
propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.') - 1)
|
|
224
247
|
this.tagsNodeStack.pop();
|
|
225
|
-
}else{
|
|
248
|
+
} else {
|
|
226
249
|
propIndex = jPath.lastIndexOf(".");
|
|
227
250
|
}
|
|
228
251
|
jPath = jPath.substring(0, propIndex);
|
|
@@ -230,74 +253,87 @@ const parseXml = function(xmlData) {
|
|
|
230
253
|
currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
|
|
231
254
|
textData = "";
|
|
232
255
|
i = closeIndex;
|
|
233
|
-
} else if(
|
|
256
|
+
} else if (xmlData[i + 1] === '?') {
|
|
234
257
|
|
|
235
|
-
let tagData = readTagExp(xmlData,i, false, "?>");
|
|
236
|
-
if(!tagData) throw new Error("Pi Tag is not closed.");
|
|
258
|
+
let tagData = readTagExp(xmlData, i, false, "?>");
|
|
259
|
+
if (!tagData) throw new Error("Pi Tag is not closed.");
|
|
237
260
|
|
|
238
261
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
239
|
-
if
|
|
262
|
+
if ((this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags) {
|
|
263
|
+
//do nothing
|
|
264
|
+
} else {
|
|
240
265
|
|
|
241
|
-
}else{
|
|
242
|
-
|
|
243
266
|
const childNode = new xmlNode(tagData.tagName);
|
|
244
267
|
childNode.add(this.options.textNodeName, "");
|
|
245
|
-
|
|
246
|
-
if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
|
|
268
|
+
|
|
269
|
+
if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) {
|
|
247
270
|
childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
|
|
248
271
|
}
|
|
249
|
-
this.addChild(currentNode, childNode, jPath)
|
|
250
|
-
|
|
272
|
+
this.addChild(currentNode, childNode, jPath, i);
|
|
251
273
|
}
|
|
252
274
|
|
|
253
275
|
|
|
254
276
|
i = tagData.closeIndex + 1;
|
|
255
|
-
} else if(xmlData.substr(i + 1, 3) === '!--') {
|
|
256
|
-
const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
|
|
257
|
-
if(this.options.commentPropName){
|
|
277
|
+
} else if (xmlData.substr(i + 1, 3) === '!--') {
|
|
278
|
+
const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.")
|
|
279
|
+
if (this.options.commentPropName) {
|
|
258
280
|
const comment = xmlData.substring(i + 4, endIndex - 2);
|
|
259
281
|
|
|
260
282
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
261
283
|
|
|
262
|
-
currentNode.add(this.options.commentPropName, [
|
|
284
|
+
currentNode.add(this.options.commentPropName, [{ [this.options.textNodeName]: comment }]);
|
|
263
285
|
}
|
|
264
286
|
i = endIndex;
|
|
265
|
-
} else if(
|
|
266
|
-
const result = readDocType(xmlData, i);
|
|
287
|
+
} else if (xmlData.substr(i + 1, 2) === '!D') {
|
|
288
|
+
const result = docTypeReader.readDocType(xmlData, i);
|
|
267
289
|
this.docTypeEntities = result.entities;
|
|
268
290
|
i = result.i;
|
|
269
|
-
}else if(xmlData.substr(i + 1, 2) === '![') {
|
|
291
|
+
} else if (xmlData.substr(i + 1, 2) === '![') {
|
|
270
292
|
const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
|
|
271
|
-
const tagExp = xmlData.substring(i + 9,closeIndex);
|
|
293
|
+
const tagExp = xmlData.substring(i + 9, closeIndex);
|
|
272
294
|
|
|
273
295
|
textData = this.saveTextToParentTag(textData, currentNode, jPath);
|
|
274
296
|
|
|
275
297
|
let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true);
|
|
276
|
-
if(val == undefined) val = "";
|
|
298
|
+
if (val == undefined) val = "";
|
|
277
299
|
|
|
278
300
|
//cdata should be set even if it is 0 length string
|
|
279
|
-
if(this.options.cdataPropName){
|
|
280
|
-
currentNode.add(this.options.cdataPropName, [
|
|
281
|
-
}else{
|
|
301
|
+
if (this.options.cdataPropName) {
|
|
302
|
+
currentNode.add(this.options.cdataPropName, [{ [this.options.textNodeName]: tagExp }]);
|
|
303
|
+
} else {
|
|
282
304
|
currentNode.add(this.options.textNodeName, val);
|
|
283
305
|
}
|
|
284
|
-
|
|
306
|
+
|
|
285
307
|
i = closeIndex + 2;
|
|
286
|
-
}else {//Opening tag
|
|
287
|
-
let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
|
|
288
|
-
let tagName= result.tagName;
|
|
308
|
+
} else {//Opening tag
|
|
309
|
+
let result = readTagExp(xmlData, i, this.options.removeNSPrefix);
|
|
310
|
+
let tagName = result.tagName;
|
|
289
311
|
const rawTagName = result.rawTagName;
|
|
290
312
|
let tagExp = result.tagExp;
|
|
291
313
|
let attrExpPresent = result.attrExpPresent;
|
|
292
314
|
let closeIndex = result.closeIndex;
|
|
293
315
|
|
|
294
316
|
if (this.options.transformTagName) {
|
|
295
|
-
|
|
317
|
+
//console.log(tagExp, tagName)
|
|
318
|
+
const newTagName = this.options.transformTagName(tagName);
|
|
319
|
+
if (tagExp === tagName) {
|
|
320
|
+
tagExp = newTagName
|
|
321
|
+
}
|
|
322
|
+
tagName = newTagName;
|
|
296
323
|
}
|
|
297
|
-
|
|
324
|
+
|
|
325
|
+
if (this.options.strictReservedNames &&
|
|
326
|
+
(tagName === this.options.commentPropName
|
|
327
|
+
|| tagName === this.options.cdataPropName
|
|
328
|
+
|| tagName === this.options.textNodeName
|
|
329
|
+
|| tagName === this.options.attributesGroupName
|
|
330
|
+
)) {
|
|
331
|
+
throw new Error(`Invalid tag name: ${tagName}`);
|
|
332
|
+
}
|
|
333
|
+
|
|
298
334
|
//save text as child node
|
|
299
335
|
if (currentNode && textData) {
|
|
300
|
-
if(currentNode.tagname !== '!xml'){
|
|
336
|
+
if (currentNode.tagname !== '!xml') {
|
|
301
337
|
//when nested tag is found
|
|
302
338
|
textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
|
|
303
339
|
}
|
|
@@ -305,80 +341,99 @@ const parseXml = function(xmlData) {
|
|
|
305
341
|
|
|
306
342
|
//check if last tag was unpaired tag
|
|
307
343
|
const lastTag = currentNode;
|
|
308
|
-
if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1
|
|
344
|
+
if (lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1) {
|
|
309
345
|
currentNode = this.tagsNodeStack.pop();
|
|
310
346
|
jPath = jPath.substring(0, jPath.lastIndexOf("."));
|
|
311
347
|
}
|
|
312
|
-
if(tagName !== xmlObj.tagname){
|
|
348
|
+
if (tagName !== xmlObj.tagname) {
|
|
313
349
|
jPath += jPath ? "." + tagName : tagName;
|
|
314
350
|
}
|
|
315
|
-
|
|
351
|
+
const startIndex = i;
|
|
352
|
+
if (this.isItStopNode(this.stopNodesExact, this.stopNodesWildcard, jPath, tagName)) {
|
|
316
353
|
let tagContent = "";
|
|
317
354
|
//self-closing tag
|
|
318
|
-
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
|
|
319
|
-
if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
|
|
355
|
+
if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
|
|
356
|
+
if (tagName[tagName.length - 1] === "/") { //remove trailing '/'
|
|
320
357
|
tagName = tagName.substr(0, tagName.length - 1);
|
|
321
358
|
jPath = jPath.substr(0, jPath.length - 1);
|
|
322
359
|
tagExp = tagName;
|
|
323
|
-
}else{
|
|
360
|
+
} else {
|
|
324
361
|
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
325
362
|
}
|
|
326
363
|
i = result.closeIndex;
|
|
327
364
|
}
|
|
328
365
|
//unpaired tag
|
|
329
|
-
else if(this.options.unpairedTags.indexOf(tagName) !== -1){
|
|
330
|
-
|
|
366
|
+
else if (this.options.unpairedTags.indexOf(tagName) !== -1) {
|
|
367
|
+
|
|
331
368
|
i = result.closeIndex;
|
|
332
369
|
}
|
|
333
370
|
//normal tag
|
|
334
|
-
else{
|
|
371
|
+
else {
|
|
335
372
|
//read until closing tag is found
|
|
336
373
|
const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
|
|
337
|
-
if(!result) throw new Error(`Unexpected end of ${rawTagName}`);
|
|
374
|
+
if (!result) throw new Error(`Unexpected end of ${rawTagName}`);
|
|
338
375
|
i = result.i;
|
|
339
376
|
tagContent = result.tagContent;
|
|
340
377
|
}
|
|
341
378
|
|
|
342
379
|
const childNode = new xmlNode(tagName);
|
|
343
|
-
if(tagName !== tagExp && attrExpPresent){
|
|
380
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
344
381
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
345
382
|
}
|
|
346
|
-
if(tagContent) {
|
|
383
|
+
if (tagContent) {
|
|
347
384
|
tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
|
|
348
385
|
}
|
|
349
|
-
|
|
386
|
+
|
|
350
387
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
351
388
|
childNode.add(this.options.textNodeName, tagContent);
|
|
352
|
-
|
|
353
|
-
this.addChild(currentNode, childNode, jPath)
|
|
354
|
-
}else{
|
|
355
|
-
|
|
356
|
-
if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
|
|
357
|
-
if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
|
|
389
|
+
|
|
390
|
+
this.addChild(currentNode, childNode, jPath, startIndex);
|
|
391
|
+
} else {
|
|
392
|
+
//selfClosing tag
|
|
393
|
+
if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
|
|
394
|
+
if (tagName[tagName.length - 1] === "/") { //remove trailing '/'
|
|
358
395
|
tagName = tagName.substr(0, tagName.length - 1);
|
|
359
396
|
jPath = jPath.substr(0, jPath.length - 1);
|
|
360
397
|
tagExp = tagName;
|
|
361
|
-
}else{
|
|
398
|
+
} else {
|
|
362
399
|
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
363
400
|
}
|
|
364
|
-
|
|
365
|
-
if(this.options.transformTagName) {
|
|
366
|
-
|
|
401
|
+
|
|
402
|
+
if (this.options.transformTagName) {
|
|
403
|
+
const newTagName = this.options.transformTagName(tagName);
|
|
404
|
+
if (tagExp === tagName) {
|
|
405
|
+
tagExp = newTagName
|
|
406
|
+
}
|
|
407
|
+
tagName = newTagName;
|
|
367
408
|
}
|
|
368
409
|
|
|
369
410
|
const childNode = new xmlNode(tagName);
|
|
370
|
-
if(tagName !== tagExp && attrExpPresent){
|
|
411
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
371
412
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
372
413
|
}
|
|
373
|
-
this.addChild(currentNode, childNode, jPath)
|
|
414
|
+
this.addChild(currentNode, childNode, jPath, startIndex);
|
|
415
|
+
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
416
|
+
}
|
|
417
|
+
else if (this.options.unpairedTags.indexOf(tagName) !== -1) {//unpaired tag
|
|
418
|
+
const childNode = new xmlNode(tagName);
|
|
419
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
420
|
+
childNode[":@"] = this.buildAttributesMap(tagExp, jPath);
|
|
421
|
+
}
|
|
422
|
+
this.addChild(currentNode, childNode, jPath, startIndex);
|
|
374
423
|
jPath = jPath.substr(0, jPath.lastIndexOf("."));
|
|
424
|
+
i = result.closeIndex;
|
|
425
|
+
// Continue to next iteration without changing currentNode
|
|
426
|
+
continue;
|
|
375
427
|
}
|
|
376
|
-
|
|
377
|
-
else{
|
|
378
|
-
const childNode = new xmlNode(
|
|
428
|
+
//opening tag
|
|
429
|
+
else {
|
|
430
|
+
const childNode = new xmlNode(tagName);
|
|
431
|
+
if (this.tagsNodeStack.length > this.options.maxNestedTags) {
|
|
432
|
+
throw new Error("Maximum nested tags exceeded");
|
|
433
|
+
}
|
|
379
434
|
this.tagsNodeStack.push(currentNode);
|
|
380
|
-
|
|
381
|
-
if(tagName !== tagExp && attrExpPresent){
|
|
435
|
+
|
|
436
|
+
if (tagName !== tagExp && attrExpPresent) {
|
|
382
437
|
childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
|
|
383
438
|
}
|
|
384
439
|
this.addChild(currentNode, childNode, jPath)
|
|
@@ -388,58 +443,142 @@ const parseXml = function(xmlData) {
|
|
|
388
443
|
i = closeIndex;
|
|
389
444
|
}
|
|
390
445
|
}
|
|
391
|
-
}else{
|
|
446
|
+
} else {
|
|
392
447
|
textData += xmlData[i];
|
|
393
448
|
}
|
|
394
449
|
}
|
|
395
450
|
return xmlObj.child;
|
|
396
451
|
}
|
|
397
452
|
|
|
398
|
-
function addChild(currentNode, childNode, jPath){
|
|
453
|
+
function addChild(currentNode, childNode, jPath, startIndex) {
|
|
454
|
+
// unset startIndex if not requested
|
|
455
|
+
if (!this.options.captureMetaData) startIndex = undefined;
|
|
399
456
|
const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
|
|
400
|
-
if(result === false){
|
|
401
|
-
|
|
457
|
+
if (result === false) {
|
|
458
|
+
//do nothing
|
|
459
|
+
} else if (typeof result === "string") {
|
|
402
460
|
childNode.tagname = result
|
|
403
|
-
currentNode.addChild(childNode);
|
|
404
|
-
}else{
|
|
405
|
-
currentNode.addChild(childNode);
|
|
461
|
+
currentNode.addChild(childNode, startIndex);
|
|
462
|
+
} else {
|
|
463
|
+
currentNode.addChild(childNode, startIndex);
|
|
406
464
|
}
|
|
407
465
|
}
|
|
408
466
|
|
|
409
|
-
const replaceEntitiesValue = function(val){
|
|
467
|
+
const replaceEntitiesValue = function (val, tagName, jPath) {
|
|
468
|
+
// Performance optimization: Early return if no entities to replace
|
|
469
|
+
if (val.indexOf('&') === -1) {
|
|
470
|
+
return val;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const entityConfig = this.options.processEntities;
|
|
474
|
+
|
|
475
|
+
if (!entityConfig.enabled) {
|
|
476
|
+
return val;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Check tag-specific filtering
|
|
480
|
+
if (entityConfig.allowedTags) {
|
|
481
|
+
if (!entityConfig.allowedTags.includes(tagName)) {
|
|
482
|
+
return val; // Skip entity replacement for current tag as not set
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (entityConfig.tagFilter) {
|
|
487
|
+
if (!entityConfig.tagFilter(tagName, jPath)) {
|
|
488
|
+
return val; // Skip based on custom filter
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Replace DOCTYPE entities
|
|
493
|
+
for (let entityName in this.docTypeEntities) {
|
|
494
|
+
const entity = this.docTypeEntities[entityName];
|
|
495
|
+
const matches = val.match(entity.regx);
|
|
496
|
+
|
|
497
|
+
if (matches) {
|
|
498
|
+
// Track expansions
|
|
499
|
+
this.entityExpansionCount += matches.length;
|
|
500
|
+
|
|
501
|
+
// Check expansion limit
|
|
502
|
+
if (entityConfig.maxTotalExpansions &&
|
|
503
|
+
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
504
|
+
throw new Error(
|
|
505
|
+
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Store length before replacement
|
|
510
|
+
const lengthBefore = val.length;
|
|
511
|
+
val = val.replace(entity.regx, entity.val);
|
|
410
512
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
513
|
+
// Check expanded length immediately after replacement
|
|
514
|
+
if (entityConfig.maxExpandedLength) {
|
|
515
|
+
this.currentExpandedLength += (val.length - lengthBefore);
|
|
516
|
+
|
|
517
|
+
if (this.currentExpandedLength > entityConfig.maxExpandedLength) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
`Total expanded content size exceeded: ${this.currentExpandedLength} > ${entityConfig.maxExpandedLength}`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
415
523
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
524
|
+
}
|
|
525
|
+
if (val.indexOf('&') === -1) return val; // Early exit
|
|
526
|
+
|
|
527
|
+
// Replace standard entities
|
|
528
|
+
for (const entityName of Object.keys(this.lastEntities)) {
|
|
529
|
+
const entity = this.lastEntities[entityName];
|
|
530
|
+
const matches = val.match(entity.regex);
|
|
531
|
+
if (matches) {
|
|
532
|
+
this.entityExpansionCount += matches.length;
|
|
533
|
+
if (entityConfig.maxTotalExpansions &&
|
|
534
|
+
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
419
539
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
540
|
+
val = val.replace(entity.regex, entity.val);
|
|
541
|
+
}
|
|
542
|
+
if (val.indexOf('&') === -1) return val; // Early exit
|
|
543
|
+
|
|
544
|
+
// Replace HTML entities if enabled
|
|
545
|
+
if (this.options.htmlEntities) {
|
|
546
|
+
for (const entityName of Object.keys(this.htmlEntities)) {
|
|
547
|
+
const entity = this.htmlEntities[entityName];
|
|
548
|
+
const matches = val.match(entity.regex);
|
|
549
|
+
if (matches) {
|
|
550
|
+
//console.log(matches);
|
|
551
|
+
this.entityExpansionCount += matches.length;
|
|
552
|
+
if (entityConfig.maxTotalExpansions &&
|
|
553
|
+
this.entityExpansionCount > entityConfig.maxTotalExpansions) {
|
|
554
|
+
throw new Error(
|
|
555
|
+
`Entity expansion limit exceeded: ${this.entityExpansionCount} > ${entityConfig.maxTotalExpansions}`
|
|
556
|
+
);
|
|
557
|
+
}
|
|
424
558
|
}
|
|
559
|
+
val = val.replace(entity.regex, entity.val);
|
|
425
560
|
}
|
|
426
|
-
val = val.replace( this.ampEntity.regex, this.ampEntity.val);
|
|
427
561
|
}
|
|
562
|
+
|
|
563
|
+
// Replace ampersand entity last
|
|
564
|
+
val = val.replace(this.ampEntity.regex, this.ampEntity.val);
|
|
565
|
+
|
|
428
566
|
return val;
|
|
429
567
|
}
|
|
430
|
-
|
|
568
|
+
|
|
569
|
+
function saveTextToParentTag(textData, parentNode, jPath, isLeafNode) {
|
|
431
570
|
if (textData) { //store previously collected data as textNode
|
|
432
|
-
if(isLeafNode === undefined) isLeafNode =
|
|
433
|
-
|
|
571
|
+
if (isLeafNode === undefined) isLeafNode = parentNode.child.length === 0
|
|
572
|
+
|
|
434
573
|
textData = this.parseTextData(textData,
|
|
435
|
-
|
|
574
|
+
parentNode.tagname,
|
|
436
575
|
jPath,
|
|
437
576
|
false,
|
|
438
|
-
|
|
577
|
+
parentNode[":@"] ? Object.keys(parentNode[":@"]).length !== 0 : false,
|
|
439
578
|
isLeafNode);
|
|
440
579
|
|
|
441
580
|
if (textData !== undefined && textData !== "")
|
|
442
|
-
|
|
581
|
+
parentNode.add(this.options.textNodeName, textData);
|
|
443
582
|
textData = "";
|
|
444
583
|
}
|
|
445
584
|
return textData;
|
|
@@ -447,17 +586,14 @@ function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
|
|
|
447
586
|
|
|
448
587
|
//TODO: use jPath to simplify the logic
|
|
449
588
|
/**
|
|
450
|
-
*
|
|
451
|
-
* @param {
|
|
589
|
+
* @param {Set} stopNodesExact
|
|
590
|
+
* @param {Set} stopNodesWildcard
|
|
452
591
|
* @param {string} jPath
|
|
453
|
-
* @param {string} currentTagName
|
|
592
|
+
* @param {string} currentTagName
|
|
454
593
|
*/
|
|
455
|
-
function isItStopNode(
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const stopNodeExp = stopNodes[stopNodePath];
|
|
459
|
-
if( allNodesExp === stopNodeExp || jPath === stopNodeExp ) return true;
|
|
460
|
-
}
|
|
594
|
+
function isItStopNode(stopNodesExact, stopNodesWildcard, jPath, currentTagName) {
|
|
595
|
+
if (stopNodesWildcard && stopNodesWildcard.has(currentTagName)) return true;
|
|
596
|
+
if (stopNodesExact && stopNodesExact.has(jPath)) return true;
|
|
461
597
|
return false;
|
|
462
598
|
}
|
|
463
599
|
|
|
@@ -467,24 +603,24 @@ function isItStopNode(stopNodes, jPath, currentTagName){
|
|
|
467
603
|
* @param {number} i starting index
|
|
468
604
|
* @returns
|
|
469
605
|
*/
|
|
470
|
-
function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
|
|
606
|
+
function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
|
|
471
607
|
let attrBoundary;
|
|
472
608
|
let tagExp = "";
|
|
473
609
|
for (let index = i; index < xmlData.length; index++) {
|
|
474
610
|
let ch = xmlData[index];
|
|
475
611
|
if (attrBoundary) {
|
|
476
|
-
|
|
612
|
+
if (ch === attrBoundary) attrBoundary = "";//reset
|
|
477
613
|
} else if (ch === '"' || ch === "'") {
|
|
478
|
-
|
|
614
|
+
attrBoundary = ch;
|
|
479
615
|
} else if (ch === closingChar[0]) {
|
|
480
|
-
if(closingChar[1]){
|
|
481
|
-
if(xmlData[index + 1] === closingChar[1]){
|
|
616
|
+
if (closingChar[1]) {
|
|
617
|
+
if (xmlData[index + 1] === closingChar[1]) {
|
|
482
618
|
return {
|
|
483
619
|
data: tagExp,
|
|
484
620
|
index: index
|
|
485
621
|
}
|
|
486
622
|
}
|
|
487
|
-
}else{
|
|
623
|
+
} else {
|
|
488
624
|
return {
|
|
489
625
|
data: tagExp,
|
|
490
626
|
index: index
|
|
@@ -497,33 +633,33 @@ function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
|
|
|
497
633
|
}
|
|
498
634
|
}
|
|
499
635
|
|
|
500
|
-
function findClosingIndex(xmlData, str, i, errMsg){
|
|
636
|
+
function findClosingIndex(xmlData, str, i, errMsg) {
|
|
501
637
|
const closingIndex = xmlData.indexOf(str, i);
|
|
502
|
-
if(closingIndex === -1){
|
|
638
|
+
if (closingIndex === -1) {
|
|
503
639
|
throw new Error(errMsg)
|
|
504
|
-
}else{
|
|
640
|
+
} else {
|
|
505
641
|
return closingIndex + str.length - 1;
|
|
506
642
|
}
|
|
507
643
|
}
|
|
508
644
|
|
|
509
|
-
function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
|
|
510
|
-
const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
|
|
511
|
-
if(!result) return;
|
|
645
|
+
function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") {
|
|
646
|
+
const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);
|
|
647
|
+
if (!result) return;
|
|
512
648
|
let tagExp = result.data;
|
|
513
649
|
const closeIndex = result.index;
|
|
514
650
|
const separatorIndex = tagExp.search(/\s/);
|
|
515
651
|
let tagName = tagExp;
|
|
516
652
|
let attrExpPresent = true;
|
|
517
|
-
if(separatorIndex !== -1){//separate tag name and attributes expression
|
|
653
|
+
if (separatorIndex !== -1) {//separate tag name and attributes expression
|
|
518
654
|
tagName = tagExp.substring(0, separatorIndex);
|
|
519
655
|
tagExp = tagExp.substring(separatorIndex + 1).trimStart();
|
|
520
656
|
}
|
|
521
657
|
|
|
522
658
|
const rawTagName = tagName;
|
|
523
|
-
if(removeNSPrefix){
|
|
659
|
+
if (removeNSPrefix) {
|
|
524
660
|
const colonIndex = tagName.indexOf(":");
|
|
525
|
-
if(colonIndex !== -1){
|
|
526
|
-
tagName = tagName.substr(colonIndex+1);
|
|
661
|
+
if (colonIndex !== -1) {
|
|
662
|
+
tagName = tagName.substr(colonIndex + 1);
|
|
527
663
|
attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
|
|
528
664
|
}
|
|
529
665
|
}
|
|
@@ -542,47 +678,47 @@ function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
|
|
|
542
678
|
* @param {string} tagName
|
|
543
679
|
* @param {number} i
|
|
544
680
|
*/
|
|
545
|
-
function readStopNodeData(xmlData, tagName, i){
|
|
681
|
+
function readStopNodeData(xmlData, tagName, i) {
|
|
546
682
|
const startIndex = i;
|
|
547
683
|
// Starting at 1 since we already have an open tag
|
|
548
684
|
let openTagCount = 1;
|
|
549
685
|
|
|
550
686
|
for (; i < xmlData.length; i++) {
|
|
551
|
-
if(
|
|
552
|
-
if (xmlData[i+1] === "/") {//close tag
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
687
|
+
if (xmlData[i] === "<") {
|
|
688
|
+
if (xmlData[i + 1] === "/") {//close tag
|
|
689
|
+
const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
|
|
690
|
+
let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
|
|
691
|
+
if (closeTagName === tagName) {
|
|
692
|
+
openTagCount--;
|
|
693
|
+
if (openTagCount === 0) {
|
|
694
|
+
return {
|
|
695
|
+
tagContent: xmlData.substring(startIndex, i),
|
|
696
|
+
i: closeIndex
|
|
562
697
|
}
|
|
563
698
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
699
|
+
}
|
|
700
|
+
i = closeIndex;
|
|
701
|
+
} else if (xmlData[i + 1] === '?') {
|
|
702
|
+
const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.")
|
|
703
|
+
i = closeIndex;
|
|
704
|
+
} else if (xmlData.substr(i + 1, 3) === '!--') {
|
|
705
|
+
const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.")
|
|
706
|
+
i = closeIndex;
|
|
707
|
+
} else if (xmlData.substr(i + 1, 2) === '![') {
|
|
708
|
+
const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
|
|
709
|
+
i = closeIndex;
|
|
710
|
+
} else {
|
|
711
|
+
const tagData = readTagExp(xmlData, i, '>')
|
|
576
712
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
i=tagData.closeIndex;
|
|
713
|
+
if (tagData) {
|
|
714
|
+
const openTagName = tagData && tagData.tagName;
|
|
715
|
+
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") {
|
|
716
|
+
openTagCount++;
|
|
583
717
|
}
|
|
718
|
+
i = tagData.closeIndex;
|
|
584
719
|
}
|
|
585
720
|
}
|
|
721
|
+
}
|
|
586
722
|
}//end for loop
|
|
587
723
|
}
|
|
588
724
|
|
|
@@ -590,8 +726,8 @@ function parseValue(val, shouldParse, options) {
|
|
|
590
726
|
if (shouldParse && typeof val === 'string') {
|
|
591
727
|
//console.log(options)
|
|
592
728
|
const newval = val.trim();
|
|
593
|
-
if(newval === 'true'
|
|
594
|
-
else if(newval === 'false'
|
|
729
|
+
if (newval === 'true') return true;
|
|
730
|
+
else if (newval === 'false') return false;
|
|
595
731
|
else return toNumber(val, options);
|
|
596
732
|
} else {
|
|
597
733
|
if (util.isExist(val)) {
|
|
@@ -602,5 +738,24 @@ function parseValue(val, shouldParse, options) {
|
|
|
602
738
|
}
|
|
603
739
|
}
|
|
604
740
|
|
|
741
|
+
function fromCodePoint(str, base, prefix) {
|
|
742
|
+
const codePoint = Number.parseInt(str, base);
|
|
743
|
+
|
|
744
|
+
if (codePoint >= 0 && codePoint <= 0x10FFFF) {
|
|
745
|
+
return String.fromCodePoint(codePoint);
|
|
746
|
+
} else {
|
|
747
|
+
return prefix + str + ";";
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
function sanitizeName(name, options) {
|
|
752
|
+
if (util.criticalProperties.includes(name)) {
|
|
753
|
+
throw new Error(`[SECURITY] Invalid name: "${name}" is a reserved JavaScript keyword that could cause prototype pollution`);
|
|
754
|
+
} else if (util.DANGEROUS_PROPERTY_NAMES.includes(name)) {
|
|
755
|
+
return options.onDangerousProperty(name);
|
|
756
|
+
}
|
|
757
|
+
return name;
|
|
758
|
+
}
|
|
605
759
|
|
|
606
760
|
module.exports = OrderedObjParser;
|
|
761
|
+
|