xml-twig 1.6.1 → 1.7.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/twig.js +68 -61
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "name": "xml-twig",
7
7
  "description": "Node module for processing huge XML documents in tree mode",
8
- "version": "1.6.1",
8
+ "version": "1.7.1",
9
9
  "main": "twig.js",
10
10
  "directories": {
11
11
  "doc": "doc"
package/twig.js CHANGED
@@ -1,9 +1,6 @@
1
1
  const SAX = 'sax';
2
2
  const EXPAT = ['expat', 'node-expat'];
3
3
 
4
- let tree;
5
- let current;
6
-
7
4
  /**
8
5
  * @external XMLWriter
9
6
  * @see {@link https://www.npmjs.com/package/xml-writer|xml-writer}
@@ -124,6 +121,9 @@ const Any = new AnyHandler();
124
121
  * @typedef Parser
125
122
  * @property {number} [currentLine] - The currently processed line in the XML-File.
126
123
  * @property {number} [currentColumn] - The currently processed column in the XML-File.
124
+ * @property {string} [file] - The name of file to be parsed. Just used for information and logging purpose.
125
+ * @property {object} [twig] - Object with XML tree and current XML element
126
+ * @property {string} [method] - The underlying parser. Either `'sax'`, `'expat'`.
127
127
  * @returns {external:sax|external:node-expat} The parser Object
128
128
  */
129
129
 
@@ -160,7 +160,7 @@ function createParser(handler, options = {}) {
160
160
  });
161
161
 
162
162
  parser.on("closetag", onClose.bind(null, handler, parser, options));
