jtlt 0.23.0 → 0.24.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.
@@ -230,7 +230,7 @@ class AbstractJoiningTransformer {
230
230
  `Function "${name}" must have either 'body' or 'sequence' attribute`
231
231
  );
232
232
  }
233
- /* c8 ignore stop */
233
+ /* c8 ignore stop -- See above */
234
234
 
235
235
  // Create function key with arity
236
236
  const arity = params.length;
@@ -280,7 +280,7 @@ class AbstractJoiningTransformer {
280
280
  `supported in this context)`
281
281
  );
282
282
  }
283
- /* c8 ignore stop */
283
+ /* c8 ignore stop -- See above */
284
284
  return functionDef.body(...args);
285
285
  }
286
286
 
@@ -361,10 +361,7 @@ class AbstractJoiningTransformer {
361
361
  this._flushPendingNamespace(alias);
362
362
 
363
363
  if (colonIdx === -1) {
364
- if (alias === '#default') {
365
- return elemName;
366
- }
367
- return alias + ':' + elemName;
364
+ return alias === '#default' ? elemName : alias + ':' + elemName;
368
365
  }
369
366
 
370
367
  return (alias === '#default'
@@ -392,13 +389,14 @@ class AbstractJoiningTransformer {
392
389
  */
393
390
  _trackAttributePrefix (attrName) {
394
391
  const colonIdx = attrName.indexOf(':');
395
- if (colonIdx !== -1 && !attrName.startsWith('xmlns')) {
396
- const prefix = attrName.slice(0, colonIdx);
397
- this._usedNamespacePrefixes.add(prefix);
398
-
399
- // If this prefix was buffered, output it now
400
- this._flushPendingNamespace(prefix);
392
+ if (colonIdx === -1 || attrName.startsWith('xmlns')) {
393
+ return;
401
394
  }
395
+ const prefix = attrName.slice(0, colonIdx);
396
+ this._usedNamespacePrefixes.add(prefix);
397
+
398
+ // If this prefix was buffered, output it now
399
+ this._flushPendingNamespace(prefix);
402
400
  }
403
401
 
404
402
  /**
@@ -445,10 +443,12 @@ class AbstractJoiningTransformer {
445
443
  const cfg = /** @type {Record<string, unknown>} */ (this._cfg);
446
444
  const oldCfgProp = cfg[prop];
447
445
  cfg[prop] = val;
448
- if (cb) {
449
- cb.call(this);
450
- cfg[prop] = oldCfgProp;
446
+ if (!cb) {
447
+ return;
451
448
  }
449
+
450
+ cb.call(this);
451
+ cfg[prop] = oldCfgProp;
452
452
  }
453
453
  }
454
454
 
@@ -357,7 +357,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
357
357
  omitXmlDeclaration, doctypePublic, doctypeSystem, method
358
358
  /* c8 ignore start -- outputConfig ?? attribution issue */
359
359
  } = this._outputConfig ?? {};
360
- /* c8 ignore stop */
360
+ /* c8 ignore stop -- See above */
361
361
 
362
362
  /* c8 ignore start -- namespace/prefix detection branches */
363
363
  const dtd = this._cfg.document.implementation.createDocumentType(
@@ -379,7 +379,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
379
379
  elementName,
380
380
  dtd
381
381
  );
382
- /* c8 ignore stop */
382
+ /* c8 ignore stop -- See above */
383
383
 
384
384
  /* c8 ignore start -- third OR condition short-circuits */
385
385
  if (!omitXmlDeclaration && (
@@ -403,7 +403,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
403
403
  doc.firstChild
404
404
  );
405
405
  }
406
- /* c8 ignore stop */
406
+ /* c8 ignore stop -- See above */
407
407
 
408
408
  // Push the document to _docs
409
409
  this._docs.push(doc);
@@ -587,15 +587,17 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
587
587
  */
588
588
  _flushPendingNamespace (prefix) {
589
589
  const pending = this._pendingNamespaceMap.get(prefix);
590
- if (pending) {
591
- /** @type {Element} */
592
- (this._dom).setAttributeNS(
593
- 'http://www.w3.org/2000/xmlns/',
594
- pending.prefix === '#default' ? 'xmlns' : 'xmlns:' + pending.prefix,
595
- this._replaceCharacterMaps(pending.namespaceURI)
596
- );
597
- this._pendingNamespaceMap.delete(prefix);
590
+ if (!pending) {
591
+ return;
598
592
  }
593
+
594
+ /** @type {Element} */
595
+ (this._dom).setAttributeNS(
596
+ 'http://www.w3.org/2000/xmlns/',
597
+ pending.prefix === '#default' ? 'xmlns' : 'xmlns:' + pending.prefix,
598
+ this._replaceCharacterMaps(pending.namespaceURI)
599
+ );
600
+ this._pendingNamespaceMap.delete(prefix);
599
601
  }
600
602
  /**
601
603
  * @param {string} name
@@ -488,10 +488,9 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
488
488
  // If children provided as array, copy (may be primitives or nested JML)
489
489
  if (Array.isArray(childNodes) && childNodes.length) {
490
490
  jmlChildren.push(...childNodes.map((childNode) => {
491
- if (typeof childNode === 'string') {
492
- return this._replaceCharacterMaps(childNode);
493
- }
494
- return childNode;
491
+ return typeof childNode === 'string'
492
+ ? this._replaceCharacterMaps(childNode)
493
+ : childNode;
495
494
  }));
496
495
  }
497
496
 
@@ -537,7 +536,7 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
537
536
  standalone
538
537
  };
539
538
  }
540
- /* c8 ignore stop */
539
+ /* c8 ignore stop -- See above */
541
540
 
542
541
  const doc = {$document: {
543
542
  ...(xmlDeclaration ? {xmlDeclaration} : {}),
@@ -641,20 +640,22 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
641
640
  }
642
641
 
643
642
  const pending = this._pendingNamespaceMap.get(prefix);
644
- if (pending) {
645
- const top = /** @type {ElementInfo} */ (this._elementStack.at(-1));
646
- const {attsObj} = top;
647
-
648
- if (!attsObj.xmlns) {
649
- attsObj.xmlns = {};
650
- }
643
+ if (!pending) {
644
+ return;
645
+ }
651
646
 
652
- /** @type {Record<string, string>} */
653
- (attsObj.xmlns)[pending.prefix === '#default' ? '' : pending.prefix] =
654
- this._replaceCharacterMaps(pending.namespaceURI);
647
+ const top = /** @type {ElementInfo} */ (this._elementStack.at(-1));
648
+ const {attsObj} = top;
655
649
 
656
- this._pendingNamespaceMap.delete(prefix);
650
+ if (!attsObj.xmlns) {
651
+ attsObj.xmlns = {};
657
652
  }
653
+
654
+ /** @type {Record<string, string>} */
655
+ (attsObj.xmlns)[pending.prefix === '#default' ? '' : pending.prefix] =
656
+ this._replaceCharacterMaps(pending.namespaceURI);
657
+
658
+ this._pendingNamespaceMap.delete(prefix);
658
659
  }
659
660
  /**
660
661
  * Adds/updates an attribute for the most recently open element built via
@@ -882,7 +883,7 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
882
883
  standalone
883
884
  };
884
885
  }
885
- /* c8 ignore stop */
886
+ /* c8 ignore stop -- See above */
886
887
 
887
888
  resultDoc = {$document: {
888
889
  ...(xmlDeclaration ? {xmlDeclaration} : {}),
@@ -921,10 +922,9 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
921
922
  */
922
923
  _usePropertySets (obj, psName) {
923
924
  // Merge the named property set (if present) into the provided object
924
- if (Object.hasOwn(this.propertySets, psName)) {
925
- return Object.assign(obj, this.propertySets[psName]);
926
- }
927
- return obj;
925
+ return Object.hasOwn(this.propertySets, psName)
926
+ ? Object.assign(obj, this.propertySets[psName])
927
+ : obj;
928
928
  }
929
929
  }
930
930
 
@@ -34,10 +34,9 @@ class JSONPathTransformer {
34
34
  */
35
35
  template (value) {
36
36
  // Emit property names for the current object context
37
- if (value && typeof value === 'object' && !Array.isArray(value)) {
38
- return Object.keys(value).join('');
39
- }
40
- return '';
37
+ return value && typeof value === 'object' && !Array.isArray(value)
38
+ ? Object.keys(value).join('')
39
+ : '';
41
40
  }
42
41
  },
43
42
  transformObjects: {
@@ -319,10 +319,8 @@ class JSONPathTransformerContext {
319
319
  /* const results = */ this._getJoiningTransformer();
320
320
  const modeMatchedTemplates = this._templates.filter((templateObj) => {
321
321
  // Exclude named-only templates (those with name but no path)
322
- if (templateObj.name && !templateObj.path) {
323
- return false;
324
- }
325
- return ((mode && mode === templateObj.mode) ||
322
+ return (!templateObj.name || templateObj.path) &&
323
+ ((mode && mode === templateObj.mode) ||
326
324
  (!mode && !templateObj.mode));
327
325
  });
328
326
 
@@ -382,10 +380,7 @@ class JSONPathTransformerContext {
382
380
  if (Number.isNaN(an)) {
383
381
  return Number(order);
384
382
  }
385
- if (Number.isNaN(bn)) {
386
- return -order;
387
- }
388
- return (an - bn) * order;
383
+ return Number.isNaN(bn) ? -order : (an - bn) * order;
389
384
  }
390
385
  // text
391
386
  /* c8 ignore next 2 -- all null/undefined tested; OR short-circuit */
@@ -460,7 +455,7 @@ class JSONPathTransformerContext {
460
455
  if (!templateObj.path) {
461
456
  return false;
462
457
  }
463
- /* c8 ignore stop */
458
+ /* c8 ignore stop -- See above */
464
459
  const queryResult = /** @type {any[]} */ (
465
460
  (/** @type {any} */ (jsonpath))({
466
461
  path: JSONPathTransformer.makeJSONPathAbsolute(
@@ -554,7 +549,7 @@ class JSONPathTransformerContext {
554
549
  : (that._config.specificityPriorityResolver && b.path
555
550
  ? that._config.specificityPriorityResolver(b.path)
556
551
  : 0);
557
- /* c8 ignore stop */
552
+ /* c8 ignore stop -- See above */
558
553
 
559
554
  if (aPriority === bPriority) {
560
555
  that._triggerEqualPriorityError();
@@ -676,13 +671,13 @@ class JSONPathTransformerContext {
676
671
  // @ts-expect-error -- _openTagState: StringJoiningTransformer
677
672
  joiner._openTagState = false;
678
673
  }
679
- /* c8 ignore stop */
674
+ /* c8 ignore stop -- See above */
680
675
  joiner.append(resolvedRet);
681
676
  }
682
677
  that._parent = parent;
683
678
  // Matches reached here always carry a parentProperty; the sync
684
679
  // path covers the root-node fallback.
685
- /* c8 ignore next */
680
+ /* c8 ignore next -- See comment just above */
686
681
  that._parentProperty = (parentProperty ?? that._parentProperty);
687
682
  that._currPath = _oldPath;
688
683
  return undefined;
@@ -700,7 +695,7 @@ class JSONPathTransformerContext {
700
695
  // @ts-expect-error -- _openTagState only on StringJoiningTransformer
701
696
  joiner._openTagState = false;
702
697
  }
703
- /* c8 ignore stop */
698
+ /* c8 ignore stop -- See above */
704
699
  joiner.append(
705
700
  /** @type {string|Node|*} */ (ret)
706
701
  );
@@ -825,9 +820,22 @@ class JSONPathTransformerContext {
825
820
  * `void`, not `void|Promise<void>` — see the note on `SimpleCallback`
826
821
  * in JSONJoiningTransformer.js.)
827
822
  * @param {SortSpec<V>} [sort] - Sort spec
823
+ * @param {string} [keyVar] - When given, binds each iteration's key —
824
+ * the array index, or the object property name when `select` is an
825
+ * object-wildcard path like `$.*` (jsonpath-plus's own `parentProperty`
826
+ * on a `resultType: 'all'` match) — to this name for the duration of
827
+ * that iteration, readable back via a bare `$name` reference (same
828
+ * convention as `variable()`), same as `_params[0]`/`this.vars` are
829
+ * already scoped fresh per iteration. Lets a template render
830
+ * "property: value" pairs, or skip one property by name via `$if`,
831
+ * while iterating an object's own properties — jsonpath-plus's match
832
+ * data already carries this; `select`s naming a bound variable (the
833
+ * bare-`$name` convention, below) carry the array index too, but never
834
+ * a meaningful key for a non-array bound value (treated as a single,
835
+ * length-1-sequence item — there's no "property name" for that case).
828
836
  * @returns {this|Promise<this>}
829
837
  */
830
- forEach (select, cb, sort) {
838
+ forEach (select, cb, sort, keyVar) {
831
839
  // eslint-disable-next-line unicorn/no-this-assignment -- Temporary
832
840
  const that = this;
833
841
  // A bare `$name` (matching `if()`/`valueOf()`/comparisons' own
@@ -840,10 +848,13 @@ class JSONPathTransformerContext {
840
848
  const param = paramRef && paramRef.groups
841
849
  ? this._lookupParam(paramRef.groups.name)
842
850
  : {has: false, value: undefined};
843
- /** @type {{value: any}[]} */
851
+ /** @type {{value: any, parentProperty?: string|number}[]} */
844
852
  const matches = param.has
845
- ? (Array.isArray(param.value) ? param.value : [param.value]).
846
- map((value) => ({value}))
853
+ ? (Array.isArray(param.value)
854
+ // An array's own index is a meaningful key (see `keyVar`, above);
855
+ // a non-array value wrapped as a length-1 sequence has none.
856
+ ? param.value.map((value, index) => ({value, parentProperty: index}))
857
+ : [{value: param.value}])
847
858
  : /** @type {any} */ (jsonpath)({
848
859
  path: select,
849
860
  json: this._contextObj,
@@ -894,10 +905,7 @@ class JSONPathTransformerContext {
894
905
  if (Number.isNaN(an)) {
895
906
  return Number(order);
896
907
  }
897
- if (Number.isNaN(bn)) {
898
- return -order;
899
- }
900
- return (an - bn) * order;
908
+ return Number.isNaN(bn) ? -order : (an - bn) * order;
901
909
  }
902
910
  /* c8 ignore next 2 -- all null/undefined tested; OR short-circuit */
903
911
  const aStr = aVal === null || aVal === undefined ? '' : String(aVal);
@@ -960,7 +968,7 @@ class JSONPathTransformerContext {
960
968
  const prevVars = that.vars;
961
969
  that._params = {0: m.value};
962
970
  that._contextObj = m.value;
963
- that.vars = {};
971
+ that.vars = keyVar ? {[keyVar]: m.parentProperty} : {};
964
972
  // `cb`'s declared return type is plain `void` (see the parameter's
965
973
  // JSDoc) — cast here to duck-type the real value; see the note on
966
974
  // `SimpleCallback` in JSONJoiningTransformer.js.
@@ -991,7 +999,7 @@ class JSONPathTransformerContext {
991
999
  const pv = that.vars;
992
1000
  that._params = {0: next.value};
993
1001
  that._contextObj = next.value;
994
- that.vars = {};
1002
+ that.vars = keyVar ? {[keyVar]: next.parentProperty} : {};
995
1003
  try {
996
1004
  // eslint-disable-next-line no-await-in-loop -- Sequential order
997
1005
  await cb.call(that, next.value);
@@ -1241,23 +1249,25 @@ class JSONPathTransformerContext {
1241
1249
  currentGroup.push(m.value);
1242
1250
  const endMatch = evalInContext(groupEndingWith, m.value);
1243
1251
 
1244
- if (endMatch) {
1245
- const prevContext = this._contextObj;
1246
- const prevParams = this._params;
1247
- const prevVars = this.vars;
1248
- this.vars = {};
1249
- try {
1250
- this._contextObj = currentGroup;
1251
- /** @type {any} */ (this)._currentGroup = currentGroup;
1252
- cb.call(this, null, currentGroup, this);
1253
- } finally {
1254
- this._contextObj = prevContext;
1255
- this._params = prevParams;
1256
- this.vars = prevVars;
1257
- delete /** @type {any} */ (this)._currentGroup;
1258
- }
1259
- currentGroup = [];
1252
+ if (!endMatch) {
1253
+ continue;
1260
1254
  }
1255
+
1256
+ const prevContext = this._contextObj;
1257
+ const prevParams = this._params;
1258
+ const prevVars = this.vars;
1259
+ this.vars = {};
1260
+ try {
1261
+ this._contextObj = currentGroup;
1262
+ /** @type {any} */ (this)._currentGroup = currentGroup;
1263
+ cb.call(this, null, currentGroup, this);
1264
+ } finally {
1265
+ this._contextObj = prevContext;
1266
+ this._params = prevParams;
1267
+ this.vars = prevVars;
1268
+ delete /** @type {any} */ (this)._currentGroup;
1269
+ }
1270
+ currentGroup = [];
1261
1271
  }
1262
1272
 
1263
1273
  // Process last group if not ended
@@ -1325,10 +1335,7 @@ class JSONPathTransformerContext {
1325
1335
  if (Number.isNaN(an)) {
1326
1336
  return order;
1327
1337
  }
1328
- if (Number.isNaN(bn)) {
1329
- return -order;
1330
- }
1331
- return (an - bn) * order;
1338
+ return Number.isNaN(bn) ? -order : (an - bn) * order;
1332
1339
  }
1333
1340
  /* c8 ignore next 2 -- all null/undefined tested; OR short-circuit */
1334
1341
  const aStr = aVal === null || aVal === undefined ? '' : String(aVal);
@@ -1626,12 +1633,11 @@ class JSONPathTransformerContext {
1626
1633
  * @returns {string} - Captured group or empty string
1627
1634
  */
1628
1635
  const getRegexGroup = (groupNumber) => {
1629
- if (!currentCapturedGroups ||
1636
+ return !currentCapturedGroups ||
1630
1637
  groupNumber < 0 ||
1631
- groupNumber >= currentCapturedGroups.length) {
1632
- return '';
1633
- }
1634
- return currentCapturedGroups[groupNumber] || '';
1638
+ groupNumber >= currentCapturedGroups.length
1639
+ ? ''
1640
+ : currentCapturedGroups[groupNumber] || '';
1635
1641
  };
1636
1642
 
1637
1643
  // Save previous context to restore later
@@ -1734,7 +1740,7 @@ class JSONPathTransformerContext {
1734
1740
  // For non-functions, attempting structuredClone again would rethrow;
1735
1741
  // use shallow clone to retain non-serializable props like Symbols.
1736
1742
  clone = Array.isArray(val) ? [...val] : {...val};
1737
- /* c8 ignore stop */
1743
+ /* c8 ignore stop -- See above */
1738
1744
  }
1739
1745
  } else {
1740
1746
  // Primitives/functions copied by value/reference semantics naturally.
@@ -1766,7 +1772,7 @@ class JSONPathTransformerContext {
1766
1772
  }
1767
1773
  } else { /* c8 ignore start -- primitive branch attribution variance */
1768
1774
  clone = src; // Primitive/function - nothing to shallow clone
1769
- } /* c8 ignore stop */
1775
+ } /* c8 ignore stop -- See above */
1770
1776
  this._getJoiningTransformer().append(clone);
1771
1777
  return this;
1772
1778
  }
@@ -1825,10 +1831,9 @@ class JSONPathTransformerContext {
1825
1831
  if (Object.hasOwn(this.vars, name)) {
1826
1832
  return {has: true, value: this.vars[name]};
1827
1833
  }
1828
- if (Object.hasOwn(this._runtimeParams, name)) {
1829
- return {has: true, value: this._runtimeParams[name]};
1830
- }
1831
- return {has: false, value: undefined};
1834
+ return Object.hasOwn(this._runtimeParams, name)
1835
+ ? {has: true, value: this._runtimeParams[name]}
1836
+ : {has: false, value: undefined};
1832
1837
  }
1833
1838
 
1834
1839
  /**
@@ -2762,15 +2767,9 @@ class JSONPathTransformerContext {
2762
2767
  }
2763
2768
  // Single item; apply scalar truthiness
2764
2769
  const single = val[0];
2765
- if (single && typeof single === 'object') {
2766
- return true;
2767
- }
2768
- return Boolean(single);
2770
+ return single && typeof single === 'object' ? true : Boolean(single);
2769
2771
  }
2770
- if (val && typeof val === 'object') {
2771
- return true;
2772
- }
2773
- return Boolean(val);
2772
+ return val && typeof val === 'object' ? true : Boolean(val);
2774
2773
  }
2775
2774
 
2776
2775
  /**
@@ -2821,10 +2820,9 @@ class JSONPathTransformerContext {
2821
2820
  return null;
2822
2821
  }
2823
2822
  const right = this._parseLiteral(m.groups.right.trim());
2824
- if (!right) {
2825
- return null;
2826
- }
2827
- return {left: m.groups.left, op: m.groups.op, right: right.value};
2823
+ return !right
2824
+ ? null
2825
+ : {left: m.groups.left, op: m.groups.op, right: right.value};
2828
2826
  }
2829
2827
 
2830
2828
  /**
@@ -2855,10 +2853,7 @@ class JSONPathTransformerContext {
2855
2853
  if (str === 'null') {
2856
2854
  return {value: null};
2857
2855
  }
2858
- if (str === 'undefined') {
2859
- return {value: undefined};
2860
- }
2861
- return null;
2856
+ return str === 'undefined' ? {value: undefined} : null;
2862
2857
  }
2863
2858
 
2864
2859
  /**
@@ -562,7 +562,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
562
562
  standalone ? ` standalone="yes"` : ''
563
563
  }?>\n`;
564
564
  }
565
- /* c8 ignore stop */
565
+ /* c8 ignore stop -- See above */
566
566
 
567
567
  const doctype = bareDoctype ||
568
568
  doctypePublic !== undefined || doctypeSystem !== undefined
@@ -733,13 +733,15 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
733
733
  */
734
734
  _flushPendingNamespace (prefix) {
735
735
  const pending = this._pendingNamespaceMap.get(prefix);
736
- if (pending) {
737
- this.append(' ' + (
738
- pending.prefix === '#default' ? 'xmlns' : 'xmlns:' + pending.prefix
739
- ) + '="' +
740
- this._replaceCharacterMaps(pending.namespaceURI) + '"');
741
- this._pendingNamespaceMap.delete(prefix);
736
+ if (!pending) {
737
+ return;
742
738
  }
739
+
740
+ this.append(' ' + (
741
+ pending.prefix === '#default' ? 'xmlns' : 'xmlns:' + pending.prefix
742
+ ) + '="' +
743
+ this._replaceCharacterMaps(pending.namespaceURI) + '"');
744
+ this._pendingNamespaceMap.delete(prefix);
743
745
  }
744
746
 
745
747
  /**
@@ -50,10 +50,9 @@ class XPathTransformer {
50
50
  return {path: template[0], template: template[1]};
51
51
  }
52
52
  // Normalize 'match' to 'path' for XSLT compatibility
53
- if (template.match && !template.path) {
54
- return {...template, path: template.match};
55
- }
56
- return template;
53
+ return template.match && !template.path
54
+ ? {...template, path: template.match}
55
+ : template;
57
56
  });
58
57
  this.templates.forEach((template) => {
59
58
  if ('name' in template && template.name &&