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
|
@@ -2,11 +2,32 @@ import {jml} from 'jamilih';
|
|
|
2
2
|
import * as JHTML from 'jhtml';
|
|
3
3
|
import AbstractJoiningTransformer from './AbstractJoiningTransformer.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {{
|
|
7
|
+
* encoding?: string,
|
|
8
|
+
* indent?: boolean,
|
|
9
|
+
* omitXmlDeclaration?: boolean,
|
|
10
|
+
* doctypePublic?: string,
|
|
11
|
+
* doctypeSystem?: string,
|
|
12
|
+
* cdataSectionElements?: string[]
|
|
13
|
+
* mediaType?: string,
|
|
14
|
+
* version?: string,
|
|
15
|
+
* standalone?: boolean,
|
|
16
|
+
* method?: "xml"|"html"|"text"|"json"|"xhtml"
|
|
17
|
+
* }} OutputConfig
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @callback SimpleCallback
|
|
22
|
+
* @this {StringJoiningTransformer}
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*/
|
|
25
|
+
|
|
5
26
|
const camelCase = /[a-z][A-Z]/gv;
|
|
6
27
|
|
|
7
28
|
/**
|
|
8
29
|
* Type guard to detect DOM Elements.
|
|
9
|
-
* @param {
|
|
30
|
+
* @param {any} item
|
|
10
31
|
* @returns {item is Element}
|
|
11
32
|
*/
|
|
12
33
|
function _isElement (item) {
|
|
@@ -21,6 +42,15 @@ function _makeDatasetAttribute (n0) {
|
|
|
21
42
|
return n0.charAt(0) + '-' + n0.charAt(1).toLowerCase();
|
|
22
43
|
}
|
|
23
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Attributes object for element() allowing standard string attributes
|
|
47
|
+
* plus special helpers: dataset (object) and $a (ordered attribute array).
|
|
48
|
+
* @typedef {Record<string, unknown> & {
|
|
49
|
+
* dataset?: Record<string, string>,
|
|
50
|
+
* $a?: Array<[string, string]>
|
|
51
|
+
* }} ElementAttributes
|
|
52
|
+
*/
|
|
53
|
+
|
|
24
54
|
/**
|
|
25
55
|
*
|
|
26
56
|
*/
|
|
@@ -50,11 +80,13 @@ function _makeDatasetAttribute (n0) {
|
|
|
50
80
|
* name mapping rules differ, etc.).
|
|
51
81
|
* - cfg.preEscapedAttributes: skip escaping attribute values.
|
|
52
82
|
* - cfg.JHTMLForJSON / cfg.mode: affect how object()/array() serialize.
|
|
83
|
+
* @extends {AbstractJoiningTransformer<"string">}
|
|
53
84
|
*/
|
|
54
85
|
class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
55
86
|
/**
|
|
56
87
|
* @param {string} s - Initial string
|
|
57
|
-
* @param {
|
|
88
|
+
* @param {import('./AbstractJoiningTransformer.js').
|
|
89
|
+
* StringJoiningTransformerConfig} [cfg] - Configuration object
|
|
58
90
|
*/
|
|
59
91
|
constructor (s, cfg) {
|
|
60
92
|
super(cfg); // Include this in any subclass of AbstractJoiningTransformer
|
|
@@ -74,12 +106,18 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
74
106
|
this._arr = [];
|
|
75
107
|
/** @type {string | undefined} */
|
|
76
108
|
this._strTemp = undefined;
|
|
77
|
-
/** @type {Record<string,
|
|
109
|
+
/** @type {Record<string, unknown>} */
|
|
78
110
|
this.propertySets = {};
|
|
111
|
+
/** @type {string[]} */
|
|
112
|
+
this._docs = [];
|
|
113
|
+
/** @type {boolean} */
|
|
114
|
+
this._insideDocument = false;
|
|
115
|
+
/** @type {Array<{href: string, document: string, format?: string}>} */
|
|
116
|
+
this._resultDocuments = [];
|
|
79
117
|
}
|
|
80
118
|
|
|
81
119
|
/**
|
|
82
|
-
* @param {string
|
|
120
|
+
* @param {string|any} s - String or value to append
|
|
83
121
|
* @returns {StringJoiningTransformer}
|
|
84
122
|
*/
|
|
85
123
|
append (s) {
|
|
@@ -104,15 +142,24 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
104
142
|
}
|
|
105
143
|
|
|
106
144
|
/**
|
|
107
|
-
* @returns {string}
|
|
145
|
+
* @returns {string|string[]}
|
|
108
146
|
*/
|
|
109
147
|
get () {
|
|
148
|
+
if (this._cfg.exposeDocuments) {
|
|
149
|
+
// If we built a document outside of document() calls and haven't
|
|
150
|
+
// pushed it yet, push the current string
|
|
151
|
+
if (this.root && !this._insideDocument && this._str &&
|
|
152
|
+
!this._docs.includes(this._str)) {
|
|
153
|
+
this._docs.push(this._str);
|
|
154
|
+
}
|
|
155
|
+
return this._docs;
|
|
156
|
+
}
|
|
110
157
|
return this._str;
|
|
111
158
|
}
|
|
112
159
|
|
|
113
160
|
/**
|
|
114
161
|
* @param {string} prop - Property name
|
|
115
|
-
* @param {
|
|
162
|
+
* @param {any} val - Property value
|
|
116
163
|
* @returns {StringJoiningTransformer}
|
|
117
164
|
*/
|
|
118
165
|
propValue (prop, val) {
|
|
@@ -127,7 +174,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
127
174
|
|
|
128
175
|
/**
|
|
129
176
|
* @param {string} prop - Property name
|
|
130
|
-
* @param {
|
|
177
|
+
* @param {(this: StringJoiningTransformer) => void} cb - Callback function
|
|
131
178
|
* @returns {StringJoiningTransformer}
|
|
132
179
|
*/
|
|
133
180
|
propOnly (prop, cb) {
|
|
@@ -154,10 +201,10 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
154
201
|
}
|
|
155
202
|
|
|
156
203
|
/**
|
|
157
|
-
* @param {
|
|
158
|
-
* @param {
|
|
204
|
+
* @param {Record<string, unknown>|Element} obj - Object to serialize
|
|
205
|
+
* @param {(this: StringJoiningTransformer) => void} cb - Callback function
|
|
159
206
|
* @param {any[]} [usePropertySets] - Property sets to use
|
|
160
|
-
* @param {
|
|
207
|
+
* @param {Record<string, unknown>} [propSets] - Additional property sets
|
|
161
208
|
* @returns {StringJoiningTransformer}
|
|
162
209
|
*/
|
|
163
210
|
object (obj, cb, usePropertySets, propSets) {
|
|
@@ -173,7 +220,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
173
220
|
/** @type {any} */
|
|
174
221
|
const oldObj = this._obj;
|
|
175
222
|
this._obj = _isElement(obj)
|
|
176
|
-
? JHTML.toJSONObject(obj, {mode: this._cfg
|
|
223
|
+
? JHTML.toJSONObject(obj, {mode: this._cfg.mode})
|
|
177
224
|
: obj || {};
|
|
178
225
|
|
|
179
226
|
// Todo: Allow in this and subsequent JSON methods ability to create
|
|
@@ -197,9 +244,9 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
197
244
|
// Not ready to serialize yet as still inside another array or object
|
|
198
245
|
if (oldObjPropState || this._arrItemState) {
|
|
199
246
|
this.append(this._obj);
|
|
200
|
-
} else if (this._cfg
|
|
247
|
+
} else if (this._cfg.JHTMLForJSON) {
|
|
201
248
|
this.append(JHTML.toJHTMLString(this._obj));
|
|
202
|
-
} else if (this._cfg
|
|
249
|
+
} else if (this._cfg.mode !== 'JavaScript') {
|
|
203
250
|
// Allow this method to operate on non-finite numbers and functions
|
|
204
251
|
const stringifier = new JHTML.Stringifier({mode: 'JavaScript'});
|
|
205
252
|
this.append(stringifier.walkJSONObject(this._obj));
|
|
@@ -212,7 +259,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
212
259
|
|
|
213
260
|
/**
|
|
214
261
|
* @param {any[]|Element} [arr] - Array to serialize
|
|
215
|
-
* @param {
|
|
262
|
+
* @param {(this: StringJoiningTransformer) => void} [cb] - Callback function
|
|
216
263
|
* @returns {StringJoiningTransformer}
|
|
217
264
|
*/
|
|
218
265
|
array (arr, cb) {
|
|
@@ -223,7 +270,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
223
270
|
const oldArr = this._arr;
|
|
224
271
|
// Todo: copy array?
|
|
225
272
|
this._arr = _isElement(arr)
|
|
226
|
-
? /** @type {any[]} */ (JHTML.toJSONObject(arr, {mode: this._cfg
|
|
273
|
+
? /** @type {any[]} */ (JHTML.toJSONObject(arr, {mode: this._cfg.mode}))
|
|
227
274
|
: arr || [];
|
|
228
275
|
|
|
229
276
|
/** @type {any} */
|
|
@@ -245,9 +292,9 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
245
292
|
this.append(this._arr);
|
|
246
293
|
/* c8 ignore next 2 -- JHTMLForJSON is a specialized output mode rarely used
|
|
247
294
|
* in combination with nested array building at the root level. */
|
|
248
|
-
} else if (this._cfg
|
|
295
|
+
} else if (this._cfg.JHTMLForJSON) {
|
|
249
296
|
this.append(JHTML.toJHTMLString(this._arr));
|
|
250
|
-
} else if (this._cfg
|
|
297
|
+
} else if (this._cfg.mode !== 'JavaScript') {
|
|
251
298
|
// Allow this method to operate on non-finite numbers and functions
|
|
252
299
|
const stringifier = new JHTML.Stringifier({mode: 'JavaScript'});
|
|
253
300
|
this.append(stringifier.walkJSONObject(this._arr));
|
|
@@ -259,8 +306,9 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
259
306
|
}
|
|
260
307
|
|
|
261
308
|
/**
|
|
262
|
-
* @param {string|Element|
|
|
263
|
-
*
|
|
309
|
+
* @param {string|Element|Record<string, unknown>} str
|
|
310
|
+
* String value or element
|
|
311
|
+
* @param {(this: StringJoiningTransformer) => void} [cb] - Callback function
|
|
264
312
|
* @returns {StringJoiningTransformer}
|
|
265
313
|
*/
|
|
266
314
|
string (str, cb) {
|
|
@@ -269,7 +317,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
269
317
|
// If a callback is provided, it composes a nested string value first.
|
|
270
318
|
if (_isElement(str)) {
|
|
271
319
|
str = /** @type {any} */ (
|
|
272
|
-
JHTML.toJSONObject(str, {mode: this._cfg
|
|
320
|
+
JHTML.toJSONObject(str, {mode: this._cfg.mode})
|
|
273
321
|
);
|
|
274
322
|
}
|
|
275
323
|
|
|
@@ -292,14 +340,15 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
292
340
|
}
|
|
293
341
|
|
|
294
342
|
/**
|
|
295
|
-
* @param {number|Element|
|
|
343
|
+
* @param {number|Element|Record<string, unknown>} num
|
|
344
|
+
* Number value or element
|
|
296
345
|
* @returns {StringJoiningTransformer}
|
|
297
346
|
*/
|
|
298
347
|
number (num) {
|
|
299
348
|
// Appends the number as a string; no localization/formatting is applied.
|
|
300
349
|
if (_isElement(num)) {
|
|
301
350
|
num = /** @type {any} */ (
|
|
302
|
-
JHTML.toJSONObject(num, {mode: this._cfg
|
|
351
|
+
JHTML.toJSONObject(num, {mode: this._cfg.mode})
|
|
303
352
|
);
|
|
304
353
|
}
|
|
305
354
|
this.append(num.toString());
|
|
@@ -307,14 +356,15 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
307
356
|
}
|
|
308
357
|
|
|
309
358
|
/**
|
|
310
|
-
* @param {boolean|Element|
|
|
359
|
+
* @param {boolean|Element|Record<string, unknown>} bool
|
|
360
|
+
* Boolean value or element
|
|
311
361
|
* @returns {StringJoiningTransformer}
|
|
312
362
|
*/
|
|
313
363
|
boolean (bool) {
|
|
314
364
|
// Appends 'true' or 'false'.
|
|
315
365
|
if (_isElement(bool)) {
|
|
316
366
|
bool = /** @type {any} */ (
|
|
317
|
-
JHTML.toJSONObject(bool, {mode: this._cfg
|
|
367
|
+
JHTML.toJSONObject(bool, {mode: this._cfg.mode})
|
|
318
368
|
);
|
|
319
369
|
}
|
|
320
370
|
this.append(bool ? 'true' : 'false');
|
|
@@ -335,7 +385,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
335
385
|
*/
|
|
336
386
|
undefined () {
|
|
337
387
|
// Appends the literal 'undefined' (only in JavaScript mode).
|
|
338
|
-
if (this._cfg
|
|
388
|
+
if (this._cfg.mode !== 'JavaScript') {
|
|
339
389
|
throw new Error(
|
|
340
390
|
'undefined is not allowed unless added in JavaScript mode'
|
|
341
391
|
);
|
|
@@ -350,14 +400,14 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
350
400
|
*/
|
|
351
401
|
nonfiniteNumber (num) {
|
|
352
402
|
// Appends NaN/Infinity/-Infinity as-is (only in JavaScript mode).
|
|
353
|
-
if (this._cfg
|
|
403
|
+
if (this._cfg.mode !== 'JavaScript') {
|
|
354
404
|
throw new Error(
|
|
355
405
|
'Non-finite numbers are not allowed unless added in JavaScript mode'
|
|
356
406
|
);
|
|
357
407
|
}
|
|
358
408
|
if (_isElement(num)) {
|
|
359
409
|
num = /** @type {any} */ (
|
|
360
|
-
JHTML.toJSONObject(num, {mode: this._cfg
|
|
410
|
+
JHTML.toJSONObject(num, {mode: this._cfg.mode})
|
|
361
411
|
);
|
|
362
412
|
}
|
|
363
413
|
this.append(num.toString());
|
|
@@ -365,19 +415,19 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
365
415
|
}
|
|
366
416
|
|
|
367
417
|
/**
|
|
368
|
-
* @param {
|
|
418
|
+
* @param {((...args: any[]) => any)|Element} func - Function to stringify
|
|
369
419
|
* @returns {StringJoiningTransformer}
|
|
370
420
|
*/
|
|
371
421
|
function (func) {
|
|
372
422
|
// Appends function source (only in JavaScript mode).
|
|
373
|
-
if (this._cfg
|
|
423
|
+
if (this._cfg.mode !== 'JavaScript') {
|
|
374
424
|
throw new Error(
|
|
375
425
|
'function is not allowed unless added in JavaScript mode'
|
|
376
426
|
);
|
|
377
427
|
}
|
|
378
428
|
if (_isElement(func)) {
|
|
379
429
|
func = /** @type {any} */ (
|
|
380
|
-
JHTML.toJSONObject(func, {mode: this._cfg
|
|
430
|
+
JHTML.toJSONObject(func, {mode: this._cfg.mode})
|
|
381
431
|
);
|
|
382
432
|
}
|
|
383
433
|
this.append(func.toString());
|
|
@@ -385,13 +435,75 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
385
435
|
}
|
|
386
436
|
|
|
387
437
|
/**
|
|
388
|
-
* @param {
|
|
389
|
-
* @
|
|
438
|
+
* @param {OutputConfig} cfg
|
|
439
|
+
* @returns {StringJoiningTransformer}
|
|
440
|
+
*/
|
|
441
|
+
output (cfg) {
|
|
442
|
+
// We wait until first element is set in `element()` to add
|
|
443
|
+
// XML declaration and DOCTYPE as latter depends on root element
|
|
444
|
+
this._outputConfig = cfg;
|
|
445
|
+
|
|
446
|
+
// Use for file extension if making downloadable?
|
|
447
|
+
this.mediaType = cfg.mediaType;
|
|
448
|
+
return this;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* @param {string|Element} elName - Element name or element object
|
|
453
|
+
* @param {ElementAttributes} [atts] - Element attributes
|
|
390
454
|
* @param {any[]} [childNodes] - Child nodes
|
|
391
|
-
* @param {
|
|
455
|
+
* @param {(this: StringJoiningTransformer) => void} [cb] - Callback function
|
|
392
456
|
* @returns {StringJoiningTransformer}
|
|
393
457
|
*/
|
|
394
458
|
element (elName, atts, childNodes, cb) {
|
|
459
|
+
// If a parent element's start tag is still open, close it before
|
|
460
|
+
// starting a new element to ensure valid nesting.
|
|
461
|
+
if (this._openTagState) {
|
|
462
|
+
this.append('>');
|
|
463
|
+
this._openTagState = false;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (!this.root) {
|
|
467
|
+
this.root = elName;
|
|
468
|
+
|
|
469
|
+
// todo: indent, cdataSectionElements
|
|
470
|
+
const {
|
|
471
|
+
omitXmlDeclaration, doctypePublic, doctypeSystem, method
|
|
472
|
+
} = this._outputConfig ?? {};
|
|
473
|
+
|
|
474
|
+
let xmlDeclaration = '';
|
|
475
|
+
/* c8 ignore start -- third OR condition short-circuits */
|
|
476
|
+
if (!omitXmlDeclaration && (
|
|
477
|
+
method === 'xml' || method === 'xhtml' || omitXmlDeclaration === false)
|
|
478
|
+
) {
|
|
479
|
+
const {version, encoding, standalone} = this._outputConfig ?? {};
|
|
480
|
+
xmlDeclaration = `<?xml${
|
|
481
|
+
version ? ` version="${version}"` : ''
|
|
482
|
+
}${
|
|
483
|
+
encoding ? ` encoding="${encoding}"` : ''
|
|
484
|
+
}${
|
|
485
|
+
standalone ? ` standalone="yes"` : ''
|
|
486
|
+
}?>\n`;
|
|
487
|
+
}
|
|
488
|
+
/* c8 ignore stop */
|
|
489
|
+
|
|
490
|
+
let doctype = '';
|
|
491
|
+
if (doctypePublic !== undefined || doctypeSystem !== undefined) {
|
|
492
|
+
/* c8 ignore start -- nested ternary attribution issue */
|
|
493
|
+
doctype = `<!DOCTYPE ${elName}${
|
|
494
|
+
doctypePublic
|
|
495
|
+
? ` PUBLIC "${doctypePublic}" "${doctypeSystem}"`
|
|
496
|
+
: doctypeSystem
|
|
497
|
+
? ` SYSTEM "${doctypeSystem}"`
|
|
498
|
+
: ''
|
|
499
|
+
}>\n`;
|
|
500
|
+
/* c8 ignore stop */
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
this._str = xmlDeclaration + doctype + this._str;
|
|
504
|
+
// Document pushing is handled by document() method or get()
|
|
505
|
+
}
|
|
506
|
+
|
|
395
507
|
// Emits an HTML/XML element using Jamilih under the hood, or allows a
|
|
396
508
|
// callback to build attributes/children incrementally. Attribute values
|
|
397
509
|
// are escaped unless cfg.preEscapedAttributes is true. When a callback is
|
|
@@ -399,23 +511,26 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
399
511
|
// eslint-disable-next-line unicorn/no-this-assignment -- Temporary
|
|
400
512
|
const that = this;
|
|
401
513
|
if (Array.isArray(atts)) {
|
|
402
|
-
cb = /** @type {
|
|
514
|
+
cb = /** @type {(this: StringJoiningTransformer) => void} */ (
|
|
515
|
+
/** @type {unknown} */ (childNodes)
|
|
516
|
+
);
|
|
403
517
|
childNodes = atts;
|
|
404
518
|
atts = {};
|
|
405
519
|
} else if (typeof atts === 'function') {
|
|
406
|
-
cb = atts;
|
|
520
|
+
cb = /** @type {(this: StringJoiningTransformer) => void} */ (atts);
|
|
407
521
|
childNodes = [];
|
|
408
522
|
atts = {};
|
|
409
523
|
}
|
|
410
524
|
if (typeof childNodes === 'function') {
|
|
411
|
-
cb = childNodes;
|
|
525
|
+
cb = /** @type {(this: StringJoiningTransformer) => void} */ (childNodes);
|
|
412
526
|
childNodes = [];
|
|
413
527
|
}
|
|
414
528
|
|
|
415
529
|
// Todo: allow for cfg to produce Jamilih string output or hXML
|
|
416
530
|
// string output
|
|
417
|
-
const method = this._cfg
|
|
531
|
+
const method = this._cfg.xmlElements ? 'toXML' : 'toHTML';
|
|
418
532
|
if (!cb) {
|
|
533
|
+
// Ensure any open parent start tag was closed (handled above)
|
|
419
534
|
// Note that Jamilih currently has an issue with 'selected', 'checked',
|
|
420
535
|
// 'value', 'defaultValue', 'for', 'on*', 'style' (workaround: pass
|
|
421
536
|
// an empty callback as the last argument to element())
|
|
@@ -428,7 +543,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
428
543
|
}
|
|
429
544
|
|
|
430
545
|
if (typeof elName === 'object') {
|
|
431
|
-
/** @type {Record<string,
|
|
546
|
+
/** @type {Record<string, string>} */
|
|
432
547
|
const objAtts = {};
|
|
433
548
|
/** @type {any} */
|
|
434
549
|
const elObj = elName;
|
|
@@ -444,9 +559,13 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
444
559
|
const oldTagState = this._openTagState;
|
|
445
560
|
this._openTagState = true;
|
|
446
561
|
if (atts) {
|
|
447
|
-
const attsObj = /** @type {Record<string,
|
|
562
|
+
const attsObj = /** @type {Record<string, unknown>} */ (atts);
|
|
448
563
|
Object.keys(attsObj).forEach((att) => {
|
|
449
|
-
that.attribute(
|
|
564
|
+
that.attribute(
|
|
565
|
+
att,
|
|
566
|
+
/** @type {string|Record<string, unknown>} */ (attsObj[att]),
|
|
567
|
+
false
|
|
568
|
+
);
|
|
450
569
|
});
|
|
451
570
|
}
|
|
452
571
|
if (childNodes && childNodes.length) {
|
|
@@ -468,7 +587,8 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
468
587
|
|
|
469
588
|
/**
|
|
470
589
|
* @param {string} name - Attribute name
|
|
471
|
-
* @param {string|
|
|
590
|
+
* @param {string|Record<string, unknown>|
|
|
591
|
+
* string[][]} val - Attribute value
|
|
472
592
|
* @param {boolean} [avoidAttEscape] - Whether to avoid escaping the
|
|
473
593
|
* attribute value
|
|
474
594
|
* @returns {StringJoiningTransformer}
|
|
@@ -486,9 +606,9 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
486
606
|
);
|
|
487
607
|
}
|
|
488
608
|
|
|
489
|
-
if (!this._cfg
|
|
609
|
+
if (!this._cfg.xmlElements) {
|
|
490
610
|
if (typeof val === 'object') {
|
|
491
|
-
/** @type {Record<string,
|
|
611
|
+
/** @type {Record<string, unknown>} */
|
|
492
612
|
const valObj = /** @type {any} */ (val);
|
|
493
613
|
switch (name) {
|
|
494
614
|
case 'dataset': {
|
|
@@ -496,16 +616,22 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
496
616
|
that.attribute(
|
|
497
617
|
'data-' + att.replaceAll(
|
|
498
618
|
camelCase, _makeDatasetAttribute
|
|
499
|
-
),
|
|
619
|
+
),
|
|
620
|
+
/** @type {string|Record<string, unknown>} */ (valObj[att]),
|
|
621
|
+
false
|
|
500
622
|
);
|
|
501
623
|
});
|
|
502
624
|
break;
|
|
503
625
|
}
|
|
504
626
|
case '$a': { // Ordered attributes
|
|
505
|
-
/** @type {
|
|
627
|
+
/** @type {unknown[][]} */
|
|
506
628
|
const valArr = /** @type {any} */ (val);
|
|
507
629
|
valArr.forEach(function (attArr) {
|
|
508
|
-
that.attribute(
|
|
630
|
+
that.attribute(
|
|
631
|
+
String(attArr[0]),
|
|
632
|
+
/** @type {string|Record<string, unknown>} */ (attArr[1]),
|
|
633
|
+
false
|
|
634
|
+
);
|
|
509
635
|
});
|
|
510
636
|
break;
|
|
511
637
|
}
|
|
@@ -519,7 +645,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
519
645
|
|
|
520
646
|
/** @type {string} */
|
|
521
647
|
const valStr = /** @type {any} */ (val);
|
|
522
|
-
val = (
|
|
648
|
+
val = (this._cfg.preEscapedAttributes || avoidAttEscape)
|
|
523
649
|
? valStr
|
|
524
650
|
: valStr.replaceAll('&', '&').replaceAll('"', '"');
|
|
525
651
|
this.append(' ' + name + '="' + val + '"');
|
|
@@ -542,12 +668,31 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
542
668
|
}
|
|
543
669
|
|
|
544
670
|
/**
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
671
|
+
* @param {string} text
|
|
672
|
+
* @returns {StringJoiningTransformer}}
|
|
673
|
+
*/
|
|
674
|
+
comment (text) {
|
|
675
|
+
this.append(`<!--${text}-->`);
|
|
676
|
+
return this;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* @param {string} target
|
|
681
|
+
* @param {string} data
|
|
682
|
+
* @returns {StringJoiningTransformer}}
|
|
683
|
+
*/
|
|
684
|
+
processingInstruction (target, data) {
|
|
685
|
+
this.append(`<?${target} ${data}?>`);
|
|
686
|
+
return this;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Unlike text(), does not escape for HTML; unlike string(), does not perform
|
|
691
|
+
* JSON stringification; unlike append(), does not do other checks (but
|
|
692
|
+
* still varies in its role across transformers).
|
|
693
|
+
* @param {string} str
|
|
694
|
+
* @returns {StringJoiningTransformer}
|
|
695
|
+
*/
|
|
551
696
|
rawAppend (str) {
|
|
552
697
|
// Lowest-level append: bypasses append() semantics and state checks.
|
|
553
698
|
this._str += str;
|
|
@@ -566,11 +711,122 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
566
711
|
return this;
|
|
567
712
|
}
|
|
568
713
|
|
|
714
|
+
/**
|
|
715
|
+
* Creates a new string document and executes a callback in its context.
|
|
716
|
+
* Similar to XSLT's xsl:document, this allows templates to generate
|
|
717
|
+
* multiple output documents. The created document is pushed to this._docs
|
|
718
|
+
* and will be included in the result when exposeDocuments is true.
|
|
719
|
+
*
|
|
720
|
+
* @param {(this: StringJoiningTransformer) => void} cb
|
|
721
|
+
* Callback that builds the document content
|
|
722
|
+
* @param {OutputConfig} [cfg]
|
|
723
|
+
* Output configuration for the document (encoding, doctype, etc.)
|
|
724
|
+
* @returns {StringJoiningTransformer}
|
|
725
|
+
*/
|
|
726
|
+
document (cb, cfg) {
|
|
727
|
+
// If there's a current document being built (this.root is set and _str
|
|
728
|
+
// has content), save it to _docs before starting a new document
|
|
729
|
+
if (this._cfg.exposeDocuments && this.root && this._str &&
|
|
730
|
+
!this._docs.includes(this._str)) {
|
|
731
|
+
this._docs.push(this._str);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// Save current state
|
|
735
|
+
/** @type {any} */
|
|
736
|
+
const oldRoot = this.root;
|
|
737
|
+
/** @type {any} */
|
|
738
|
+
const oldOutputConfig = this._outputConfig;
|
|
739
|
+
const oldStr = this._str;
|
|
740
|
+
/** @type {any} */
|
|
741
|
+
const oldOpenTagState = this._openTagState;
|
|
742
|
+
|
|
743
|
+
// Reset state for new document
|
|
744
|
+
this.root = undefined;
|
|
745
|
+
/** @type {any} */
|
|
746
|
+
this._outputConfig = cfg;
|
|
747
|
+
this._str = '';
|
|
748
|
+
this._openTagState = false;
|
|
749
|
+
this._insideDocument = true;
|
|
750
|
+
|
|
751
|
+
// Execute callback to build document content
|
|
752
|
+
cb.call(this);
|
|
753
|
+
|
|
754
|
+
// Save the newly created document string
|
|
755
|
+
const newDoc = this._str;
|
|
756
|
+
this._docs.push(newDoc);
|
|
757
|
+
|
|
758
|
+
// Restore previous state
|
|
759
|
+
this.root = oldRoot;
|
|
760
|
+
this._outputConfig = oldOutputConfig;
|
|
761
|
+
this._str = oldStr;
|
|
762
|
+
this._openTagState = oldOpenTagState;
|
|
763
|
+
this._insideDocument = false;
|
|
764
|
+
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Creates a new result document with metadata (href, format).
|
|
770
|
+
* Similar to XSLT's xsl:result-document, this allows templates to generate
|
|
771
|
+
* multiple output documents with associated metadata like URIs. The created
|
|
772
|
+
* document is stored in this._resultDocuments with the provided href.
|
|
773
|
+
*
|
|
774
|
+
* @param {string} href - URI/path for the result document
|
|
775
|
+
* @param {(this: StringJoiningTransformer) => void} cb
|
|
776
|
+
* Callback that builds the document content
|
|
777
|
+
* @param {OutputConfig} [cfg]
|
|
778
|
+
* Output configuration for the document (encoding, doctype, format, etc.)
|
|
779
|
+
* @returns {StringJoiningTransformer}
|
|
780
|
+
*/
|
|
781
|
+
resultDocument (href, cb, cfg) {
|
|
782
|
+
// If there's a current document being built, save it first
|
|
783
|
+
if (this.root && this._str && !this._docs.includes(this._str)) {
|
|
784
|
+
this._docs.push(this._str);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// Save current state
|
|
788
|
+
/** @type {any} */
|
|
789
|
+
const oldRoot = this.root;
|
|
790
|
+
/** @type {any} */
|
|
791
|
+
const oldOutputConfig = this._outputConfig;
|
|
792
|
+
const oldStr = this._str;
|
|
793
|
+
/** @type {any} */
|
|
794
|
+
const oldOpenTagState = this._openTagState;
|
|
795
|
+
|
|
796
|
+
// Reset state for new document
|
|
797
|
+
this.root = undefined;
|
|
798
|
+
/** @type {any} */
|
|
799
|
+
this._outputConfig = cfg;
|
|
800
|
+
this._str = '';
|
|
801
|
+
this._openTagState = false;
|
|
802
|
+
this._insideDocument = true;
|
|
803
|
+
|
|
804
|
+
// Execute callback to build document content
|
|
805
|
+
cb.call(this);
|
|
806
|
+
|
|
807
|
+
// Save the newly created document string with metadata
|
|
808
|
+
const resultDoc = this._str;
|
|
809
|
+
this._resultDocuments.push({
|
|
810
|
+
href,
|
|
811
|
+
document: resultDoc,
|
|
812
|
+
format: this._outputConfig?.method || cfg?.method
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// Restore previous state
|
|
816
|
+
this.root = oldRoot;
|
|
817
|
+
this._outputConfig = oldOutputConfig;
|
|
818
|
+
this._str = oldStr;
|
|
819
|
+
this._openTagState = oldOpenTagState;
|
|
820
|
+
this._insideDocument = false;
|
|
821
|
+
|
|
822
|
+
return this;
|
|
823
|
+
}
|
|
824
|
+
|
|
569
825
|
/**
|
|
570
826
|
* Helper method to use property sets.
|
|
571
|
-
* @param {
|
|
827
|
+
* @param {Record<string, unknown>} obj - Object to apply property set to
|
|
572
828
|
* @param {string} psName - Property set name
|
|
573
|
-
* @returns {
|
|
829
|
+
* @returns {Record<string, unknown>}
|
|
574
830
|
*/
|
|
575
831
|
_usePropertySets (obj, psName) {
|
|
576
832
|
// Merge named property set from this.propertySets into obj
|
|
@@ -584,6 +840,4 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
584
840
|
}
|
|
585
841
|
}
|
|
586
842
|
|
|
587
|
-
// Todo: Implement comment(), processingInstruction(), etc.
|
|
588
|
-
|
|
589
843
|
export default StringJoiningTransformer;
|