@brickhouse-tech/xml2js 0.6.3 → 1.1.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/lib/parser.js CHANGED
@@ -1,395 +1,409 @@
1
- // Generated by CoffeeScript 1.12.7
2
- (function() {
3
- "use strict";
4
- var bom, defaults, defineProperty, events, isEmpty, processItem, processors, sax, setImmediate,
5
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
6
- extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
7
- hasProp = {}.hasOwnProperty;
1
+ import sax from 'sax';
2
+ import { EventEmitter } from 'events';
3
+ import { stripBOM } from './bom.js';
4
+ import * as processors from './processors.js';
5
+ import { setImmediate } from 'timers';
6
+ import { defaults } from './defaults.js';
8
7
 
9
- sax = require('sax');
8
+ // Underscore has a nice function for this, but we try to go without dependencies
9
+ function isEmpty(thing) {
10
+ return typeof thing === "object" && thing != null && Object.keys(thing).length === 0;
11
+ }
10
12
 
11
- events = require('events');
13
+ function processItem(processors, item, key) {
14
+ for (const process of processors) {
15
+ item = process(item, key);
16
+ }
17
+ return item;
18
+ }
12
19
 
13
- bom = require('./bom');
20
+ function defineProperty(obj, key, value) {
21
+ // make sure the descriptor hasn't been prototype polluted
22
+ const descriptor = Object.create(null);
23
+ descriptor.value = value;
24
+ descriptor.writable = true;
25
+ descriptor.enumerable = true;
26
+ descriptor.configurable = true;
27
+ Object.defineProperty(obj, key, descriptor);
28
+ }
14
29
 
15
- processors = require('./processors');
16
-
17
- setImmediate = require('timers').setImmediate;
30
+ export class Parser extends EventEmitter {
31
+ constructor(opts) {
32
+ super();
33
+ // if this was called without 'new', create an instance with new and return
34
+ if (!(this instanceof Parser)) {
35
+ return new Parser(opts);
36
+ }
37
+ // copy this versions default options
38
+ this.options = {};
39
+ for (const key in defaults["0.2"]) {
40
+ if (Object.prototype.hasOwnProperty.call(defaults["0.2"], key)) {
41
+ this.options[key] = defaults["0.2"][key];
42
+ }
43
+ }
44
+ // overwrite them with the specified options, if any
45
+ if (opts) {
46
+ for (const key in opts) {
47
+ if (Object.prototype.hasOwnProperty.call(opts, key)) {
48
+ this.options[key] = opts[key];
49
+ }
50
+ }
51
+ }
52
+ // define the key used for namespaces
53
+ if (this.options.xmlns) {
54
+ this.options.xmlnskey = this.options.attrkey + "ns";
55
+ }
56
+ if (this.options.normalizeTags) {
57
+ if (!this.options.tagNameProcessors) {
58
+ this.options.tagNameProcessors = [];
59
+ }
60
+ this.options.tagNameProcessors.unshift(processors.normalize);
61
+ }
18
62
 
19
- defaults = require('./defaults').defaults;
63
+ this.reset();
64
+ }
20
65
 
21
- isEmpty = function(thing) {
22
- return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
66
+ processAsync = () => {
67
+ try {
68
+ if (this.remaining.length <= this.options.chunkSize) {
69
+ const chunk = this.remaining;
70
+ this.remaining = '';
71
+ this.saxParser = this.saxParser.write(chunk);
72
+ this.saxParser.close();
73
+ } else {
74
+ const chunk = this.remaining.substr(0, this.options.chunkSize);
75
+ this.remaining = this.remaining.substr(this.options.chunkSize, this.remaining.length);
76
+ this.saxParser = this.saxParser.write(chunk);
77
+ setImmediate(this.processAsync);
78
+ }
79
+ } catch (err) {
80
+ if (!this.saxParser.errThrown) {
81
+ this.saxParser.errThrown = true;
82
+ this.emit('error', err);
83
+ }
84
+ }
23
85
  };
24
86
 
25
- processItem = function(processors, item, key) {
26
- var i, len, process;
27
- for (i = 0, len = processors.length; i < len; i++) {
28
- process = processors[i];
29
- item = process(item, key);
87
+ assignOrPush = (obj, key, newValue) => {
88
+ if (!(key in obj)) {
89
+ if (!this.options.explicitArray) {
90
+ defineProperty(obj, key, newValue);
91
+ } else {
92
+ defineProperty(obj, key, [newValue]);
93
+ }
94
+ } else {
95
+ if (!(obj[key] instanceof Array)) {
96
+ defineProperty(obj, key, [obj[key]]);
97
+ }
98
+ obj[key].push(newValue);
30
99
  }
31
- return item;
32
100
  };
33
101
 
34
- defineProperty = function(obj, key, value) {
35
- var descriptor;
36
- descriptor = Object.create(null);
37
- descriptor.value = value;
38
- descriptor.writable = true;
39
- descriptor.enumerable = true;
40
- descriptor.configurable = true;
41
- return Object.defineProperty(obj, key, descriptor);
42
- };
102
+ reset = () => {
103
+ // remove all previous listeners for events, to prevent event listener
104
+ // accumulation
105
+ this.removeAllListeners();
106
+ // make the SAX parser. tried trim and normalize, but they are not
107
+ // very helpful
108
+ this.saxParser = sax.parser(this.options.strict, {
109
+ trim: false,
110
+ normalize: false,
111
+ xmlns: this.options.xmlns
112
+ });
43
113
 
44
- exports.Parser = (function(superClass) {
45
- extend(Parser, superClass);
46
-
47
- function Parser(opts) {
48
- this.parseStringPromise = bind(this.parseStringPromise, this);
49
- this.parseString = bind(this.parseString, this);
50
- this.reset = bind(this.reset, this);
51
- this.assignOrPush = bind(this.assignOrPush, this);
52
- this.processAsync = bind(this.processAsync, this);
53
- var key, ref, value;
54
- if (!(this instanceof exports.Parser)) {
55
- return new exports.Parser(opts);
114
+ // emit one error event if the sax parser fails. this is mostly a hack, but
115
+ // the sax parser isn't state of the art either.
116
+ this.saxParser.errThrown = false;
117
+ this.saxParser.onerror = (error) => {
118
+ this.saxParser.resume();
119
+ if (!this.saxParser.errThrown) {
120
+ this.saxParser.errThrown = true;
121
+ this.emit("error", error);
56
122
  }
57
- this.options = {};
58
- ref = defaults["0.2"];
59
- for (key in ref) {
60
- if (!hasProp.call(ref, key)) continue;
61
- value = ref[key];
62
- this.options[key] = value;
123
+ };
124
+
125
+ this.saxParser.onend = () => {
126
+ if (!this.saxParser.ended) {
127
+ this.saxParser.ended = true;
128
+ this.emit("end", this.resultObject);
63
129
  }
64
- for (key in opts) {
65
- if (!hasProp.call(opts, key)) continue;
66
- value = opts[key];
67
- this.options[key] = value;
130
+ };
131
+
132
+ // another hack to avoid throwing exceptions when the parsing has ended
133
+ // but the user-supplied callback throws an error
134
+ this.saxParser.ended = false;
135
+
136
+ // always use the '#' key, even if there are no subkeys
137
+ // setting this property by and is deprecated, yet still supported.
138
+ // better pass it as explicitCharkey option to the constructor
139
+ this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
140
+ this.resultObject = null;
141
+ const stack = [];
142
+ // aliases, so we don't have to type so much
143
+ const attrkey = this.options.attrkey;
144
+ const charkey = this.options.charkey;
145
+
146
+ this.saxParser.onopentag = (node) => {
147
+ const obj = {};
148
+ obj[charkey] = "";
149
+ if (!this.options.ignoreAttrs) {
150
+ for (const key in node.attributes) {
151
+ if (Object.prototype.hasOwnProperty.call(node.attributes, key)) {
152
+ if (!(attrkey in obj) && !this.options.mergeAttrs) {
153
+ obj[attrkey] = {};
154
+ }
155
+ const newValue = this.options.attrValueProcessors
156
+ ? processItem(this.options.attrValueProcessors, node.attributes[key], key)
157
+ : node.attributes[key];
158
+ const processedKey = this.options.attrNameProcessors
159
+ ? processItem(this.options.attrNameProcessors, key)
160
+ : key;
161
+ if (this.options.mergeAttrs) {
162
+ this.assignOrPush(obj, processedKey, newValue);
163
+ } else {
164
+ defineProperty(obj[attrkey], processedKey, newValue);
165
+ }
166
+ }
167
+ }
68
168
  }
169
+
170
+ // need a place to store the node name
171
+ obj["#name"] = this.options.tagNameProcessors
172
+ ? processItem(this.options.tagNameProcessors, node.name)
173
+ : node.name;
69
174
  if (this.options.xmlns) {
70
- this.options.xmlnskey = this.options.attrkey + "ns";
175
+ obj[this.options.xmlnskey] = { uri: node.uri, local: node.local };
71
176
  }
72
- if (this.options.normalizeTags) {
73
- if (!this.options.tagNameProcessors) {
74
- this.options.tagNameProcessors = [];
75
- }
76
- this.options.tagNameProcessors.unshift(processors.normalize);
177
+ stack.push(obj);
178
+ };
179
+
180
+ this.saxParser.onclosetag = () => {
181
+ let obj = stack.pop();
182
+ const nodeName = obj["#name"];
183
+ if (!this.options.explicitChildren || !this.options.preserveChildrenOrder) {
184
+ delete obj["#name"];
77
185
  }
78
- this.reset();
79
- }
80
186
 
81
- Parser.prototype.processAsync = function() {
82
- var chunk, err;
83
- try {
84
- if (this.remaining.length <= this.options.chunkSize) {
85
- chunk = this.remaining;
86
- this.remaining = '';
87
- this.saxParser = this.saxParser.write(chunk);
88
- return this.saxParser.close();
89
- } else {
90
- chunk = this.remaining.substr(0, this.options.chunkSize);
91
- this.remaining = this.remaining.substr(this.options.chunkSize, this.remaining.length);
92
- this.saxParser = this.saxParser.write(chunk);
93
- return setImmediate(this.processAsync);
187
+ let cdata;
188
+ if (obj.cdata === true) {
189
+ cdata = obj.cdata;
190
+ delete obj.cdata;
191
+ }
192
+
193
+ const s = stack[stack.length - 1];
194
+ let emptyStr;
195
+ // remove the '#' key altogether if it's blank
196
+ if (obj[charkey].match(/^\s*$/) && !cdata) {
197
+ emptyStr = obj[charkey];
198
+ delete obj[charkey];
199
+ } else {
200
+ if (this.options.trim) {
201
+ obj[charkey] = obj[charkey].trim();
202
+ }
203
+ if (this.options.normalize) {
204
+ obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
94
205
  }
95
- } catch (error1) {
96
- err = error1;
97
- if (!this.saxParser.errThrown) {
98
- this.saxParser.errThrown = true;
99
- return this.emit(err);
206
+ obj[charkey] = this.options.valueProcessors
207
+ ? processItem(this.options.valueProcessors, obj[charkey], nodeName)
208
+ : obj[charkey];
209
+ // also do away with '#' key altogether, if there's no subkeys
210
+ // unless EXPLICIT_CHARKEY is set
211
+ if (Object.keys(obj).length === 1 && charkey in obj && !this.EXPLICIT_CHARKEY) {
212
+ obj = obj[charkey];
100
213
  }
101
214
  }
102
- };
103
215
 
104
- Parser.prototype.assignOrPush = function(obj, key, newValue) {
105
- if (!(key in obj)) {
106
- if (!this.options.explicitArray) {
107
- return defineProperty(obj, key, newValue);
216
+ if (isEmpty(obj)) {
217
+ if (typeof this.options.emptyTag === 'function') {
218
+ obj = this.options.emptyTag();
108
219
  } else {
109
- return defineProperty(obj, key, [newValue]);
220
+ obj = this.options.emptyTag !== '' ? this.options.emptyTag : emptyStr;
110
221
  }
111
- } else {
112
- if (!(obj[key] instanceof Array)) {
113
- defineProperty(obj, key, [obj[key]]);
114
- }
115
- return obj[key].push(newValue);
116
222
  }
117
- };
118
223
 
119
- Parser.prototype.reset = function() {
120
- var attrkey, charkey, ontext, stack;
121
- this.removeAllListeners();
122
- this.saxParser = sax.parser(this.options.strict, {
123
- trim: false,
124
- normalize: false,
125
- xmlns: this.options.xmlns
126
- });
127
- this.saxParser.errThrown = false;
128
- this.saxParser.onerror = (function(_this) {
129
- return function(error) {
130
- _this.saxParser.resume();
131
- if (!_this.saxParser.errThrown) {
132
- _this.saxParser.errThrown = true;
133
- return _this.emit("error", error);
134
- }
135
- };
136
- })(this);
137
- this.saxParser.onend = (function(_this) {
138
- return function() {
139
- if (!_this.saxParser.ended) {
140
- _this.saxParser.ended = true;
141
- return _this.emit("end", _this.resultObject);
142
- }
143
- };
144
- })(this);
145
- this.saxParser.ended = false;
146
- this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
147
- this.resultObject = null;
148
- stack = [];
149
- attrkey = this.options.attrkey;
150
- charkey = this.options.charkey;
151
- this.saxParser.onopentag = (function(_this) {
152
- return function(node) {
153
- var key, newValue, obj, processedKey, ref;
154
- obj = {};
155
- obj[charkey] = "";
156
- if (!_this.options.ignoreAttrs) {
157
- ref = node.attributes;
158
- for (key in ref) {
159
- if (!hasProp.call(ref, key)) continue;
160
- if (!(attrkey in obj) && !_this.options.mergeAttrs) {
161
- obj[attrkey] = {};
162
- }
163
- newValue = _this.options.attrValueProcessors ? processItem(_this.options.attrValueProcessors, node.attributes[key], key) : node.attributes[key];
164
- processedKey = _this.options.attrNameProcessors ? processItem(_this.options.attrNameProcessors, key) : key;
165
- if (_this.options.mergeAttrs) {
166
- _this.assignOrPush(obj, processedKey, newValue);
167
- } else {
168
- defineProperty(obj[attrkey], processedKey, newValue);
169
- }
170
- }
171
- }
172
- obj["#name"] = _this.options.tagNameProcessors ? processItem(_this.options.tagNameProcessors, node.name) : node.name;
173
- if (_this.options.xmlns) {
174
- obj[_this.options.xmlnskey] = {
175
- uri: node.uri,
176
- local: node.local
177
- };
224
+ if (this.options.validator != null) {
225
+ const xpath = "/" + stack.map(node => node["#name"]).concat(nodeName).join("/");
226
+ // Wrap try/catch with an inner function to allow V8 to optimise the containing function
227
+ // See https://github.com/Leonidas-from-XIV/node-xml2js/pull/369
228
+ (() => {
229
+ try {
230
+ obj = this.options.validator(xpath, s && s[nodeName], obj);
231
+ } catch (err) {
232
+ this.emit("error", err);
178
233
  }
179
- return stack.push(obj);
180
- };
181
- })(this);
182
- this.saxParser.onclosetag = (function(_this) {
183
- return function() {
184
- var cdata, emptyStr, key, node, nodeName, obj, objClone, old, s, xpath;
185
- obj = stack.pop();
186
- nodeName = obj["#name"];
187
- if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) {
188
- delete obj["#name"];
189
- }
190
- if (obj.cdata === true) {
191
- cdata = obj.cdata;
192
- delete obj.cdata;
193
- }
194
- s = stack[stack.length - 1];
195
- if (obj[charkey].match(/^\s*$/) && !cdata) {
196
- emptyStr = obj[charkey];
197
- delete obj[charkey];
198
- } else {
199
- if (_this.options.trim) {
200
- obj[charkey] = obj[charkey].trim();
201
- }
202
- if (_this.options.normalize) {
203
- obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
204
- }
205
- obj[charkey] = _this.options.valueProcessors ? processItem(_this.options.valueProcessors, obj[charkey], nodeName) : obj[charkey];
206
- if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
207
- obj = obj[charkey];
208
- }
209
- }
210
- if (isEmpty(obj)) {
211
- if (typeof _this.options.emptyTag === 'function') {
212
- obj = _this.options.emptyTag();
213
- } else {
214
- obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr;
215
- }
216
- }
217
- if (_this.options.validator != null) {
218
- xpath = "/" + ((function() {
219
- var i, len, results;
220
- results = [];
221
- for (i = 0, len = stack.length; i < len; i++) {
222
- node = stack[i];
223
- results.push(node["#name"]);
224
- }
225
- return results;
226
- })()).concat(nodeName).join("/");
227
- (function() {
228
- var err;
229
- try {
230
- return obj = _this.options.validator(xpath, s && s[nodeName], obj);
231
- } catch (error1) {
232
- err = error1;
233
- return _this.emit("error", err);
234
- }
235
- })();
234
+ })();
235
+ }
236
+
237
+ // put children into <childkey> property and unfold chars if necessary
238
+ if (this.options.explicitChildren && !this.options.mergeAttrs && typeof obj === 'object') {
239
+ if (!this.options.preserveChildrenOrder) {
240
+ let node = {};
241
+ // separate attributes
242
+ if (this.options.attrkey in obj) {
243
+ node[this.options.attrkey] = obj[this.options.attrkey];
244
+ delete obj[this.options.attrkey];
236
245
  }
237
- if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') {
238
- if (!_this.options.preserveChildrenOrder) {
239
- node = {};
240
- if (_this.options.attrkey in obj) {
241
- node[_this.options.attrkey] = obj[_this.options.attrkey];
242
- delete obj[_this.options.attrkey];
243
- }
244
- if (!_this.options.charsAsChildren && _this.options.charkey in obj) {
245
- node[_this.options.charkey] = obj[_this.options.charkey];
246
- delete obj[_this.options.charkey];
247
- }
248
- if (Object.getOwnPropertyNames(obj).length > 0) {
249
- node[_this.options.childkey] = obj;
250
- }
251
- obj = node;
252
- } else if (s) {
253
- s[_this.options.childkey] = s[_this.options.childkey] || [];
254
- objClone = {};
255
- for (key in obj) {
256
- if (!hasProp.call(obj, key)) continue;
257
- defineProperty(objClone, key, obj[key]);
258
- }
259
- s[_this.options.childkey].push(objClone);
260
- delete obj["#name"];
261
- if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
262
- obj = obj[charkey];
263
- }
264
- }
246
+ // separate char data
247
+ if (!this.options.charsAsChildren && this.options.charkey in obj) {
248
+ node[this.options.charkey] = obj[this.options.charkey];
249
+ delete obj[this.options.charkey];
265
250
  }
266
- if (stack.length > 0) {
267
- return _this.assignOrPush(s, nodeName, obj);
268
- } else {
269
- if (_this.options.explicitRoot) {
270
- old = obj;
271
- obj = {};
272
- defineProperty(obj, nodeName, old);
273
- }
274
- _this.resultObject = obj;
275
- _this.saxParser.ended = true;
276
- return _this.emit("end", _this.resultObject);
251
+
252
+ if (Object.getOwnPropertyNames(obj).length > 0) {
253
+ node[this.options.childkey] = obj;
277
254
  }
278
- };
279
- })(this);
280
- ontext = (function(_this) {
281
- return function(text) {
282
- var charChild, s;
283
- s = stack[stack.length - 1];
284
- if (s) {
285
- s[charkey] += text;
286
- if (_this.options.explicitChildren && _this.options.preserveChildrenOrder && _this.options.charsAsChildren && (_this.options.includeWhiteChars || text.replace(/\\n/g, '').trim() !== '')) {
287
- s[_this.options.childkey] = s[_this.options.childkey] || [];
288
- charChild = {
289
- '#name': '__text__'
290
- };
291
- charChild[charkey] = text;
292
- if (_this.options.normalize) {
293
- charChild[charkey] = charChild[charkey].replace(/\s{2,}/g, " ").trim();
294
- }
295
- s[_this.options.childkey].push(charChild);
255
+
256
+ obj = node;
257
+ } else if (s) {
258
+ // append current node onto parent's <childKey> array
259
+ s[this.options.childkey] = s[this.options.childkey] || [];
260
+ // push a clone so that the node in the children array can receive the #name property while the original obj can do without it
261
+ const objClone = {};
262
+ for (const key in obj) {
263
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
264
+ defineProperty(objClone, key, obj[key]);
296
265
  }
297
- return s;
298
266
  }
299
- };
300
- })(this);
301
- this.saxParser.ontext = ontext;
302
- return this.saxParser.oncdata = (function(_this) {
303
- return function(text) {
304
- var s;
305
- s = ontext(text);
306
- if (s) {
307
- return s.cdata = true;
267
+ s[this.options.childkey].push(objClone);
268
+ delete obj["#name"];
269
+ // re-check whether we can collapse the node now to just the charkey value
270
+ if (Object.keys(obj).length === 1 && charkey in obj && !this.EXPLICIT_CHARKEY) {
271
+ obj = obj[charkey];
308
272
  }
309
- };
310
- })(this);
311
- };
312
-
313
- Parser.prototype.parseString = function(str, cb) {
314
- var err;
315
- if ((cb != null) && typeof cb === "function") {
316
- this.on("end", function(result) {
317
- this.reset();
318
- return cb(null, result);
319
- });
320
- this.on("error", function(err) {
321
- this.reset();
322
- return cb(err);
323
- });
324
- }
325
- try {
326
- str = str.toString();
327
- if (str.trim() === '') {
328
- this.emit("end", null);
329
- return true;
330
- }
331
- str = bom.stripBOM(str);
332
- if (this.options.async) {
333
- this.remaining = str;
334
- setImmediate(this.processAsync);
335
- return this.saxParser;
336
273
  }
337
- return this.saxParser.write(str).close();
338
- } catch (error1) {
339
- err = error1;
340
- if (!(this.saxParser.errThrown || this.saxParser.ended)) {
341
- this.emit('error', err);
342
- return this.saxParser.errThrown = true;
343
- } else if (this.saxParser.ended) {
344
- throw err;
274
+ }
275
+
276
+ // check whether we closed all the open tags
277
+ if (stack.length > 0) {
278
+ this.assignOrPush(s, nodeName, obj);
279
+ } else {
280
+ // if explicitRoot was specified, wrap stuff in the root tag name
281
+ if (this.options.explicitRoot) {
282
+ // avoid circular references
283
+ const old = obj;
284
+ obj = {};
285
+ defineProperty(obj, nodeName, old);
345
286
  }
287
+
288
+ this.resultObject = obj;
289
+ // parsing has ended, mark that so we won't throw exceptions from
290
+ // here anymore
291
+ this.saxParser.ended = true;
292
+ this.emit("end", this.resultObject);
346
293
  }
347
294
  };
348
295
 
349
- Parser.prototype.parseStringPromise = function(str) {
350
- return new Promise((function(_this) {
351
- return function(resolve, reject) {
352
- return _this.parseString(str, function(err, value) {
353
- if (err) {
354
- return reject(err);
355
- } else {
356
- return resolve(value);
357
- }
358
- });
359
- };
360
- })(this));
296
+ const ontext = (text) => {
297
+ const s = stack[stack.length - 1];
298
+ if (s) {
299
+ s[charkey] += text;
300
+
301
+ if (this.options.explicitChildren && this.options.preserveChildrenOrder &&
302
+ this.options.charsAsChildren &&
303
+ (this.options.includeWhiteChars || text.replace(/\\n/g, '').trim() !== '')) {
304
+ s[this.options.childkey] = s[this.options.childkey] || [];
305
+ const charChild = {
306
+ '#name': '__text__'
307
+ };
308
+ charChild[charkey] = text;
309
+ if (this.options.normalize) {
310
+ charChild[charkey] = charChild[charkey].replace(/\s{2,}/g, " ").trim();
311
+ }
312
+ s[this.options.childkey].push(charChild);
313
+ }
314
+
315
+ return s;
316
+ }
361
317
  };
362
318
 
363
- return Parser;
319
+ this.saxParser.ontext = ontext;
320
+ this.saxParser.oncdata = (text) => {
321
+ const s = ontext(text);
322
+ if (s) {
323
+ s.cdata = true;
324
+ }
325
+ };
326
+ };
364
327
 
365
- })(events);
328
+ parseString = (str, cb) => {
329
+ if (cb != null && typeof cb === "function") {
330
+ this.on("end", (result) => {
331
+ this.reset();
332
+ cb(null, result);
333
+ });
334
+ this.on("error", (err) => {
335
+ this.reset();
336
+ cb(err);
337
+ });
338
+ }
366
339
 
367
- exports.parseString = function(str, a, b) {
368
- var cb, options, parser;
369
- if (b != null) {
370
- if (typeof b === 'function') {
371
- cb = b;
340
+ try {
341
+ str = str.toString();
342
+ if (str.trim() === '') {
343
+ this.emit("end", null);
344
+ return true;
372
345
  }
373
- if (typeof a === 'object') {
374
- options = a;
346
+
347
+ str = stripBOM(str);
348
+ if (this.options.async) {
349
+ this.remaining = str;
350
+ setImmediate(this.processAsync);
351
+ return this.saxParser;
375
352
  }
376
- } else {
377
- if (typeof a === 'function') {
378
- cb = a;
353
+ this.saxParser.write(str).close();
354
+ } catch (err) {
355
+ if (!this.saxParser.errThrown && !this.saxParser.ended) {
356
+ this.emit('error', err);
357
+ this.saxParser.errThrown = true;
358
+ } else if (this.saxParser.ended) {
359
+ throw err;
379
360
  }
380
- options = {};
381
361
  }
382
- parser = new exports.Parser(options);
383
- return parser.parseString(str, cb);
384
362
  };
385
363
 
386
- exports.parseStringPromise = function(str, a) {
387
- var options, parser;
364
+ parseStringPromise = (str) => {
365
+ return new Promise((resolve, reject) => {
366
+ this.parseString(str, (err, value) => {
367
+ if (err) {
368
+ reject(err);
369
+ } else {
370
+ resolve(value);
371
+ }
372
+ });
373
+ });
374
+ };
375
+ }
376
+
377
+ export function parseString(str, a, b) {
378
+ let cb, options;
379
+ // let's determine what we got as arguments
380
+ if (b != null) {
381
+ if (typeof b === 'function') {
382
+ cb = b;
383
+ }
388
384
  if (typeof a === 'object') {
389
385
  options = a;
390
386
  }
391
- parser = new exports.Parser(options);
392
- return parser.parseStringPromise(str);
393
- };
387
+ } else {
388
+ // well, b is not set, so a has to be a callback
389
+ if (typeof a === 'function') {
390
+ cb = a;
391
+ }
392
+ // and options should be empty - default
393
+ options = {};
394
+ }
395
+
396
+ // the rest is super-easy
397
+ const parser = new Parser(options);
398
+ return parser.parseString(str, cb);
399
+ }
400
+
401
+ export function parseStringPromise(str, a) {
402
+ let options = {};
403
+ if (typeof a === 'object') {
404
+ options = a;
405
+ }
394
406
 
395
- }).call(this);
407
+ const parser = new Parser(options);
408
+ return parser.parseStringPromise(str);
409
+ }