jtlt 0.2.0 → 0.3.0
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/CHANGES.md +18 -0
- package/README.md +16 -87
- package/demo/calltemplate-params-demo.js +138 -0
- package/demo/index.html +31 -0
- package/demo/index.js +30 -0
- package/demo/xpath2-placeholder.js +1 -0
- package/dist/AbstractJoiningTransformer.d.ts +83 -9
- package/dist/AbstractJoiningTransformer.d.ts.map +1 -1
- package/dist/DOMJoiningTransformer.d.ts +85 -25
- package/dist/DOMJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONJoiningTransformer.d.ts +159 -51
- package/dist/JSONJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformer.d.ts +37 -38
- package/dist/JSONPathTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformerContext.d.ts +247 -121
- package/dist/JSONPathTransformerContext.d.ts.map +1 -1
- package/dist/StringJoiningTransformer.d.ts +132 -41
- package/dist/StringJoiningTransformer.d.ts.map +1 -1
- package/dist/XPathTransformer.d.ts +35 -20
- package/dist/XPathTransformer.d.ts.map +1 -1
- package/dist/XPathTransformerContext.d.ts +191 -99
- package/dist/XPathTransformerContext.d.ts.map +1 -1
- package/dist/index-browser.d.ts +4 -0
- package/dist/index-browser.d.ts.map +1 -0
- package/dist/index-node.d.ts +4 -0
- package/dist/index-node.d.ts.map +1 -0
- package/dist/index.d.ts +330 -57
- package/dist/index.d.ts.map +1 -1
- package/dist/types.d.ts +204 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/API.expanded.md +167 -2
- package/docs/API.md +91 -1
- package/docs/TO-DO.md +144 -0
- package/docs/calltemplate-params.md +251 -0
- package/eslint.config.js +9 -5
- package/package.json +13 -7
- package/pnpm-workspace.yaml +1 -0
- package/src/AbstractJoiningTransformer.js +54 -15
- package/src/DOMJoiningTransformer.js +275 -28
- package/src/JSONJoiningTransformer.js +351 -70
- package/src/JSONPathTransformer.js +48 -30
- package/src/JSONPathTransformerContext.js +308 -104
- package/src/StringJoiningTransformer.js +311 -57
- package/src/XPathTransformer.js +27 -12
- package/src/XPathTransformerContext.js +467 -89
- package/src/index-browser.js +5 -0
- package/src/index-node.js +7 -0
- package/src/index.js +498 -97
- package/typings/xpath2-js.d.ts +40 -1
- package/src/types/xpath2-js.d.ts +0 -2
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
import {JSONPath as jsonpath} from 'jsonpath-plus';
|
|
2
2
|
import JSONPathTransformer from './JSONPathTransformer.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Sort spec types used by applyTemplates() and forEach().
|
|
6
|
+
* @typedef {{
|
|
7
|
+
* select?: string,
|
|
8
|
+
* order?: 'ascending' | 'descending',
|
|
9
|
+
* type?: 'text'|'number',
|
|
10
|
+
* locale?: string,
|
|
11
|
+
* localeOptions?: unknown
|
|
12
|
+
* }} SortObject
|
|
13
|
+
* @typedef {(a: unknown, b: unknown,
|
|
14
|
+
* ctx: JSONPathTransformerContext
|
|
15
|
+
* ) => number} SortComparator
|
|
16
|
+
* @typedef {string | SortObject | SortComparator |
|
|
17
|
+
* Array<string|SortObject>} SortSpec
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @template [T = "json"]
|
|
22
|
+
* @typedef {object} JSONPathTransformerContextConfig
|
|
23
|
+
* @property {null|boolean|number|string|object} data - Data to transform
|
|
24
|
+
* @property {object} [parent] - Parent object
|
|
25
|
+
* @property {string} [parentProperty] - Parent property name
|
|
26
|
+
* @property {boolean} [errorOnEqualPriority] - Whether to error on
|
|
27
|
+
* equal priority
|
|
28
|
+
* @property {T extends "json" ? import('./JSONJoiningTransformer.js').
|
|
29
|
+
* default : T extends "string" ? import('./StringJoiningTransformer.js').
|
|
30
|
+
* default : import('./DOMJoiningTransformer.js').
|
|
31
|
+
* default} joiningTransformer - Joining transformer
|
|
32
|
+
* @property {boolean} [preventEval] - Whether to prevent eval in
|
|
33
|
+
* JSONPath
|
|
34
|
+
* @property {(path: string) => number} [specificityPriorityResolver]
|
|
35
|
+
* Priority resolver function
|
|
36
|
+
* @property {import('./index.js').JSONPathTemplateObject<T>[]} templates
|
|
37
|
+
*/
|
|
38
|
+
|
|
4
39
|
/**
|
|
5
40
|
* Execution context for JSONPath-driven template application.
|
|
6
41
|
*
|
|
@@ -8,23 +43,13 @@ import JSONPathTransformer from './JSONPathTransformer.js';
|
|
|
8
43
|
* running templates. Exposes helper methods that mirror the underlying
|
|
9
44
|
* joining transformer (e.g., string(), object(), array()) so templates can
|
|
10
45
|
* emit results without referencing the joiner directly.
|
|
46
|
+
* @template [T = "json"]
|
|
11
47
|
*/
|
|
12
48
|
class JSONPathTransformerContext {
|
|
13
49
|
/**
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
16
|
-
*
|
|
17
|
-
* @param {string} [config.parentProperty] - Parent property name
|
|
18
|
-
* @param {boolean} [config.errorOnEqualPriority] - Whether to error on
|
|
19
|
-
* equal priority
|
|
20
|
-
* @param {{append: Function, get: Function, string: Function,
|
|
21
|
-
* object: Function, array: Function}} config.joiningTransformer -
|
|
22
|
-
* Joining transformer
|
|
23
|
-
* @param {boolean} [config.preventEval] - Whether to prevent eval in
|
|
24
|
-
* JSONPath
|
|
25
|
-
* @param {Function} [config.specificityPriorityResolver] - Function to
|
|
26
|
-
* resolve priority
|
|
27
|
-
* @param {any[]} templates - Array of template objects
|
|
50
|
+
* @param {JSONPathTransformerContextConfig<T>} config
|
|
51
|
+
* @param {import('./index.js').JSONPathTemplateObject<T>[]} templates - Array
|
|
52
|
+
* of template objects
|
|
28
53
|
*/
|
|
29
54
|
constructor (config, templates) {
|
|
30
55
|
this._config = config;
|
|
@@ -32,16 +57,18 @@ class JSONPathTransformerContext {
|
|
|
32
57
|
this._contextObj = this._origObj = config.data;
|
|
33
58
|
this._parent = config.parent || this._config;
|
|
34
59
|
this._parentProperty = config.parentProperty || 'data';
|
|
35
|
-
/** @type {Record<string,
|
|
60
|
+
/** @type {Record<string, unknown>} */
|
|
36
61
|
this.vars = {};
|
|
37
|
-
/** @type {Record<string,
|
|
62
|
+
/** @type {Record<string, Record<string, unknown>>} */
|
|
38
63
|
this.propertySets = {};
|
|
39
|
-
/** @type {Record<string,
|
|
64
|
+
/** @type {Record<string, {match: string, use: string}>} */
|
|
40
65
|
this.keys = {};
|
|
41
66
|
/** @type {boolean | undefined} */
|
|
42
67
|
this._initialized = undefined;
|
|
43
68
|
/** @type {string | undefined} */
|
|
44
69
|
this._currPath = undefined;
|
|
70
|
+
/** @type {Record<string, any> | undefined} */
|
|
71
|
+
this._params = undefined;
|
|
45
72
|
}
|
|
46
73
|
|
|
47
74
|
/**
|
|
@@ -59,28 +86,30 @@ class JSONPathTransformerContext {
|
|
|
59
86
|
|
|
60
87
|
/**
|
|
61
88
|
* Gets the joining transformer from config.
|
|
62
|
-
* @returns {
|
|
89
|
+
* @returns {T extends "json" ? import('./JSONJoiningTransformer.js').
|
|
90
|
+
* default : T extends "string" ? import('./StringJoiningTransformer.js').
|
|
91
|
+
* default : import('./DOMJoiningTransformer.js').
|
|
92
|
+
* default} The joining transformer
|
|
63
93
|
*/
|
|
64
94
|
_getJoiningTransformer () {
|
|
65
95
|
return this._config.joiningTransformer;
|
|
66
96
|
}
|
|
67
97
|
|
|
68
98
|
/**
|
|
69
|
-
* @param {
|
|
70
|
-
* @returns {
|
|
99
|
+
* @param {string | Node} item - Item to append to output
|
|
100
|
+
* @returns {this}
|
|
71
101
|
*/
|
|
72
102
|
appendOutput (item) {
|
|
73
|
-
|
|
103
|
+
this._getJoiningTransformer().append(item);
|
|
74
104
|
return this;
|
|
75
105
|
}
|
|
76
106
|
|
|
77
|
-
/* c8 ignore next 4 -- JSDoc block incorrectly counted as coverable by c8 */
|
|
78
107
|
/**
|
|
79
108
|
* Gets the current output.
|
|
80
|
-
* @returns {
|
|
109
|
+
* @returns {any} The output from the joining transformer
|
|
81
110
|
*/
|
|
82
111
|
getOutput () {
|
|
83
|
-
return
|
|
112
|
+
return this._getJoiningTransformer().get();
|
|
84
113
|
}
|
|
85
114
|
|
|
86
115
|
/**
|
|
@@ -89,7 +118,7 @@ class JSONPathTransformerContext {
|
|
|
89
118
|
* to the result tree instead).
|
|
90
119
|
* @param {string} select - JSONPath selector
|
|
91
120
|
* @param {boolean} wrap - Whether to wrap results
|
|
92
|
-
* @returns {
|
|
121
|
+
* @returns {any} The selected value(s)
|
|
93
122
|
*/
|
|
94
123
|
get (select, wrap) {
|
|
95
124
|
if (select) {
|
|
@@ -103,8 +132,8 @@ class JSONPathTransformerContext {
|
|
|
103
132
|
}
|
|
104
133
|
|
|
105
134
|
/**
|
|
106
|
-
* @param {
|
|
107
|
-
* @returns {
|
|
135
|
+
* @param {any} v - Value to set
|
|
136
|
+
* @returns {this}
|
|
108
137
|
*/
|
|
109
138
|
set (v) {
|
|
110
139
|
(/** @type {Record<string, any>} */ (this._parent))[
|
|
@@ -123,10 +152,12 @@ class JSONPathTransformerContext {
|
|
|
123
152
|
* locale, localeOptions }
|
|
124
153
|
* - array: multiple key objects/strings in priority order.
|
|
125
154
|
*
|
|
126
|
-
* @param {string|
|
|
155
|
+
* @param {string|null|
|
|
156
|
+
* {mode?: string, select?: string}
|
|
157
|
+
* } [select] - JSONPath selector or options object
|
|
127
158
|
* @param {string} [mode] - Mode to apply
|
|
128
|
-
* @param {
|
|
129
|
-
* @returns {
|
|
159
|
+
* @param {SortSpec} [sort] - Sort spec
|
|
160
|
+
* @returns {this}
|
|
130
161
|
*/
|
|
131
162
|
applyTemplates (select, mode, sort) {
|
|
132
163
|
// Matches templates by (path, mode), resolves priority, and invokes each
|
|
@@ -135,10 +166,8 @@ class JSONPathTransformerContext {
|
|
|
135
166
|
// eslint-disable-next-line unicorn/no-this-assignment -- Temporary
|
|
136
167
|
const that = this;
|
|
137
168
|
if (select && typeof select === 'object') {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
mode = selectObj.mode ?? mode;
|
|
141
|
-
select = selectObj.select ?? select;
|
|
169
|
+
mode = select.mode ?? mode;
|
|
170
|
+
select = select.select ?? select;
|
|
142
171
|
}
|
|
143
172
|
if (!this._initialized) {
|
|
144
173
|
select = select || '$';
|
|
@@ -154,7 +183,11 @@ class JSONPathTransformerContext {
|
|
|
154
183
|
const jsonPathExpr = propertyNamesMode ? select.slice(0, -1) : select;
|
|
155
184
|
// Todo: Use results here?
|
|
156
185
|
/* const results = */ this._getJoiningTransformer();
|
|
157
|
-
const modeMatchedTemplates = this._templates.filter(
|
|
186
|
+
const modeMatchedTemplates = this._templates.filter((templateObj) => {
|
|
187
|
+
// Exclude named-only templates (those with name but no path)
|
|
188
|
+
if (templateObj.name && !templateObj.path) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
158
191
|
return ((mode && mode === templateObj.mode) ||
|
|
159
192
|
(!mode && !templateObj.mode));
|
|
160
193
|
});
|
|
@@ -162,7 +195,7 @@ class JSONPathTransformerContext {
|
|
|
162
195
|
// Collect matches first (to allow sorting), then process
|
|
163
196
|
/**
|
|
164
197
|
* @type {{
|
|
165
|
-
* value:any, parent:any, parentProperty?:string, path:string
|
|
198
|
+
* value: any, parent: any, parentProperty?: string, path: string
|
|
166
199
|
* }[]}
|
|
167
200
|
*/
|
|
168
201
|
const matches = /** @type {any} */ (jsonpath)({
|
|
@@ -231,7 +264,7 @@ class JSONPathTransformerContext {
|
|
|
231
264
|
}
|
|
232
265
|
/**
|
|
233
266
|
* @param {any} sortSpec
|
|
234
|
-
* @returns {((a:{value:any}, b:{value:any})=>number)|null}
|
|
267
|
+
* @returns {((a: {value: any}, b: {value: any}) => number) | null}
|
|
235
268
|
*/
|
|
236
269
|
function buildComparator (sortSpec) {
|
|
237
270
|
if (!sortSpec) {
|
|
@@ -283,6 +316,13 @@ class JSONPathTransformerContext {
|
|
|
283
316
|
that._currPath += path.replace(/^\$/v, '');
|
|
284
317
|
const pathMatchedTemplates = modeMatchedTemplates.filter(
|
|
285
318
|
function (templateObj) {
|
|
319
|
+
// At this point, we know templateObj.path exists because we filtered
|
|
320
|
+
// out named-only templates in modeMatchedTemplates
|
|
321
|
+
/* c8 ignore start -- defensive check, already filtered at line 188 */
|
|
322
|
+
if (!templateObj.path) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
/* c8 ignore stop */
|
|
286
326
|
const queryResult = /** @type {any[]} */ (
|
|
287
327
|
(/** @type {any} */ (jsonpath))({
|
|
288
328
|
path: JSONPathTransformer.makeJSONPathAbsolute(
|
|
@@ -323,12 +363,12 @@ class JSONPathTransformerContext {
|
|
|
323
363
|
* trigger error, making the `: 0` branch nearly unreachable. */
|
|
324
364
|
const aPriority = typeof a.priority === 'number'
|
|
325
365
|
? a.priority
|
|
326
|
-
: (that._config.specificityPriorityResolver
|
|
366
|
+
: (that._config.specificityPriorityResolver && a.path
|
|
327
367
|
? that._config.specificityPriorityResolver(a.path)
|
|
328
368
|
: 0);
|
|
329
369
|
const bPriority = typeof b.priority === 'number'
|
|
330
370
|
? b.priority
|
|
331
|
-
: (that._config.specificityPriorityResolver
|
|
371
|
+
: (that._config.specificityPriorityResolver && b.path
|
|
332
372
|
? that._config.specificityPriorityResolver(b.path)
|
|
333
373
|
: 0);
|
|
334
374
|
/* c8 ignore stop */
|
|
@@ -340,18 +380,27 @@ class JSONPathTransformerContext {
|
|
|
340
380
|
return (aPriority > bPriority) ? -1 : 1;
|
|
341
381
|
});
|
|
342
382
|
|
|
343
|
-
templateObj =
|
|
383
|
+
templateObj =
|
|
384
|
+
/** @type {import('./index.js').JSONPathTemplateObject<T>} */ (
|
|
385
|
+
pathMatchedTemplates.shift()
|
|
386
|
+
);
|
|
344
387
|
}
|
|
345
388
|
|
|
346
389
|
that._contextObj = value;
|
|
347
390
|
that._parent = parent;
|
|
348
391
|
that._parentProperty = (parentProperty ?? that._parentProperty);
|
|
349
392
|
|
|
350
|
-
const ret =
|
|
351
|
-
|
|
352
|
-
|
|
393
|
+
const ret =
|
|
394
|
+
/** @type {import('./index.js').JSONPathTemplateObject<T>} */ (
|
|
395
|
+
templateObj
|
|
396
|
+
).template.call(
|
|
397
|
+
that, value, {mode, parent, parentProperty}
|
|
398
|
+
);
|
|
353
399
|
if (typeof ret !== 'undefined') {
|
|
354
|
-
|
|
400
|
+
// After the undefined check, ret is ResultType<T>
|
|
401
|
+
that._getJoiningTransformer().append(
|
|
402
|
+
/** @type {string|Node|*} */ (ret)
|
|
403
|
+
);
|
|
355
404
|
}
|
|
356
405
|
|
|
357
406
|
that._contextObj = value;
|
|
@@ -368,26 +417,41 @@ class JSONPathTransformerContext {
|
|
|
368
417
|
}
|
|
369
418
|
|
|
370
419
|
/**
|
|
371
|
-
* @param {string|
|
|
420
|
+
* @param {string|
|
|
421
|
+
* {name: string, withParam?: any[]}} name - Template name or
|
|
422
|
+
* options object
|
|
372
423
|
* @param {any[]} [withParams] - Parameters to pass to template
|
|
373
|
-
* @returns {
|
|
424
|
+
* @returns {this}
|
|
374
425
|
*/
|
|
375
426
|
callTemplate (name, withParams) {
|
|
376
427
|
// Invokes a named template, optionally passing values via withParam.
|
|
377
|
-
// eslint-disable-next-line unicorn/no-this-assignment -- Temporary
|
|
378
|
-
const that = this;
|
|
379
428
|
if (name && typeof name === 'object') {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
withParams = nameObj.withParam || withParams;
|
|
383
|
-
name = nameObj.name ?? name;
|
|
429
|
+
withParams = name.withParam || withParams;
|
|
430
|
+
({name} = name);
|
|
384
431
|
}
|
|
385
432
|
withParams = withParams || [];
|
|
386
|
-
|
|
387
|
-
|
|
433
|
+
|
|
434
|
+
// Store parameters in a temporary context for valueOf() access
|
|
435
|
+
const prevParams = this._params;
|
|
436
|
+
/** @type {Record<string, any>} */
|
|
437
|
+
const params = {};
|
|
438
|
+
this._params = params;
|
|
439
|
+
|
|
440
|
+
withParams.forEach((withParam, index) => {
|
|
441
|
+
const value = withParam.value !== undefined
|
|
442
|
+
? withParam.value
|
|
443
|
+
: this.get(withParam.select, false);
|
|
444
|
+
|
|
445
|
+
// Store by name if provided, otherwise by index
|
|
446
|
+
if (withParam.name) {
|
|
447
|
+
params[withParam.name] = value;
|
|
448
|
+
} else {
|
|
449
|
+
params[String(index)] = value;
|
|
450
|
+
}
|
|
388
451
|
});
|
|
452
|
+
|
|
389
453
|
const results = this._getJoiningTransformer();
|
|
390
|
-
const templateObj = this._templates.find(
|
|
454
|
+
const templateObj = this._templates.find((template) => {
|
|
391
455
|
return template.name === name;
|
|
392
456
|
});
|
|
393
457
|
if (!templateObj) {
|
|
@@ -396,8 +460,14 @@ class JSONPathTransformerContext {
|
|
|
396
460
|
);
|
|
397
461
|
}
|
|
398
462
|
|
|
399
|
-
const result = templateObj.template.
|
|
400
|
-
|
|
463
|
+
const result = templateObj.template.call(this, this._contextObj, {});
|
|
464
|
+
if (typeof result !== 'undefined') {
|
|
465
|
+
/** @type {any} */ (results).append(result);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Restore previous parameter context
|
|
469
|
+
this._params = prevParams;
|
|
470
|
+
|
|
401
471
|
return this;
|
|
402
472
|
}
|
|
403
473
|
|
|
@@ -406,14 +476,16 @@ class JSONPathTransformerContext {
|
|
|
406
476
|
*
|
|
407
477
|
* Sort parameter forms are the same as applyTemplates().
|
|
408
478
|
* @param {string} select - JSONPath selector
|
|
409
|
-
* @param {
|
|
410
|
-
*
|
|
411
|
-
*
|
|
479
|
+
* @param {(this: JSONPathTransformerContext<T>,
|
|
480
|
+
* value: any
|
|
481
|
+
* ) => void} cb - Callback function
|
|
482
|
+
* @param {SortSpec} [sort] - Sort spec
|
|
483
|
+
* @returns {this}
|
|
412
484
|
*/
|
|
413
485
|
forEach (select, cb, sort) {
|
|
414
486
|
// eslint-disable-next-line unicorn/no-this-assignment -- Temporary
|
|
415
487
|
const that = this;
|
|
416
|
-
/** @type {{value:any}[]} */
|
|
488
|
+
/** @type {{value: any}[]} */
|
|
417
489
|
const matches = /** @type {any} */ (jsonpath)({
|
|
418
490
|
path: select,
|
|
419
491
|
json: this._contextObj,
|
|
@@ -478,7 +550,7 @@ class JSONPathTransformerContext {
|
|
|
478
550
|
}
|
|
479
551
|
/**
|
|
480
552
|
* @param {any} sortSpec
|
|
481
|
-
* @returns {((a:{value:any}, b:{value:any})=>number)|null}
|
|
553
|
+
* @returns {((a: {value: any}, b: {value: any}) => number) | null}
|
|
482
554
|
*/
|
|
483
555
|
function feBuildComparator (sortSpec) {
|
|
484
556
|
if (!sortSpec) {
|
|
@@ -524,16 +596,33 @@ class JSONPathTransformerContext {
|
|
|
524
596
|
|
|
525
597
|
/**
|
|
526
598
|
* @param {string|object} [select] - JSONPath selector
|
|
527
|
-
* @returns {
|
|
599
|
+
* @returns {this}
|
|
528
600
|
*/
|
|
529
601
|
valueOf (select) {
|
|
530
602
|
// Appends the value of the given JSONPath (or the current context when
|
|
531
603
|
// `{select: '.'}` is passed) to the output via the joining transformer.
|
|
532
604
|
const results = this._getJoiningTransformer();
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
605
|
+
let result;
|
|
606
|
+
|
|
607
|
+
if (select && typeof select === 'object' &&
|
|
608
|
+
/** @type {{select?: string}} */ (select).select === '.') {
|
|
609
|
+
result = this._contextObj;
|
|
610
|
+
} else {
|
|
611
|
+
const selectStr = typeof select === 'object'
|
|
612
|
+
? /** @type {{select?: string}} */ (select).select
|
|
613
|
+
: select;
|
|
614
|
+
|
|
615
|
+
// Check if this is a parameter reference (starts with $)
|
|
616
|
+
if (selectStr && selectStr.startsWith('$')) {
|
|
617
|
+
const paramName = selectStr.slice(1);
|
|
618
|
+
result = (this._params && paramName in this._params)
|
|
619
|
+
? this._params[paramName]
|
|
620
|
+
: this.get(/** @type {string} */ (selectStr), false);
|
|
621
|
+
} else {
|
|
622
|
+
result = this.get(/** @type {string} */ (select), false);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
537
626
|
/** @type {any} */ (results).append(result);
|
|
538
627
|
return this;
|
|
539
628
|
}
|
|
@@ -541,7 +630,7 @@ class JSONPathTransformerContext {
|
|
|
541
630
|
/**
|
|
542
631
|
* Deep copy selection or current context when omitted.
|
|
543
632
|
* @param {string} [select] - JSONPath selector
|
|
544
|
-
* @returns {
|
|
633
|
+
* @returns {this}
|
|
545
634
|
*/
|
|
546
635
|
copyOf (select) { // Deep
|
|
547
636
|
// Deeply clones the value at `select` (or current context if omitted)
|
|
@@ -567,11 +656,9 @@ class JSONPathTransformerContext {
|
|
|
567
656
|
* vary across environments; behavior covered by tests. */
|
|
568
657
|
// structuredClone failed (e.g., Symbols); if any functions present
|
|
569
658
|
// on own enumerable string-keyed properties, preserve via shallow.
|
|
570
|
-
/** @type {boolean} */ let hasFunc = false;
|
|
571
659
|
for (const k of Object.keys(val)) {
|
|
572
660
|
const v = /** @type {any} */ (val)[k];
|
|
573
661
|
if (typeof v === 'function') {
|
|
574
|
-
hasFunc = true;
|
|
575
662
|
break;
|
|
576
663
|
}
|
|
577
664
|
}
|
|
@@ -584,14 +671,14 @@ class JSONPathTransformerContext {
|
|
|
584
671
|
// Primitives/functions copied by value/reference semantics naturally.
|
|
585
672
|
clone = val;
|
|
586
673
|
}
|
|
587
|
-
|
|
674
|
+
this._getJoiningTransformer().append(clone);
|
|
588
675
|
return this;
|
|
589
676
|
}
|
|
590
677
|
|
|
591
678
|
/**
|
|
592
679
|
* Shallow copy current context; optionally merge property set names.
|
|
593
680
|
* @param {string[]} [propertySets] - Property sets to merge
|
|
594
|
-
* @returns {
|
|
681
|
+
* @returns {this}
|
|
595
682
|
*/
|
|
596
683
|
copy (propertySets) { // Shallow
|
|
597
684
|
// Creates a shallow clone of current context object/array (or primitive)
|
|
@@ -611,14 +698,14 @@ class JSONPathTransformerContext {
|
|
|
611
698
|
} else { /* c8 ignore start -- primitive branch attribution variance */
|
|
612
699
|
clone = src; // Primitive/function - nothing to shallow clone
|
|
613
700
|
} /* c8 ignore stop */
|
|
614
|
-
|
|
701
|
+
this._getJoiningTransformer().append(clone);
|
|
615
702
|
return this;
|
|
616
703
|
}
|
|
617
704
|
|
|
618
705
|
/**
|
|
619
706
|
* @param {string} name - Variable name
|
|
620
707
|
* @param {string} select - JSONPath selector
|
|
621
|
-
* @returns {
|
|
708
|
+
* @returns {this}
|
|
622
709
|
*/
|
|
623
710
|
variable (name, select) {
|
|
624
711
|
this.vars[name] = this.get(select, false);
|
|
@@ -626,7 +713,7 @@ class JSONPathTransformerContext {
|
|
|
626
713
|
}
|
|
627
714
|
|
|
628
715
|
/**
|
|
629
|
-
* @param {
|
|
716
|
+
* @param {unknown} json - JSON data to log
|
|
630
717
|
* @returns {void}
|
|
631
718
|
*/
|
|
632
719
|
// eslint-disable-next-line class-methods-use-this -- Convenient
|
|
@@ -637,10 +724,11 @@ class JSONPathTransformerContext {
|
|
|
637
724
|
|
|
638
725
|
/**
|
|
639
726
|
* @param {string} str - String value
|
|
640
|
-
* @param {
|
|
641
|
-
*
|
|
727
|
+
* @param {import('./JSONJoiningTransformer.js').
|
|
728
|
+
* SimpleCallback<T>} [cb] - Optional callback to build nested
|
|
729
|
+
* string content
|
|
730
|
+
* @returns {this}
|
|
642
731
|
*/
|
|
643
|
-
// Todo: Add other methods from the joining transformers
|
|
644
732
|
string (str, cb) {
|
|
645
733
|
/** @type {any} */ (this._getJoiningTransformer()).string(str, cb);
|
|
646
734
|
return this;
|
|
@@ -650,10 +738,10 @@ class JSONPathTransformerContext {
|
|
|
650
738
|
* Append a number to JSON output. Mirrors the joining transformer API so
|
|
651
739
|
* templates can call `this.number()`.
|
|
652
740
|
* @param {number} num - Number value to append
|
|
653
|
-
* @returns {
|
|
741
|
+
* @returns {this}
|
|
654
742
|
*/
|
|
655
743
|
number (num) {
|
|
656
|
-
|
|
744
|
+
this._getJoiningTransformer().number(num);
|
|
657
745
|
return this;
|
|
658
746
|
}
|
|
659
747
|
|
|
@@ -662,10 +750,10 @@ class JSONPathTransformerContext {
|
|
|
662
750
|
* stringification. Mirrors the joining transformer API so templates can
|
|
663
751
|
* call `this.plainText()`.
|
|
664
752
|
* @param {string} str - Plain text to append
|
|
665
|
-
* @returns {
|
|
753
|
+
* @returns {this}
|
|
666
754
|
*/
|
|
667
755
|
plainText (str) {
|
|
668
|
-
|
|
756
|
+
this._getJoiningTransformer().plainText(str);
|
|
669
757
|
return this;
|
|
670
758
|
}
|
|
671
759
|
|
|
@@ -673,11 +761,11 @@ class JSONPathTransformerContext {
|
|
|
673
761
|
* Set a property value on the current object (JSON joiner). Mirrors the
|
|
674
762
|
* joining transformer API so templates can call `this.propValue()`.
|
|
675
763
|
* @param {string} prop - Property name
|
|
676
|
-
* @param {
|
|
677
|
-
* @returns {
|
|
764
|
+
* @param {any} val - Property value
|
|
765
|
+
* @returns {this}
|
|
678
766
|
*/
|
|
679
767
|
propValue (prop, val) {
|
|
680
|
-
|
|
768
|
+
this._getJoiningTransformer().propValue(prop, val);
|
|
681
769
|
return this;
|
|
682
770
|
}
|
|
683
771
|
|
|
@@ -686,7 +774,7 @@ class JSONPathTransformerContext {
|
|
|
686
774
|
* support both signatures: (obj, cb, usePropertySets, propSets) with seed
|
|
687
775
|
* object or (cb, usePropertySets, propSets) without.
|
|
688
776
|
* @param {...any} args - Arguments to pass to joiner
|
|
689
|
-
* @returns {
|
|
777
|
+
* @returns {this}
|
|
690
778
|
*/
|
|
691
779
|
object (...args) {
|
|
692
780
|
/** @type {any} */ (this._getJoiningTransformer()).object(...args);
|
|
@@ -697,21 +785,32 @@ class JSONPathTransformerContext {
|
|
|
697
785
|
* Build an array. Mirrors the joining transformer API. All joiners now
|
|
698
786
|
* support both signatures: (arr, cb) with seed array or (cb) without.
|
|
699
787
|
* @param {...any} args - Arguments to pass to joiner
|
|
700
|
-
* @returns {
|
|
788
|
+
* @returns {this}
|
|
701
789
|
*/
|
|
702
790
|
array (...args) {
|
|
703
791
|
/** @type {any} */ (this._getJoiningTransformer()).array(...args);
|
|
704
792
|
return this;
|
|
705
793
|
}
|
|
706
794
|
|
|
795
|
+
/**
|
|
796
|
+
* Set document-level configuration.
|
|
797
|
+
* @param {import('./StringJoiningTransformer.js').OutputConfig} cfg Text
|
|
798
|
+
* @returns {this}
|
|
799
|
+
*/
|
|
800
|
+
output (cfg) {
|
|
801
|
+
this._getJoiningTransformer().output(cfg);
|
|
802
|
+
return this;
|
|
803
|
+
}
|
|
804
|
+
|
|
707
805
|
/**
|
|
708
806
|
* Create an element. Mirrors the joining transformer API so templates can
|
|
709
807
|
* call `this.element()`.
|
|
710
808
|
* @param {string} name - Element name
|
|
711
|
-
* @param {
|
|
809
|
+
* @param {Record<string, string>} [atts] - Attributes object
|
|
712
810
|
* @param {any[]} [children] - Child nodes
|
|
713
|
-
* @param {
|
|
714
|
-
*
|
|
811
|
+
* @param {import('./JSONJoiningTransformer.js').
|
|
812
|
+
* SimpleCallback<T>} [cb] - Callback function
|
|
813
|
+
* @returns {this}
|
|
715
814
|
*/
|
|
716
815
|
element (name, atts, children, cb) {
|
|
717
816
|
/** @type {any} */ (this._getJoiningTransformer()).element(
|
|
@@ -724,13 +823,12 @@ class JSONPathTransformerContext {
|
|
|
724
823
|
* Add an attribute to the most recently opened element. Mirrors the joining
|
|
725
824
|
* transformer API so templates can call `this.attribute()`.
|
|
726
825
|
* @param {string} name - Attribute name
|
|
727
|
-
* @param {string|
|
|
728
|
-
* @
|
|
729
|
-
* @returns {JSONPathTransformerContext}
|
|
826
|
+
* @param {string|Record<string, unknown>} val - Attribute value
|
|
827
|
+
* @returns {this}
|
|
730
828
|
*/
|
|
731
|
-
attribute (name, val
|
|
829
|
+
attribute (name, val) {
|
|
732
830
|
/** @type {any} */ (this._getJoiningTransformer()).attribute(
|
|
733
|
-
name, val
|
|
831
|
+
name, val
|
|
734
832
|
);
|
|
735
833
|
return this;
|
|
736
834
|
}
|
|
@@ -739,18 +837,46 @@ class JSONPathTransformerContext {
|
|
|
739
837
|
* Append text content. Mirrors the joining transformer API so templates can
|
|
740
838
|
* call `this.text()`.
|
|
741
839
|
* @param {string} txt - Text content
|
|
742
|
-
* @returns {
|
|
840
|
+
* @returns {this}
|
|
743
841
|
*/
|
|
744
842
|
text (txt) {
|
|
745
|
-
|
|
843
|
+
this._getJoiningTransformer().text(txt);
|
|
844
|
+
return this;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Add a comment to the most recently opened element. Mirrors the joining
|
|
849
|
+
* transformer API so templates can call `this.comment()`.
|
|
850
|
+
* @param {string} text - Comment text
|
|
851
|
+
* @returns {this}
|
|
852
|
+
*/
|
|
853
|
+
comment (text) {
|
|
854
|
+
this._getJoiningTransformer().comment(
|
|
855
|
+
text
|
|
856
|
+
);
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Add a processing instruction to the most recently opened element.
|
|
862
|
+
* Mirrors the joining transformer API so templates can call
|
|
863
|
+
* `this.processingInstruction()`.
|
|
864
|
+
* @param {string} target - Processing instruction target
|
|
865
|
+
* @param {string} data - Processing instruction data
|
|
866
|
+
* @returns {this}
|
|
867
|
+
*/
|
|
868
|
+
processingInstruction (target, data) {
|
|
869
|
+
this._getJoiningTransformer().processingInstruction(
|
|
870
|
+
target, data
|
|
871
|
+
);
|
|
746
872
|
return this;
|
|
747
873
|
}
|
|
748
874
|
|
|
749
875
|
/**
|
|
750
876
|
* @param {string} name - Property set name
|
|
751
|
-
* @param {
|
|
877
|
+
* @param {Record<string, unknown>} propertySetObj - Property set object
|
|
752
878
|
* @param {any[]} [usePropertySets] - Property sets to use
|
|
753
|
-
* @returns {
|
|
879
|
+
* @returns {this}
|
|
754
880
|
*/
|
|
755
881
|
propertySet (name, propertySetObj, usePropertySets) {
|
|
756
882
|
// eslint-disable-next-line unicorn/no-this-assignment -- Temporary
|
|
@@ -767,9 +893,9 @@ class JSONPathTransformerContext {
|
|
|
767
893
|
}
|
|
768
894
|
|
|
769
895
|
/**
|
|
770
|
-
* @param {
|
|
896
|
+
* @param {Record<string, unknown>} obj - Object to assign properties to
|
|
771
897
|
* @param {string} name - Property set name
|
|
772
|
-
* @returns {
|
|
898
|
+
* @returns {Record<string, unknown>}
|
|
773
899
|
*/
|
|
774
900
|
_usePropertySets (obj, name) {
|
|
775
901
|
return Object.assign(obj, this.propertySets[name]);
|
|
@@ -777,8 +903,8 @@ class JSONPathTransformerContext {
|
|
|
777
903
|
|
|
778
904
|
/**
|
|
779
905
|
* @param {string} name - Key name
|
|
780
|
-
* @param {
|
|
781
|
-
* @returns {
|
|
906
|
+
* @param {any} value - Value to match
|
|
907
|
+
* @returns {any}
|
|
782
908
|
*/
|
|
783
909
|
getKey (name, value) {
|
|
784
910
|
const key = this.keys[name];
|
|
@@ -796,12 +922,90 @@ class JSONPathTransformerContext {
|
|
|
796
922
|
* @param {string} name - Key name
|
|
797
923
|
* @param {string} match - Match expression
|
|
798
924
|
* @param {string} use - Use expression
|
|
799
|
-
* @returns {
|
|
925
|
+
* @returns {this}
|
|
800
926
|
*/
|
|
801
927
|
key (name, match, use) {
|
|
802
928
|
this.keys[name] = {match, use};
|
|
803
929
|
return this;
|
|
804
930
|
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Conditionally execute a callback when a JSONPath selector evaluates
|
|
934
|
+
* to a truthy scalar or a non-empty result set (node set analogue).
|
|
935
|
+
* Mirrors XSLT's xsl:if semantics where a non-empty node set is truthy.
|
|
936
|
+
*
|
|
937
|
+
* Truthiness rules:
|
|
938
|
+
* - If the selection (with wrap) yields an array with length > 0, the
|
|
939
|
+
* condition passes.
|
|
940
|
+
* - Otherwise the (non-wrapped) scalar value is coerced with Boolean();
|
|
941
|
+
* e.g., 0, '', null, undefined => false; others => true.
|
|
942
|
+
*
|
|
943
|
+
* @param {string} select - JSONPath selector expression
|
|
944
|
+
* @param {(this: JSONPathTransformerContext<T>)
|
|
945
|
+
* => void} cb - Callback to invoke if condition is met
|
|
946
|
+
* @returns {this}
|
|
947
|
+
*/
|
|
948
|
+
if (select, cb) {
|
|
949
|
+
const passes = this._passesIf(select);
|
|
950
|
+
if (passes && typeof cb === 'function') {
|
|
951
|
+
cb.call(this);
|
|
952
|
+
}
|
|
953
|
+
return this;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Internal helper: determine if `select` passes truthiness test.
|
|
958
|
+
* Non-empty wrapped results => true; single item: objects truthy,
|
|
959
|
+
* primitives coerced via Boolean().
|
|
960
|
+
* @param {string} select
|
|
961
|
+
* @returns {boolean}
|
|
962
|
+
*/
|
|
963
|
+
_passesIf (select) {
|
|
964
|
+
// Evaluate with wrapping to detect non-empty match sets
|
|
965
|
+
/** @type {any} */ const wrapped = this.get(select, true);
|
|
966
|
+
if (Array.isArray(wrapped)) {
|
|
967
|
+
if (wrapped.length === 0) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
if (wrapped.length > 1) {
|
|
971
|
+
// Multiple matches (node set analogue) => truthy
|
|
972
|
+
return true;
|
|
973
|
+
}
|
|
974
|
+
// Single item; apply scalar truthiness
|
|
975
|
+
const single = wrapped[0];
|
|
976
|
+
// Objects (arrays) always truthy; primitives use Boolean()
|
|
977
|
+
if (single && typeof single === 'object') {
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
return Boolean(single);
|
|
981
|
+
}
|
|
982
|
+
/* c8 ignore next 3 -- unreachable defensive non-array branch:
|
|
983
|
+
* jsonpath-plus with wrap:true always returns arrays. */
|
|
984
|
+
// Fallback if library behavior changed in future
|
|
985
|
+
return Boolean(wrapped);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Like `if()`, but also supports an optional fallback callback executed
|
|
990
|
+
* when the test does not pass (similar to xsl:choose/xsl:otherwise).
|
|
991
|
+
* @param {string} select JSONPath selector
|
|
992
|
+
* @param {(this: JSONPathTransformerContext<T>)
|
|
993
|
+
* => void} whenCb Callback when condition passes
|
|
994
|
+
* @param {(this: JSONPathTransformerContext<T>)
|
|
995
|
+
* => void} [otherwiseCb] Callback when condition fails
|
|
996
|
+
* @returns {this}
|
|
997
|
+
*/
|
|
998
|
+
choose (select, whenCb, otherwiseCb) {
|
|
999
|
+
const passes = this._passesIf(select);
|
|
1000
|
+
if (passes) {
|
|
1001
|
+
if (typeof whenCb === 'function') {
|
|
1002
|
+
whenCb.call(this);
|
|
1003
|
+
}
|
|
1004
|
+
} else if (typeof otherwiseCb === 'function') {
|
|
1005
|
+
otherwiseCb.call(this);
|
|
1006
|
+
}
|
|
1007
|
+
return this;
|
|
1008
|
+
}
|
|
805
1009
|
}
|
|
806
1010
|
|
|
807
1011
|
export default JSONPathTransformerContext;
|