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
package/src/index.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import {getJSON} from 'simple-get-json';
|
|
2
|
-
// import JHTML from 'jhtml';
|
|
3
|
-
// import jsonpath from 'jsonpath-plus';
|
|
4
|
-
// import {jml} from 'jamilih';
|
|
5
|
-
import {JSDOM} from 'jsdom';
|
|
6
|
-
|
|
7
|
-
// import Stringifier from 'jhtml/SAJJ/SAJJ.Stringifier.js';
|
|
8
2
|
|
|
9
3
|
import DOMJoiningTransformer from './DOMJoiningTransformer.js';
|
|
10
4
|
import JSONJoiningTransformer from './JSONJoiningTransformer.js';
|
|
@@ -13,18 +7,105 @@ import XPathTransformer from './XPathTransformer.js';
|
|
|
13
7
|
import StringJoiningTransformer from './StringJoiningTransformer.js';
|
|
14
8
|
import XSLTStyleJSONPathResolver from './XSLTStyleJSONPathResolver.js';
|
|
15
9
|
|
|
10
|
+
/** @type {import('jsdom').DOMWindow | typeof globalThis} */
|
|
11
|
+
let _win;
|
|
12
|
+
/**
|
|
13
|
+
* @param {import('jsdom').DOMWindow | typeof globalThis} win
|
|
14
|
+
*/
|
|
15
|
+
export const setWindow = (win) => {
|
|
16
|
+
_win = win;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Internal options extension adding private runtime state flags.
|
|
21
|
+
* Not part of the public API surface but used for narrowing casts.
|
|
22
|
+
* @typedef {JTLTOptions & {
|
|
23
|
+
* _customJoiningTransformer?: boolean
|
|
24
|
+
* }} InternalJTLTOptions
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A template declaration whose `template` executes with `this` bound
|
|
29
|
+
* to the engine-specific context type `TCtx`.
|
|
30
|
+
* Either `path` must be provided (for pattern matching), or `name` must be
|
|
31
|
+
* provided (for named templates callable via callTemplate), or both.
|
|
32
|
+
* @template T
|
|
33
|
+
* @template U
|
|
34
|
+
* @template TCtx
|
|
35
|
+
* @typedef {object} TemplateObject
|
|
36
|
+
* @property {string} [path] - JSONPath or XPath selector for matching nodes
|
|
37
|
+
* @property {string} [name] - Optional name for calling via callTemplate
|
|
38
|
+
* @property {string} [mode] - Optional mode for template matching
|
|
39
|
+
* @property {number} [priority] - Priority for template selection
|
|
40
|
+
* @property {TemplateFunction<T, U, TCtx>} template - Template function
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A callable template function with an engine-specific `this`.
|
|
45
|
+
* @template T
|
|
46
|
+
* @template U
|
|
47
|
+
* @template TCtx
|
|
48
|
+
* @typedef {(this: TCtx,
|
|
49
|
+
* value: ResultType<U>,
|
|
50
|
+
* cfg?: {mode?: string}
|
|
51
|
+
* ) => ResultType<T>|void} TemplateFunction
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @template T
|
|
56
|
+
* @typedef {TemplateObject<T, "json",
|
|
57
|
+
* import('./JSONPathTransformerContext.js').default<T>
|
|
58
|
+
* >} JSONPathTemplateObject
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* @template T
|
|
62
|
+
* @typedef {TemplateObject<T, "dom",
|
|
63
|
+
* import('./XPathTransformerContext.js').default
|
|
64
|
+
* >} XPathTemplateObject
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* @template T
|
|
68
|
+
* @typedef {(XPathTemplateObject<T> | [string, TemplateFunction<T, "dom",
|
|
69
|
+
* import('./XPathTransformerContext.js').default
|
|
70
|
+
* >])[]} XPathTemplateArray
|
|
71
|
+
*/
|
|
72
|
+
/**
|
|
73
|
+
* @template T
|
|
74
|
+
* @typedef {JSONPathTemplateObject<T> | [string, TemplateFunction<T, "json",
|
|
75
|
+
* import('./JSONPathTransformerContext.js').default
|
|
76
|
+
* >]} JSONPathTemplateArray
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @typedef {(
|
|
81
|
+
* StringJoiningTransformer|
|
|
82
|
+
* DOMJoiningTransformer|
|
|
83
|
+
* JSONJoiningTransformer
|
|
84
|
+
* )} JoiningTransformer
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @typedef {"json"|"string"|"dom"} joiningTypes
|
|
89
|
+
*/
|
|
90
|
+
|
|
16
91
|
/**
|
|
17
|
-
* @
|
|
18
|
-
* @
|
|
19
|
-
*
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
* @
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
92
|
+
* @template T
|
|
93
|
+
* @typedef {T extends "json" ? unknown : T extends "string" ? string :
|
|
94
|
+
* DocumentFragment|Element} ResultType
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Options common to both engines.
|
|
99
|
+
* @template T
|
|
100
|
+
* @typedef {object} BaseJTLTOptions
|
|
101
|
+
* @property {(
|
|
102
|
+
* result: ResultType<T>
|
|
103
|
+
* ) => void} success A callback supplied
|
|
104
|
+
* with a single argument that is the result of this instance's
|
|
105
|
+
* transform() method. When used in TypeScript, this can be made
|
|
106
|
+
* generic as `success<T>(result: T): void`.
|
|
107
|
+
* @property {null|boolean|number|string|object} [data] A JSON
|
|
108
|
+
* object or DOM document (XPath)
|
|
28
109
|
* @property {string} [ajaxData] URL of a JSON file to retrieve for
|
|
29
110
|
* evaluation
|
|
30
111
|
* @property {boolean} [errorOnEqualPriority] Whether or not to
|
|
@@ -36,30 +117,77 @@ import XSLTStyleJSONPathResolver from './XSLTStyleJSONPathResolver.js';
|
|
|
36
117
|
* input, but reduces capabilities of JSONPath.
|
|
37
118
|
* @property {boolean} [unwrapSingleResult] For JSON output, whether to
|
|
38
119
|
* unwrap single-element root arrays to return just the element
|
|
120
|
+
* @property {boolean} [exposeDocuments] When true, joiners return an array
|
|
121
|
+
* of complete documents: XMLDocument[] for DOM, document wrapper objects[]
|
|
122
|
+
* for JSON, and string[] for string joiners. Each array element corresponds
|
|
123
|
+
* to a root element built during transformation.
|
|
39
124
|
* @property {string} [mode] The mode in which to begin the transform.
|
|
40
|
-
* @property {
|
|
41
|
-
*
|
|
125
|
+
* @property {(opts: JTLTOptions &
|
|
126
|
+
* Required<Pick<JTLTOptions, "joiningTransformer">>
|
|
127
|
+
* ) => ResultType<T>} [engine] Will be based on the
|
|
42
128
|
* same config as passed to this instance. Defaults to a transforming
|
|
43
129
|
* function based on JSONPath and with its own set of priorities for
|
|
44
130
|
* processing templates.
|
|
45
|
-
* @property {
|
|
46
|
-
* Defaults to 'jsonpath'. When 'xpath', `xpathVersion` may be used.
|
|
47
|
-
* @property {1|2} [xpathVersion] XPath engine version: 1 (native) or 2
|
|
48
|
-
* (xpath2.js) when `engineType` is 'xpath'.
|
|
49
|
-
* @property {Function} [specificityPriorityResolver]
|
|
131
|
+
* @property {(path: string) => 0 | 0.5 | -0.5} [specificityPriorityResolver]
|
|
50
132
|
* Callback for getting the priority by specificity
|
|
51
|
-
* @property {
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* @property {
|
|
56
|
-
*
|
|
57
|
-
*
|
|
133
|
+
* @property {JoiningTransformer} [joiningTransformer]
|
|
134
|
+
* A concrete joining transformer instance (or custom subclass) responsible
|
|
135
|
+
* for accumulating output. When omitted, one is created automatically based
|
|
136
|
+
* on `outputType`.
|
|
137
|
+
* @property {import('./AbstractJoiningTransformer.js').
|
|
138
|
+
* JoiningTransformerConfig<T>} [joiningConfig] Config for the joining
|
|
139
|
+
* transformer.
|
|
140
|
+
* @property {object} [parent] Parent object for context
|
|
58
141
|
* @property {string} [parentProperty] Parent property name for context
|
|
59
142
|
*/
|
|
60
143
|
|
|
61
|
-
|
|
62
|
-
|
|
144
|
+
/**
|
|
145
|
+
* JSONPath engine options with context-aware template typing.
|
|
146
|
+
* @template [T = "json"]
|
|
147
|
+
* @typedef {BaseJTLTOptions<T> & {
|
|
148
|
+
* templates?: JSONPathTemplateArray<T>[],
|
|
149
|
+
* template?: JSONPathTemplateObject<T> | TemplateFunction<T, "json",
|
|
150
|
+
* import('./JSONPathTransformerContext.js').default
|
|
151
|
+
* >,
|
|
152
|
+
* query?: TemplateFunction<T, "json",
|
|
153
|
+
* import('./JSONPathTransformerContext.js').default
|
|
154
|
+
* >,
|
|
155
|
+
* forQuery?: [string, TemplateFunction<T, "json",
|
|
156
|
+
* import('./XPathTransformerContext.js').default
|
|
157
|
+
* >],
|
|
158
|
+
* engineType?: 'jsonpath',
|
|
159
|
+
* outputType?: T
|
|
160
|
+
* }} JSONPathJTLTOptions
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* XPath engine options with context-aware template typing.
|
|
165
|
+
* @template T
|
|
166
|
+
* @typedef {BaseJTLTOptions<T> & {
|
|
167
|
+
* templates?: XPathTemplateArray<T>,
|
|
168
|
+
* template?: XPathTemplateObject<T> | TemplateFunction<T, "dom",
|
|
169
|
+
* import('./XPathTransformerContext.js').default
|
|
170
|
+
* >,
|
|
171
|
+
* query?: TemplateFunction<T, "dom",
|
|
172
|
+
* import('./XPathTransformerContext.js').default
|
|
173
|
+
* >,
|
|
174
|
+
* forQuery?: [string, TemplateFunction<T, "dom",
|
|
175
|
+
* import('./JSONPathTransformerContext.js').default
|
|
176
|
+
* >],
|
|
177
|
+
* engineType: 'xpath',
|
|
178
|
+
* xpathVersion?: 1|2,
|
|
179
|
+
* outputType?: 'string'|'dom'|'json'
|
|
180
|
+
* }} XPathJTLTOptions
|
|
181
|
+
*/
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @typedef {JSONPathJTLTOptions |
|
|
185
|
+
* JSONPathJTLTOptions<"string"> |
|
|
186
|
+
* JSONPathJTLTOptions<"dom"> |
|
|
187
|
+
* XPathJTLTOptions<"json">|
|
|
188
|
+
* XPathJTLTOptions<"string">|
|
|
189
|
+
* XPathJTLTOptions<"dom">} JTLTOptions
|
|
190
|
+
*/
|
|
63
191
|
|
|
64
192
|
/**
|
|
65
193
|
* High-level façade for running a JTLT transform.
|
|
@@ -75,6 +203,30 @@ class JTLT {
|
|
|
75
203
|
* config.template, or config.templates, but one must be
|
|
76
204
|
* present and of valid type. For the source json, one must use
|
|
77
205
|
* either a valid config.ajaxData or config.data parameter.
|
|
206
|
+
* @overload
|
|
207
|
+
* @param {JSONPathJTLTOptions} config Options for JSONPath engine
|
|
208
|
+
*/
|
|
209
|
+
/**
|
|
210
|
+
* @overload
|
|
211
|
+
* @param {JSONPathJTLTOptions<"string">} config Options for JSONPath engine
|
|
212
|
+
*/
|
|
213
|
+
/**
|
|
214
|
+
* @overload
|
|
215
|
+
* @param {JSONPathJTLTOptions<"dom">} config Options for JSONPath engine
|
|
216
|
+
*/
|
|
217
|
+
/**
|
|
218
|
+
* @overload
|
|
219
|
+
* @param {XPathJTLTOptions<"json">} config Options for XPath engine
|
|
220
|
+
*/
|
|
221
|
+
/**
|
|
222
|
+
* @overload
|
|
223
|
+
* @param {XPathJTLTOptions<"string">} config Options for XPath engine
|
|
224
|
+
*/
|
|
225
|
+
/**
|
|
226
|
+
* @overload
|
|
227
|
+
* @param {XPathJTLTOptions<"dom">} config Options for XPath engine
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
78
230
|
* @param {JTLTOptions} config Options
|
|
79
231
|
* @todo Remove JSONPath dependency in query use of '$'?
|
|
80
232
|
*/
|
|
@@ -83,7 +235,10 @@ class JTLT {
|
|
|
83
235
|
this.config = config || {};
|
|
84
236
|
|
|
85
237
|
// Track if a custom joiner was provided
|
|
86
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* @type {InternalJTLTOptions}
|
|
240
|
+
*/
|
|
241
|
+
(this.config)._customJoiningTransformer =
|
|
87
242
|
Boolean(this.config.joiningTransformer);
|
|
88
243
|
|
|
89
244
|
this.setDefaults(config);
|
|
@@ -94,7 +249,7 @@ class JTLT {
|
|
|
94
249
|
getJSON(this.config.ajaxData, (function (cfg) {
|
|
95
250
|
return function (json) {
|
|
96
251
|
that.config.data = json;
|
|
97
|
-
that._autoStart(
|
|
252
|
+
that._autoStart(cfg.mode);
|
|
98
253
|
};
|
|
99
254
|
}(config)));
|
|
100
255
|
return;
|
|
@@ -102,7 +257,7 @@ class JTLT {
|
|
|
102
257
|
if (this.config.data === undefined) {
|
|
103
258
|
throw new Error('You must supply either config.ajaxData or config.data');
|
|
104
259
|
}
|
|
105
|
-
this._autoStart(
|
|
260
|
+
this._autoStart(config.mode);
|
|
106
261
|
}
|
|
107
262
|
|
|
108
263
|
/**
|
|
@@ -110,62 +265,78 @@ class JTLT {
|
|
|
110
265
|
* StringJoiningTransformer}
|
|
111
266
|
*/
|
|
112
267
|
_createJoiningTransformer () {
|
|
113
|
-
let JT;
|
|
114
|
-
switch (this.config.outputType) {
|
|
115
|
-
case 'dom':
|
|
116
|
-
JT = DOMJoiningTransformer;
|
|
117
|
-
break;
|
|
118
|
-
case 'json':
|
|
119
|
-
JT = JSONJoiningTransformer;
|
|
120
|
-
break;
|
|
121
|
-
case 'string': default:
|
|
122
|
-
JT = StringJoiningTransformer;
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** @type {any} */
|
|
127
|
-
let initial;
|
|
128
|
-
|
|
129
268
|
// Derive a document to use for joiners when running XPath engine
|
|
130
269
|
/** @type {Document|undefined} */
|
|
131
270
|
let docForJoiner;
|
|
132
|
-
if (
|
|
133
|
-
const {data} =
|
|
271
|
+
if (this.config.engineType === 'xpath') {
|
|
272
|
+
const {data} = this.config;
|
|
134
273
|
if (data && typeof data === 'object') {
|
|
274
|
+
const dataNode = /** @type {Document|Element} */ (data);
|
|
135
275
|
// Document
|
|
136
|
-
if (
|
|
137
|
-
docForJoiner =
|
|
276
|
+
if ((/** @type {Document} */ (dataNode)).nodeType === 9) {
|
|
277
|
+
docForJoiner = /** @type {Document} */ (dataNode);
|
|
138
278
|
// Element or Node with ownerDocument
|
|
139
|
-
} else if (
|
|
140
|
-
docForJoiner =
|
|
279
|
+
} else if ((/** @type {Element} */ (dataNode)).ownerDocument) {
|
|
280
|
+
docForJoiner = (/** @type {Element} */ (dataNode)).ownerDocument;
|
|
141
281
|
}
|
|
142
282
|
}
|
|
143
283
|
}
|
|
144
|
-
if (JT === StringJoiningTransformer) {
|
|
145
|
-
initial = '';
|
|
146
|
-
} else if (JT === DOMJoiningTransformer) {
|
|
147
|
-
initial = (docForJoiner || document).createDocumentFragment();
|
|
148
|
-
} else {
|
|
149
|
-
initial = [];
|
|
150
|
-
}
|
|
151
284
|
|
|
152
|
-
// Build config
|
|
153
|
-
const
|
|
154
|
-
string: {}, json: {}, dom: {}, jamilih: {},
|
|
155
|
-
document: docForJoiner || document
|
|
156
|
-
};
|
|
285
|
+
// Build config, supporting both direct config or nested structure
|
|
286
|
+
const baseConfig = this.config.joiningConfig || {};
|
|
157
287
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
288
|
+
switch (this.config.outputType) {
|
|
289
|
+
case 'dom': {
|
|
290
|
+
/**
|
|
291
|
+
* @type {import('./AbstractJoiningTransformer.js').
|
|
292
|
+
* DOMJoiningTransformerConfig}
|
|
293
|
+
*/
|
|
294
|
+
const domConfig = /** @type {typeof domConfig} */ ({
|
|
295
|
+
...baseConfig,
|
|
296
|
+
document: docForJoiner || _win.document
|
|
297
|
+
});
|
|
298
|
+
if (this.config.exposeDocuments) {
|
|
299
|
+
domConfig.exposeDocuments = true;
|
|
300
|
+
}
|
|
301
|
+
const initial = (docForJoiner || _win.document).createDocumentFragment();
|
|
302
|
+
return new DOMJoiningTransformer(initial, domConfig);
|
|
303
|
+
}
|
|
304
|
+
case 'json': {
|
|
305
|
+
/**
|
|
306
|
+
* @type {import('./AbstractJoiningTransformer.js').
|
|
307
|
+
* JSONJoiningTransformerConfig}
|
|
308
|
+
*/
|
|
309
|
+
const jsonConfig = /** @type {typeof jsonConfig} */ ({
|
|
310
|
+
...baseConfig
|
|
311
|
+
});
|
|
312
|
+
// Pass unwrapSingleResult to JSON joiner if configured
|
|
313
|
+
if (this.config.unwrapSingleResult) {
|
|
314
|
+
jsonConfig.unwrapSingleResult = true;
|
|
315
|
+
}
|
|
316
|
+
// Pass exposeDocuments to JSON joiner if configured
|
|
317
|
+
if (this.config.exposeDocuments) {
|
|
318
|
+
jsonConfig.exposeDocuments = true;
|
|
319
|
+
}
|
|
320
|
+
return new JSONJoiningTransformer([], jsonConfig);
|
|
321
|
+
}
|
|
322
|
+
case 'string': default: {
|
|
323
|
+
/**
|
|
324
|
+
* @type {import('./AbstractJoiningTransformer.js').
|
|
325
|
+
* StringJoiningTransformerConfig}
|
|
326
|
+
*/
|
|
327
|
+
const stringConfig = /** @type {typeof stringConfig} */ ({
|
|
328
|
+
...baseConfig
|
|
329
|
+
});
|
|
330
|
+
if (this.config.exposeDocuments) {
|
|
331
|
+
stringConfig.exposeDocuments = true;
|
|
332
|
+
}
|
|
333
|
+
return new StringJoiningTransformer('', stringConfig);
|
|
334
|
+
}
|
|
162
335
|
}
|
|
163
|
-
|
|
164
|
-
return new JT(initial, joiningConfig);
|
|
165
336
|
}
|
|
166
337
|
|
|
167
338
|
/**
|
|
168
|
-
* @param {string} mode
|
|
339
|
+
* @param {string|undefined} mode
|
|
169
340
|
* @returns {void}
|
|
170
341
|
*/
|
|
171
342
|
_autoStart (mode) {
|
|
@@ -177,7 +348,7 @@ class JTLT {
|
|
|
177
348
|
return;
|
|
178
349
|
}
|
|
179
350
|
|
|
180
|
-
this.transform(mode);
|
|
351
|
+
this.transform(/** @type {string} */ (mode));
|
|
181
352
|
}
|
|
182
353
|
|
|
183
354
|
/**
|
|
@@ -191,11 +362,25 @@ class JTLT {
|
|
|
191
362
|
const query = cfg.forQuery
|
|
192
363
|
// eslint-disable-next-line @stylistic/operator-linebreak -- TS
|
|
193
364
|
? /**
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
365
|
+
* @this {import('./JSONPathTransformerContext.js').default |
|
|
366
|
+
* import('./XPathTransformerContext.js').default}
|
|
367
|
+
* @returns {void}
|
|
368
|
+
*/
|
|
197
369
|
function () {
|
|
198
|
-
|
|
370
|
+
const [path, fn] =
|
|
371
|
+
/**
|
|
372
|
+
* @type {[string, TemplateFunction<
|
|
373
|
+
* joiningTypes,
|
|
374
|
+
* "dom"|"json",
|
|
375
|
+
* import('./JSONPathTransformerContext.js').default |
|
|
376
|
+
* import('./XPathTransformerContext.js').default
|
|
377
|
+
* >]}
|
|
378
|
+
*/ (
|
|
379
|
+
cfg.forQuery
|
|
380
|
+
);
|
|
381
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
382
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument -- Not array
|
|
383
|
+
this.forEach(path, fn);
|
|
199
384
|
}
|
|
200
385
|
: cfg.query || (
|
|
201
386
|
typeof cfg.templates === 'function'
|
|
@@ -205,23 +390,102 @@ class JTLT {
|
|
|
205
390
|
: null
|
|
206
391
|
);
|
|
207
392
|
this.config.templates = query
|
|
208
|
-
|
|
393
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
394
|
+
? /** @type {JSONPathTemplateObject<joiningTypes>[]|XPathTemplateObject<joiningTypes>[]} */ ([
|
|
209
395
|
{name: 'root', path: '$', template: query}
|
|
210
|
-
]
|
|
211
|
-
|
|
396
|
+
])
|
|
397
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
398
|
+
: /** @type {JSONPathTemplateObject<joiningTypes>[]|XPathTemplateObject<joiningTypes>[]} */ (
|
|
399
|
+
cfg.templates || [cfg.template]
|
|
400
|
+
);
|
|
212
401
|
this.config.errorOnEqualPriority = cfg.errorOnEqualPriority || false;
|
|
213
402
|
this.config.engine = this.config.engine ||
|
|
214
403
|
/**
|
|
215
|
-
* @param {JTLTOptions
|
|
216
|
-
*
|
|
404
|
+
* @param {JTLTOptions &
|
|
405
|
+
* Required<Pick<JTLTOptions, "joiningTransformer">>} configParam
|
|
406
|
+
* @returns {ResultType<joiningTypes>}
|
|
217
407
|
*/
|
|
218
408
|
function (configParam) {
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
409
|
+
if (configParam.engineType === 'xpath') {
|
|
410
|
+
let xt;
|
|
411
|
+
/* c8 ignore next -- Defensive: outputType set in setDefaults */
|
|
412
|
+
const outputType = cfg.outputType || 'json';
|
|
413
|
+
// eslint-disable-next-line sonarjs/no-all-duplicated-branches -- TS
|
|
414
|
+
if (outputType === 'string') {
|
|
415
|
+
xt = new (/** @type {typeof XPathTransformer<"string">} */ (
|
|
416
|
+
XPathTransformer
|
|
417
|
+
))(
|
|
418
|
+
/**
|
|
419
|
+
* @type {import('./XPathTransformer.js').
|
|
420
|
+
* XPathTransformerConfig<"string"> &
|
|
421
|
+
* import('./XPathTransformerContext.js').
|
|
422
|
+
* XPathTransformerContextConfig}
|
|
423
|
+
*/
|
|
424
|
+
(configParam)
|
|
425
|
+
);
|
|
426
|
+
// eslint-disable-next-line sonarjs/no-duplicated-branches -- TS
|
|
427
|
+
} else if (outputType === 'json') {
|
|
428
|
+
xt = new (/** @type {typeof XPathTransformer<"json">} */ (
|
|
429
|
+
XPathTransformer
|
|
430
|
+
))(
|
|
431
|
+
/**
|
|
432
|
+
* @type {import('./XPathTransformer.js').
|
|
433
|
+
* XPathTransformerConfig<"json"> &
|
|
434
|
+
* import('./XPathTransformerContext.js').
|
|
435
|
+
* XPathTransformerContextConfig}
|
|
436
|
+
*/
|
|
437
|
+
(configParam)
|
|
438
|
+
);
|
|
439
|
+
// eslint-disable-next-line sonarjs/no-duplicated-branches -- TS
|
|
440
|
+
} else {
|
|
441
|
+
xt = new (/** @type {typeof XPathTransformer<"dom">} */ (
|
|
442
|
+
XPathTransformer
|
|
443
|
+
))(
|
|
444
|
+
/**
|
|
445
|
+
* @type {import('./XPathTransformer.js').
|
|
446
|
+
* XPathTransformerConfig<"dom"> &
|
|
447
|
+
* import('./XPathTransformerContext.js').
|
|
448
|
+
* XPathTransformerContextConfig}
|
|
449
|
+
*/
|
|
450
|
+
(configParam)
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
return xt.transform(configParam.mode);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Type assertion is safe here because _createJoiningTransformer
|
|
457
|
+
// ensures the joiningTransformer type matches outputType
|
|
458
|
+
const outputType = configParam.outputType || 'json';
|
|
459
|
+
|
|
460
|
+
// Branch based on outputType to help TypeScript narrow the type
|
|
461
|
+
if (outputType === 'string') {
|
|
462
|
+
const jpt = new JSONPathTransformer(
|
|
463
|
+
/**
|
|
464
|
+
* @type {import('./JSONPathTransformerContext.js').
|
|
465
|
+
* JSONPathTransformerContextConfig<"string">}
|
|
466
|
+
*/
|
|
467
|
+
(configParam)
|
|
468
|
+
);
|
|
469
|
+
return jpt.transform(configParam.mode);
|
|
222
470
|
}
|
|
223
|
-
|
|
224
|
-
|
|
471
|
+
if (outputType === 'dom') {
|
|
472
|
+
const jpt = new JSONPathTransformer(
|
|
473
|
+
/**
|
|
474
|
+
* @type {import('./JSONPathTransformerContext.js').
|
|
475
|
+
* JSONPathTransformerContextConfig<"dom">}
|
|
476
|
+
*/
|
|
477
|
+
(configParam)
|
|
478
|
+
);
|
|
479
|
+
return jpt.transform(configParam.mode);
|
|
480
|
+
}
|
|
481
|
+
const jpt = new JSONPathTransformer(
|
|
482
|
+
/**
|
|
483
|
+
* @type {import('./JSONPathTransformerContext.js').
|
|
484
|
+
* JSONPathTransformerContextConfig<"json">}
|
|
485
|
+
*/
|
|
486
|
+
(configParam)
|
|
487
|
+
);
|
|
488
|
+
return jpt.transform(configParam.mode);
|
|
225
489
|
};
|
|
226
490
|
// Todo: Let's also, unlike XSLT and the following, give options for
|
|
227
491
|
// higher priority to absolute fixed paths over recursive descent
|
|
@@ -237,8 +501,8 @@ class JTLT {
|
|
|
237
501
|
}
|
|
238
502
|
|
|
239
503
|
/**
|
|
240
|
-
* @param {string} mode The mode of the transformation
|
|
241
|
-
* @returns {
|
|
504
|
+
* @param {string} [mode] The mode of the transformation
|
|
505
|
+
* @returns {void}
|
|
242
506
|
* @todo Allow for a success callback in case the jsonpath code is modified
|
|
243
507
|
* to work asynchronously (as with queries to access remote JSON
|
|
244
508
|
* stores)
|
|
@@ -256,18 +520,155 @@ class JTLT {
|
|
|
256
520
|
|
|
257
521
|
// Create a fresh joining transformer for each transform to avoid
|
|
258
522
|
// accumulation, but only if a custom one wasn't provided
|
|
259
|
-
if (!(
|
|
523
|
+
if (!(
|
|
524
|
+
/**
|
|
525
|
+
* @type {InternalJTLTOptions}
|
|
526
|
+
*/
|
|
527
|
+
(this.config)
|
|
528
|
+
)._customJoiningTransformer) {
|
|
260
529
|
this.config.joiningTransformer = this._createJoiningTransformer();
|
|
261
530
|
}
|
|
262
531
|
|
|
263
532
|
this.config.mode = mode;
|
|
264
|
-
const
|
|
265
|
-
|
|
533
|
+
const {engine} = this.config;
|
|
534
|
+
/* c8 ignore next 3 -- Defensive: always configured by setDefaults */
|
|
535
|
+
if (!engine) {
|
|
536
|
+
throw new Error('Engine is not configured');
|
|
537
|
+
}
|
|
538
|
+
const result = engine(
|
|
539
|
+
// eslint-disable-next-line @stylistic/max-len -- Long type
|
|
540
|
+
/** @type {JTLTOptions & Required<Pick<JTLTOptions, "joiningTransformer">>} */ (
|
|
541
|
+
this.config
|
|
542
|
+
)
|
|
543
|
+
);
|
|
544
|
+
// The engine returns ResultType<T>. We cast through never to bypass
|
|
545
|
+
// the impossible intersection type that TypeScript infers for the union.
|
|
546
|
+
const ret = this.config.success(
|
|
547
|
+
/** @type {never} */ (result)
|
|
266
548
|
);
|
|
267
549
|
return ret;
|
|
268
550
|
}
|
|
269
551
|
}
|
|
270
552
|
|
|
553
|
+
/**
|
|
554
|
+
* Create and run a JTLT instance with the appropriate engine typing.
|
|
555
|
+
*
|
|
556
|
+
* Overloads help TypeScript select the correct constructor signature.
|
|
557
|
+
* @overload
|
|
558
|
+
* @param {Omit<JSONPathJTLTOptions<"json">, "success">} cfg
|
|
559
|
+
* @returns {Promise<ResultType<"json">>}
|
|
560
|
+
*/
|
|
561
|
+
/**
|
|
562
|
+
* @overload
|
|
563
|
+
* @param {Omit<JSONPathJTLTOptions<"string">, "success">} cfg
|
|
564
|
+
* @returns {Promise<ResultType<"string">>}
|
|
565
|
+
*/
|
|
566
|
+
/**
|
|
567
|
+
* @overload
|
|
568
|
+
* @param {Omit<JSONPathJTLTOptions<"dom">, "success">} cfg
|
|
569
|
+
* @returns {Promise<ResultType<"dom">>}
|
|
570
|
+
*/
|
|
571
|
+
/**
|
|
572
|
+
* @overload
|
|
573
|
+
* @param {Omit<XPathJTLTOptions<"json">, "success">} cfg
|
|
574
|
+
* @returns {Promise<ResultType<"json">>}
|
|
575
|
+
*/
|
|
576
|
+
/**
|
|
577
|
+
* @overload
|
|
578
|
+
* @param {Omit<XPathJTLTOptions<"dom">, "success">} cfg
|
|
579
|
+
* @returns {Promise<ResultType<"dom">>}
|
|
580
|
+
*/
|
|
581
|
+
/**
|
|
582
|
+
* @overload
|
|
583
|
+
* @param {Omit<XPathJTLTOptions<"string">, "success">} cfg
|
|
584
|
+
* @returns {Promise<ResultType<"string">>}
|
|
585
|
+
*/
|
|
586
|
+
/**
|
|
587
|
+
* @param {Omit<JTLTOptions, "success">} cfg Options
|
|
588
|
+
*/
|
|
589
|
+
export function jtlt (cfg) {
|
|
590
|
+
// eslint-disable-next-line promise/avoid-new -- Own API
|
|
591
|
+
return new Promise((resolve) => {
|
|
592
|
+
// Narrow the constructor overload based on engineType
|
|
593
|
+
if (cfg && cfg.engineType === 'xpath') {
|
|
594
|
+
const outputType = cfg.outputType || 'string';
|
|
595
|
+
|
|
596
|
+
if (outputType === 'json') {
|
|
597
|
+
// eslint-disable-next-line no-new -- API
|
|
598
|
+
new JTLT(
|
|
599
|
+
/** @type {XPathJTLTOptions<"json">} */ ({
|
|
600
|
+
...cfg,
|
|
601
|
+
outputType: 'json',
|
|
602
|
+
success (val) {
|
|
603
|
+
resolve(val);
|
|
604
|
+
}
|
|
605
|
+
})
|
|
606
|
+
);
|
|
607
|
+
} else if (outputType === 'dom') {
|
|
608
|
+
// eslint-disable-next-line no-new -- API
|
|
609
|
+
new JTLT(
|
|
610
|
+
/** @type {XPathJTLTOptions<"dom">} */ ({
|
|
611
|
+
...cfg,
|
|
612
|
+
outputType: 'dom',
|
|
613
|
+
success (val) {
|
|
614
|
+
resolve(val);
|
|
615
|
+
}
|
|
616
|
+
})
|
|
617
|
+
);
|
|
618
|
+
} else {
|
|
619
|
+
// eslint-disable-next-line no-new -- API
|
|
620
|
+
new JTLT(
|
|
621
|
+
/** @type {XPathJTLTOptions<"string">} */ ({
|
|
622
|
+
...cfg,
|
|
623
|
+
outputType: 'string',
|
|
624
|
+
success (val) {
|
|
625
|
+
resolve(val);
|
|
626
|
+
}
|
|
627
|
+
})
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const outputType = cfg.outputType || 'json';
|
|
634
|
+
|
|
635
|
+
if (outputType === 'string') {
|
|
636
|
+
// eslint-disable-next-line no-new -- API
|
|
637
|
+
new JTLT(
|
|
638
|
+
/** @type {JSONPathJTLTOptions<"string">} */ ({
|
|
639
|
+
...cfg,
|
|
640
|
+
outputType: 'string',
|
|
641
|
+
success (val) {
|
|
642
|
+
resolve(val);
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
);
|
|
646
|
+
} else if (outputType === 'dom') {
|
|
647
|
+
// eslint-disable-next-line no-new -- API
|
|
648
|
+
new JTLT(
|
|
649
|
+
/** @type {JSONPathJTLTOptions<"dom">} */ ({
|
|
650
|
+
...cfg,
|
|
651
|
+
outputType: 'dom',
|
|
652
|
+
success (val) {
|
|
653
|
+
resolve(val);
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
);
|
|
657
|
+
} else {
|
|
658
|
+
// eslint-disable-next-line no-new -- API
|
|
659
|
+
new JTLT(
|
|
660
|
+
/** @type {JSONPathJTLTOptions<"json">} */ ({
|
|
661
|
+
...cfg,
|
|
662
|
+
outputType: 'json',
|
|
663
|
+
success (val) {
|
|
664
|
+
resolve(val);
|
|
665
|
+
}
|
|
666
|
+
})
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
271
672
|
export {
|
|
272
673
|
default as AbstractJoiningTransformer
|
|
273
674
|
} from './AbstractJoiningTransformer.js';
|