jtlt 0.17.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/.gitignore +3 -0
- package/.npmignore +3 -0
- package/CHANGES.md +14 -0
- package/README.md +76 -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 +106 -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 +44 -0
- 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 +337 -47
- package/src/StringJoiningTransformer.js +35 -9
- package/src/XPathTransformerContext.js +140 -2
- package/src/index.js +115 -4
- package/src/jsonTemplate.js +410 -0
|
@@ -391,6 +391,12 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
391
391
|
if (_oldStrTemp !== undefined) {
|
|
392
392
|
this._strTemp = (this._strTemp || '') + str;
|
|
393
393
|
} else {
|
|
394
|
+
// Close any open parent start tag before appending content, matching
|
|
395
|
+
// text()'s behavior — string() is content too, not an attribute value.
|
|
396
|
+
if (this._openTagState) {
|
|
397
|
+
this.append('>');
|
|
398
|
+
this._openTagState = false;
|
|
399
|
+
}
|
|
394
400
|
// Append to the output (or current container via append()).
|
|
395
401
|
this.append(tmpStr + str);
|
|
396
402
|
}
|
|
@@ -497,9 +503,14 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
497
503
|
* @param {string|Element} elem - Element name or element object
|
|
498
504
|
* @param {ElementAttributes} [atts] - Element attributes
|
|
499
505
|
* @param {any[]} [childNodes] - Child nodes
|
|
500
|
-
* @param {(this: StringJoiningTransformer) => void} [cb] -
|
|
506
|
+
* @param {(this: StringJoiningTransformer) => void} [cb] -
|
|
507
|
+
* Callback function; may be async (e.g. to `await` a `$indexedDB`
|
|
508
|
+
* fetch), in which case `element()` itself returns a `Promise` instead
|
|
509
|
+
* of `this` — check for `.then` (or `await`) rather than assuming a
|
|
510
|
+
* synchronous return. (Typed as plain `void`, not `void|Promise<void>`
|
|
511
|
+
* — see the note on `SimpleCallback` in JSONJoiningTransformer.js.)
|
|
501
512
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
502
|
-
* @returns {StringJoiningTransformer}
|
|
513
|
+
* @returns {StringJoiningTransformer|Promise<StringJoiningTransformer>}
|
|
503
514
|
*/
|
|
504
515
|
element (elem, atts, childNodes, cb, useAttributeSets) {
|
|
505
516
|
// If a parent element's start tag is still open, close it before
|
|
@@ -635,17 +646,32 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
635
646
|
jml[method]({'#': childNodes})
|
|
636
647
|
));
|
|
637
648
|
}
|
|
638
|
-
cb
|
|
649
|
+
// `cb`'s declared return type is plain `void` (see the parameter's
|
|
650
|
+
// JSDoc) so the leniency that lets any actual return value through
|
|
651
|
+
// still applies to callers — cast here to duck-type the real value.
|
|
652
|
+
const cbResult = /** @type {any} */ (cb.call(this._context || this));
|
|
639
653
|
|
|
640
654
|
// Todo: Depending on an this._cfg.xmlElements option, allow for
|
|
641
655
|
// XML self-closing when empty or as per the tag, HTML
|
|
642
656
|
// self-closing tags (or polyglot-friendly self-closing)
|
|
643
|
-
|
|
644
|
-
this.
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
657
|
+
const finish = () => {
|
|
658
|
+
if (this._openTagState) {
|
|
659
|
+
this.append('>');
|
|
660
|
+
}
|
|
661
|
+
this.append('</' + elName + '>');
|
|
662
|
+
this._openTagState = oldTagState;
|
|
663
|
+
return this;
|
|
664
|
+
};
|
|
665
|
+
// `cb` may be an async function (e.g. one that awaits
|
|
666
|
+
// `this.indexedDB(...)`/`this.renderDefault()` before appending
|
|
667
|
+
// content) — mirroring how a root/matched/named template's own return
|
|
668
|
+
// value is already handled elsewhere, closing the tag only after that
|
|
669
|
+
// settles keeps output correctly ordered.
|
|
670
|
+
if (cbResult && typeof cbResult.then === 'function') {
|
|
671
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- Not async
|
|
672
|
+
return cbResult.then(() => finish());
|
|
673
|
+
}
|
|
674
|
+
return finish();
|
|
649
675
|
}
|
|
650
676
|
|
|
651
677
|
/**
|
|
@@ -619,12 +619,36 @@ class XPathTransformerContext {
|
|
|
619
619
|
const prevTemplateParams = this._params;
|
|
620
620
|
this._params = {0: node, ...appliedParams};
|
|
621
621
|
|
|
622
|
+
const {template: xpathTemplateFn} = templateObj;
|
|
623
|
+
/* c8 ignore start -- Todo: compile via compileJSONTemplate() once it
|
|
624
|
+
exists (see ~/idb-manager/JTLT-JSON-TEMPLATES-PROPOSAL.md) instead
|
|
625
|
+
of rejecting; no matched template can reach here with an Array
|
|
626
|
+
`template` until that lands (and the declarative subset is
|
|
627
|
+
JSONPath-only per that plan, so this may stay unreachable longer
|
|
628
|
+
than the JSONPath-side guards). */
|
|
629
|
+
if (Array.isArray(xpathTemplateFn)) {
|
|
630
|
+
throw new TypeError(
|
|
631
|
+
'JSON (jamilih-shaped) Array templates are not yet supported by ' +
|
|
632
|
+
'the XPath engine; compile with compileJSONTemplate() first.'
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
/* c8 ignore stop */
|
|
622
636
|
/**
|
|
623
637
|
* The template may return synchronously or return a Promise (e.g. from
|
|
624
638
|
* `await this.indexedDB(...)`), which is awaited unless `config.sync`.
|
|
625
639
|
* @type {any}
|
|
626
640
|
*/
|
|
627
|
-
const ret =
|
|
641
|
+
const ret = xpathTemplateFn.call(
|
|
642
|
+
// `this` carries runtime `extensions`; a consumer's
|
|
643
|
+
// `ContextExtensions` augmentation would otherwise reject it here
|
|
644
|
+
// (matching the cast already used at the other three call sites).
|
|
645
|
+
// `node` is cast too: `templateObj.template`'s static type is a
|
|
646
|
+
// union across a `dom`-typed `TemplateFunction` (expecting
|
|
647
|
+
// `DocumentFragment | Element`) and the mode-callback signature
|
|
648
|
+
// above (expecting plain `Node`) — the same node value satisfies
|
|
649
|
+
// whichever one actually runs.
|
|
650
|
+
/** @type {any} */ (this), /** @type {any} */ (node), {mode}
|
|
651
|
+
);
|
|
628
652
|
|
|
629
653
|
// Restore previous parameter context
|
|
630
654
|
this._params = prevTemplateParams;
|
|
@@ -2469,6 +2493,13 @@ class XPathTransformerContext {
|
|
|
2469
2493
|
* supplied via `withParam()`, or provided at runtime as `config.params` —
|
|
2470
2494
|
* rather than an XPath expression.
|
|
2471
2495
|
*
|
|
2496
|
+
* The test may also be a simple binary comparison of a `$name` parameter
|
|
2497
|
+
* against a literal, evaluated without `eval`, e.g.
|
|
2498
|
+
* `this.if('$name === "x"')` or `this.if('$count < 50')`. The operator is
|
|
2499
|
+
* one of `===`, `!==`, `==`, `!=`, `<`, `<=`, `>`, `>=`; the right side is
|
|
2500
|
+
* a string, number, boolean, `null`, or `undefined` literal. Anything else
|
|
2501
|
+
* is left to the XPath engine.
|
|
2502
|
+
*
|
|
2472
2503
|
* Truthiness rules:
|
|
2473
2504
|
* - Node set: length > 0 passes.
|
|
2474
2505
|
* - Scalar: Boolean(value) must be true.
|
|
@@ -2514,13 +2545,20 @@ class XPathTransformerContext {
|
|
|
2514
2545
|
*/
|
|
2515
2546
|
_passesIf (select) {
|
|
2516
2547
|
if (typeof select === 'string') {
|
|
2517
|
-
const
|
|
2548
|
+
const trimmed = select.trim();
|
|
2549
|
+
const paramRef = trimmed.match(/^\$(?<name>[\w\-]+)$/v);
|
|
2518
2550
|
if (paramRef && paramRef.groups) {
|
|
2519
2551
|
const param = this._lookupParam(paramRef.groups.name);
|
|
2520
2552
|
if (param.has) {
|
|
2521
2553
|
return this._isTruthyResult(param.value);
|
|
2522
2554
|
}
|
|
2523
2555
|
}
|
|
2556
|
+
const cmp = this._parseComparison(trimmed);
|
|
2557
|
+
if (cmp) {
|
|
2558
|
+
return this._compareValues(
|
|
2559
|
+
this._resolveComparand(cmp.left), cmp.op, cmp.right
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2524
2562
|
}
|
|
2525
2563
|
let passes = false;
|
|
2526
2564
|
// Try scalar evaluation first (handles boolean/comparison expressions)
|
|
@@ -2567,6 +2605,106 @@ class XPathTransformerContext {
|
|
|
2567
2605
|
return passes;
|
|
2568
2606
|
}
|
|
2569
2607
|
|
|
2608
|
+
/**
|
|
2609
|
+
* Parse a simple `$name <op> <literal>` comparison test (no `eval`), such
|
|
2610
|
+
* as `$name === "x"` or `$count < 50`. The left side must be a bare `$name`
|
|
2611
|
+
* parameter reference; the right side a string, number, boolean, `null`,
|
|
2612
|
+
* or `undefined` literal. Returns `null` when the string is not such a
|
|
2613
|
+
* comparison, so richer XPath expressions fall through untouched.
|
|
2614
|
+
* @param {string} str
|
|
2615
|
+
* @returns {{left: string, op: string, right: unknown}|null}
|
|
2616
|
+
* @private
|
|
2617
|
+
*/
|
|
2618
|
+
_parseComparison (str) {
|
|
2619
|
+
const m = str.match(
|
|
2620
|
+
/^(?<left>\$[\w\-]+)\s*(?<op>===|!==|==|!=|<=|>=|<|>)\s*(?<right>.+)$/v
|
|
2621
|
+
);
|
|
2622
|
+
if (!m || !m.groups) {
|
|
2623
|
+
return null;
|
|
2624
|
+
}
|
|
2625
|
+
const right = this._parseLiteral(m.groups.right.trim());
|
|
2626
|
+
if (!right) {
|
|
2627
|
+
return null;
|
|
2628
|
+
}
|
|
2629
|
+
return {left: m.groups.left, op: m.groups.op, right: right.value};
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2632
|
+
/**
|
|
2633
|
+
* Parse a JSON-ish scalar literal: a double- or single-quoted string, a
|
|
2634
|
+
* number, or `true` / `false` / `null` / `undefined`. Returns `null` when
|
|
2635
|
+
* `str` is none of these.
|
|
2636
|
+
* @param {string} str
|
|
2637
|
+
* @returns {{value: unknown}|null}
|
|
2638
|
+
* @private
|
|
2639
|
+
*/
|
|
2640
|
+
// eslint-disable-next-line class-methods-use-this -- pure helper
|
|
2641
|
+
_parseLiteral (str) {
|
|
2642
|
+
if ((/^"(?:[^"\\]|\\.)*"$/v).test(str)) {
|
|
2643
|
+
return {value: JSON.parse(str)};
|
|
2644
|
+
}
|
|
2645
|
+
if ((/^'(?:[^'\\]|\\.)*'$/v).test(str)) {
|
|
2646
|
+
return {value: str.slice(1, -1).replaceAll(/\\(?=['\\])/gv, '')};
|
|
2647
|
+
}
|
|
2648
|
+
if ((/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?$/v).test(str)) {
|
|
2649
|
+
return {value: Number(str)};
|
|
2650
|
+
}
|
|
2651
|
+
if (str === 'true') {
|
|
2652
|
+
return {value: true};
|
|
2653
|
+
}
|
|
2654
|
+
if (str === 'false') {
|
|
2655
|
+
return {value: false};
|
|
2656
|
+
}
|
|
2657
|
+
if (str === 'null') {
|
|
2658
|
+
return {value: null};
|
|
2659
|
+
}
|
|
2660
|
+
if (str === 'undefined') {
|
|
2661
|
+
return {value: undefined};
|
|
2662
|
+
}
|
|
2663
|
+
return null;
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
/**
|
|
2667
|
+
* Resolve the left side of a simple comparison: a bare `$name` parameter
|
|
2668
|
+
* reference (local, with-param, then runtime `config.params`).
|
|
2669
|
+
* @param {string} ref
|
|
2670
|
+
* @returns {unknown}
|
|
2671
|
+
* @private
|
|
2672
|
+
*/
|
|
2673
|
+
_resolveComparand (ref) {
|
|
2674
|
+
const paramRef = ref.match(/^\$(?<name>[\w\-]+)$/v);
|
|
2675
|
+
/* c8 ignore next 3 -- `_parseComparison` only yields a `$name` left side */
|
|
2676
|
+
if (!paramRef || !paramRef.groups) {
|
|
2677
|
+
return undefined;
|
|
2678
|
+
}
|
|
2679
|
+
return this._lookupParam(paramRef.groups.name).value;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
/**
|
|
2683
|
+
* Apply a comparison operator to two already-resolved values.
|
|
2684
|
+
* @param {any} a - Left operand
|
|
2685
|
+
* @param {string} op - One of `===`, `!==`, `==`, `!=`, `<`, `<=`, `>`, `>=`
|
|
2686
|
+
* @param {any} b - Right operand
|
|
2687
|
+
* @returns {boolean}
|
|
2688
|
+
* @private
|
|
2689
|
+
*/
|
|
2690
|
+
// eslint-disable-next-line class-methods-use-this -- pure helper
|
|
2691
|
+
_compareValues (a, op, b) {
|
|
2692
|
+
/* eslint-disable eqeqeq -- `==`/`!=` are deliberately offered */
|
|
2693
|
+
switch (op) {
|
|
2694
|
+
case '===': return a === b;
|
|
2695
|
+
case '!==': return a !== b;
|
|
2696
|
+
case '==': return a == b;
|
|
2697
|
+
case '!=': return a != b;
|
|
2698
|
+
case '<': return a < b;
|
|
2699
|
+
case '<=': return a <= b;
|
|
2700
|
+
case '>': return a > b;
|
|
2701
|
+
case '>=': return a >= b;
|
|
2702
|
+
/* c8 ignore next -- the regex only yields the operators handled above */
|
|
2703
|
+
default: return false;
|
|
2704
|
+
}
|
|
2705
|
+
/* eslint-enable eqeqeq -- restore */
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2570
2708
|
/**
|
|
2571
2709
|
* Conditional with optional fallback (like choose/otherwise).
|
|
2572
2710
|
* Truthiness same as `if()`.
|
package/src/index.js
CHANGED
|
@@ -40,7 +40,16 @@ export const setWindow = (win) => {
|
|
|
40
40
|
* @property {string} [name] - Optional name for calling via callTemplate
|
|
41
41
|
* @property {string} [mode] - Optional mode for template matching
|
|
42
42
|
* @property {number} [priority] - Priority for template selection
|
|
43
|
-
* @property {
|
|
43
|
+
* @property {'json'|'javascript'} [format] - For an Array `template` only:
|
|
44
|
+
* the jamilih validation strictness `compileJSONTemplate` applies
|
|
45
|
+
* (`isValidJamilih`'s own `format` option) — `'json'` (default) rejects
|
|
46
|
+
* live values (functions, DOM nodes, …) embedded in the structure,
|
|
47
|
+
* `'javascript'` allows them. Ignored for a function `template`. Falls
|
|
48
|
+
* back to `config.defaultTemplateFormat`, then `'json'`, when omitted.
|
|
49
|
+
* @property {TemplateFunction<T, U, TCtx> | JSONTemplateNode[]} template -
|
|
50
|
+
* Template function, or a declarative (jamilih-shaped) node array
|
|
51
|
+
* compiled via `compileJSONTemplate` — detected by `Array.isArray`, since
|
|
52
|
+
* a `TemplateFunction` is never an array.
|
|
44
53
|
*/
|
|
45
54
|
|
|
46
55
|
/**
|
|
@@ -80,7 +89,78 @@ export const setWindow = (win) => {
|
|
|
80
89
|
* @template {"json"|"string"|"dom"} T
|
|
81
90
|
* @typedef {JSONPathTemplateObject<T> | [string, TemplateFunction<T, "json",
|
|
82
91
|
* import('./JSONPathTransformerContext.js').default
|
|
83
|
-
* >]} JSONPathTemplateArray
|
|
92
|
+
* > | JSONTemplateNode[]]} JSONPathTemplateArray
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A jamilih-shaped element node — see
|
|
97
|
+
* `~/idb-manager/ROUTE-OVERRIDES-PLAN.md` §3.1. An attributes object may be
|
|
98
|
+
* omitted when there are no attributes, in which case the second item is
|
|
99
|
+
* the children array directly. Attribute *values* are left `unknown` rather
|
|
100
|
+
* than modeled precisely: they range from HTML-attribute primitives to
|
|
101
|
+
* jamilih's own richer magic-key values (`$on` handler arrays, etc.), and
|
|
102
|
+
* that shape is validated at runtime by `isValidJamilih`/`validateJamilih`,
|
|
103
|
+
* not statically here.
|
|
104
|
+
* @typedef {[string] |
|
|
105
|
+
* [string, Record<string, unknown>] |
|
|
106
|
+
* [string, JSONTemplateNode[]] |
|
|
107
|
+
* [string, Record<string, unknown>, JSONTemplateNode[]]
|
|
108
|
+
* } JSONElementNode
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The declarative operation vocabulary (see
|
|
113
|
+
* `~/idb-manager/ROUTE-OVERRIDES-PLAN.md` §3.2). Every key on the leading
|
|
114
|
+
* object is `$`-prefixed — jamilih's own validator requires this of any
|
|
115
|
+
* first-position plain object. Most keys use a bare `$`-prefix. `$jtltMode`
|
|
116
|
+
* is namespaced unconditionally: jamilih hard-rejects `$mode` even bare
|
|
117
|
+
* (`RESERVED_OPTION`). `$text` / `$jtltText` are the general form and a
|
|
118
|
+
* jamilih-native-compatible shorthand for the same thing, not two distinct
|
|
119
|
+
* operations: `$jtltText` (never seen by jamilih's own `$text` handling, so
|
|
120
|
+
* an unrecognized `$select` alongside it is just ordinary dialect data) is
|
|
121
|
+
* the canonical form and works with or without `$select`; a **bare**
|
|
122
|
+
* `{$text: value}` (literal only, no `$select`) is jamilih's own native
|
|
123
|
+
* text-node form, equivalent to `{$jtltText: value}`, and validates as-is —
|
|
124
|
+
* but `{$text: value, $select}` does not, because jamilih rejects the
|
|
125
|
+
* unrecognized `$select` sitting next to its own reserved `$text`
|
|
126
|
+
* (`UNKNOWN_MAGIC_PROPERTY`); that combined form needs `$jtltText` instead.
|
|
127
|
+
* See §3.5. `$indexedDB`'s children are optional (unlike `$if`/`$forEach`,
|
|
128
|
+
* which both require theirs): a bare prefetch — binding via `$as` for later
|
|
129
|
+
* use, or simply discarding the rows — is a legitimate leaf use.
|
|
130
|
+
* @typedef {[{$text: unknown}] |
|
|
131
|
+
* [{$jtltText: unknown, $select?: string}] |
|
|
132
|
+
* [{$string: unknown, $select?: string}] |
|
|
133
|
+
* [{$valueOf: string}] |
|
|
134
|
+
* [{$applyTemplates: string, $jtltMode?: string, $sort?: unknown}] |
|
|
135
|
+
* [{$if: string}, JSONTemplateNode[]] |
|
|
136
|
+
* [{$if: string}, JSONTemplateNode[], JSONTemplateNode[]] |
|
|
137
|
+
* [{$forEach: string, $sort?: unknown}, JSONTemplateNode[]] |
|
|
138
|
+
* [{$variable: string, $select: string}] |
|
|
139
|
+
* [{
|
|
140
|
+
* $indexedDB: {
|
|
141
|
+
* db: string, store: string,
|
|
142
|
+
* options?: import('./indexedDB.js').QueryOptions
|
|
143
|
+
* },
|
|
144
|
+
* $as?: string
|
|
145
|
+
* }] |
|
|
146
|
+
* [{
|
|
147
|
+
* $indexedDB: {
|
|
148
|
+
* db: string, store: string,
|
|
149
|
+
* options?: import('./indexedDB.js').QueryOptions
|
|
150
|
+
* },
|
|
151
|
+
* $as?: string
|
|
152
|
+
* }, JSONTemplateNode[]] |
|
|
153
|
+
* [{$renderDefault: true}]
|
|
154
|
+
* } JSONOperationNode
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A declarative (jamilih-shaped) template node: a text string, an element
|
|
159
|
+
* node, or an operation node. Passed as a `TemplateObject.template` (or a
|
|
160
|
+
* `[path, JSONTemplateNode[]]` tuple's second slot) wherever a
|
|
161
|
+
* `TemplateFunction` is otherwise accepted, compiled via
|
|
162
|
+
* `compileJSONTemplate` — see `~/idb-manager/JTLT-JSON-TEMPLATES-PROPOSAL.md`.
|
|
163
|
+
* @typedef {string | JSONElementNode | JSONOperationNode} JSONTemplateNode
|
|
84
164
|
*/
|
|
85
165
|
|
|
86
166
|
/**
|
|
@@ -223,6 +303,10 @@ export const setWindow = (win) => {
|
|
|
223
303
|
* A `this.param(name, default)` declaration whose name appears here resolves
|
|
224
304
|
* to this value instead of its default, and `$name` references such a
|
|
225
305
|
* parameter from any template.
|
|
306
|
+
* @property {'json'|'javascript'} [defaultTemplateFormat] Config-wide
|
|
307
|
+
* default for an Array `template`'s jamilih validation strictness (see
|
|
308
|
+
* `TemplateObject.format`), used for any entry that doesn't specify its
|
|
309
|
+
* own `format`. Defaults to `'json'` when omitted here too.
|
|
226
310
|
*/
|
|
227
311
|
|
|
228
312
|
/**
|
|
@@ -230,7 +314,7 @@ export const setWindow = (win) => {
|
|
|
230
314
|
* @template {"json"|"string"|"dom"} [T = "json"]
|
|
231
315
|
* @template {boolean|undefined} [E=false]
|
|
232
316
|
* @typedef {BaseJTLTOptions<T, E> & {
|
|
233
|
-
* templates?: JSONPathTemplateArray<T>[] |
|
|
317
|
+
* templates?: JSONPathTemplateArray<T>[] | JSONPathTemplateArray<T> |
|
|
234
318
|
* TemplateFunction<T, "json",
|
|
235
319
|
* import('./JSONPathTransformerContext.js').default<T>>,
|
|
236
320
|
* template?: JSONPathTemplateObject<T> | TemplateFunction<T, "json",
|
|
@@ -288,6 +372,29 @@ export const setWindow = (win) => {
|
|
|
288
372
|
* XPathJTLTOptions<"dom", E>} JTLTOptions
|
|
289
373
|
*/
|
|
290
374
|
|
|
375
|
+
/**
|
|
376
|
+
* `config.templates` (and, for symmetry, `config.template`) may be given as
|
|
377
|
+
* a bare single entry — a `TemplateObject` (`{path, template}`) or a
|
|
378
|
+
* `[path, template]` tuple — rather than always wrapped in an outer array.
|
|
379
|
+
* A bare tuple is distinguished from a list of entries by its own first
|
|
380
|
+
* item being a string: no valid entry (a plain object, or itself a tuple
|
|
381
|
+
* whose own first item is a path string) is ever a bare string, so this
|
|
382
|
+
* can't collide with a real list.
|
|
383
|
+
* @param {unknown} templates
|
|
384
|
+
* @returns {unknown[]|undefined}
|
|
385
|
+
*/
|
|
386
|
+
function normalizeBareTemplatesShape (templates) {
|
|
387
|
+
if (!templates) {
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
if (Array.isArray(templates)) {
|
|
391
|
+
return typeof templates[0] === 'string' ? [templates] : templates;
|
|
392
|
+
}
|
|
393
|
+
// A bare `TemplateObject` (a plain object, not a function — functions are
|
|
394
|
+
// already routed through the `query` branch above this call).
|
|
395
|
+
return [templates];
|
|
396
|
+
}
|
|
397
|
+
|
|
291
398
|
/**
|
|
292
399
|
* High-level façade for running a JTLT transform.
|
|
293
400
|
*
|
|
@@ -519,7 +626,8 @@ class JTLT {
|
|
|
519
626
|
])
|
|
520
627
|
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
521
628
|
: /** @type {JSONPathTemplateObject<joiningTypes>[]|XPathTemplateObject<joiningTypes>[]} */ (
|
|
522
|
-
cfg.templates ||
|
|
629
|
+
normalizeBareTemplatesShape(cfg.templates) ||
|
|
630
|
+
normalizeBareTemplatesShape(cfg.template)
|
|
523
631
|
);
|
|
524
632
|
this.config.errorOnEqualPriority = cfg.errorOnEqualPriority || false;
|
|
525
633
|
this.config.engine ||=
|
|
@@ -859,5 +967,8 @@ export {
|
|
|
859
967
|
export {
|
|
860
968
|
default as XPathTransformer
|
|
861
969
|
} from './XPathTransformer.js';
|
|
970
|
+
export {
|
|
971
|
+
compileJSONTemplate, isJSONTemplateNodeArray, validateJSONTemplate
|
|
972
|
+
} from './jsonTemplate.js';
|
|
862
973
|
|
|
863
974
|
export default JTLT;
|