jtlt 0.4.0 → 0.6.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 (34) hide show
  1. package/CHANGES.md +16 -0
  2. package/demo/index.css +4 -0
  3. package/demo/index.html +4 -4
  4. package/demo/index.js +6 -2
  5. package/demo/vendor/fontoxpath/dist/fontoxpath.esm.js +600 -0
  6. package/demo/vendor/prsc/dist/prsc.esm.js +2 -0
  7. package/demo/vendor/whynot/dist/whynot.esm.js +2 -0
  8. package/demo/vendor/xspattern/dist/xspattern.esm.js +2 -0
  9. package/dist/AbstractJoiningTransformer.d.ts +35 -0
  10. package/dist/AbstractJoiningTransformer.d.ts.map +1 -1
  11. package/dist/DOMJoiningTransformer.d.ts +85 -8
  12. package/dist/DOMJoiningTransformer.d.ts.map +1 -1
  13. package/dist/JSONJoiningTransformer.d.ts +28 -10
  14. package/dist/JSONJoiningTransformer.d.ts.map +1 -1
  15. package/dist/JSONPathTransformer.d.ts.map +1 -1
  16. package/dist/JSONPathTransformerContext.d.ts +168 -3
  17. package/dist/JSONPathTransformerContext.d.ts.map +1 -1
  18. package/dist/StringJoiningTransformer.d.ts +25 -8
  19. package/dist/StringJoiningTransformer.d.ts.map +1 -1
  20. package/dist/XPathTransformer.d.ts.map +1 -1
  21. package/dist/XPathTransformerContext.d.ts +107 -1
  22. package/dist/XPathTransformerContext.d.ts.map +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/docs/TO-DO.md +38 -36
  25. package/package.json +6 -2
  26. package/src/AbstractJoiningTransformer.js +53 -0
  27. package/src/DOMJoiningTransformer.js +199 -24
  28. package/src/JSONJoiningTransformer.js +89 -27
  29. package/src/JSONPathTransformer.js +2 -0
  30. package/src/JSONPathTransformerContext.js +572 -9
  31. package/src/StringJoiningTransformer.js +60 -22
  32. package/src/XPathTransformer.js +2 -0
  33. package/src/XPathTransformerContext.js +487 -11
  34. package/src/index.js +7 -1
package/docs/TO-DO.md CHANGED
@@ -84,85 +84,87 @@
84
84
 
85
85
  7. See code for other possible to-dos
86
86
 
87
- 8. Consider implementing the following elements from <https://www.w3.org/TR/xslt-30/>
87
+ 8. Promise-based templates
88
+
89
+ 9. Consider implementing the following elements from <https://www.w3.org/TR/xslt-30/>
88
90
  which are not yet implemented.
89
91
 
90
92
  xsl:accept
91
93
  xsl:accumulator
92
94
  xsl:accumulator-rule
93
- ~~"xsl:analyze-string",~~
94
95
  "xsl:apply-imports",
95
- ~~"xsl:apply-templates",~~
96
- xsl:assert
97
- ~~"xsl:attribute",~~
98
- "xsl:attribute-set",
99
96
  xsl:break
100
- ~~"xsl:call-template",~~
101
97
  xsl:catch
102
- "xsl:character-map",
103
- ~~"xsl:choose",~~
104
- ~~"xsl:comment",~~
105
98
  xsl:context-item
106
- ~~"xsl:copy",~~
107
- ~~"xsl:copy-of",~~
108
- "xsl:decimal-format",
109
- ~~"xsl:document",~~
110
- ~~"xsl:element",~~
111
99
  xsl:evaluate
112
100
  xsl:expose
113
101
  "xsl:fallback",
114
- ~~"xsl:for-each",~~
115
- "xsl:for-each-group",
116
102
  xsl:fork
117
103
  "xsl:function",
118
104
  xsl:global-context-item
119
- ~~"xsl:if",~~
120
105
  "xsl:import",
121
106
  "xsl:import-schema",
122
107
  "xsl:include",
123
108
  xsl:iterate
