fast-xml-parser 3.21.0 → 4.0.0-beta.3

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/src/fxp.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ type X2jOptions = {
2
+ preserveOrder: boolean;
3
+ attributeNamePrefix: string;
4
+ attributesGroupName: false | string;
5
+ textNodeName: string;
6
+ ignoreAttributes: boolean;
7
+ removeNSPrefix: boolean;
8
+ allowBooleanAttributes: boolean;
9
+ parseTagValue: boolean;
10
+ parseAttributeValue: boolean;
11
+ trimValues: boolean;
12
+ cdataPropName: false | string;
13
+ commentPropName: false | string;
14
+ tagValueProcessor: (tagName: string, tagValue: string, jPath: string, hasAttributes: boolean, isLeafNode: boolean) => string;
15
+ attributeValueProcessor: (attrName: string, attrValue: string, jPath: string) => string;
16
+ numberParseOptions: strnumOptions;
17
+ stopNodes: string[];
18
+ unpairedTags: string[];
19
+ alwaysCreateTextNode: boolean;
20
+ isArray: (tagName: string, jPath: string, isLeafNode: boolean, isAttribute: boolean) => boolean;
21
+ processEntities: boolean;
22
+ htmlEntities: boolean;
23
+ };
24
+ type strnumOptions = {
25
+ hex: boolean;
26
+ leadingZeros: boolean,
27
+ skipLike?: RegExp
28
+ }
29
+ type X2jOptionsOptional = Partial<X2jOptions>;
30
+ type validationOptions = {
31
+ allowBooleanAttributes: boolean;
32
+ unpairedTags: string[];
33
+ };
34
+ type validationOptionsOptional = Partial<validationOptions>;
35
+
36
+ type XmlBuilderOptions = {
37
+ attributeNamePrefix: string;
38
+ attributesGroupName: false | string;
39
+ textNodeName: string;
40
+ ignoreAttributes: boolean;
41
+ cdataPropName: false | string;
42
+ commentPropName: false | string;
43
+ format: boolean;
44
+ indentBy: string;
45
+ arrayNodeName: string;
46
+ suppressEmptyNode: boolean;
47
+ preserveOrder: boolean;
48
+ unpairedTags: string[];
49
+ tagValueProcessor: (name: string, value: string) => string;
50
+ attributeValueProcessor: (name: string, value: string) => string;
51
+ processEntities: boolean;
52
+ };
53
+ type XmlBuilderOptionsOptional = Partial<XmlBuilderOptions>;
54
+
55
+ type ESchema = string | object | Array<string|object>;
56
+
57
+ type ValidationError = {
58
+ err: {
59
+ code: string;
60
+ msg: string,
61
+ line: number,
62
+ col: number
63
+ };
64
+ };
65
+
66
+ export class XMLParser {
67
+ constructor(options?: X2jOptionsOptional);
68
+ parse(xmlData: string | Buffer ,validationOptions?: validationOptionsOptional | boolean): any;
69
+ }
70
+
71
+ export class XMLValidator{
72
+ static validate( xmlData: string, options?: validationOptionsOptional): true | ValidationError;
73
+ }
74
+ export class XMLBuilder {
75
+ constructor(options: XmlBuilderOptionsOptional);
76
+ build(jObj: any): any;
77
+ }
package/src/fxp.js ADDED
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const validator = require('./validator');
4
+ const XMLParser = require('./xmlparser/XMLParser');
5
+ const XMLBuilder = require('./xmlbuilder/json2xml');
6
+
7
+ module.exports = {
8
+ XMLParser: XMLParser,
9
+ XMLValidator: validator,
10
+ XMLBuilder: XMLBuilder
11
+ }
package/src/util.js CHANGED
@@ -67,7 +67,7 @@ exports.getValue = function(v) {
67
67
  // const fakeCall = function(a) {return a;};
68
68
  // const fakeCallNoReturn = function() {};
69
69
 
70
- exports.buildOptions = function(options, defaultOptions, props) {
70
+ const buildOptions = function(options, defaultOptions, props) {
71
71
  let newOptions = {};
72
72
  if (!options) {
73
73
  return defaultOptions; //if there are not options
@@ -83,26 +83,7 @@ exports.buildOptions = function(options, defaultOptions, props) {
83
83
  return newOptions;
84
84
  };
85
85
 
86
- /**
87
- * Check if a tag name should be treated as array
88
- *
89
- * @param tagName the node tagname
90
- * @param arrayMode the array mode option
91
- * @param parentTagName the parent tag name
92
- * @returns {boolean} true if node should be parsed as array
93
- */
94
- exports.isTagNameInArrayMode = function (tagName, arrayMode, parentTagName) {
95
- if (arrayMode === false) {
96
- return false;
97
- } else if (arrayMode instanceof RegExp) {
98
- return arrayMode.test(tagName);
99
- } else if (typeof arrayMode === 'function') {
100
- return !!arrayMode(tagName, parentTagName);
101
- }
102
-
103
- return arrayMode === "strict";
104
- }
105
-
86
+ exports.buildOptions = buildOptions;
106
87
  exports.isName = isName;
107
88
  exports.getAllMatches = getAllMatches;
108
89
  exports.nameRegexp = nameRegexp;
package/src/validator.js CHANGED
@@ -4,9 +4,13 @@ const util = require('./util');
4
4
 
5
5
  const defaultOptions = {
6
6
  allowBooleanAttributes: false, //A tag can have attributes without any value
7
+ unpairedTags: []
7
8
  };
8
9
 
9
- const props = ['allowBooleanAttributes'];
10
+ const props = [
11
+ 'allowBooleanAttributes',
12
+ 'unpairedTags'
13
+ ];
10
14
 
11
15
  //const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
12
16
  exports.validate = function (xmlData, options) {
@@ -25,7 +29,7 @@ exports.validate = function (xmlData, options) {
25
29
  // check for byte order mark (BOM)
26
30
  xmlData = xmlData.substr(1);
27
31
  }
28
-
32
+
29
33
  for (let i = 0; i < xmlData.length; i++) {
30
34
 
31
35
  if (xmlData[i] === '<' && xmlData[i+1] === '?') {
@@ -130,6 +134,8 @@ exports.validate = function (xmlData, options) {
130
134
  //if the root level has been reached before ...
131
135
  if (reachedRoot === true) {
132
136
  return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
137
+ } else if(options.unpairedTags.indexOf(tagName) !== -1){
138
+ //don't push into stack
133
139
  } else {
134
140
  tags.push({tagName, tagStartPos});
135
141
  }
@@ -156,6 +162,10 @@ exports.validate = function (xmlData, options) {
156
162
  if (afterAmp == -1)
157
163
  return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
158
164
  i = afterAmp;
165
+ }else{
166
+ if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
167
+ return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
168
+ }
159
169
  }
160
170
  } //end of reading tag text value
161
171
  if (xmlData[i] === '<') {
@@ -163,7 +173,7 @@ exports.validate = function (xmlData, options) {
163
173
  }
164
174
  }
165
175
  } else {
166
- if (xmlData[i] === ' ' || xmlData[i] === '\t' || xmlData[i] === '\n' || xmlData[i] === '\r') {
176
+ if ( isWhiteSpace(xmlData[i])) {
167
177
  continue;
168
178
  }
169
179
  return getErrorObject('InvalidChar', "char '"+xmlData[i]+"' is not expected.", getLineNumberForPosition(xmlData, i));
@@ -183,6 +193,9 @@ exports.validate = function (xmlData, options) {
183
193
  return true;
184
194
  };
185
195
 
196
+ function isWhiteSpace(char){
197
+ return char === ' ' || char === '\t' || char === '\n' || char === '\r';
198
+ }
186
199
  /**
187
200
  * Read Processing insstructions and skip
188
201
  * @param {*} xmlData
@@ -0,0 +1,263 @@
1
+ 'use strict';
2
+ //parse Empty Node as self closing node
3
+ const buildOptions = require('../util').buildOptions;
4
+ const buildFromOrderedJs = require('./orderedJs2Xml');
5
+
6
+ const defaultOptions = {
7
+ attributeNamePrefix: '@_',
8
+ attributesGroupName: false,
9
+ textNodeName: '#text',
10
+ ignoreAttributes: true,
11
+ cdataPropName: false,
12
+ format: false,
13
+ indentBy: ' ',
14
+ suppressEmptyNode: false,
15
+ tagValueProcessor: function(key, a) {
16
+ return a;
17
+ },
18
+ attributeValueProcessor: function(attrName, a) {
19
+ return a;
20
+ },
21
+ preserveOrder: false,
22
+ commentPropName: false,
23
+ unpairedTags: [],
24
+ entities: {
25
+ ">" : { regex: new RegExp(">", "g"), val: "&gt;" },
26
+ "<" : { regex: new RegExp("<", "g"), val: "&lt;" },
27
+ "sQuot" : { regex: new RegExp("\'", "g"), val: "&apos;" },
28
+ "dQuot" : { regex: new RegExp("\"", "g"), val: "&quot;" }
29
+ },
30
+ processEntities: true
31
+ };
32
+
33
+ const props = [
34
+ 'attributeNamePrefix',
35
+ 'attributesGroupName',
36
+ 'textNodeName',
37
+ 'ignoreAttributes',
38
+ 'cdataPropName',
39
+ 'format',
40
+ 'indentBy',
41
+ 'suppressEmptyNode',
42
+ 'tagValueProcessor',
43
+ 'attributeValueProcessor',
44
+ 'arrayNodeName', //when array as root
45
+ 'preserveOrder',
46
+ "commentPropName",
47
+ "unpairedTags",
48
+ "entities",
49
+ "processEntities",
50
+ // 'rootNodeName', //when jsObject have multiple properties on root level
51
+ ];
52
+
53
+ function Builder(options) {
54
+ this.options = buildOptions(options, defaultOptions, props);
55
+ if (this.options.ignoreAttributes || this.options.attributesGroupName) {
56
+ this.isAttribute = function(/*a*/) {
57
+ return false;
58
+ };
59
+ } else {
60
+ this.attrPrefixLen = this.options.attributeNamePrefix.length;
61
+ this.isAttribute = isAttribute;
62
+ }
63
+
64
+ this.processTextOrObjNode = processTextOrObjNode
65
+
66
+ if (this.options.format) {
67
+ this.indentate = indentate;
68
+ this.tagEndChar = '>\n';
69
+ this.newLine = '\n';
70
+ } else {
71
+ this.indentate = function() {
72
+ return '';
73
+ };
74
+ this.tagEndChar = '>';
75
+ this.newLine = '';
76
+ }
77
+
78
+ if (this.options.suppressEmptyNode) {
79
+ this.buildTextNode = buildEmptyTextNode;
80
+ this.buildObjNode = buildEmptyObjNode;
81
+ } else {
82
+ this.buildTextNode = buildTextValNode;
83
+ this.buildObjNode = buildObjectNode;
84
+ }
85
+
86
+ this.buildTextValNode = buildTextValNode;
87
+ this.buildObjectNode = buildObjectNode;
88
+
89
+ this.replaceEntitiesValue = replaceEntitiesValue;
90
+ }
91
+
92
+ Builder.prototype.build = function(jObj) {
93
+ if(this.options.preserveOrder){
94
+ return buildFromOrderedJs(jObj, this.options);
95
+ }else {
96
+ if(Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1){
97
+ jObj = {
98
+ [this.options.arrayNodeName] : jObj
99
+ }
100
+ }
101
+ return this.j2x(jObj, 0).val;
102
+ }
103
+ };
104
+
105
+ Builder.prototype.j2x = function(jObj, level) {
106
+ let attrStr = '';
107
+ let val = '';
108
+ for (let key in jObj) {
109
+ if (typeof jObj[key] === 'undefined') {
110
+ // supress undefined node
111
+ } else if (jObj[key] === null) {
112
+ val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
113
+ } else if (jObj[key] instanceof Date) {
114
+ val += this.buildTextNode(jObj[key], key, '', level);
115
+ } else if (typeof jObj[key] !== 'object') {
116
+ //premitive type
117
+ const attr = this.isAttribute(key);
118
+ if (attr) {
119
+ let val = this.options.attributeValueProcessor(attr, '' + jObj[key]);
120
+ val = this.replaceEntitiesValue(val);
121
+ attrStr += ' ' + attr + '="' + val + '"';
122
+ }else {
123
+ //tag value
124
+ if (key === this.options.textNodeName) {
125
+ let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
126
+ val += this.replaceEntitiesValue(newval);
127
+ } else {
128
+ val += this.buildTextNode(jObj[key], key, '', level);
129
+ }
130
+ }
131
+ } else if (Array.isArray(jObj[key])) {
132
+ //repeated nodes
133
+ const arrLen = jObj[key].length;
134
+ for (let j = 0; j < arrLen; j++) {
135
+ const item = jObj[key][j];
136
+ if (typeof item === 'undefined') {
137
+ // supress undefined node
138
+ } else if (item === null) {
139
+ val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
140
+ } else if (typeof item === 'object') {
141
+ val += this.processTextOrObjNode(item, key, level)
142
+ } else {
143
+ val += this.buildTextNode(item, key, '', level);
144
+ }
145
+ }
146
+ } else {
147
+ //nested node
148
+ if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
149
+ const Ks = Object.keys(jObj[key]);
150
+ const L = Ks.length;
151
+ for (let j = 0; j < L; j++) {
152
+ let val = this.options.attributeValueProcessor(Ks[j], '' + jObj[key][Ks[j]]);
153
+ val = this.replaceEntitiesValue(val);
154
+ attrStr += ' ' + Ks[j] + '="' + val + '"';
155
+ }
156
+ } else {
157
+ val += this.processTextOrObjNode(jObj[key], key, level)
158
+ }
159
+ }
160
+ }
161
+ return {attrStr: attrStr, val: val};
162
+ };
163
+
164
+ function processTextOrObjNode (object, key, level) {
165
+ const result = this.j2x(object, level + 1);
166
+ if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
167
+ return this.buildTextNode(result.val, key, result.attrStr, level);
168
+ } else {
169
+ return this.buildObjNode(result.val, key, result.attrStr, level);
170
+ }
171
+ }
172
+
173
+ function buildObjectNode(val, key, attrStr, level) {
174
+ if (attrStr && val.indexOf('<') === -1) {
175
+ return (
176
+ this.indentate(level) +
177
+ '<' +
178
+ key +
179
+ attrStr +
180
+ '>' +
181
+ val +
182
+ //+ this.newLine
183
+ // + this.indentate(level)
184
+ '</' +
185
+ key +
186
+ this.tagEndChar
187
+ );
188
+ } else {
189
+ return (
190
+ this.indentate(level) +
191
+ '<' +
192
+ key +
193
+ attrStr +
194
+ this.tagEndChar +
195
+ val +
196
+ //+ this.newLine
197
+ this.indentate(level) +
198
+ '</' +
199
+ key +
200
+ this.tagEndChar
201
+ );
202
+ }
203
+ }
204
+
205
+ function buildEmptyObjNode(val, key, attrStr, level) {
206
+ if (val !== '') {
207
+ return this.buildObjectNode(val, key, attrStr, level);
208
+ } else {
209
+ return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
210
+ //+ this.newLine
211
+ }
212
+ }
213
+
214
+ function buildTextValNode(val, key, attrStr, level) {
215
+ let textValue = this.options.tagValueProcessor(key, val);
216
+ textValue = this.replaceEntitiesValue(textValue);
217
+
218
+ return (
219
+ this.indentate(level) +
220
+ '<' +
221
+ key +
222
+ attrStr +
223
+ '>' +
224
+ textValue +
225
+ '</' +
226
+ key +
227
+ this.tagEndChar
228
+ );
229
+ }
230
+
231
+ function replaceEntitiesValue(textValue){
232
+ if(textValue && textValue.length > 0 && this.options.processEntities){
233
+ for (const entityName in this.options.entities) {
234
+ const entity = this.options.entities[entityName];
235
+ textValue = textValue.replace(entity.regex, entity.val);
236
+ }
237
+ }
238
+ return textValue;
239
+ }
240
+
241
+ function buildEmptyTextNode(val, key, attrStr, level) {
242
+ if( val === '' && this.options.unpairedTags.indexOf(key) !== -1){
243
+ return this.indentate(level) + '<' + key + attrStr + this.tagEndChar;
244
+ }else if (val !== '') {
245
+ return this.buildTextValNode(val, key, attrStr, level);
246
+ } else {
247
+ return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
248
+ }
249
+ }
250
+
251
+ function indentate(level) {
252
+ return this.options.indentBy.repeat(level);
253
+ }
254
+
255
+ function isAttribute(name /*, options*/) {
256
+ if (name.startsWith(this.options.attributeNamePrefix)) {
257
+ return name.substr(this.attrPrefixLen);
258
+ } else {
259
+ return false;
260
+ }
261
+ }
262
+
263
+ module.exports = Builder;
@@ -0,0 +1,84 @@
1
+ const {EOL} = require('os');
2
+
3
+ /**
4
+ *
5
+ * @param {array} jArray
6
+ * @param {any} options
7
+ * @returns
8
+ */
9
+ function toXml(jArray, options){
10
+ return arrToStr( jArray, options, 0);
11
+ }
12
+
13
+ function arrToStr(arr, options, level){
14
+ let xmlStr = "";
15
+
16
+ let indentation = "";
17
+ if(options.format && options.indentBy.length > 0){//TODO: this logic can be avoided for each call
18
+ indentation = EOL + "" + options.indentBy.repeat(level);
19
+ }
20
+
21
+ for (let i = 0; i < arr.length; i++) {
22
+ const tagObj = arr[i];
23
+ const tagName = propName(tagObj);
24
+
25
+ if(tagName === options.textNodeName){
26
+ let tagText = options.tagValueProcessor( tagName, tagObj[tagName]);
27
+ tagText = replaceEntitiesValue(tagText, options);
28
+ xmlStr += indentation + tagText;
29
+ continue;
30
+ }else if( tagName === options.cdataPropName){
31
+ xmlStr += indentation + `<![CDATA[${tagObj[tagName][0][options.textNodeName]}]]>`;
32
+ continue;
33
+ }else if( tagName === options.commentPropName){
34
+ xmlStr += indentation + `<!--${tagObj[tagName][0][options.textNodeName]}-->`;
35
+ continue;
36
+ }
37
+ const attStr = attr_to_str(tagObj.attributes, options);
38
+ let tagStart = indentation + `<${tagName}${attStr}`;
39
+ let tagValue = arrToStr(tagObj[tagName], options, level + 1);
40
+ if( (!tagValue || tagValue.length === 0) && options.suppressEmptyNode){
41
+ if(options.unpairedTags.indexOf(tagName) !== -1){
42
+ xmlStr += tagStart + ">";
43
+ }else{
44
+ xmlStr += tagStart + "/>";
45
+ }
46
+ }else{
47
+ //TODO: node with only text value should not parse the text value in next line
48
+ xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>` ;
49
+ }
50
+ }
51
+
52
+ return xmlStr;
53
+ }
54
+
55
+ function propName(obj){
56
+ const keys = Object.keys(obj);
57
+ for (let i = 0; i < keys.length; i++) {
58
+ const key = keys[i];
59
+ if(key !== "attributes") return key;
60
+ }
61
+ }
62
+
63
+ function attr_to_str(attrMap, options){
64
+ let attrStr = "";
65
+ if(attrMap && !options.ignoreAttributes){
66
+ for( attr in attrMap){
67
+ let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
68
+ attrVal = replaceEntitiesValue(attrVal, options);
69
+ attrStr+= ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
70
+ }
71
+ }
72
+ return attrStr;
73
+ }
74
+
75
+ function replaceEntitiesValue(textValue, options){
76
+ if(textValue && textValue.length > 0 && options.processEntities){
77
+ for (const entityName in options.entities) {
78
+ const entity = options.entities[entityName];
79
+ textValue = textValue.replace(entity.regex, entity.val);
80
+ }
81
+ }
82
+ return textValue;
83
+ }
84
+ module.exports = toXml;
File without changes
@@ -0,0 +1,92 @@
1
+ //TODO: handle comments
2
+ function readDocType(xmlData, i){
3
+
4
+ const entities = {};
5
+ if( xmlData[i + 3] === 'O' &&
6
+ xmlData[i + 4] === 'C' &&
7
+ xmlData[i + 5] === 'T' &&
8
+ xmlData[i + 6] === 'Y' &&
9
+ xmlData[i + 7] === 'P' &&
10
+ xmlData[i + 8] === 'E')
11
+ {
12
+ i = i+9;
13
+ let angleBracketsCount = 1;
14
+ let hasBody = false, entity = false, comment = false;
15
+ let exp = "";
16
+ for(;i<xmlData.length;i++){
17
+ if (xmlData[i] === '<') {
18
+ if( hasBody &&
19
+ xmlData[i+1] === '!' &&
20
+ xmlData[i+2] === 'E' &&
21
+ xmlData[i+3] === 'N' &&
22
+ xmlData[i+4] === 'T' &&
23
+ xmlData[i+5] === 'I' &&
24
+ xmlData[i+6] === 'T' &&
25
+ xmlData[i+7] === 'Y'
26
+ ){
27
+ i += 7;
28
+ entity = true;
29
+ }else if( hasBody &&
30
+ xmlData[i+1] === '!' &&
31
+ xmlData[i+2] === 'E' &&
32
+ xmlData[i+3] === 'L' &&
33
+ xmlData[i+4] === 'E' &&
34
+ xmlData[i+5] === 'M' &&
35
+ xmlData[i+6] === 'E' &&
36
+ xmlData[i+7] === 'N' &&
37
+ xmlData[i+8] === 'T'
38
+ ){
39
+ //Not supported
40
+ i += 8;
41
+ }else if( //comment
42
+ xmlData[i+1] === '!' &&
43
+ xmlData[i+2] === '-' &&
44
+ xmlData[i+3] === '-'
45
+ ){
46
+ comment = true;
47
+ }else{
48
+ throw new Error("Invalid DOCTYPE");
49
+ }
50
+ angleBracketsCount++;
51
+ exp = "";
52
+ } else if (xmlData[i] === '>') {
53
+ if(comment){
54
+ if( xmlData[i - 1] === "-" && xmlData[i - 2] === "-"){
55
+ comment = false;
56
+ }else{
57
+ throw new Error(`Invalid XML comment in DOCTYPE`);
58
+ }
59
+ }else if(entity){
60
+ parseEntityExp(exp, entities);
61
+ entity = false;
62
+ }
63
+ angleBracketsCount--;
64
+ if (angleBracketsCount === 0) {
65
+ break;
66
+ }
67
+ }else if( xmlData[i] === '['){
68
+ hasBody = true;
69
+ }else{
70
+ exp += xmlData[i];
71
+ }
72
+ }
73
+ if(angleBracketsCount !== 0){
74
+ throw new Error(`Unclosed DOCTYPE`);
75
+ }
76
+ }else{
77
+ throw new Error(`Invalid Tag instead of DOCTYPE`);
78
+ }
79
+ return {entities, i};
80
+ }
81
+
82
+ const entityRegex = RegExp("^\\s([a-zA-z0-0]+)[ \t](['\"])([^&]+)\\2");
83
+ function parseEntityExp(exp, entities){
84
+ const match = entityRegex.exec(exp);
85
+ if(match){
86
+ entities[ match[1] ] = {
87
+ regx : RegExp( `&${match[1]};`,"g"),
88
+ val: match[3]
89
+ };
90
+ }
91
+ }
92
+ module.exports = readDocType;
@@ -0,0 +1,66 @@
1
+
2
+ const defaultOptions = {
3
+ preserveOrder: false,
4
+ attributeNamePrefix: '@_',
5
+ attributesGroupName: false,
6
+ textNodeName: '#text',
7
+ ignoreAttributes: true,
8
+ removeNSPrefix: false, // remove NS from tag name or attribute name if true
9
+ allowBooleanAttributes: false, //a tag can have attributes without any value
10
+ //ignoreRootElement : false,
11
+ parseTagValue: true,
12
+ parseAttributeValue: false,
13
+ trimValues: true, //Trim string values of tag and attributes
14
+ cdataPropName: false,
15
+ numberParseOptions: {
16
+ hex: true,
17
+ leadingZeros: true
18
+ },
19
+ tagValueProcessor: function(tagName, val) {
20
+ return val;
21
+ },
22
+ attributeValueProcessor: function(attrName, val) {
23
+ return val;
24
+ },
25
+ stopNodes: [], //nested tags will not be parsed even for errors
26
+ alwaysCreateTextNode: false,
27
+ isArray: () => false,
28
+ commentPropName: false,
29
+ unpairedTags: [],
30
+ processEntities: true,
31
+ htmlEntities: false,
32
+ };
33
+
34
+ const props = [
35
+ 'preserveOrder',
36
+ 'attributeNamePrefix',
37
+ 'attributesGroupName',
38
+ 'textNodeName',
39
+ 'ignoreAttributes',
40
+ 'removeNSPrefix',
41
+ 'allowBooleanAttributes',
42
+ 'parseTagValue',
43
+ 'parseAttributeValue',
44
+ 'trimValues',
45
+ 'cdataPropName',
46
+ 'tagValueProcessor',
47
+ 'attributeValueProcessor',
48
+ 'numberParseOptions',
49
+ 'stopNodes',
50
+ 'alwaysCreateTextNode',
51
+ 'isArray',
52
+ 'commentPropName',
53
+ 'unpairedTags',
54
+ 'processEntities',
55
+ 'htmlEntities'
56
+ ];
57
+
58
+ const util = require('../util');
59
+
60
+ const buildOptions = function(options) {
61
+ return util.buildOptions(options, defaultOptions, props);
62
+ };
63
+
64
+ exports.buildOptions = buildOptions;
65
+ exports.defaultOptions = defaultOptions;
66
+ exports.props = props;