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.
Files changed (43) hide show
  1. package/.editorconfig +16 -0
  2. package/CHANGES.md +5 -0
  3. package/LICENSE-MIT.txt +21 -0
  4. package/README.md +534 -0
  5. package/dist/AbstractJoiningTransformer.d.ts +42 -0
  6. package/dist/AbstractJoiningTransformer.d.ts.map +1 -0
  7. package/dist/DOMJoiningTransformer.d.ts +113 -0
  8. package/dist/DOMJoiningTransformer.d.ts.map +1 -0
  9. package/dist/JSONJoiningTransformer.d.ts +160 -0
  10. package/dist/JSONJoiningTransformer.d.ts.map +1 -0
  11. package/dist/JSONPathTransformer.d.ts +95 -0
  12. package/dist/JSONPathTransformer.d.ts.map +1 -0
  13. package/dist/JSONPathTransformerContext.d.ts +263 -0
  14. package/dist/JSONPathTransformerContext.d.ts.map +1 -0
  15. package/dist/StringJoiningTransformer.d.ts +168 -0
  16. package/dist/StringJoiningTransformer.d.ts.map +1 -0
  17. package/dist/XPathTransformer.d.ts +51 -0
  18. package/dist/XPathTransformer.d.ts.map +1 -0
  19. package/dist/XPathTransformerContext.d.ts +260 -0
  20. package/dist/XPathTransformerContext.d.ts.map +1 -0
  21. package/dist/XSLTStyleJSONPathResolver.d.ts +16 -0
  22. package/dist/XSLTStyleJSONPathResolver.d.ts.map +1 -0
  23. package/dist/index.d.ts +168 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/docs/API.expanded.md +263 -0
  26. package/docs/API.md +69 -0
  27. package/eslint.config.js +30 -0
  28. package/package.json +53 -0
  29. package/pnpm-workspace.yaml +3 -0
  30. package/src/AbstractJoiningTransformer.js +73 -0
  31. package/src/DOMJoiningTransformer.js +237 -0
  32. package/src/JSONJoiningTransformer.js +472 -0
  33. package/src/JSONPathTransformer.js +159 -0
  34. package/src/JSONPathTransformerContext.js +807 -0
  35. package/src/StringJoiningTransformer.js +589 -0
  36. package/src/XPathTransformer.js +94 -0
  37. package/src/XPathTransformerContext.js +496 -0
  38. package/src/XSLTStyleJSONPathResolver.js +39 -0
  39. package/src/index.js +299 -0
  40. package/src/types/xpath2-js.d.ts +2 -0
  41. package/tsconfig-prod.json +19 -0
  42. package/tsconfig.json +14 -0
  43. 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,2 @@
1
+ // Ambient module declaration for xpath2.js (library lacks bundled types)
2
+ declare module 'xpath2.js';
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ // Ambient module declaration for xpath2.js (library lacks bundled types)
2
+ declare module 'xpath2.js';