124
- ~~"xsl:key",~~
125
- xsl:map
126
- xsl:map-entry
127
- ~~"xsl:matching-substring",~~
128
109
  xsl:merge
129
110
  xsl:merge-action
130
111
  xsl:merge-key
131
112
  xsl:merge-source
132
- ~~"xsl:message",~~
133
113
  xsl:mode
134
- "xsl:namespace",
135
114
  "xsl:namespace-alias",
136
115
  xsl:next-iteration
137
116
  "xsl:next-match",
138
- ~~"xsl:non-matching-substring",~~
139
- ~~"xsl:number",~~
140
117
  xsl:on-completion
141
118
  xsl:on-empty
142
119
  xsl:on-non-empty
143
- ~~"xsl:otherwise",~~
144
- ~~"xsl:output",~~
145
- "xsl:output-character",
146
120
  xsl:override
147
121
  xsl:package
148
122
  "xsl:param",
149
123
  "xsl:perform-sort",
150
124
  "xsl:preserve-space",
151
- ~~"xsl:processing-instruction",~~
152
- ~~"xsl:result-document",~~
153
125
  "xsl:sequence",
154
- ~~"xsl:sort",~~
155
126
  xsl:source-document
156
127
  "xsl:strip-space",
128
+ xsl:try
129
+ xsl:use-package
130
+ xsl:where-populated
131
+
132
+ ~~"xsl:analyze-string",~~
133
+ ~~"xsl:apply-templates",~~
134
+ ~~xsl:assert~~
135
+ ~~"xsl:attribute",~~
136
+ ~~"xsl:attribute-set",~~
137
+ ~~"xsl:call-template",~~
138
+ ~~"xsl:character-map",~~
139
+ ~~"xsl:choose",~~
140
+ ~~"xsl:comment",~~
141
+ ~~"xsl:copy",~~
142
+ ~~"xsl:copy-of",~~
143
+ ~~"xsl:decimal-format",~~
144
+ ~~"xsl:document",~~
145
+ ~~"xsl:element",~~
146
+ ~~"xsl:for-each",~~
147
+ ~~"xsl:for-each-group",~~
148
+ ~~"xsl:if",~~
149
+ ~~"xsl:key",~~
150
+ ~~xsl:map~~
151
+ ~~xsl:map-entry~~
152
+ ~~"xsl:matching-substring",~~
153
+ ~~"xsl:message",~~
154
+ ~~"xsl:namespace",~~
155
+ ~~"xsl:non-matching-substring",~~
156
+ ~~"xsl:number",~~
157
+ ~~"xsl:otherwise",~~
158
+ ~~"xsl:output",~~
159
+ ~~"xsl:output-character",~~
160
+ ~~"xsl:processing-instruction",~~
161
+ ~~"xsl:result-document",~~
162
+ ~~"xsl:sort",~~
157
163
  ~~"xsl:stylesheet",~~
158
164
  ~~"xsl:template",~~
159
165
  ~~"xsl:text",~~
160
166
  ~~"xsl:transform",~~
161
- xsl:try
162
- xsl:use-package
163
167
  ~~"xsl:value-of",~~
164
168
  ~~"xsl:variable",~~
165
169
  ~~"xsl:when",~~
166
- xsl:where-populated
167
170
  ~~"xsl:with-param"~~
168
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jtlt",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "author": "Brett Zamir",
6
6
  "contributors": [],
@@ -77,7 +77,11 @@
77
77
  "copy-jhtml": "cp -Rf node_modules/jhtml/src demo/vendor/jhtml",
78
78
  "copy-jamilih": "cp node_modules/jamilih/dist/jml.mjs demo/vendor/jamilih/dist/jml.mjs",
79
79
  "copy-jsonpath-plus": "cp node_modules/jsonpath-plus/dist/index-browser-esm.js demo/vendor/jsonpath-plus/dist/index-browser-esm.js",
