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
package/.gitignore
ADDED
package/.npmignore
ADDED
package/CHANGES.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# jtlt CHANGES
|
|
2
2
|
|
|
3
|
+
## 0.19.0
|
|
4
|
+
|
|
5
|
+
- feat: declarative jamilih node format
|
|
6
|
+
|
|
7
|
+
## 0.18.0
|
|
8
|
+
|
|
9
|
+
- feat: `this.if()` / `choose()` / `assert()` accept a simple, non-`eval`
|
|
10
|
+
binary comparison as their test, e.g. `this.if('$name === "x"')` or
|
|
11
|
+
`this.if('$count < 50')` — a bare `$name` parameter (or, for the JSONPath
|
|
12
|
+
engine, a plain dotted/indexed `$...` path) on the left, one of
|
|
13
|
+
`===` / `!==` / `==` / `!=` / `<` / `<=` / `>` / `>=`, and a string,
|
|
14
|
+
number, boolean, `null`, or `undefined` literal on the right; anything
|
|
15
|
+
more complex is left to the JSONPath/XPath engine
|
|
16
|
+
|
|
3
17
|
## 0.17.0
|
|
4
18
|
|
|
5
19
|
- feat: `this.if('$name', ...)` (and `choose()`/`assert()`) test a parameter
|
package/README.md
CHANGED
|
@@ -419,6 +419,7 @@ Advantages (strong parallels with XSLT):
|
|
|
419
419
|
- Built‑in default rules: when no template matches, defaults traverse and render objects, arrays, scalars, property names, and functions, similar to XSLT’s built‑in templates.
|
|
420
420
|
- applyTemplates/forEach and sorting: `applyTemplates(select, mode, sort)` and `forEach(select, cb, sort)` mirror `xsl:apply-templates`/`xsl:for-each` and `xsl:sort`.
|
|
421
421
|
- Named templates and parameters: `callTemplate(name, withParam)` reflects `xsl:call-template` + `xsl:with-param`. `this.param(name, default)` mirrors `xsl:param` — the declared default is used unless the caller supplied a value (via `this.withParam(name, value)` before the call, or `callTemplate`'s `withParam` array) or a runtime value was passed as `config.params` (like an XSLT processor's stylesheet parameters). `this.withParam(name, value)` stages a parameter for the next `callTemplate()`/`applyTemplates()`, which consumes and clears the staged set. `config.params` values are also readable as `$name` from any template. In each of `param()`/`withParam()`, the value argument is a selector expression string (or `{select}`), or a literal `{value}`.
|
|
422
|
+
- Conditionals: `this.if(test, cb)`, `this.choose(test, whenCb, otherwiseCb)`, and `this.assert(test, message)` mirror `xsl:if`, `xsl:choose`, and `xsl:assert`. `test` is a JSONPath/XPath expression (a non-empty result set is truthy), a bare `$name` parameter reference, or a simple non-`eval` comparison such as `'$name === "x"'` or `'$count < 50'` — a `$name` parameter (or, for the JSONPath engine, a plain dotted/indexed `$...` path) on the left, one of `===` / `!==` / `==` / `!=` / `<` / `<=` / `>` / `>=`, and a string, number, boolean, `null`, or `undefined` literal on the right.
|
|
422
423
|
- Keys and lookups: `key(name, match, use)` + `getKey(name, value)` provide `xsl:key`-style indexing for joins and fast lookups.
|
|
423
424
|
- Multiple output forms: string, DOM, and JSON builders ("joiners") allow emitting different result trees like XSLT’s result tree model.
|
|
424
425
|
|
|
@@ -442,6 +443,81 @@ from XSLT):
|
|
|
442
443
|
|
|
443
444
|
1. The method `this.stylesheet()` (or `this.transform()`) is used similarly to XSLT for configuration, but it does not call for including the templates within it as nested content.
|
|
444
445
|
2. JTLT adds `path` as an alias for `match` on templates.
|
|
446
|
+
3. **No single-root-node restriction.** XSLT's content model has, at root,
|
|
447
|
+
a single node per template (not counting processing instructions) —
|
|
448
|
+
`xsl:output` is a separate top-level stylesheet declaration, and
|
|
449
|
+
`xsl:template`'s own content is one nested tree. JTLT has no equivalent
|
|
450
|
+
of that single `xsl:stylesheet`/`xsl:transform` wrapper to enforce it:
|
|
451
|
+
- A **function template** is a plain JavaScript function body, so it can
|
|
452
|
+
make any number of top-level calls in sequence — there is nothing
|
|
453
|
+
that limits it to producing (or delegating to) one node:
|
|
454
|
+
```js
|
|
455
|
+
const templateObj = {path: '$', template () {
|
|
456
|
+
// Two independent things at the root, not one nested tree:
|
|
457
|
+
this.output({method: 'html'}); // configure output
|
|
458
|
+
this.applyTemplates('$.items[*]'); // then produce content
|
|
459
|
+
}};
|
|
460
|
+
```
|
|
461
|
+
- A **declarative (jamilih-shaped) template** — the `Array`-of-nodes
|
|
462
|
+
form compiled by `compileJSONTemplate()` — is likewise an array of
|
|
463
|
+
*sibling* top-level nodes, not one node:
|
|
464
|
+
```js
|
|
465
|
+
const templateObj = {
|
|
466
|
+
template: [
|
|
467
|
+
['h1', ['Custom heading']], // a literal element, then
|
|
468
|
+
[{$applyTemplates: '$.items[*]'}] // an operation node
|
|
469
|
+
]
|
|
470
|
+
// -> <h1>Custom heading</h1><li>...</li>...
|
|
471
|
+
};
|
|
472
|
+
```
|
|
473
|
+
jamilih's own `#` fragment node offers a similar grouping when only
|
|
474
|
+
one node is structurally expected, but the top level here never
|
|
475
|
+
requires it — an array of nodes is already accepted directly.
|
|
476
|
+
|
|
477
|
+
This is also why a template holding just *one* operation node, with
|
|
478
|
+
no other siblings, still needs two levels of array, not one:
|
|
479
|
+
|
|
480
|
+
```js
|
|
481
|
+
// Rejected: a bare `{$applyTemplates}` object is not itself a node —
|
|
482
|
+
// every node (elements included) is array-shaped, `[head, ...args]`.
|
|
483
|
+
const rejected = {template: [{$applyTemplates: '$.items[*]'}]};
|
|
484
|
+
|
|
485
|
+
// Accepted: the outer array is the sibling-node list (point 3, above);
|
|
486
|
+
// the inner array is this one operation node's own `[head, ...args]`
|
|
487
|
+
// shape — `$applyTemplates` just happens to need no further `args`.
|
|
488
|
+
const accepted = {template: [[{$applyTemplates: '$.items[*]'}]]};
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
The inner array isn't there *for* `$applyTemplates` specifically — it's
|
|
492
|
+
the same shape every operation node uses, whether or not that
|
|
493
|
+
operation happens to need trailing arguments: `$if` and `$forEach`
|
|
494
|
+
both use later array items for their bodies (`[{$if}, thenNodes,
|
|
495
|
+
elseNodes?]`, `[{$forEach}, childNodes]`), so the node model treats
|
|
496
|
+
`[head, ...args]` as the one uniform shape rather than special-casing
|
|
497
|
+
the argument-less operations to a bare head object.
|
|
498
|
+
|
|
499
|
+
`$if`'s then/else and `$forEach`'s children are, in turn, each a
|
|
500
|
+
`thenNodes`/`elseNodes`/`childNodes` **array of sibling nodes** — the
|
|
501
|
+
exact same shape as `template` itself (point 3, above) — not a single
|
|
502
|
+
unwrapped node, even when the body happens to hold just one:
|
|
503
|
+
|
|
504
|
+
```js
|
|
505
|
+
// Not this — a single node, unwrapped:
|
|
506
|
+
const unwrapped = [[{$if: '$flag'}, ['p', ['yes']]]];
|
|
507
|
+
|
|
508
|
+
// This — an array of (one) sibling node(s):
|
|
509
|
+
const wrapped = [[{$if: '$flag'}, [['p', ['yes']]]]];
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Requiring the wrapper isn't just consistency for its own sake — an
|
|
513
|
+
*unwrapped* single node would be genuinely ambiguous, silently so:
|
|
514
|
+
an element node is itself an array, `[name, attrs?, children?]`, so
|
|
515
|
+
`['p', ['yes']]` is indistinguishable, at the array level, from a
|
|
516
|
+
two-node *list*: a bare string `'p'` (a text node) followed by an
|
|
517
|
+
element node `['yes']` (an empty `<yes>` tag). That is exactly what
|
|
518
|
+
`unwrapped` above produces — `p<yes></yes>`, not `<p>yes</p>` — with
|
|
519
|
+
no error, since both readings are structurally valid nodes on their
|
|
520
|
+
own; only the wrapped array-of-nodes form is unambiguous.
|
|
445
521
|
|
|
446
522
|
## To-dos
|
|
447
523
|
|