jtlt 0.18.0 → 0.19.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 +4 -0
- package/README.md +75 -0
- package/demo/vendor/jamilih/dist/jml.mjs +898 -25
- package/dist/DOMJoiningTransformer.d.ts +73 -25
- package/dist/DOMJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONJoiningTransformer.d.ts +5 -3
- package/dist/JSONJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformerContext.d.ts +59 -19
- package/dist/JSONPathTransformerContext.d.ts.map +1 -1
- package/dist/StringJoiningTransformer.d.ts +8 -3
- package/dist/StringJoiningTransformer.d.ts.map +1 -1
- package/dist/XPathTransformerContext.d.ts.map +1 -1
- package/dist/index.d.ts +104 -261
- package/dist/index.d.ts.map +1 -1
- package/dist/jsonTemplate.d.ts +37 -0
- package/dist/jsonTemplate.d.ts.map +1 -0
- package/docs/API.expanded.md +75 -1
- package/docs/API.md +20 -0
- package/docs/TO-DO.md +10 -4
- package/package.json +6 -6
- package/pnpm-workspace.yaml +4 -1
- package/src/DOMJoiningTransformer.js +37 -11
- package/src/JSONJoiningTransformer.js +94 -67
- package/src/JSONPathTransformer.js +40 -10
- package/src/JSONPathTransformerContext.js +217 -46
- package/src/StringJoiningTransformer.js +35 -9
- package/src/XPathTransformerContext.js +25 -1
- package/src/index.js +115 -4
- package/src/jsonTemplate.js +410 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jtlt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Brett Zamir",
|
|
6
6
|
"contributors": [],
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"codemirror": "^6.0.2",
|
|
40
40
|
"fontoxpath": "^3.34.0",
|
|
41
41
|
"idb": "^8.0.3",
|
|
42
|
-
"jamilih": "0.
|
|
42
|
+
"jamilih": "0.70.0",
|
|
43
43
|
"jhtml": "0.7.3",
|
|
44
44
|
"jsdom": "30.0.1",
|
|
45
45
|
"jsonpath-plus": "10.4.0",
|
|
@@ -51,17 +51,17 @@
|
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
54
|
-
"@node-static/node-static": "^0.9.
|
|
54
|
+
"@node-static/node-static": "^0.9.2",
|
|
55
55
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
56
56
|
"@types/chai": "^5.2.3",
|
|
57
57
|
"@types/jsdom": "^30.0.0",
|
|
58
58
|
"@types/mocha": "^10.0.10",
|
|
59
|
-
"baseline-browser-mapping": "^2.11.
|
|
59
|
+
"baseline-browser-mapping": "^2.11.22",
|
|
60
60
|
"c8": "^12.0.0",
|
|
61
61
|
"chai": "^6.2.2",
|
|
62
62
|
"eslint": "^10.10.0",
|
|
63
|
-
"eslint-config-ash-nazg": "^43.1.
|
|
64
|
-
"indexeddbshim": "^19.0.
|
|
63
|
+
"eslint-config-ash-nazg": "^43.1.8",
|
|
64
|
+
"indexeddbshim": "^19.0.2",
|
|
65
65
|
"mocha": "^12.0.0",
|
|
66
66
|
"open": "^11.0.2",
|
|
67
67
|
"rollup": "^4.63.1",
|
package/pnpm-workspace.yaml
CHANGED
|
@@ -35,7 +35,10 @@ minimumReleaseAgeExclude:
|
|
|
35
35
|
- eslint-config-ash-nazg@43.1.0 || 43.1.6
|
|
36
36
|
- eslint-plugin-escompat@3.12.0
|
|
37
37
|
- eslint-plugin-jsdoc@64.3.0 || 64.3.3
|
|
38
|
-
- indexeddbshim@17.4.1 || 17.4.2 || 17.4.3 || 17.5.0 || 17.6.0
|
|
38
|
+
- indexeddbshim@17.4.1 || 17.4.2 || 17.4.3 || 17.5.0 || 17.6.0 || 19.0.2
|
|
39
39
|
- open@11.0.2
|
|
40
40
|
- powershell-utils@0.2.1
|
|
41
41
|
- websql-configurable@4.0.0
|
|
42
|
+
- '@node-static/node-static@0.9.2'
|
|
43
|
+
- baseline-browser-mapping@2.11.22
|
|
44
|
+
- jamilih@0.70.0
|
|
@@ -300,7 +300,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
300
300
|
* @overload
|
|
301
301
|
* @param {Element|string} elName
|
|
302
302
|
* @param {(this: DOMJoiningTransformer) => void} cb
|
|
303
|
-
* @returns {DOMJoiningTransformer}
|
|
303
|
+
* @returns {DOMJoiningTransformer|Promise<DOMJoiningTransformer>}
|
|
304
304
|
*/
|
|
305
305
|
/**
|
|
306
306
|
* @overload
|
|
@@ -314,9 +314,15 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
314
314
|
* childNodes, or callback
|
|
315
315
|
* @param {(Node|string)[]|((this: DOMJoiningTransformer) => void)
|
|
316
316
|
* } [childNodes] - Child nodes or callback
|
|
317
|
-
* @param {(this: DOMJoiningTransformer) => void} [cb] -
|
|
317
|
+
* @param {(this: DOMJoiningTransformer) => void} [cb] -
|
|
318
|
+
* Callback; may be async (e.g. to `await` a `$indexedDB` fetch), in
|
|
319
|
+
* which case `element()` itself returns a `Promise` instead of `this`.
|
|
320
|
+
* (Typed as returning plain `void`, not `void|Promise<void>` — a union
|
|
321
|
+
* there would lose TypeScript's usual "a void-returning callback
|
|
322
|
+
* parameter accepts any actual return value" leniency, which existing
|
|
323
|
+
* callers rely on for e.g. `() => this.text(...)` concise arrows.)
|
|
318
324
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
319
|
-
* @returns {DOMJoiningTransformer}
|
|
325
|
+
* @returns {DOMJoiningTransformer|Promise<DOMJoiningTransformer>}
|
|
320
326
|
*/
|
|
321
327
|
element (elem, atts, childNodes, cb, useAttributeSets) {
|
|
322
328
|
// Handle argument overloading like other transformers
|
|
@@ -440,12 +446,24 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
440
446
|
}
|
|
441
447
|
}
|
|
442
448
|
|
|
449
|
+
const finishRoot = () => {
|
|
450
|
+
this._dom = oldDOM;
|
|
451
|
+
return this;
|
|
452
|
+
};
|
|
443
453
|
if (cb) {
|
|
444
|
-
cb
|
|
454
|
+
// `cb`'s declared return type is plain `void` (see the parameter's
|
|
455
|
+
// JSDoc), preserving TypeScript's void-return leniency for
|
|
456
|
+
// callers — cast here to duck-type the real value.
|
|
457
|
+
const cbResult = /** @type {any} */ (cb.call(this._context || this));
|
|
458
|
+
// `cb` may be an async function (e.g. one that awaits a
|
|
459
|
+
// `$indexedDB` fetch); resume only once it settles, so `_dom` is
|
|
460
|
+
// restored in the right order.
|
|
461
|
+
if (cbResult && typeof cbResult.then === 'function') {
|
|
462
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- Not async
|
|
463
|
+
return cbResult.then(() => finishRoot());
|
|
464
|
+
}
|
|
445
465
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return this;
|
|
466
|
+
return finishRoot();
|
|
449
467
|
}
|
|
450
468
|
|
|
451
469
|
// Non-root elements
|
|
@@ -494,12 +512,20 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
494
512
|
}
|
|
495
513
|
}
|
|
496
514
|
|
|
515
|
+
const finish = () => {
|
|
516
|
+
this._dom = oldDOM;
|
|
517
|
+
return this;
|
|
518
|
+
};
|
|
497
519
|
if (cb) {
|
|
498
|
-
cb
|
|
520
|
+
// `cb`'s declared return type is plain `void` — cast here to
|
|
521
|
+
// duck-type the real value; see the note above the other branch.
|
|
522
|
+
const cbResult = /** @type {any} */ (cb.call(this._context || this));
|
|
523
|
+
if (cbResult && typeof cbResult.then === 'function') {
|
|
524
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- Not async
|
|
525
|
+
return cbResult.then(() => finish());
|
|
526
|
+
}
|
|
499
527
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
return this;
|
|
528
|
+
return finish();
|
|
503
529
|
}
|
|
504
530
|
|
|
505
531
|
/**
|
|
@@ -30,6 +30,12 @@ function _makeDatasetAttribute (n0) {
|
|
|
30
30
|
* : import('./DOMJoiningTransformer.js').default}
|
|
31
31
|
* @returns {void}
|
|
32
32
|
*/
|
|
33
|
+
// Kept as plain `void` (not `void|Promise<void>`) rather than a union:
|
|
34
|
+
// TypeScript's "a void-returning callback parameter accepts any actual
|
|
35
|
+
// return value" leniency — relied on throughout the test suite for
|
|
36
|
+
// concise-body arrows like `() => this.text(...)` — only applies to a
|
|
37
|
+
// literal `void` return type, not a union. An async callback (returning
|
|
38
|
+
// `Promise<void>`) is still assignable here for the same reason.
|
|
33
39
|
|
|
34
40
|
/**
|
|
35
41
|
* Attributes object for element() allowing standard string attributes
|
|
@@ -392,9 +398,11 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
392
398
|
* @param {ElementAttributes|any[]|SimpleCallback} [atts]
|
|
393
399
|
* Attrs, children, or cb.
|
|
394
400
|
* @param {any[]|SimpleCallback} [childNodes] Children or cb.
|
|
395
|
-
* @param {SimpleCallback} [cb] Builder callback.
|
|
401
|
+
* @param {SimpleCallback} [cb] Builder callback; may be async (e.g. to
|
|
402
|
+
* `await` a `$indexedDB` fetch), in which case `element()` itself
|
|
403
|
+
* returns a `Promise` instead of `this`.
|
|
396
404
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
397
|
-
* @returns {JSONJoiningTransformer}
|
|
405
|
+
* @returns {JSONJoiningTransformer|Promise<JSONJoiningTransformer>}
|
|
398
406
|
*/
|
|
399
407
|
element (elem, atts, childNodes, cb, useAttributeSets) {
|
|
400
408
|
this._requireSameChildren('json', 'element');
|
|
@@ -485,77 +493,96 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
485
493
|
}));
|
|
486
494
|
}
|
|
487
495
|
|
|
488
|
-
//
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
/** @type {any[]} */
|
|
501
|
-
const jmlEl = [elementName];
|
|
502
|
-
if (Object.keys(attsObj).length) {
|
|
503
|
-
jmlEl.push(attsObj, jmlChildren);
|
|
504
|
-
} else {
|
|
505
|
-
jmlEl.push(jmlChildren);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (isRoot) {
|
|
509
|
-
// todo: indent, cdataSectionElements
|
|
510
|
-
const {
|
|
511
|
-
omitXmlDeclaration, bareDoctype, doctypePublic, doctypeSystem, method
|
|
512
|
-
} = this._outputConfig ?? {};
|
|
513
|
-
|
|
514
|
-
const dtd = bareDoctype !== false
|
|
515
|
-
? [{$DOCTYPE: {
|
|
516
|
-
name: elementName,
|
|
517
|
-
publicId: doctypePublic ?? null, // Public ID (optional)
|
|
518
|
-
systemId: doctypeSystem ?? null // System ID (optional)
|
|
519
|
-
}}]
|
|
520
|
-
: [];
|
|
521
|
-
|
|
522
|
-
let xmlDeclaration;
|
|
523
|
-
/* c8 ignore start -- third OR condition short-circuits */
|
|
524
|
-
if (!omitXmlDeclaration && (
|
|
525
|
-
method === 'xml' || method === 'xhtml' || omitXmlDeclaration === false)
|
|
526
|
-
) {
|
|
527
|
-
const {version, encoding, standalone} = this._outputConfig ?? {};
|
|
528
|
-
|
|
529
|
-
xmlDeclaration = {
|
|
530
|
-
version,
|
|
531
|
-
encoding,
|
|
532
|
-
standalone
|
|
533
|
-
};
|
|
496
|
+
// Everything from here on depends on `attsObj`/`jmlChildren` having
|
|
497
|
+
// been fully populated by `cb` (when given) — deferred into a
|
|
498
|
+
// continuation so an async `cb` (e.g. one that awaits a `$indexedDB`
|
|
499
|
+
// fetch) can be awaited first, keeping output correctly ordered.
|
|
500
|
+
const finishElement = () => {
|
|
501
|
+
// Build Jamilih array
|
|
502
|
+
/** @type {any[]} */
|
|
503
|
+
const jmlEl = [elementName];
|
|
504
|
+
if (Object.keys(attsObj).length) {
|
|
505
|
+
jmlEl.push(attsObj, jmlChildren);
|
|
506
|
+
} else {
|
|
507
|
+
jmlEl.push(jmlChildren);
|
|
534
508
|
}
|
|
535
|
-
/* c8 ignore stop */
|
|
536
509
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
510
|
+
if (isRoot) {
|
|
511
|
+
// todo: indent, cdataSectionElements
|
|
512
|
+
const {
|
|
513
|
+
omitXmlDeclaration, bareDoctype, doctypePublic, doctypeSystem, method
|
|
514
|
+
} = this._outputConfig ?? {};
|
|
515
|
+
|
|
516
|
+
const dtd = bareDoctype !== false
|
|
517
|
+
? [{$DOCTYPE: {
|
|
518
|
+
name: elementName,
|
|
519
|
+
publicId: doctypePublic ?? null, // Public ID (optional)
|
|
520
|
+
systemId: doctypeSystem ?? null // System ID (optional)
|
|
521
|
+
}}]
|
|
522
|
+
: [];
|
|
523
|
+
|
|
524
|
+
let xmlDeclaration;
|
|
525
|
+
/* c8 ignore start -- third OR condition short-circuits */
|
|
526
|
+
if (!omitXmlDeclaration && (
|
|
527
|
+
method === 'xml' || method === 'xhtml' ||
|
|
528
|
+
omitXmlDeclaration === false)
|
|
529
|
+
) {
|
|
530
|
+
const {version, encoding, standalone} = this._outputConfig ?? {};
|
|
531
|
+
|
|
532
|
+
xmlDeclaration = {
|
|
533
|
+
version,
|
|
534
|
+
encoding,
|
|
535
|
+
standalone
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/* c8 ignore stop */
|
|
539
|
+
|
|
540
|
+
const doc = {$document: {
|
|
541
|
+
...(xmlDeclaration ? {xmlDeclaration} : {}),
|
|
542
|
+
childNodes: [
|
|
543
|
+
...(method === 'xml' || method === 'xhtml' ? dtd : []),
|
|
544
|
+
jmlEl
|
|
545
|
+
]
|
|
546
|
+
}};
|
|
547
|
+
|
|
548
|
+
// Removed this._doc; use this._docs only
|
|
549
|
+
if (this._cfg.exposeDocuments) {
|
|
550
|
+
this._docs.push(doc);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
544
553
|
|
|
545
|
-
//
|
|
546
|
-
|
|
547
|
-
|
|
554
|
+
// If inside a parent element, append as its child; otherwise
|
|
555
|
+
// append to root
|
|
556
|
+
if (this._elementStack.length) {
|
|
557
|
+
const top = /** @type {ElementInfo} */ (this._elementStack.at(-1));
|
|
558
|
+
top.jmlChildren.push(jmlEl);
|
|
559
|
+
} else {
|
|
560
|
+
this.append(jmlEl);
|
|
548
561
|
}
|
|
549
|
-
|
|
562
|
+
return this;
|
|
563
|
+
};
|
|
550
564
|
|
|
551
|
-
//
|
|
552
|
-
if (
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
565
|
+
// Callback-driven building (attribute/text/nested element mutate stack)
|
|
566
|
+
if (cb) {
|
|
567
|
+
// Push current state onto a stack
|
|
568
|
+
this._elementStack.push({attsObj, jmlChildren});
|
|
569
|
+
// `cb`'s declared return type is plain `void` (see `SimpleCallback`)
|
|
570
|
+
// — cast here to duck-type the real value.
|
|
571
|
+
const cbResult = /** @type {any} */ (cb.call(this._context || this));
|
|
572
|
+
const finishWithStack = () => {
|
|
573
|
+
const state = /** @type {ElementInfo} */ (this._elementStack.pop());
|
|
574
|
+
({attsObj} = state);
|
|
575
|
+
// Children may have been mutated by nested element()/text();
|
|
576
|
+
// already in jmlChildren
|
|
577
|
+
return finishElement();
|
|
578
|
+
};
|
|
579
|
+
if (cbResult && typeof cbResult.then === 'function') {
|
|
580
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- Not async
|
|
581
|
+
return cbResult.then(() => finishWithStack());
|
|
582
|
+
}
|
|
583
|
+
return finishWithStack();
|
|
557
584
|
}
|
|
558
|
-
return
|
|
585
|
+
return finishElement();
|
|
559
586
|
}
|
|
560
587
|
|
|
561
588
|
/**
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import JSONPathTransformerContext from './JSONPathTransformerContext.js';
|
|
2
|
+
import {
|
|
3
|
+
compileJSONTemplate, isJSONTemplateNodeArray
|
|
4
|
+
} from './jsonTemplate.js';
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Applies named JSONPath-driven templates to JSON data.
|
|
@@ -106,18 +109,35 @@ class JSONPathTransformer {
|
|
|
106
109
|
throw new TypeError('config.templates is required');
|
|
107
110
|
}
|
|
108
111
|
this.templates = config.templates.map(function (template) {
|
|
112
|
+
/** @type {import('./index.js').JSONPathTemplateObject<T>} */
|
|
113
|
+
let normalized;
|
|
109
114
|
if (Array.isArray(template)) {
|
|
110
115
|
// Todo: We could allow a third argument (at beginning or
|
|
111
116
|
// end?) to represent template name
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
normalized = /**
|
|
118
|
+
* @type {import('./index.js').
|
|
119
|
+
JSONPathTemplateObject<T>} */ (
|
|
120
|
+
{path: template[0], template: template[1]}
|
|
121
|
+
);
|
|
122
|
+
} else if (template.match && !template.path) {
|
|
123
|
+
// Normalize 'match' to 'path' for XSLT compatibility
|
|
124
|
+
normalized = {...template, path: template.match};
|
|
125
|
+
} else {
|
|
126
|
+
normalized = template;
|
|
115
127
|
}
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
// A declarative (jamilih-shaped) node array, as opposed to a
|
|
129
|
+
// `TemplateFunction` — compile it once, up front, so every later
|
|
130
|
+
// dispatch site (root, applyTemplates, callTemplate) only ever sees
|
|
131
|
+
// a plain function.
|
|
132
|
+
if (isJSONTemplateNodeArray(normalized.template)) {
|
|
133
|
+
const format = normalized.format || config.defaultTemplateFormat ||
|
|
134
|
+
'json';
|
|
135
|
+
normalized = {
|
|
136
|
+
...normalized,
|
|
137
|
+
template: compileJSONTemplate(normalized.template, {format})
|
|
138
|
+
};
|
|
119
139
|
}
|
|
120
|
-
return
|
|
140
|
+
return normalized;
|
|
121
141
|
});
|
|
122
142
|
this.templates.forEach((template, i, templates) => {
|
|
123
143
|
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
@@ -164,16 +184,26 @@ class JSONPathTransformer {
|
|
|
164
184
|
}
|
|
165
185
|
// Set up parameter context for valueOf() access in root template
|
|
166
186
|
jte._params = {0: jte._contextObj};
|
|
187
|
+
// `template` is always a function here: a declarative (jamilih-shaped)
|
|
188
|
+
// Array is compiled to one up front, in the constructor.
|
|
189
|
+
const {template: rootTemplateFn} =
|
|
190
|
+
/**
|
|
191
|
+
* @type {import('./index.js').JSONPathTemplateObject<T> &
|
|
192
|
+
* {template: import('./index.js').
|
|
193
|
+
* TemplateFunction<T, "json", JSONPathTransformerContext<T>>}}
|
|
194
|
+
*/ (
|
|
195
|
+
templateObj
|
|
196
|
+
);
|
|
167
197
|
/**
|
|
168
198
|
* The template may return a value synchronously or a Promise (e.g. from
|
|
169
199
|
* `await this.indexedDB(...)`), which is awaited unless `config.sync`.
|
|
170
200
|
* @type {any}
|
|
171
201
|
*/
|
|
172
|
-
const ret =
|
|
173
|
-
templateObj
|
|
202
|
+
const ret = (
|
|
174
203
|
// `this` carries runtime `extensions`; a consumer's `ContextExtensions`
|
|
175
204
|
// augmentation would otherwise reject the bare context here.
|
|
176
|
-
|
|
205
|
+
rootTemplateFn
|
|
206
|
+
).call(/** @type {any} */ (jte), undefined, {mode});
|
|
177
207
|
|
|
178
208
|
if (ret !== null && typeof ret !== 'undefined' &&
|
|
179
209
|
typeof ret.then === 'function') {
|