80
- "copy": "npm run copy-simple-get-json && npm run copy-jhtml && npm run copy-jamilih && npm run copy-jsonpath-plus",
80
+ "copy-fontoxpath": "cp node_modules/fontoxpath/dist/fontoxpath.esm.js demo/vendor/fontoxpath/dist/fontoxpath.esm.js",
81
+ "copy-xspattern": "cp node_modules/xspattern/dist/xspattern.esm.js demo/vendor/xspattern/dist/xspattern.esm.js",
82
+ "copy-prsc": "cp node_modules/prsc/dist/prsc.esm.js demo/vendor/prsc/dist/prsc.esm.js",
83
+ "copy-whynot": "cp node_modules/whynot/dist/whynot.esm.js demo/vendor/whynot/dist/whynot.esm.js",
84
+ "copy": "pnpm copy-simple-get-json && pnpm copy-jhtml && pnpm copy-jamilih && pnpm copy-jsonpath-plus && pnpm copy-fontoxpath && pnpm copy-xspattern && pnpm copy-prsc && pnpm copy-whynot",
81
85
  "lint": "eslint .",
82
86
  "mocha": "mocha",
83
87
  "test:browser": "open http://localhost:8020/test/browser/ && static -p 8020",
@@ -2,6 +2,10 @@
2
2
  // mid-transformation (e.g., building strings with
3
3
  // string transformer but adding as text node in a DOM transformer)
4
4
 
5
+ /**
6
+ * @typedef {{character: string, string: string}[]} OutputCharacters
7
+ */
8
+
5
9
  /**
6
10
  * @typedef {{
7
11
  * requireSameChildren?: boolean,
@@ -66,6 +70,10 @@ class AbstractJoiningTransformer {
66
70
  constructor (cfg) {
67
71
  // Todo: Might set some reasonable defaults across all classes
68
72
  this._cfg = cfg ?? /** @type {JoiningTransformerConfig<T>} */ ({});
73
+ /** @type {Record<string, OutputCharacters>} */
74
+ this._characterMap = {};
75
+ /** @type {Record<string, Record<string, string>>} */
76
+ this._attributeSet = {};
69
77
  }
70
78
 
71
79
  /**
@@ -76,6 +84,51 @@ class AbstractJoiningTransformer {
76
84
  this._cfg = cfg;
77
85
  }
78
86
 
87
+ /**
88
+ * @param {import('./StringJoiningTransformer.js').OutputConfig} cfg
89
+ * @returns {this}
90
+ */
91
+ output (cfg) {
92
+ // We wait until first element is set in `element()` to add
93
+ // XML declaration and DOCTYPE as latter depends on root element
94
+ this._outputConfig = cfg;
95
+
96
+ // Use for file extension if making downloadable?
97
+ this.mediaType = cfg.mediaType;
98
+ return this;
99
+ }
100
+
101
+ /**
102
+ * @param {string} name
103
+ * @param {OutputCharacters} outputCharacters
104
+ * @returns {void}
105
+ */
106
+ characterMap (name, outputCharacters) {
107
+ this._characterMap[name] = outputCharacters;
108
+ }
109
+
110
+ /**
111
+ * @param {string} name
112
+ * @param {Record<string, string>} attributes
113
+ * @returns {void}
114
+ */
115
+ attributeSet (name, attributes) {
116
+ this._attributeSet[name] = attributes;
117
+ }
118
+
119
+ /**
120
+ * @param {string} str
121
+ * @returns {string}
122
+ */
123
+ _replaceCharacterMaps (str) {
124
+ this._outputConfig?.useCharacterMaps?.forEach((name) => {
125
+ this._characterMap[name].forEach(({character, string}) => {
126
+ str = str.replaceAll(character, string);
127
+ });
128
+ });
129
+ return str;
130
+ }
131
+
79
132
  /**
80
133
  * @param {string} type - Type name
81
134
  * @param {string} embedType - Embed type name
@@ -29,6 +29,8 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
29
29
  this._docs = [];
30
30
  /** @type {Array<{href: string, document: XMLDocument, format?: string}>} */
31
31
  this._resultDocuments = [];
32
+ /** @type {Record<string, unknown>} */
33
+ this.propertySets = {};
32
34
  }
33
35
 
