jtlt 0.1.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/.editorconfig +16 -0
- package/CHANGES.md +5 -0
- package/LICENSE-MIT.txt +21 -0
- package/README.md +534 -0
- package/dist/AbstractJoiningTransformer.d.ts +42 -0
- package/dist/AbstractJoiningTransformer.d.ts.map +1 -0
- package/dist/DOMJoiningTransformer.d.ts +113 -0
- package/dist/DOMJoiningTransformer.d.ts.map +1 -0
- package/dist/JSONJoiningTransformer.d.ts +160 -0
- package/dist/JSONJoiningTransformer.d.ts.map +1 -0
- package/dist/JSONPathTransformer.d.ts +95 -0
- package/dist/JSONPathTransformer.d.ts.map +1 -0
- package/dist/JSONPathTransformerContext.d.ts +263 -0
- package/dist/JSONPathTransformerContext.d.ts.map +1 -0
- package/dist/StringJoiningTransformer.d.ts +168 -0
- package/dist/StringJoiningTransformer.d.ts.map +1 -0
- package/dist/XPathTransformer.d.ts +51 -0
- package/dist/XPathTransformer.d.ts.map +1 -0
- package/dist/XPathTransformerContext.d.ts +260 -0
- package/dist/XPathTransformerContext.d.ts.map +1 -0
- package/dist/XSLTStyleJSONPathResolver.d.ts +16 -0
- package/dist/XSLTStyleJSONPathResolver.d.ts.map +1 -0
- package/dist/index.d.ts +168 -0
- package/dist/index.d.ts.map +1 -0
- package/docs/API.expanded.md +263 -0
- package/docs/API.md +69 -0
- package/eslint.config.js +30 -0
- package/package.json +53 -0
- package/pnpm-workspace.yaml +3 -0
- package/src/AbstractJoiningTransformer.js +73 -0
- package/src/DOMJoiningTransformer.js +237 -0
- package/src/JSONJoiningTransformer.js +472 -0
- package/src/JSONPathTransformer.js +159 -0
- package/src/JSONPathTransformerContext.js +807 -0
- package/src/StringJoiningTransformer.js +589 -0
- package/src/XPathTransformer.js +94 -0
- package/src/XPathTransformerContext.js +496 -0
- package/src/XSLTStyleJSONPathResolver.js +39 -0
- package/src/index.js +299 -0
- package/src/types/xpath2-js.d.ts +2 -0
- package/tsconfig-prod.json +19 -0
- package/tsconfig.json +14 -0
- package/typings/xpath2-js.d.ts +2 -0
package/src/index.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
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
|
+
|
|
9
|
+
import DOMJoiningTransformer from './DOMJoiningTransformer.js';
|
|
10
|
+
import JSONJoiningTransformer from './JSONJoiningTransformer.js';
|
|
11
|
+
import JSONPathTransformer from './JSONPathTransformer.js';
|
|
12
|
+
import XPathTransformer from './XPathTransformer.js';
|
|
13
|
+
import StringJoiningTransformer from './StringJoiningTransformer.js';
|
|
14
|
+
import XSLTStyleJSONPathResolver from './XSLTStyleJSONPathResolver.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {object} JTLTOptions
|
|
18
|
+
* @property {Function} success A callback supplied with a single
|
|
19
|
+
* argument that is the result of this instance's transform() method.
|
|
20
|
+
* @property {any[]} [templates] An array of template objects
|
|
21
|
+
* @property {object|Function} [template] A function assumed to be a
|
|
22
|
+
* root template or a single, complete template object
|
|
23
|
+
* @property {Function} [query] A function assumed to be a root template
|
|
24
|
+
* @property {any[]} [forQuery] An array with arguments to be supplied
|
|
25
|
+
* to a single call to `forEach` (and which will serve as the root
|
|
26
|
+
* template)
|
|
27
|
+
* @property {any} [data] A JSON object
|
|
28
|
+
* @property {string} [ajaxData] URL of a JSON file to retrieve for
|
|
29
|
+
* evaluation
|
|
30
|
+
* @property {boolean} [errorOnEqualPriority] Whether or not to
|
|
31
|
+
* report an error when equal priority templates are found
|
|
32
|
+
* @property {boolean} [autostart] Whether to begin transform()
|
|
33
|
+
* immediately.
|
|
34
|
+
* @property {boolean} [preventEval] Whether to prevent
|
|
35
|
+
* parenthetical evaluations in JSONPath. Safer if relying on user
|
|
36
|
+
* input, but reduces capabilities of JSONPath.
|
|
37
|
+
* @property {boolean} [unwrapSingleResult] For JSON output, whether to
|
|
38
|
+
* unwrap single-element root arrays to return just the element
|
|
39
|
+
* @property {string} [mode] The mode in which to begin the transform.
|
|
40
|
+
* @property {string} [outputType] Output type: 'string', 'dom', or 'json'
|
|
41
|
+
* @property {Function} [engine] Will be based the
|
|
42
|
+
* same config as passed to this instance. Defaults to a transforming
|
|
43
|
+
* function based on JSONPath and with its own set of priorities for
|
|
44
|
+
* processing templates.
|
|
45
|
+
* @property {'jsonpath'|'xpath'} [engineType] Choose built-in engine.
|
|
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]
|
|
50
|
+
* Callback for getting the priority by specificity
|
|
51
|
+
* @property {{get: Function, append: Function, string?: Function,
|
|
52
|
+
* object?: Function, array?: Function}} [joiningTransformer] Can
|
|
53
|
+
* be a singleton or class instance. Defaults to string joining for output
|
|
54
|
+
* transformation.
|
|
55
|
+
* @property {object} [joiningConfig] Config to pass on to the joining
|
|
56
|
+
* transformer
|
|
57
|
+
* @property {any} [parent] Parent object for context
|
|
58
|
+
* @property {string} [parentProperty] Parent property name for context
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
const {window} = new JSDOM();
|
|
62
|
+
const {document} = window;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* High-level façade for running a JTLT transform.
|
|
66
|
+
*
|
|
67
|
+
* Accepts data and templates (or a root template/query), constructs a joining
|
|
68
|
+
* transformer based on `outputType`, and invokes the JSONPath-based engine.
|
|
69
|
+
* The result is returned to the required `success` callback and also returned
|
|
70
|
+
* from transform().
|
|
71
|
+
*/
|
|
72
|
+
class JTLT {
|
|
73
|
+
/**
|
|
74
|
+
* For templates/queries, one may choose among config.query,
|
|
75
|
+
* config.template, or config.templates, but one must be
|
|
76
|
+
* present and of valid type. For the source json, one must use
|
|
77
|
+
* either a valid config.ajaxData or config.data parameter.
|
|
78
|
+
* @param {JTLTOptions} config Options
|
|
79
|
+
* @todo Remove JSONPath dependency in query use of '$'?
|
|
80
|
+
*/
|
|
81
|
+
constructor (config) {
|
|
82
|
+
/** @type {JTLTOptions} */
|
|
83
|
+
this.config = config || {};
|
|
84
|
+
|
|
85
|
+
// Track if a custom joiner was provided
|
|
86
|
+
/** @type {any} */ (this.config)._customJoiningTransformer =
|
|
87
|
+
Boolean(this.config.joiningTransformer);
|
|
88
|
+
|
|
89
|
+
this.setDefaults(config);
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line unicorn/no-this-assignment -- Temporary
|
|
92
|
+
const that = this;
|
|
93
|
+
if (this.config.ajaxData) {
|
|
94
|
+
getJSON(this.config.ajaxData, (function (cfg) {
|
|
95
|
+
return function (json) {
|
|
96
|
+
that.config.data = json;
|
|
97
|
+
that._autoStart(/** @type {any} */ (cfg).mode);
|
|
98
|
+
};
|
|
99
|
+
}(config)));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (this.config.data === undefined) {
|
|
103
|
+
throw new Error('You must supply either config.ajaxData or config.data');
|
|
104
|
+
}
|
|
105
|
+
this._autoStart(/** @type {any} */ (config).mode);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @returns {DOMJoiningTransformer|JSONJoiningTransformer|
|
|
110
|
+
* StringJoiningTransformer}
|
|
111
|
+
*/
|
|
112
|
+
_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
|
+
// Derive a document to use for joiners when running XPath engine
|
|
130
|
+
/** @type {Document|undefined} */
|
|
131
|
+
let docForJoiner;
|
|
132
|
+
if ((/** @type {any} */ (this.config)).engineType === 'xpath') {
|
|
133
|
+
const {data} = /** @type {any} */ (this.config);
|
|
134
|
+
if (data && typeof data === 'object') {
|
|
135
|
+
// Document
|
|
136
|
+
if (data.nodeType === 9) {
|
|
137
|
+
docForJoiner = data;
|
|
138
|
+
// Element or Node with ownerDocument
|
|
139
|
+
} else if (data.ownerDocument) {
|
|
140
|
+
docForJoiner = data.ownerDocument;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (JT === StringJoiningTransformer) {
|
|
145
|
+
initial = '';
|
|
146
|
+
} else if (JT === DOMJoiningTransformer) {
|
|
147
|
+
initial = (docForJoiner || document).createDocumentFragment();
|
|
148
|
+
} else {
|
|
149
|
+
initial = [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Build config for joining transformer
|
|
153
|
+
const joiningConfig = this.config.joiningConfig || {
|
|
154
|
+
string: {}, json: {}, dom: {}, jamilih: {},
|
|
155
|
+
document: docForJoiner || document
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Pass unwrapSingleResult to JSON joiner if configured
|
|
159
|
+
if (JT === JSONJoiningTransformer &&
|
|
160
|
+
/** @type {any} */ (this.config).unwrapSingleResult) {
|
|
161
|
+
/** @type {any} */ (joiningConfig).unwrapSingleResult = true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return new JT(initial, joiningConfig);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {string} mode
|
|
169
|
+
* @returns {void}
|
|
170
|
+
*/
|
|
171
|
+
_autoStart (mode) {
|
|
172
|
+
// We wait to set this default as we want to pass in the data
|
|
173
|
+
this.config.joiningTransformer = this.config.joiningTransformer ||
|
|
174
|
+
this._createJoiningTransformer();
|
|
175
|
+
|
|
176
|
+
if (this.config.autostart === false) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this.transform(mode);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {JTLTOptions} config
|
|
185
|
+
* @returns {JTLT}
|
|
186
|
+
*/
|
|
187
|
+
setDefaults (config) {
|
|
188
|
+
/** @type {JTLTOptions} */
|
|
189
|
+
this.config = config || {};
|
|
190
|
+
const cfg = this.config;
|
|
191
|
+
const query = cfg.forQuery
|
|
192
|
+
// eslint-disable-next-line @stylistic/operator-linebreak -- TS
|
|
193
|
+
? /**
|
|
194
|
+
* @this {any}
|
|
195
|
+
* @returns {void}
|
|
196
|
+
*/
|
|
197
|
+
function () {
|
|
198
|
+
this.forEach([].slice.call(cfg.forQuery));
|
|
199
|
+
}
|
|
200
|
+
: cfg.query || (
|
|
201
|
+
typeof cfg.templates === 'function'
|
|
202
|
+
? cfg.templates
|
|
203
|
+
: typeof cfg.template === 'function'
|
|
204
|
+
? cfg.template
|
|
205
|
+
: null
|
|
206
|
+
);
|
|
207
|
+
this.config.templates = query
|
|
208
|
+
? [
|
|
209
|
+
{name: 'root', path: '$', template: query}
|
|
210
|
+
]
|
|
211
|
+
: cfg.templates || [cfg.template];
|
|
212
|
+
this.config.errorOnEqualPriority = cfg.errorOnEqualPriority || false;
|
|
213
|
+
this.config.engine = this.config.engine ||
|
|
214
|
+
/**
|
|
215
|
+
* @param {JTLTOptions} configParam
|
|
216
|
+
* @returns {any}
|
|
217
|
+
*/
|
|
218
|
+
function (configParam) {
|
|
219
|
+
if ((/** @type {any} */ (configParam)).engineType === 'xpath') {
|
|
220
|
+
const xt = new XPathTransformer(/** @type {any} */ (configParam));
|
|
221
|
+
return xt.transform(/** @type {any} */ (configParam).mode);
|
|
222
|
+
}
|
|
223
|
+
const jpt = new JSONPathTransformer(/** @type {any} */ (configParam));
|
|
224
|
+
return jpt.transform(/** @type {any} */ (configParam).mode);
|
|
225
|
+
};
|
|
226
|
+
// Todo: Let's also, unlike XSLT and the following, give options for
|
|
227
|
+
// higher priority to absolute fixed paths over recursive descent
|
|
228
|
+
// and priority to longer paths and lower to wildcard terminal points
|
|
229
|
+
this.config.specificityPriorityResolver =
|
|
230
|
+
this.config.specificityPriorityResolver || (function () {
|
|
231
|
+
const xsjpr = new XSLTStyleJSONPathResolver();
|
|
232
|
+
return function (/** @type {string} */ path) {
|
|
233
|
+
return xsjpr.getPriorityBySpecificity(path);
|
|
234
|
+
};
|
|
235
|
+
}());
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @param {string} mode The mode of the transformation
|
|
241
|
+
* @returns {any} Result of transformation
|
|
242
|
+
* @todo Allow for a success callback in case the jsonpath code is modified
|
|
243
|
+
* to work asynchronously (as with queries to access remote JSON
|
|
244
|
+
* stores)
|
|
245
|
+
*/
|
|
246
|
+
transform (mode) {
|
|
247
|
+
if (this.config.data === undefined) {
|
|
248
|
+
if (this.config.ajaxData === undefined) {
|
|
249
|
+
throw new Error("You must supply a 'data' or 'ajaxData' property");
|
|
250
|
+
}
|
|
251
|
+
throw new Error('You must wait until the ajax file is retrieved');
|
|
252
|
+
}
|
|
253
|
+
if (typeof this.config.success !== 'function') {
|
|
254
|
+
throw new TypeError("You must supply a 'success' callback");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Create a fresh joining transformer for each transform to avoid
|
|
258
|
+
// accumulation, but only if a custom one wasn't provided
|
|
259
|
+
if (!(/** @type {any} */ (this.config))._customJoiningTransformer) {
|
|
260
|
+
this.config.joiningTransformer = this._createJoiningTransformer();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.config.mode = mode;
|
|
264
|
+
const ret = /** @type {Function} */ (this.config.success)(
|
|
265
|
+
(/** @type {any} */ (this.config.engine))(this.config)
|
|
266
|
+
);
|
|
267
|
+
return ret;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export {
|
|
272
|
+
default as AbstractJoiningTransformer
|
|
273
|
+
} from './AbstractJoiningTransformer.js';
|
|
274
|
+
export {
|
|
275
|
+
default as StringJoiningTransformer
|
|
276
|
+
} from './StringJoiningTransformer.js';
|
|
277
|
+
export {
|
|
278
|
+
default as DOMJoiningTransformer
|
|
279
|
+
} from './DOMJoiningTransformer.js';
|
|
280
|
+
export {
|
|
281
|
+
default as JSONJoiningTransformer
|
|
282
|
+
} from './JSONJoiningTransformer.js';
|
|
283
|
+
export {
|
|
284
|
+
default as XSLTStyleJSONPathResolver
|
|
285
|
+
} from './XSLTStyleJSONPathResolver.js';
|
|
286
|
+
export {
|
|
287
|
+
default as JSONPathTransformerContext
|
|
288
|
+
} from './JSONPathTransformerContext.js';
|
|
289
|
+
export {
|
|
290
|
+
default as JSONPathTransformer
|
|
291
|
+
} from './JSONPathTransformer.js';
|
|
292
|
+
export {
|
|
293
|
+
default as XPathTransformerContext
|
|
294
|
+
} from './XPathTransformerContext.js';
|
|
295
|
+
export {
|
|
296
|
+
default as XPathTransformer
|
|
297
|
+
} from './XPathTransformer.js';
|
|
298
|
+
|
|
299
|
+
export default JTLT;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext", "DOM"],
|
|
4
|
+
"skipLibCheck": true,
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
|
+
"module": "NodeNext",
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"checkJs": true,
|
|
9
|
+
"noEmit": false,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"declarationMap": true,
|
|
12
|
+
"emitDeclarationOnly": true,
|
|
13
|
+
"strict": true,
|
|
14
|
+
"target": "es2024",
|
|
15
|
+
"outDir": "dist"
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*.js", "typings/xpath2-js.d.ts"],
|
|
18
|
+
"exclude": ["node_modules", "./dist/**/*.js"]
|
|
19
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext", "DOM"],
|
|
4
|
+
"skipLibCheck": true,
|
|
5
|
+
"moduleResolution": "nodenext",
|
|
6
|
+
"module": "NodeNext",
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"checkJs": true,
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"strict": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["*.js", "*.d.ts", "src/**/*.js", "test/*.js", "typings/xpath2-js.d.ts"],
|
|
13
|
+
"exclude": ["node_modules", "./dist/**/*.js"]
|
|
14
|
+
}
|