jtlt 0.2.0 → 0.4.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 +27 -0
- package/README.md +61 -164
- package/demo/calltemplate-params-demo.js +138 -0
- package/demo/codemirror.esm.js +28242 -0
- package/demo/codemirror.js +94 -0
- package/demo/index.css +7 -0
- package/demo/index.html +28 -0
- package/demo/index.js +210 -0
- package/demo/vendor/jamilih/dist/jml.mjs +2341 -0
- package/demo/vendor/jhtml/src/SAJJ/SAJJ.ObjectArrayDelegator.js +356 -0
- package/demo/vendor/jhtml/src/SAJJ/SAJJ.Stringifier.js +186 -0
- package/demo/vendor/jhtml/src/SAJJ/SAJJ.js +746 -0
- package/demo/vendor/jhtml/src/SAJJ/testing/SAJJ.html +33 -0
- package/demo/vendor/jhtml/src/SAJJ/testing/SAJJ.testing.js +25 -0
- package/demo/vendor/jhtml/src/jhtml-browser.js +5 -0
- package/demo/vendor/jhtml/src/jhtml-node.cts +3 -0
- package/demo/vendor/jhtml/src/jhtml-node.js +8 -0
- package/demo/vendor/jhtml/src/jhtml-node.mts +1 -0
- package/demo/vendor/jhtml/src/jhtml.cts +3 -0
- package/demo/vendor/jhtml/src/jhtml.js +602 -0
- package/demo/vendor/jhtml/src/jhtml.mts +1 -0
- package/demo/vendor/jsonpath-plus/dist/index-browser-esm.js +2158 -0
- package/demo/vendor/simple-get-json/dist/index-es.js +151 -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 +334 -124
- 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 +281 -105
- 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 +172 -5
- package/docs/API.md +92 -2
- package/docs/TO-DO.md +168 -0
- package/docs/calltemplate-params.md +251 -0
- package/eslint.config.js +11 -5
- package/package.json +35 -9
- package/pnpm-workspace.yaml +1 -0
- package/rollup.config.js +13 -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 +729 -107
- package/src/StringJoiningTransformer.js +311 -57
- package/src/XPathTransformer.js +27 -12
- package/src/XPathTransformerContext.js +928 -99
- package/src/index-browser.js +5 -0
- package/src/index-node.js +7 -0
- package/src/index.js +502 -98
- package/tsconfig.json +5 -2
- package/typings/xpath2-js.d.ts +40 -1
- package/src/types/xpath2-js.d.ts +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH;;;GAGG;AACH;;;;;GAKG;AACH;;;GAGG;AACH;;;GAGG;AACH;;;GAGG;AAEH;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;GAGG;AACH;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;GAWG;AAEH,oEAAoE;AAEpE;;;;;;GAMG;AAGH,gCAAiC,IAAI,CAAC;yCA3HzB,OAAO,iCAAiC,EAAE,OAAO;sCAIjD,OAAO,8BAA8B,EAAE,OAAO;yCAI9C,OAAO,iCAAiC,EAAE,OAAO,CAAC,QAAQ,CAAC,GACvE,OAAW,iCAAiC,EAAE,OAAO,CAAC,MAAM,CAAC,GAC7D,OAAW,iCAAiC,EAAE,OAAO,CAAC,KAAK,CAAC;oCAIhD,OAAO,4BAA4B,EAAE,OAAO;qCAI5C,OAAO,6BAA6B,EAAE,OAAO;uCAI7C,OAAO,+BAA+B,EAAE,OAAO;;;;6BAM/C,IAAI,IACJ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,EACpC,GAAG,CAAC,EAAE;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,KAClB,OAAO;;;;2BAKF,IAAI;UAEH,MAAM;;;;cAeN,gBAAgB,CAAC,IAAI,CAAC;;qCAIvB,cAAc,CAAC,0BAA0B,CAAC;kCAI1C,cAAc,CAAC,uBAAuB,CAAC;;;;;;;;;aAOtC,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI;;;;WAEzB,OAAO;;;;;;;;;;;qBAQA,WAAW,KAAK,OAAO;0CACvB,MAAM,KAAK,CAAC,GAAC,GAAG,GAAC,CAAC,GAAG;;;aAG5B,OAAO;;;;;;;kCAOR,eAAe,GAAG;IAC1B,SAAS,CAAC,EAAE,sBAAsB,EAAE,GAClC,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,sBAAsB,GAC/B,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IAC/C,KAAK,CAAC,EAAE,gBAAgB,CAAC,0BAA0B,CAAC,CAAC;IACrD,UAAU,CAAC,EAAE,UAAU,CAAA;CACxB;;;;+BAKS,eAAe,GAAG;IAC1B,SAAS,CAAC,EAAE,mBAAmB,EAAE,GAC/B,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IAC5C,QAAQ,CAAC,EAAE,mBAAmB,GAC5B,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IAC5C,KAAK,CAAC,EAAE,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IAClD,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,CAAC,GAAC,CAAC,CAAA;CACnB;0BAGU,mBAAmB,GAAG,gBAAgB;;;;;kCAKvC,WAAW,GAAG;IACtB,yBAAyB,CAAC,EAAE,OAAO,CAAA;CACpC"}
|
package/docs/API.expanded.md
CHANGED
|
@@ -54,7 +54,7 @@ const out = new JTLT({
|
|
|
54
54
|
```ts
|
|
55
55
|
interface TemplateObject {
|
|
56
56
|
name?: string; // Optional identifier (for callTemplate)
|
|
57
|
-
path
|
|
57
|
+
path?: string; // JSONPath or XPath expression (required for pattern-matching)
|
|
58
58
|
mode?: string; // Optional mode segregation
|
|
59
59
|
priority?: number; // Numeric priority (higher wins); fallback uses specificity resolver
|
|
60
60
|
template(nodeValue, cfg): any; // Executed with `this` bound to context
|
|
@@ -62,6 +62,8 @@ interface TemplateObject {
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
Edge cases:
|
|
65
|
+
- Either `path` or `name` (or both) must be provided
|
|
66
|
+
- Templates with only `name` (no `path`) are callable only via `callTemplate`
|
|
65
67
|
- For JSONPath: `path` examples: `$.items[*]`, `$['prop']`, `$..deep`.
|
|
66
68
|
- For XPath: `//item`, `/root/item`, `//*[@id='x']`.
|
|
67
69
|
- Root template: path `$` (JSONPath) or `/` (XPath).
|
|
@@ -83,17 +85,18 @@ Responsibilities:
|
|
|
83
85
|
- Sorts by priority (numeric or specificity resolver).
|
|
84
86
|
- Invokes winning template; falls back to default rules when no match.
|
|
85
87
|
|
|
86
|
-
### `XPathTransformer`
|
|
88
|
+
### `XPathTransformer`
|
|
87
89
|
|
|
88
90
|
Same pattern for XML/HTML DOM data using XPath selectors.
|
|
89
91
|
|
|
90
92
|
Config additions:
|
|
91
|
-
- `xpathVersion`: `1` (native `XPathEvaluator`), `2` (`xpath2.js`).
|
|
93
|
+
- `xpathVersion`: `1` (native `XPathEvaluator`), `2` (`xpath2.js`), `3.1` (`fontoxpath`).
|
|
94
|
+
Default `1`.
|
|
92
95
|
|
|
93
96
|
```js
|
|
94
97
|
import {XPathTransformer, StringJoiningTransformer} from 'jtlt';
|
|
95
98
|
// Assume `doc` is an XML Document
|
|
96
|
-
const joiner = new StringJoiningTransformer(''
|
|
99
|
+
const joiner = new StringJoiningTransformer('');
|
|
97
100
|
const templates = [
|
|
98
101
|
{
|
|
99
102
|
name: 'root',
|
|
@@ -120,7 +123,8 @@ const out = engine.transform('');
|
|
|
120
123
|
```
|
|
121
124
|
|
|
122
125
|
Limitations:
|
|
123
|
-
-
|
|
126
|
+
- Versions 2 and 3 (`xpath2.js` and `fontoxpath`) may lack some XPath functions;
|
|
127
|
+
stick to basic location paths and simple predicates.
|
|
124
128
|
- Namespace resolution not yet exposed (future `namespaceResolver` option).
|
|
125
129
|
|
|
126
130
|
## Contexts
|
|
@@ -131,10 +135,19 @@ Methods (subset):
|
|
|
131
135
|
- `applyTemplates(select, mode?, sort?)`
|
|
132
136
|
- `forEach(select, cb, sort?)`
|
|
133
137
|
- `valueOf(select?)`
|
|
138
|
+
- `if(select, cb)` — conditionally execute `cb` when selection is truthy
|
|
139
|
+
(non-empty result set or truthy scalar)
|
|
140
|
+
- `choose(select, whenCb, otherwiseCb?)` — like `if()`, but invokes
|
|
141
|
+
`otherwiseCb` when the condition is not met (akin to xsl:choose +
|
|
142
|
+
xsl:otherwise). Selection is evaluated relative to current context
|
|
143
|
+
object (its own `$`).
|
|
134
144
|
- `variable(name, select)` – stores value/array from JSONPath.
|
|
135
145
|
- `callTemplate(name, withParam?)`
|
|
136
146
|
- `key(name, match, use)` / `getKey(name, value)`
|
|
137
147
|
- Joiner passthrough: `string()`, `text()`, `element()`, `object()`, `array()`, `number()`, `boolean()`, etc.
|
|
148
|
+
\- Cloning helpers:
|
|
149
|
+
- `copy(propertySets?)` — Shallow clone of current context value (object/array). Nested references are preserved. Optional `propertySets` (array of registered names) merge into the top-level clone when object-like.
|
|
150
|
+
- `copyOf(select?, propertySets?)` — Deep clone (prefers `structuredClone`; falls back to JSON + manual). When `select` is provided, clones that target instead of the current value; primitives append directly. Optional `propertySets` merge when cloning objects.
|
|
138
151
|
|
|
139
152
|
### `XPathTransformerContext`
|
|
140
153
|
|
|
@@ -142,9 +155,18 @@ Parallels JSONPath context with XPath evaluation:
|
|
|
142
155
|
- `get(select, asNodes?)` – returns node array when `asNodes=true` (v1 uses snapshot type; v2 coerces scalar to array).
|
|
143
156
|
- `forEach(select, cb)` – iterates matches.
|
|
144
157
|
- `applyTemplates(select, mode?)` – default initialization to `.` then `*` for subsequent calls.
|
|
158
|
+
- `if(select, cb)` — conditionally executes `cb` when XPath selects nodes
|
|
159
|
+
(non-empty) or evaluates to a truthy scalar value. In XPath v1 environments
|
|
160
|
+
without strict result typing, node-set existence is the primary check.
|
|
161
|
+
- `choose(select, whenCb, otherwiseCb?)` — same semantics as `if()` but with
|
|
162
|
+
fallback callback when condition fails. Relative XPath resolves from the
|
|
163
|
+
current context node; absolute paths (`/`, `//`) target the document root.
|
|
145
164
|
- `variable(name, select)` – always stores node arrays for XPath.
|
|
146
165
|
- `key(name, match, use)` – index by attribute value; `getKey` returns matching Element or context sentinel (`this`).
|
|
147
166
|
- Default template rules: root traverses `.`, element traverses `*`, text nodes emit `nodeValue`, scalars emit `valueOf('.')`.
|
|
167
|
+
\- Cloning helpers:
|
|
168
|
+
- `copy()` — Shallow clone of the current context node (`cloneNode(false)`) appended to the joiner.
|
|
169
|
+
- `copyOf(select?)` — Deep clone of each node matched by `select` (node-set) or the current node if omitted, using `cloneNode(true)`. If `select` yields a scalar, that scalar is appended. Document nodes in scalar fallback emit their `documentElement` text. Chainable.
|
|
148
170
|
|
|
149
171
|
## Sorting API
|
|
150
172
|
|
|
@@ -226,6 +248,11 @@ When no user template matches:
|
|
|
226
248
|
Common joiner methods summary:
|
|
227
249
|
- `append(value)`
|
|
228
250
|
- `get()`
|
|
251
|
+
- `document(cb, cfg?)` — Creates a new output document. Similar to XSLT's
|
|
252
|
+
`xsl:document`, this allows templates to generate multiple output documents.
|
|
253
|
+
The callback builds the document content, and optional `cfg` provides output
|
|
254
|
+
configuration (encoding, doctype, etc.). When `exposeDocuments` is enabled,
|
|
255
|
+
each document is added to the array returned by `get()`.
|
|
229
256
|
- `object(seed?, cb?, usePropertySets?, propSets?)`
|
|
230
257
|
- `array(seed?, cb?)`
|
|
231
258
|
- `element(name, attrs?, children?, cb?)`
|
|
@@ -233,6 +260,146 @@ Common joiner methods summary:
|
|
|
233
260
|
- `text(str)` vs `string(str, cb?)` vs `plainText(str)`
|
|
234
261
|
- `number()`, `boolean()`, `null()`, `undefined()` (JS mode), `nonfiniteNumber()`, `function(fn)`
|
|
235
262
|
|
|
263
|
+
### document() method details
|
|
264
|
+
|
|
265
|
+
The `document()` method creates a new output document in isolation:
|
|
266
|
+
|
|
267
|
+
**DOM Joiner**: Creates a new XMLDocument with proper declaration and DOCTYPE
|
|
268
|
+
when configured via `output()`.
|
|
269
|
+
|
|
270
|
+
**JSON Joiner**: Creates a new document wrapper object with `$document` property
|
|
271
|
+
containing the Jamilih representation.
|
|
272
|
+
|
|
273
|
+
**String Joiner**: Creates a new document string with XML declaration and
|
|
274
|
+
DOCTYPE when configured.
|
|
275
|
+
|
|
276
|
+
**Signature**: `document(callback, outputConfig?)`
|
|
277
|
+
|
|
278
|
+
**Example**:
|
|
279
|
+
```js
|
|
280
|
+
joiner.document(() => {
|
|
281
|
+
joiner.output({
|
|
282
|
+
method: 'xml',
|
|
283
|
+
version: '1.0',
|
|
284
|
+
encoding: 'utf8',
|
|
285
|
+
doctypePublic: '-//W3C//DTD XHTML 1.0//EN',
|
|
286
|
+
doctypeSystem: 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
|
|
287
|
+
});
|
|
288
|
+
joiner.element('html', {xmlns: 'http://www.w3.org/1999/xhtml'}, () => {
|
|
289
|
+
joiner.element('head', {}, () => {
|
|
290
|
+
joiner.element('title', {}, () => {
|
|
291
|
+
joiner.text('Document 1');
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
joiner.document(() => {
|
|
298
|
+
joiner.element('html', {}, () => {
|
|
299
|
+
joiner.element('body', {}, () => {
|
|
300
|
+
joiner.text('Document 2');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// When exposeDocuments is true, get() returns array of documents
|
|
306
|
+
const docs = joiner.get();
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The method preserves the current joiner state, resets it for the new document,
|
|
310
|
+
executes the callback, captures the result, then restores the previous state.
|
|
311
|
+
|
|
312
|
+
### resultDocument() method details
|
|
313
|
+
|
|
314
|
+
The `resultDocument()` method creates output documents with associated metadata,
|
|
315
|
+
paralleling XSLT's `xsl:result-document` functionality. Unlike `document()`,
|
|
316
|
+
which stores documents in `_docs`, `resultDocument()` stores them in
|
|
317
|
+
`_resultDocuments` with metadata including href URI and output format.
|
|
318
|
+
|
|
319
|
+
**DOM Joiner**: Creates XMLDocument with href and format metadata.
|
|
320
|
+
|
|
321
|
+
**JSON Joiner**: Creates document wrapper object or raw JSON with metadata.
|
|
322
|
+
|
|
323
|
+
**String Joiner**: Creates document string with XML/HTML markup and metadata.
|
|
324
|
+
|
|
325
|
+
**Signature**: `resultDocument(href, callback, outputConfig?)`
|
|
326
|
+
|
|
327
|
+
**Parameters**:
|
|
328
|
+
- `href` (string): URI or path for the result document (e.g., `'output/page1.html'`)
|
|
329
|
+
- `callback` (function): Builds the document content with `this` bound to joiner
|
|
330
|
+
- `outputConfig` (optional): Output configuration (encoding, doctype, method, etc.)
|
|
331
|
+
|
|
332
|
+
**Result document structure**:
|
|
333
|
+
```ts
|
|
334
|
+
{
|
|
335
|
+
href: string, // The URI/path provided
|
|
336
|
+
document: any, // The generated document (XMLDocument, object, or string)
|
|
337
|
+
format: string // Output format from config.method ('xml', 'html', 'text', 'xhtml', 'json')
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Example use case - Multi-page site generation**:
|
|
342
|
+
```js
|
|
343
|
+
const pages = [
|
|
344
|
+
{title: 'Home', slug: 'index', content: 'Welcome'},
|
|
345
|
+
{title: 'About', slug: 'about', content: 'About us'},
|
|
346
|
+
{title: 'Contact', slug: 'contact', content: 'Get in touch'}
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
pages.forEach((page) => {
|
|
350
|
+
joiner.resultDocument(`output/${page.slug}.html`, () => {
|
|
351
|
+
joiner.output({
|
|
352
|
+
method: 'html',
|
|
353
|
+
doctypePublic: '-//W3C//DTD XHTML 1.0 Strict//EN',
|
|
354
|
+
doctypeSystem: 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
|
|
355
|
+
});
|
|
356
|
+
joiner.element('html', {xmlns: 'http://www.w3.org/1999/xhtml'}, () => {
|
|
357
|
+
joiner.element('head', {}, () => {
|
|
358
|
+
joiner.element('title', {}, () => {
|
|
359
|
+
joiner.text(page.title);
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
joiner.element('body', {}, () => {
|
|
363
|
+
joiner.element('h1', {}, () => {
|
|
364
|
+
joiner.text(page.title);
|
|
365
|
+
});
|
|
366
|
+
joiner.element('p', {}, () => {
|
|
367
|
+
joiner.text(page.content);
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Write each result document to its href location
|
|
375
|
+
joiner._resultDocuments.forEach((result) => {
|
|
376
|
+
fs.writeFileSync(result.href, result.document);
|
|
377
|
+
console.log(`Generated: ${result.href} (${result.format})`);
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Interoperability**: `document()` and `resultDocument()` can be used together in
|
|
382
|
+
the same transformation. `document()` populates `_docs` (accessible via `get()`
|
|
383
|
+
when `exposeDocuments` is true), while `resultDocument()` populates
|
|
384
|
+
`_resultDocuments` with metadata.
|
|
385
|
+
|
|
386
|
+
### Output configuration and formats
|
|
387
|
+
|
|
388
|
+
Joiners accept an output configuration via `output(cfg)` and within `document()`/`resultDocument()`.
|
|
389
|
+
|
|
390
|
+
- `cfg.method`: one of `xml`, `html`, `text`, `xhtml`, or `json`.
|
|
391
|
+
- `xml` and `xhtml` are XML-like: an XML declaration is emitted unless `omitXmlDeclaration` is true; DOCTYPE is included when `doctypePublic` or `doctypeSystem` is provided. For the JSON joiner, DOCTYPE entries are included in `$document.childNodes` only when `method` is `xml` or `xhtml`.
|
|
392
|
+
- `html` emits HTML; the String and DOM joiners serialize accordingly. The JSON joiner does not include a DOCTYPE node in its `$document` wrapper for `html`.
|
|
393
|
+
- `text` emits raw text (no element wrappers). The JSON joiner will not add DOCTYPE; the String joiner writes plain strings.
|
|
394
|
+
- `json` indicates a JSON-centric output; for the JSON joiner, this is the natural mode and does not add XML declarations or DOCTYPE.
|
|
395
|
+
- `omitXmlDeclaration`: when false (or for `xml`/`xhtml` with default), include XML declaration with optional `version`, `encoding`, `standalone`.
|
|
396
|
+
- `doctypePublic`/`doctypeSystem`: when provided and `method` is `xml`/`xhtml`, include a DOCTYPE entry.
|
|
397
|
+
|
|
398
|
+
Notes for JSON joiner with `exposeDocuments`:
|
|
399
|
+
|
|
400
|
+
- When `exposeDocuments` is enabled in the joiner config, `get()` returns an array of `$document` wrappers. Each wrapper has a `childNodes` array where the root element is the last entry; the first entry may be a DOCTYPE object when `method` is `xml` or `xhtml`.
|
|
401
|
+
- For `html`, `text`, or `json` methods, no DOCTYPE is included in the JSON wrapper by default.
|
|
402
|
+
|
|
236
403
|
## Error handling
|
|
237
404
|
|
|
238
405
|
- Equal priority templates: either last wins (default) or error if `errorOnEqualPriority=true`.
|
package/docs/API.md
CHANGED
|
@@ -25,7 +25,7 @@ new JTLT({
|
|
|
25
25
|
- JSONPathTransformer: on JSON with JSONPath selectors; resolves
|
|
26
26
|
priority and falls back to defaults when no template matches.
|
|
27
27
|
- XPathTransformer (experimental): on XML/HTML DOM. Choose
|
|
28
|
-
`engineType: 'xpath'` on JTLT and set `xpathVersion: 1 | 2`.
|
|
28
|
+
`engineType: 'xpath'` on JTLT and set `xpathVersion: 1 | 2 | 3.1`.
|
|
29
29
|
|
|
30
30
|
## Context basics
|
|
31
31
|
|
|
@@ -33,11 +33,101 @@ new JTLT({
|
|
|
33
33
|
`variable(name, select)`, `key(name, match, use)`, `getKey(name, value)`.
|
|
34
34
|
JSONPath variables store values; XPath variables store arrays of nodes.
|
|
35
35
|
|
|
36
|
+
Cloning:
|
|
37
|
+
`copy()` (shallow clone current JSON value or DOM node) and
|
|
38
|
+
`copyOf(select?)` (deep clone; optional selector overrides target; scalar
|
|
39
|
+
selectors append the scalar). JSONPath deep cloning uses `structuredClone`
|
|
40
|
+
fallbacks; XPath uses `cloneNode(true)`.
|
|
41
|
+
|
|
42
|
+
Conditional execution:
|
|
43
|
+
- `if(select, cb)` — runs `cb` when the selector matches (non-empty
|
|
44
|
+
result set) or coerces to a truthy scalar.
|
|
45
|
+
- `choose(select, whenCb, otherwiseCb?)` — like `if()`, but also runs
|
|
46
|
+
`otherwiseCb` when the condition fails.
|
|
47
|
+
|
|
36
48
|
## Joiners
|
|
37
49
|
|
|
38
50
|
StringJoiningTransformer, DOMJoiningTransformer, JSONJoiningTransformer with
|
|
39
51
|
helpers: `string`, `text`, `element`, `attribute`, `object`, `array`,
|
|
40
|
-
`number`, `boolean`, `plainText`, `append`, `get`.
|
|
52
|
+
`number`, `boolean`, `plainText`, `append`, `get`, `document`.
|
|
53
|
+
|
|
54
|
+
### document() method
|
|
55
|
+
|
|
56
|
+
Similar to XSLT's `xsl:document`, the `document()` method allows templates to
|
|
57
|
+
generate multiple output documents. It accepts a callback that builds the
|
|
58
|
+
document content, and an optional output configuration.
|
|
59
|
+
|
|
60
|
+
**Signature**: `document(callback, outputConfig?)`
|
|
61
|
+
|
|
62
|
+
When `exposeDocuments` is enabled, each document created with `document()` is
|
|
63
|
+
added to the array returned by `get()`.
|
|
64
|
+
|
|
65
|
+
**Example**:
|
|
66
|
+
```js
|
|
67
|
+
joiner.document(() => {
|
|
68
|
+
joiner.output({method: 'xml', version: '1.0'});
|
|
69
|
+
joiner.element('doc1', {}, () => {
|
|
70
|
+
joiner.text('First document');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
joiner.document(() => {
|
|
75
|
+
joiner.element('doc2', {}, () => {
|
|
76
|
+
joiner.text('Second document');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const docs = joiner.get(); // Returns array of 2 documents
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### resultDocument() method
|
|
84
|
+
|
|
85
|
+
Similar to XSLT's `xsl:result-document`, the `resultDocument()` method allows
|
|
86
|
+
templates to generate multiple output documents **with metadata** (href URI,
|
|
87
|
+
format). Documents are stored in `joiner._resultDocuments` array.
|
|
88
|
+
|
|
89
|
+
**Signature**: `resultDocument(href, callback, outputConfig?)`
|
|
90
|
+
|
|
91
|
+
Each result document is stored with:
|
|
92
|
+
- `href`: The URI/path for the document (e.g., `'output/page1.html'`)
|
|
93
|
+
- `document`: The generated document content
|
|
94
|
+
- `format`: The output format (from `method` in config). One of `xml`, `html`, `text`, `xhtml`, or `json`.
|
|
95
|
+
|
|
96
|
+
**Example**:
|
|
97
|
+
```js
|
|
98
|
+
joiner.resultDocument('output/doc1.xml', () => {
|
|
99
|
+
joiner.output({method: 'xml', version: '1.0'});
|
|
100
|
+
joiner.element('doc1', {}, () => {
|
|
101
|
+
joiner.text('First document');
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
joiner.resultDocument('output/doc2.html', () => {
|
|
106
|
+
joiner.output({method: 'html'});
|
|
107
|
+
joiner.element('html', {}, () => {
|
|
108
|
+
joiner.text('Second document');
|
|
109
|
+
});
|
|
110
|
+
}, {method: 'html'});
|
|
111
|
+
|
|
112
|
+
// Access result documents with metadata
|
|
113
|
+
joiner._resultDocuments.forEach((result) => {
|
|
114
|
+
console.log(result.href); // e.g., 'output/doc1.xml'
|
|
115
|
+
console.log(result.format); // e.g., 'xml'
|
|
116
|
+
console.log(result.document); // The document content
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Output configuration (quick reference)
|
|
121
|
+
|
|
122
|
+
- `method`: `xml` | `html` | `text` | `xhtml` | `json`
|
|
123
|
+
- `xml`/`xhtml`: XML declaration unless `omitXmlDeclaration`; DOCTYPE included when `doctypePublic`/`doctypeSystem` present. For the JSON joiner, a DOCTYPE entry appears in `$document.childNodes` only for these methods.
|
|
124
|
+
- `html`: HTML output; JSON joiner wrapper omits DOCTYPE.
|
|
125
|
+
- `text`: raw text; no DOCTYPE.
|
|
126
|
+
- `json`: native JSON output; no XML declaration or DOCTYPE.
|
|
127
|
+
- `omitXmlDeclaration`, `version`, `encoding`, `standalone`: control XML declaration (applicable to `xml`/`xhtml`).
|
|
128
|
+
- `doctypePublic`, `doctypeSystem`: control DOCTYPE (applicable to `xml`/`xhtml`).
|
|
129
|
+
|
|
130
|
+
Note: With `exposeDocuments` enabled on a joiner, `get()` returns an array of `$document` wrappers. In the JSON joiner, the root element is the last entry of `$document.childNodes`; a DOCTYPE may precede it for `xml`/`xhtml`.
|
|
41
131
|
|
|
42
132
|
## Sorting
|
|
43
133
|
|
package/docs/TO-DO.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
## To-dos
|
|
2
|
+
|
|
3
|
+
1. Testing: Could adapt an XSLT/XQuery test suite
|
|
4
|
+
|
|
5
|
+
1. Document
|
|
6
|
+
|
|
7
|
+
1. Implement and demo equivalent to applying and calling templates, and
|
|
8
|
+
root template
|
|
9
|
+
|
|
10
|
+
2. Demo chaining of methods, including [equivalents](https://www.saxonica.com/papers/XTech2005/mhkpaper.html#S4.)
|
|
11
|
+
to XQuery's FLWOR expressions (see also Promises to-do), perhaps
|
|
12
|
+
even making aliases so that XQuery's friendlier terms can be used
|
|
13
|
+
instead of XSLT's.
|
|
14
|
+
|
|
15
|
+
2. Support processing of JSON documents with `$jtlt-stylesheet` to let
|
|
16
|
+
documents define their own targeted stylesheets and make own
|
|
17
|
+
`<?xml-stylesheet type="text/javascript" href="..."?>` for XML
|
|
18
|
+
|
|
19
|
+
3. When sufficiently documented, add as example library/tool to
|
|
20
|
+
JSONPath wiki.
|
|
21
|
+
|
|
22
|
+
4. Allow alternative to `element()`, `array()`, etc. methods by just
|
|
23
|
+
detecting those types from return values (and generic of each
|
|
24
|
+
type like `dom()` and `json()`).
|
|
25
|
+
|
|
26
|
+
5. Allow, depending on mode, containers to contain containers of other
|
|
27
|
+
types (e.g., a JS container containing DOM objects, or temporary use
|
|
28
|
+
of a string container, etc.).
|
|
29
|
+
|
|
30
|
+
1. Support XML and add [hXML](https://github.com/brettz9/hxml) methods.
|
|
31
|
+
|
|
32
|
+
2. Support `appendJSON()`/`appendDOM()` and
|
|
33
|
+
`appendType('json', ...)` (allowing type extensions).
|
|
34
|
+
|
|
35
|
+
6. Add `appendResult(function () {return result})`.
|
|
36
|
+
|
|
37
|
+
7. Add JSON update functions (equivalent to Xquery Update Facility for
|
|
38
|
+
XML ([overview](http://www.xmlplease.com/xquery-update))) and create
|
|
39
|
+
JSON serialization (as with XSLT expressed itself in declarative XML)
|
|
40
|
+
so one can submit and evaluate
|
|
41
|
+
through [HTTPQuery](https://github.com/brettz9/httpquery) (and also
|
|
42
|
+
supply to JSONEditor, etc.). Utilize updating by reference.
|
|
43
|
+
|
|
44
|
+
8. Demo narrowing to subset of JavaScript (as with `jsep`) to make
|
|
45
|
+
JTLT truly "declarative" as far as freedom from scripting
|
|
46
|
+
|
|
47
|
+
## Possible to-dos
|
|
48
|
+
|
|
49
|
+
1. Make schema-aware so that templates could target types. Most reusable
|
|
50
|
+
application may be having a type-driven view of a JSON Schema instance
|
|
51
|
+
(e.g., dates could be shown inside a calendar widget). Perhaps this
|
|
52
|
+
schema-awareness could also drive a JSON editor (as with other existing
|
|
53
|
+
projects) (even using same API as JSONEditor?) (or type-aware filtered
|
|
54
|
+
search/raw queries). This would help not only for editors which edit a
|
|
55
|
+
JSON file in full, but also for providing schema paths or other identifiers
|
|
56
|
+
so that a transformed/queried subset of a file (or joining of multiple
|
|
57
|
+
files) could point the way for edited contents to be saved back to the
|
|
58
|
+
correct JSON file and position in the JSON file.
|
|
59
|
+
|
|
60
|
+
2. ~~Add a [non-eval PR for JSONPath](https://github.com/s3u/JSONPath/pull/4).~~
|
|
61
|
+
The OR condition (outside of filters) is another important feature as
|
|
62
|
+
would be schema-aware path results.
|
|
63
|
+
|
|
64
|
+
3. Allow hybrid JSON/[Jamilih](https://github.com/brettz9/jamilih) or
|
|
65
|
+
JSON/(X)HTML/XML so that one can add
|
|
66
|
+
XPath or query into HTML in a relevant manner
|
|
67
|
+
|
|
68
|
+
4. Support pull parsing/streaming? Pass `done()` function to templates to
|
|
69
|
+
signal completion?
|
|
70
|
+
|
|
71
|
+
5. Add [XQuery Functions](https://code.google.com/p/jsxqueryparser/source/browse/trunk/jsxqueryparser/XQueryParser.js#1768)
|
|
72
|
+
(also supporting DOM and JSON where possible) as plug-in (also any
|
|
73
|
+
missing from XSLT/XQuery 3.0). Also add, if not present among these
|
|
74
|
+
functions (or in XQuery), add equivalents to XSLT's
|
|
75
|
+
`document()` and `unparsed-text()` for allowing non-JSON file
|
|
76
|
+
retrieval (as well as variables/parameters) and also methods for
|
|
77
|
+
iterating or retrieving IndexedDB, `localStorage`, and cookies
|
|
78
|
+
(names, keys and values).
|
|
79
|
+
|
|
80
|
+
6. Add `outputType` which uses a DOM joiner but allows specialized
|
|
81
|
+
serialized output (e.g., pretty-printed HTML) so the users
|
|
82
|
+
don't need to build it themselves (likewise with stringified
|
|
83
|
+
JSON output).
|
|
84
|
+
|
|
85
|
+
7. See code for other possible to-dos
|
|
86
|
+
|
|
87
|
+
8. Consider implementing the following elements from <https://www.w3.org/TR/xslt-30/>
|
|
88
|
+
which are not yet implemented.
|
|
89
|
+
|
|
90
|
+
xsl:accept
|
|
91
|
+
xsl:accumulator
|
|
92
|
+
xsl:accumulator-rule
|
|
93
|
+
~~"xsl:analyze-string",~~
|
|
94
|
+
"xsl:apply-imports",
|
|
95
|
+
~~"xsl:apply-templates",~~
|
|
96
|
+
xsl:assert
|
|
97
|
+
~~"xsl:attribute",~~
|
|
98
|
+
"xsl:attribute-set",
|
|
99
|
+
xsl:break
|
|
100
|
+
~~"xsl:call-template",~~
|
|
101
|
+
xsl:catch
|
|
102
|
+
"xsl:character-map",
|
|
103
|
+
~~"xsl:choose",~~
|
|
104
|
+
~~"xsl:comment",~~
|
|
105
|
+
xsl:context-item
|
|
106
|
+
~~"xsl:copy",~~
|
|
107
|
+
~~"xsl:copy-of",~~
|
|
108
|
+
"xsl:decimal-format",
|
|
109
|
+
~~"xsl:document",~~
|
|
110
|
+
~~"xsl:element",~~
|
|
111
|
+
xsl:evaluate
|
|
112
|
+
xsl:expose
|
|
113
|
+
"xsl:fallback",
|
|
114
|
+
~~"xsl:for-each",~~
|
|
115
|
+
"xsl:for-each-group",
|
|
116
|
+
xsl:fork
|
|
117
|
+
"xsl:function",
|
|
118
|
+
xsl:global-context-item
|
|
119
|
+
~~"xsl:if",~~
|
|
120
|
+
"xsl:import",
|
|
121
|
+
"xsl:import-schema",
|
|
122
|
+
"xsl:include",
|
|
123
|
+
xsl:iterate
|
|
124
|
+
~~"xsl:key",~~
|
|
125
|
+
xsl:map
|
|
126
|
+
xsl:map-entry
|
|
127
|
+
~~"xsl:matching-substring",~~
|
|
128
|
+
xsl:merge
|
|
129
|
+
xsl:merge-action
|
|
130
|
+
xsl:merge-key
|
|
131
|
+
xsl:merge-source
|
|
132
|
+
~~"xsl:message",~~
|
|
133
|
+
xsl:mode
|
|
134
|
+
"xsl:namespace",
|
|
135
|
+
"xsl:namespace-alias",
|
|
136
|
+
xsl:next-iteration
|
|
137
|
+
"xsl:next-match",
|
|
138
|
+
~~"xsl:non-matching-substring",~~
|
|
139
|
+
~~"xsl:number",~~
|
|
140
|
+
xsl:on-completion
|
|
141
|
+
xsl:on-empty
|
|
142
|
+
xsl:on-non-empty
|
|
143
|
+
~~"xsl:otherwise",~~
|
|
144
|
+
~~"xsl:output",~~
|
|
145
|
+
"xsl:output-character",
|
|
146
|
+
xsl:override
|
|
147
|
+
xsl:package
|
|
148
|
+
"xsl:param",
|
|
149
|
+
"xsl:perform-sort",
|
|
150
|
+
"xsl:preserve-space",
|
|
151
|
+
~~"xsl:processing-instruction",~~
|
|
152
|
+
~~"xsl:result-document",~~
|
|
153
|
+
"xsl:sequence",
|
|
154
|
+
~~"xsl:sort",~~
|
|
155
|
+
xsl:source-document
|
|
156
|
+
"xsl:strip-space",
|
|
157
|
+
~~"xsl:stylesheet",~~
|
|
158
|
+
~~"xsl:template",~~
|
|
159
|
+
~~"xsl:text",~~
|
|
160
|
+
~~"xsl:transform",~~
|
|
161
|
+
xsl:try
|
|
162
|
+
xsl:use-package
|
|
163
|
+
~~"xsl:value-of",~~
|
|
164
|
+
~~"xsl:variable",~~
|
|
165
|
+
~~"xsl:when",~~
|
|
166
|
+
xsl:where-populated
|
|
167
|
+
~~"xsl:with-param"~~
|
|
168
|
+
|