34
36
  /**
@@ -67,6 +69,16 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
67
69
  this._dom[prop] = val;
68
70
  }
69
71
 
72
+ /**
73
+ * Alias for propValue(). Set a key-value pair in the current map/object.
74
+ * @param {string} prop - Property name
75
+ * @param {any} val - Property value
76
+ * @returns {void}
77
+ */
78
+ mapEntry (prop, val) {
79
+ return this.propValue(prop, val);
80
+ }
81
+
70
82
  /**
71
83
  * @param {Record<string, unknown>} obj - Object to serialize
72
84
  * @param {(this: DOMJoiningTransformer) => void} [cb] - Callback function.
@@ -75,9 +87,20 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
75
87
  * @returns {DOMJoiningTransformer}
76
88
  */
77
89
  object (obj, cb, usePropertySets, propSets) {
90
+ // eslint-disable-next-line unicorn/no-this-assignment -- Temporary
91
+ const that = this;
78
92
  this._requireSameChildren('dom', 'object');
79
- if (this._cfg.JHTMLForJSON) {
93
+
94
+ if (usePropertySets !== undefined) {
95
+ obj = usePropertySets.reduce(function (o, psName) {
96
+ return that._usePropertySets(o, psName);
97
+ }, obj);
98
+ }
99
+ if (propSets !== undefined) {
80
100
  Object.assign(obj, propSets);
101
+ }
102
+
103
+ if (this._cfg.JHTMLForJSON) {
81
104
  this.append(JHTML.toJHTMLDOM(/** @type {any} */ (obj)));
82
105
  } else {
83
106
  // Todo: set current position and deal with children
@@ -86,6 +109,18 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
86
109
  return this;
87
110
  }
88
111
 
112
+ /**
113
+ * Alias for object(). Build an object/map.
114
+ * @param {Record<string, unknown>} obj - Object to serialize
115
+ * @param {(this: DOMJoiningTransformer) => void} [cb] - Callback function.
116
+ * @param {any[]} [usePropertySets] - Property sets to use
117
+ * @param {Record<string, unknown>} [propSets] - Additional property sets
118
+ * @returns {DOMJoiningTransformer}
119
+ */
120
+ map (obj, cb, usePropertySets, propSets) {
121
+ return this.object(obj, cb, usePropertySets, propSets);
122
+ }
123
+
89
124
  /**
90
125
  * @param {any[]|Element} arr
91
126
  * @param {(this: DOMJoiningTransformer) => void} [cb] - Callback function
@@ -182,31 +217,85 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
182
217
  }
183
218
 
184
219
  /**
185
- * @param {import('./StringJoiningTransformer.js').OutputConfig} cfg
220
+ * @overload
221
+ * @param {Element|string} elName
222
+ * @param {Record<string, string>} atts
223
+ * @param {(Node|string)[]} childNodes
224
+ * @param {(this: DOMJoiningTransformer) => void} cb
225
+ * @returns {DOMJoiningTransformer}
226
+ */
227
+ /**
228
+ * @overload
229
+ * @param {Element|string} elName
230
+ * @param {Record<string, string>} atts
231
+ * @param {(Node|string)[]} childNodes
232
+ * @returns {DOMJoiningTransformer}
233
+ */
234
+ /**
235
+ * @overload
236
+ * @param {Element|string} elName
237
+ * @param {Record<string, string>} atts
238
+ * @param {(this: DOMJoiningTransformer) => void} cb
239
+ * @returns {DOMJoiningTransformer}
240
+ */
241
+ /**
242
+ * @overload
243
+ * @param {Element|string} elName
244
+ * @param {Record<string, string>} atts
245
+ * @returns {DOMJoiningTransformer}
246
+ */
247
+ /**
248
+ * @overload
249
+ * @param {Element|string} elName
250
+ * @param {(Node|string)[]} childNodes
251
+ * @param {(this: DOMJoiningTransformer) => void} cb
252
+ * @returns {DOMJoiningTransformer}
253
+ */
254
+ /**
255
+ * @overload
256
+ * @param {Element|string} elName
257
+ * @param {(Node|string)[]} childNodes
258
+ * @returns {DOMJoiningTransformer}
259
+ */
260
+ /**
261
+ * @overload
262
+ * @param {Element|string} elName
263
+ * @param {(this: DOMJoiningTransformer) => void} cb
264
+ * @returns {DOMJoiningTransformer}
265
+ */
266
+ /**
267
+ * @overload
268
+ * @param {Element|string} elName
186
269
  * @returns {DOMJoiningTransformer}
187
270
  */
188
- output (cfg) {
189
- // We wait until first element is set in `element()` to add
190
- // XML declaration and DOCTYPE as latter depends on root element
191
- this._outputConfig = cfg;
192
-
193
- // Use for file extension if making downloadable?
194
- this.mediaType = cfg.mediaType;
195
- return this;
196
- }
197
-
198
271
  /**
199
272
  * @param {Element|string} elName - Element name
200
- * @param {Record<string, string>} [atts] - Attributes object
201
- * @param {(this: DOMJoiningTransformer) => void} [cb] - Callback function
273
+ * @param {Record<string, string>|(Node|string)[
274
+ * ]|((this: DOMJoiningTransformer) => void)} [atts] - Attributes,
275
+ * childNodes, or callback
276
+ * @param {(Node|string)[]|((this: DOMJoiningTransformer) => void)
277
+ * } [childNodes] - Child nodes or callback
278
+ * @param {(this: DOMJoiningTransformer) => void} [cb] - Callback
279
+ * @param {string[]} [useAttributeSets] - Attribute set names to apply
202
280
  * @returns {DOMJoiningTransformer}
203
281
  */
204
- element (elName, atts, cb) {
205
- // Todo: allow third argument to be array following Jamilih (also let
206
- // "atts" follow Jamilih)
207
- // Todo: allow for cfg to produce Jamilih DOM output or hXML
208
- // Todo: allow separate XML DOM one with XML String and hXML conversions
209
- // (HTML to XHTML is inevitably safe?)
282
+ element (elName, atts, childNodes, cb, useAttributeSets) {
283
+ // Handle argument overloading like other transformers
284
+ if (Array.isArray(atts)) {
285
+ cb = /** @type {(this: DOMJoiningTransformer) => void} */ (
286
+ /** @type {unknown} */ (childNodes)
287
+ );
288
+ childNodes = atts;
289
+ atts = {};
290
+ } else if (typeof atts === 'function') {
291
+ cb = /** @type {(this: DOMJoiningTransformer) => void} */ (atts);
292
+ childNodes = [];
293
+ atts = {};
294
+ }
295
+ if (typeof childNodes === 'function') {
296
+ cb = /** @type {(this: DOMJoiningTransformer) => void} */ (childNodes);
297
+ childNodes = [];
298
+ }
210
299
 
211
300
  if (!this.root && this._outputConfig) {
212
301
  this.root = elName;
@@ -275,15 +364,41 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
275
364
  // Use the document's root element
276
365
  const el = doc.documentElement;
277
366
 
367
+ // Apply attribute sets if specified
368
+ if (useAttributeSets && useAttributeSets.length) {
369
+ useAttributeSets.forEach((setName) => {
370
+ if (this._attributeSet[setName]) {
371
+ for (const att in this._attributeSet[setName]) {
372
+ if (Object.hasOwn(this._attributeSet[setName], att)) {
373
+ el.setAttribute(att, this._replaceCharacterMaps(
374
+ this._attributeSet[setName][att]
375
+ ));
376
+ }
377
+ }
378
+ }
379
+ });
380
+ }
381
+
278
382
  for (const att in atts) {
279
383
  if (Object.hasOwn(atts, att)) {
280
- el.setAttribute(att, atts[att]);
384
+ el.setAttribute(att, this._replaceCharacterMaps(atts[att]));
281
385
  }
282
386
  }
283
387
 
284
388
  const oldDOM = this._dom;
285
389
  this._dom = el;
286
390
 
391
+ // Add childNodes if provided
392
+ if (childNodes && childNodes.length) {
393
+ for (const child of childNodes) {
394
+ if (typeof child === 'string') {
395
+ el.append(this._replaceCharacterMaps(child));
396
+ } else {
397
+ el.append(child);
398
+ }
399
+ }
400
+ }
401
+
287
402
  if (cb) {
288
403
  cb.call(this);
289
404
  }
@@ -299,9 +414,24 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
299
414
  this._cfg.document.createElement(elName)
300
415
  );
301
416
 
417
+ // Apply attribute sets if specified
418
+ if (useAttributeSets && useAttributeSets.length) {
419
+ useAttributeSets.forEach((setName) => {
420
+ if (this._attributeSet[setName]) {
421
+ for (const att in this._attributeSet[setName]) {
422
+ if (Object.hasOwn(this._attributeSet[setName], att)) {
423
+ el.setAttribute(att, this._replaceCharacterMaps(
424
+ this._attributeSet[setName][att]
425
+ ));
426
+ }
427
+ }
428
+ }
429
+ });
430
+ }
431
+
302
432
  for (const att in atts) {
303
433
  if (Object.hasOwn(atts, att)) {
304
- el.setAttribute(att, atts[att]);
434
+ el.setAttribute(att, this._replaceCharacterMaps(atts[att]));
305
435
  }
306
436
  }
307
437
  this.append(el);
@@ -310,6 +440,17 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
310
440
 
311
441
  this._dom = el;
312
442
 
443
+ // Add childNodes if provided
444
+ if (childNodes && childNodes.length) {
445
+ for (const child of childNodes) {
446
+ if (typeof child === 'string') {
447
+ el.append(this._replaceCharacterMaps(child));
448
+ } else {
449
+ el.append(child);
450
+ }
451
+ }
452
+ }
453
+
313
454
  if (cb) {
314
455
  cb.call(this);
315
456
  }
@@ -318,6 +459,21 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
318
459
  return this;
319
460
  }
320
461
 
462
+ /**
463
+ * @param {string} prefix
464
+ * @param {string} namespaceURI
465
+ * @returns {DOMJoiningTransformer}
466
+ */
467
+ namespace (prefix, namespaceURI) {
468
+ /** @type {Element} */
469
+ (this._dom).setAttributeNS(
470
+ 'http://www.w3.org/2000/xmlns/',
471
+ 'xmlns:' + prefix,
472
+ this._replaceCharacterMaps(namespaceURI)
473
+ );
474
+ return this;
475
+ }
476
+
321
477
  /**
322
478
  * @param {string} name
323
479
  * @param {string} val
@@ -328,7 +484,9 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
328
484
  this._dom.nodeType !== 1) {
329
485
  throw new Error('You may only set an attribute on an element');
330
486
  }
331
- (/** @type {Element} */ (this._dom)).setAttribute(name, val);
487
+ (/** @type {Element} */ (this._dom)).setAttribute(
488
+ name, this._replaceCharacterMaps(val)
489
+ );
332
490
  return this;
333
491
  }
334
492
 
@@ -337,7 +495,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
337
495
  * @returns {DOMJoiningTransformer}
338
496
  */
339
497
  text (txt) {
340
- this.append(txt);
498
+ this.append(this._replaceCharacterMaps(txt));
341
499
  return this;
342
500
  }
343
501
 
@@ -479,6 +637,23 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
479
637
 
480
638
  return this;
481
639
  }
640
+
641
+ /**
642
+ * Helper method to use property sets.
643
+ * @param {Record<string, unknown>} obj - Object to apply property set to
644
+ * @param {string} psName - Property set name
645
+ * @returns {Record<string, unknown>}
646
+ */
647
+ _usePropertySets (obj, psName) {
648
+ // Merge named property set from this.propertySets into obj
649
+ if (this.propertySets && this.propertySets[psName]) {
650
+ return {
651
+ ...obj,
652
+ ...this.propertySets[psName]
653
+ };
654
+ }
655
+ return obj;
656
+ }
482
657
  }
483
658
 
484
659
  export default DOMJoiningTransformer;