163
- parser.on("opentagstart", onStart.bind(null, {
163
+ parser.on("opentagstart", onStart.bind(null, parser, {
164
164
  handler: Array.isArray(handler) ? handler : [handler],
165
165
  options: options,
166
166
  namespaces: namespaces
@@ -174,14 +174,14 @@ function createParser(handler, options = {}) {
174
174
  let [k, v] = item.split('=');
175
175
  declaration[k] = v.replaceAll('"', '').replaceAll("'", '');
176
176
  }
177
- tree = new Twig(null);
178
- Object.defineProperty(tree, 'declaration', {
177
+ parser.twig.tree = new Twig(parser, null);
178
+ Object.defineProperty(parser.twig.tree, 'declaration', {
179
179
  value: declaration,
180
180
  writable: false,
181
181
  enumerable: true
182
182
  });
183
- } else if (tree.PI === undefined) {
184
- Object.defineProperty(tree, 'PI', {
183
+ } else if (parser.twig.tree.PI === undefined) {
184
+ Object.defineProperty(parser.twig.tree, 'PI', {
185
185
  value: { target: pi.name, data: pi.body },
186
186
  writable: false,
187
187
  enumerable: true
@@ -192,26 +192,25 @@ function createParser(handler, options = {}) {
192
192
  parser.on("attribute", function (attr) {
193
193
  if (options.xmlns && (attr.uri ?? '') !== '' && attr.local !== undefined) {
194
194
  namespaces[attr.local] = attr.uri;
195
- if (current.name.includes(':')) {
196
- Object.defineProperty(current, 'namespace', {
195
+ if (parser.twig.current.name.includes(':')) {
196
+ Object.defineProperty(parser.twig.current, 'namespace', {
197
197
  value: { local: attr.local, uri: attr.uri },
198
198
  writable: false,
199
199
  enumerable: true
200
200
  });
201
201
  } else {
202
- current.attribute(attr.name, attr.value);
202
+ parser.twig.current.attribute(attr.name, attr.value);
203
203
  }
204
204
  } else {
205
- current.attribute(attr.name, attr.value);
205
+ parser.twig.current.attribute(attr.name, attr.value);
206
206
  }
207
207
  });
208
208
  parser.on("cdata", function (str) {
209
- current.text = options.trim ? str.trim() : str;
209
+ parser.twig.current.text = options.trim ? str.trim() : str;
210
210
  });
211
211
 
212
212
  parser.on('end', function () {
213
- tree = undefined;
214
- current = undefined;
213
+ parser.twig = { current: null, tree: null };
215
214
  parser.emit("finish");
216
215
  parser.emit("close");
217
216
  });
@@ -228,19 +227,19 @@ function createParser(handler, options = {}) {
228
227
  });
229
228
 
230
229
  parser.on("endElement", onClose.bind(null, handler, parser, options));
231
- parser.on("startElement", onStart.bind(null, {
230
+ parser.on("startElement", onStart.bind(null, parser, {
232
231
  handler: Array.isArray(handler) ? handler : [handler],
233
232
  options: options,
234
233
  namespaces: namespaces
235
234
  }));
236
235
 
237
236
  parser.on('xmlDecl', function (version, encoding, standalone) {
238
- tree = new Twig(null);
237
+ parser.twig.tree = new Twig(parser, null);
239
238
  let dec = {};
240
239
  if (version !== undefined) dec.version = version;
241
240
  if (encoding !== undefined) dec.encoding = encoding;
242
241
  if (standalone !== undefined) dec.standalone = standalone;
243
- Object.defineProperty(tree, 'declaration', {
242
+ Object.defineProperty(parser.twig.tree, 'declaration', {
244
243
  value: dec,
245
244
  writable: false,
246
245
  enumerable: true
@@ -248,12 +247,11 @@ function createParser(handler, options = {}) {
248
247
  });
249
248
 
250
249
  parser.on('processingInstruction', function (target, data) {
251
- tree.PI = { target: target, data: data };
250
+ parser.twig.tree.PI = { target: target, data: data };
252
251
  });
253
252
 
254
253
  parser.on('end', function () {
255
- tree = undefined;
256
- current = undefined;
254
+ parser.twig = { current: null, tree: null };
257
255
  parser.emit("finish");
258
256
  });
259
257
 
@@ -261,6 +259,12 @@ function createParser(handler, options = {}) {
261
259
  throw new UnsupportedParser(options.method);
262
260
  }
263
261
 
262
+ Object.defineProperty(parser, 'twig', {
263
+ enumerable: true,
264
+ value: { current: null, tree: null },
265
+ writable: true
266
+ });
267
+
264
268
  Object.defineProperty(parser, 'method', {
265
269
  value: options.method,
266
270
  writable: false,
@@ -277,19 +281,19 @@ function createParser(handler, options = {}) {
277
281
 
278
282
  // Common events
279
283
  parser.on('text', function (str) {
280
- if (current === undefined || current === null) return;
281
- current.text = options.trim ? str.trim() : str;
284
+ if (parser.twig.current === null) return;
285
+ parser.twig.current.text = options.trim ? str.trim() : str;
282
286
  });
283
287
 
284
288
  parser.on("comment", function (str) {
285
- if (current.hasOwnProperty('comment')) {
286
- if (typeof current.comment === 'string') {
287
- current.comment = [current.comment, str.trim()];
289
+ if (parser.twig.current.hasOwnProperty('comment')) {
290
+ if (typeof parser.twig.current.comment === 'string') {
291
+ parser.twig.current.comment = [parser.twig.current.comment, str.trim()];
288
292
  } else {
289
- current.comment.push(str.trim());
293
+ parser.twig.current.comment.push(str.trim());
290
294
  }
291
295
  } else {
292
- Object.defineProperty(current, 'comment', {
296
+ Object.defineProperty(parser.twig.current, 'comment', {
293
297
  value: str.trim(),
294
298
  writable: true,
295
299
  enumerable: true,
@@ -312,11 +316,12 @@ function createParser(handler, options = {}) {
312
316
 
313
317
  /**
314
318
  * Common Event hanlder for starting tag
319
+ * @param {Parser} parser - The main parser object
315
320
  * @param {object} binds - Additional parameter object
316
321
  * @param {object|string} node - Node or Node name
317
322
  * @param {object} attrs - Node Attributes
318
323
  */
319
- function onStart(binds, node, attrs) {
324
+ function onStart(parser, binds, node, attrs) {
320
325
 
321
326
  const name = typeof node === 'string' ? node : node.name;
322
327
  const handler = binds.handler;
@@ -329,17 +334,17 @@ function onStart(binds, node, attrs) {
329
334
  attrNS[key] = attrs[key];
330
335
  }
331
336
 
332
- if (tree === undefined) {
333
- tree = new Twig(name, current, options.xmlns ? attrNS : attrs);
337
+ if (parser.twig.tree === null) {
338
+ parser.twig.tree = new Twig(parser, name, parser.twig.current, options.xmlns ? attrNS : attrs);
334
339
  } else {
335
- if (current.isRoot && current.name === undefined) {
336
- current.setRoot(name);
340
+ if (parser.twig.current.isRoot && parser.twig.current.name === undefined) {
341
+ parser.twig.current.setRoot(name);
337
342
  if (attrs !== undefined) {
338
343
  for (let [key, val] of Object.entries(options.xmlns ? attrNS : attrs))
339
- current.attribute(key, val);
344
+ parser.twig.current.attribute(key, val);
340
345
  }
341
346
  } else {
342
- let elt = new Twig(name, current, options.xmlns ? attrNS : attrs);
347
+ let elt = new Twig(parser, name, parser.twig.current, options.xmlns ? attrNS : attrs);
343
348
  if (options.partial) {
344
349
  for (let hndl of handler) {
345
350
  if (typeof hndl.tag === 'string' && name === hndl.tag) {
@@ -351,7 +356,7 @@ function onStart(binds, node, attrs) {
351
356
  } else if (hndl.tag instanceof RegExp && hndl.tag.test(name)) {
352
357
  elt.pin();
353
358
  break;
354
- } else if (typeof hndl.tag === 'function' && hndl.tag(name, current ?? tree)) {
359
+ } else if (typeof hndl.tag === 'function' && hndl.tag(name, parser.twig.current ?? parser.twig.tree)) {
355
360
  elt.pin();
356
361
  break;
357
362
  }
@@ -368,7 +373,7 @@ function onStart(binds, node, attrs) {
368
373
  if (name.includes(':')) {
369
374
  let prefix = name.split(':')[0];
370
375
  if (namespaces[prefix] !== undefined) {
371
- Object.defineProperty(current, 'namespace', {
376
+ Object.defineProperty(parser.twig.current, 'namespace', {
372
377
  value: { local: prefix, uri: namespaces[prefix] },
373
378
  writable: false,
374
379
  enumerable: true
@@ -381,45 +386,46 @@ function onStart(binds, node, attrs) {
381
386
  /**
382
387
  * Common Event hanlder for closing tag. On closed elements it either calls the Handler function or emits the specified event.
383
388
  * @param {TwigHandler|TwigHandler[]} handler - Object or array of element specification and function to handle elements
389
+ * @param {Parser} parser - The main parser object
384
390
  * @param {external:sax|external:node-expat} parser - SAXStream or node-expat Stream object
385
391
  * @param {ParserOptions} options - Object of optional options
386
392
  * @param {string} name - Event handler parameter
387
393
  */
388
394
  function onClose(handler, parser, options, name) {
389
- current.close();
395
+ parser.twig.current.close();
390
396
  let purge = true;
391
397
 
392
398
  for (let hndl of Array.isArray(handler) ? handler : [handler]) {
393
399
  if (hndl.tag instanceof AnyHandler) {
394
- if (typeof hndl.function === 'function') hndl.function(current ?? tree, parser);
395
- if (typeof hndl.event === 'string') parser.emit(hndl.event, current ?? tree);
400
+ if (typeof hndl.function === 'function') hndl.function(parser.twig.current ?? parser.twig.tree, parser);
401
+ if (typeof hndl.event === 'string') parser.emit(hndl.event, parser.twig.current ?? parser.twig.tree);
396
402
  purge = false;
397
- } else if (hndl.tag instanceof RootHandler && current.isRoot) {
398
- if (typeof hndl.function === 'function') hndl.function(tree, parser);
399
- if (typeof hndl.event === 'string') parser.emit(hndl.event, tree);
403
+ } else if (hndl.tag instanceof RootHandler && parser.twig.current.isRoot) {
404
+ if (typeof hndl.function === 'function') hndl.function(parser.twig.tree, parser);
405
+ if (typeof hndl.event === 'string') parser.emit(hndl.event, parser.twig.tree);
400
406
  purge = false;
401
407
  } else if (Array.isArray(hndl.tag) && hndl.tag.includes(name)) {
402
- if (typeof hndl.function === 'function') hndl.function(current ?? tree, parser);
403
- if (typeof hndl.event === 'string') parser.emit(hndl.event, current ?? tree);
408
+ if (typeof hndl.function === 'function') hndl.function(parser.twig.current ?? parser.twig.tree, parser);
409
+ if (typeof hndl.event === 'string') parser.emit(hndl.event, parser.twig.current ?? parser.twig.tree);
404
410
  purge = false;
405
411
  } else if (typeof hndl.tag === 'string' && name === hndl.tag) {
406
- if (typeof hndl.function === 'function') hndl.function(current ?? tree, parser);
407
- if (typeof hndl.event === 'string') parser.emit(hndl.event, current ?? tree);
412
+ if (typeof hndl.function === 'function') hndl.function(parser.twig.current ?? parser.twig.tree, parser);
413
+ if (typeof hndl.event === 'string') parser.emit(hndl.event, parser.twig.current ?? parser.twig.tree);
408
414
  purge = false;
409
415
  } else if (hndl.tag instanceof RegExp && hndl.tag.test(name)) {
410
- if (typeof hndl.function === 'function') hndl.function(current ?? tree, parser);
411
- if (typeof hndl.event === 'string') parser.emit(hndl.event, current ?? tree);
416
+ if (typeof hndl.function === 'function') hndl.function(parser.twig.current ?? parser.twig.tree, parser);
417
+ if (typeof hndl.event === 'string') parser.emit(hndl.event, parser.twig.current ?? parser.twig.tree);
412
418
  purge = false;
413
- } else if (typeof hndl.tag === 'function' && hndl.tag(name, current ?? tree)) {
414
- if (typeof hndl.function === 'function') hndl.function(current ?? tree, parser);
415
- if (typeof hndl.event === 'string') parser.emit(hndl.event, current ?? tree);
419
+ } else if (typeof hndl.tag === 'function' && hndl.tag(name, parser.twig.current ?? parser.twig.tree)) {
420
+ if (typeof hndl.function === 'function') hndl.function(parser.twig.current ?? parser.twig.tree, parser);
421
+ if (typeof hndl.event === 'string') parser.emit(hndl.event, parser.twig.current ?? parser.twig.tree);
416
422
  purge = false;
417
423
  }
418
424
  }
419
425
 
420
- if (options.partial && purge && !current.pinned && !current.isRoot)
421
- current.purge();
422
- current = current.parent();
426
+ if (options.partial && purge && !parser.twig.current.pinned && !parser.twig.current.isRoot)
427
+ parser.twig.parser.twig.current.purge();
428
+ parser.twig.current = parser.twig.current.parent();
423
429
 
424
430
  }
425
431
 
@@ -502,25 +508,26 @@ class Twig {
502
508
 
503
509
  /**
504
510
  * Create a new Twig object
511
+ * @param {Parser} parser - The main parser object
505
512
  * @param {?string} name - The name of the XML element
506
513
  * @param {Twig} [parent] - The parent object
507
514
  * @param {object} [attributes] - Attribute object
508
515
  * @param {string|number} [index] - Position name 'first', 'last' or the position in the current `children` array.<br>Defaults to 'last'
509
516
  */
510
- constructor(name, parent, attributes, index) {
517
+ constructor(parser, name, parent, attributes, index) {
511
518
  if (index === undefined)
512
- current = this;
519
+ parser.twig.current = this;
513
520
 
514
521
  if (name === null) {
515
522
  // Root element not available yet
516
- tree = this;
523
+ parser.twig.tree = this;
517
524
  } else {
518
525
  this.#name = name;
519
526
  if (attributes !== undefined)
520
527
  this.#attributes = attributes;
521
528
  if (parent === undefined) {
522
529
  // Root element
523
- tree = this;
530
+ parser.twig.tree = this;
524
531
  } else {
525
532
  this.#parent = parent;
526
533
  if (this.#parent.#pinned)
@@ -1216,8 +1223,8 @@ class Twig {
1216
1223
  * @param {name|number} [position] - Position name 'first', 'last' or the position in the `children`
1217
1224
  * @returns {Twig} - The appended element
1218
1225
  */
1219
- addElement = function (name, text, attributes, position) {
1220
- let twig = new Twig(name, this, attributes ?? {}, position ?? 'last');
1226
+ addElement = function (parser, name, text, attributes, position) {
1227
+ let twig = new Twig(parser, name, this, attributes ?? {}, position ?? 'last');
1221
1228
  twig.#text = text ?? null;
1222
1229
  twig.close();
1223
1230
  return twig;