jtlt 0.7.3 → 0.9.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/.c8rc.json +26 -0
- package/CHANGES.md +10 -0
- package/dist/AbstractJoiningTransformer.d.ts +55 -0
- package/dist/AbstractJoiningTransformer.d.ts.map +1 -1
- package/dist/DOMJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONJoiningTransformer.d.ts +2 -2
- package/dist/JSONJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformerContext.d.ts +34 -7
- package/dist/JSONPathTransformerContext.d.ts.map +1 -1
- package/dist/StringJoiningTransformer.d.ts +2 -2
- package/dist/StringJoiningTransformer.d.ts.map +1 -1
- package/dist/XPathTransformer.d.ts.map +1 -1
- package/dist/XPathTransformerContext.d.ts +28 -3
- package/dist/XPathTransformerContext.d.ts.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/docs/API.expanded.md +33 -0
- package/docs/API.md +22 -0
- package/docs/TO-DO.md +2 -2
- package/eslint.config.js +2 -1
- package/package.json +1 -1
- package/src/AbstractJoiningTransformer.js +95 -0
- package/src/DOMJoiningTransformer.js +16 -12
- package/src/JSONJoiningTransformer.js +15 -10
- package/src/JSONPathTransformer.js +7 -0
- package/src/JSONPathTransformerContext.js +134 -6
- package/src/StringJoiningTransformer.js +33 -22
- package/src/XPathTransformer.js +4 -0
- package/src/XPathTransformerContext.js +148 -6
- package/src/index.js +1 -0
|
@@ -74,6 +74,20 @@ class AbstractJoiningTransformer {
|
|
|
74
74
|
this._characterMap = {};
|
|
75
75
|
/** @type {Record<string, Record<string, string>>} */
|
|
76
76
|
this._attributeSet = {};
|
|
77
|
+
|
|
78
|
+
/** @type {Map<string, string>} */
|
|
79
|
+
this._namespaceAliases = new Map();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @type {{
|
|
83
|
+
* onMultipleMatch?: "use-last"|"fail",
|
|
84
|
+
* warningOnMultipleMatch?: boolean,
|
|
85
|
+
* onNoMatch?: "shallow-copy"|"deep-copy"|"fail"|"apply-templates"|
|
|
86
|
+
* "shallow-skip"|"deep-skip"|"text-only-copy",
|
|
87
|
+
* warningOnNoMatch?: boolean
|
|
88
|
+
* }|undefined}
|
|
89
|
+
*/
|
|
90
|
+
this._modeConfig = undefined;
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
/**
|
|
@@ -98,6 +112,22 @@ class AbstractJoiningTransformer {
|
|
|
98
112
|
return this;
|
|
99
113
|
}
|
|
100
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Configure mode behavior (similar to xsl:mode).
|
|
117
|
+
* @param {{
|
|
118
|
+
* onMultipleMatch?: "use-last"|"fail",
|
|
119
|
+
* warningOnMultipleMatch?: boolean,
|
|
120
|
+
* onNoMatch?: "shallow-copy"|"deep-copy"|"fail"|"apply-templates"|
|
|
121
|
+
* "shallow-skip"|"deep-skip"|"text-only-copy",
|
|
122
|
+
* warningOnNoMatch?: boolean
|
|
123
|
+
* }} cfg - Mode configuration
|
|
124
|
+
* @returns {this}
|
|
125
|
+
*/
|
|
126
|
+
mode (cfg) {
|
|
127
|
+
this._modeConfig = cfg;
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
|
|
101
131
|
/**
|
|
102
132
|
* @param {string} name
|
|
103
133
|
* @param {OutputCharacters} outputCharacters
|
|
@@ -116,6 +146,71 @@ class AbstractJoiningTransformer {
|
|
|
116
146
|
this._attributeSet[name] = attributes;
|
|
117
147
|
}
|
|
118
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @param {string} stylesheetPrefix
|
|
151
|
+
* @param {string} resultPrefix
|
|
152
|
+
* @returns {void}
|
|
153
|
+
*/
|
|
154
|
+
namespaceAlias (stylesheetPrefix, resultPrefix) {
|
|
155
|
+
// Convert empty string to '#default' to match _getNamespaceAlias behavior
|
|
156
|
+
const key = stylesheetPrefix || '#default';
|
|
157
|
+
this._namespaceAliases.set(key, resultPrefix);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @param {string} prefix
|
|
162
|
+
* @returns {string}
|
|
163
|
+
*/
|
|
164
|
+
_getNamespaceAlias (prefix) {
|
|
165
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
166
|
+
// eslint-disable-next-line unicorn/prefer-default-parameters -- Empty string fallback
|
|
167
|
+
const lookupPrefix = prefix || '#default';
|
|
168
|
+
return this._namespaceAliases.get(lookupPrefix) ?? (
|
|
169
|
+
lookupPrefix
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {string} attName
|
|
175
|
+
* @returns {string}
|
|
176
|
+
*/
|
|
177
|
+
_replaceNamespaceAliasInNamespaceDeclaration (attName) {
|
|
178
|
+
if (!attName.startsWith('xmlns')) {
|
|
179
|
+
return attName;
|
|
180
|
+
}
|
|
181
|
+
const namespacedAtt = attName.startsWith('xmlns:');
|
|
182
|
+
|
|
183
|
+
const prefix = namespacedAtt ? attName.slice(0, 6) : '#default';
|
|
184
|
+
const alias = this._getNamespaceAlias(prefix);
|
|
185
|
+
|
|
186
|
+
return alias === '#default'
|
|
187
|
+
? 'xmlns'
|
|
188
|
+
: 'xmlns:' + alias;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @param {string} elemName
|
|
193
|
+
* @returns {string}
|
|
194
|
+
*/
|
|
195
|
+
_replaceNamespaceAliasInElement (elemName) {
|
|
196
|
+
const colonIdx = elemName.indexOf(':');
|
|
197
|
+
|
|
198
|
+
const prefix = colonIdx === -1 ? '#default' : elemName.slice(0, colonIdx);
|
|
199
|
+
const alias = this._getNamespaceAlias(prefix);
|
|
200
|
+
|
|
201
|
+
if (colonIdx === -1) {
|
|
202
|
+
if (alias === '#default') {
|
|
203
|
+
return elemName;
|
|
204
|
+
}
|
|
205
|
+
return alias + ':' + elemName;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return (alias === '#default'
|
|
209
|
+
? ''
|
|
210
|
+
: alias + ':'
|
|
211
|
+
) + elemName.slice(colonIdx + 1);
|
|
212
|
+
}
|
|
213
|
+
|
|
119
214
|
/**
|
|
120
215
|
* @param {string} str
|
|
121
216
|
* @returns {string}
|
|
@@ -281,7 +281,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
281
281
|
* @returns {DOMJoiningTransformer}
|
|
282
282
|
*/
|
|
283
283
|
/**
|
|
284
|
-
* @param {Element|string}
|
|
284
|
+
* @param {Element|string} elem - Element name
|
|
285
285
|
* @param {Record<string, string>|(Node|string)[
|
|
286
286
|
* ]|((this: DOMJoiningTransformer) => void)} [atts] - Attributes,
|
|
287
287
|
* childNodes, or callback
|
|
@@ -291,7 +291,7 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
291
291
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
292
292
|
* @returns {DOMJoiningTransformer}
|
|
293
293
|
*/
|
|
294
|
-
element (
|
|
294
|
+
element (elem, atts, childNodes, cb, useAttributeSets) {
|
|
295
295
|
// Handle argument overloading like other transformers
|
|
296
296
|
if (Array.isArray(atts)) {
|
|
297
297
|
cb = /** @type {(this: DOMJoiningTransformer) => void} */ (
|
|
@@ -309,12 +309,14 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
309
309
|
childNodes = [];
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
const elementName = this._replaceNamespaceAliasInElement(
|
|
313
|
+
typeof elem === 'string'
|
|
314
|
+
? elem
|
|
315
|
+
: elem.localName
|
|
316
|
+
);
|
|
314
317
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
: elName.localName;
|
|
318
|
+
if (!this.root && this._outputConfig) {
|
|
319
|
+
this.root = elementName;
|
|
318
320
|
|
|
319
321
|
const {
|
|
320
322
|
omitXmlDeclaration, doctypePublic, doctypeSystem, method
|
|
@@ -420,10 +422,10 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
420
422
|
}
|
|
421
423
|
|
|
422
424
|
// Non-root elements
|
|
423
|
-
const el =
|
|
424
|
-
?
|
|
425
|
+
const el = elem && typeof elem === 'object'
|
|
426
|
+
? elem
|
|
425
427
|
: /** @type {Element} */ (
|
|
426
|
-
this._cfg.document.createElement(
|
|
428
|
+
this._cfg.document.createElement(elem)
|
|
427
429
|
);
|
|
428
430
|
|
|
429
431
|
// Apply attribute sets if specified
|
|
@@ -477,10 +479,11 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
477
479
|
* @returns {DOMJoiningTransformer}
|
|
478
480
|
*/
|
|
479
481
|
namespace (prefix, namespaceURI) {
|
|
482
|
+
const alias = this._getNamespaceAlias(prefix);
|
|
480
483
|
/** @type {Element} */
|
|
481
484
|
(this._dom).setAttributeNS(
|
|
482
485
|
'http://www.w3.org/2000/xmlns/',
|
|
483
|
-
'xmlns:' +
|
|
486
|
+
alias === '#default' ? 'xmlns' : 'xmlns:' + alias,
|
|
484
487
|
this._replaceCharacterMaps(namespaceURI)
|
|
485
488
|
);
|
|
486
489
|
return this;
|
|
@@ -497,7 +500,8 @@ class DOMJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
497
500
|
throw new Error('You may only set an attribute on an element');
|
|
498
501
|
}
|
|
499
502
|
(/** @type {Element} */ (this._dom)).setAttribute(
|
|
500
|
-
|
|
503
|
+
this._replaceNamespaceAliasInNamespaceDeclaration(name),
|
|
504
|
+
this._replaceCharacterMaps(val)
|
|
501
505
|
);
|
|
502
506
|
return this;
|
|
503
507
|
}
|
|
@@ -372,7 +372,7 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
372
372
|
* Result form: ['tag', {attr: 'val'}, child1, child2, ...]
|
|
373
373
|
* Helpers: dataset -> data-*; $a -> ordered attributes.
|
|
374
374
|
* Supported signatures mirror StringJoiningTransformer.element.
|
|
375
|
-
* @param {string|Element}
|
|
375
|
+
* @param {string|Element} elem Element name or Element-like.
|
|
376
376
|
* @param {ElementAttributes|any[]|SimpleCallback} [atts]
|
|
377
377
|
* Attrs, children, or cb.
|
|
378
378
|
* @param {any[]|SimpleCallback} [childNodes] Children or cb.
|
|
@@ -380,11 +380,16 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
380
380
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
381
381
|
* @returns {JSONJoiningTransformer}
|
|
382
382
|
*/
|
|
383
|
-
element (
|
|
383
|
+
element (elem, atts, childNodes, cb, useAttributeSets) {
|
|
384
384
|
this._requireSameChildren('json', 'element');
|
|
385
|
+
const elementName = this._replaceNamespaceAliasInElement(
|
|
386
|
+
typeof elem === 'string'
|
|
387
|
+
? elem
|
|
388
|
+
: elem.localName
|
|
389
|
+
);
|
|
385
390
|
const isRoot = !this.root;
|
|
386
391
|
if (isRoot) {
|
|
387
|
-
this.root =
|
|
392
|
+
this.root = elementName;
|
|
388
393
|
}
|
|
389
394
|
// Normalize arguments similarly to StringJoiningTransformer.element
|
|
390
395
|
if (Array.isArray(atts)) {
|
|
@@ -401,14 +406,11 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
401
406
|
childNodes = [];
|
|
402
407
|
}
|
|
403
408
|
|
|
404
|
-
const elementName = typeof elName === 'string'
|
|
405
|
-
? elName
|
|
406
|
-
: elName.localName;
|
|
407
409
|
// Element-like object (DOM Element) -> extract attributes
|
|
408
|
-
if (typeof
|
|
410
|
+
if (typeof elem === 'object' && elem && 'attributes' in elem) {
|
|
409
411
|
/** @type {Record<string, string>} */
|
|
410
412
|
const objAtts = {};
|
|
411
|
-
[...
|
|
413
|
+
[...elem.attributes].forEach((att) => {
|
|
412
414
|
objAtts[att.name] = att.value;
|
|
413
415
|
});
|
|
414
416
|
atts = Object.assign(objAtts, atts);
|
|
@@ -556,8 +558,10 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
556
558
|
attsObj.xmlns = {};
|
|
557
559
|
}
|
|
558
560
|
|
|
561
|
+
const alias = this._getNamespaceAlias(prefix);
|
|
559
562
|
/** @type {Record<string, string>} */
|
|
560
|
-
(attsObj.xmlns)[
|
|
563
|
+
(attsObj.xmlns)[alias === '#default' ? '' : alias] =
|
|
564
|
+
this._replaceCharacterMaps(namespaceURI);
|
|
561
565
|
|
|
562
566
|
return this;
|
|
563
567
|
}
|
|
@@ -600,7 +604,8 @@ class JSONJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
600
604
|
});
|
|
601
605
|
return this;
|
|
602
606
|
}
|
|
603
|
-
attsObj[name] =
|
|
607
|
+
attsObj[this._replaceNamespaceAliasInNamespaceDeclaration(name)] =
|
|
608
|
+
this._replaceCharacterMaps(/** @type {string} */ (val));
|
|
604
609
|
return this;
|
|
605
610
|
}
|
|
606
611
|
|
|
@@ -19,6 +19,9 @@ class JSONPathTransformer {
|
|
|
19
19
|
this._config = config;
|
|
20
20
|
/** @type {import('./index.js').JSONPathTemplateObject<T>[]} */
|
|
21
21
|
this.rootTemplates = [];
|
|
22
|
+
if (!config.templates) {
|
|
23
|
+
throw new TypeError('config.templates is required');
|
|
24
|
+
}
|
|
22
25
|
this.templates = config.templates.map(function (template) {
|
|
23
26
|
if (Array.isArray(template)) {
|
|
24
27
|
// Todo: We could allow a third argument (at beginning or
|
|
@@ -27,6 +30,10 @@ class JSONPathTransformer {
|
|
|
27
30
|
{path: template[0], template: template[1]}
|
|
28
31
|
);
|
|
29
32
|
}
|
|
33
|
+
// Normalize 'match' to 'path' for XSLT compatibility
|
|
34
|
+
if (template.match && !template.path) {
|
|
35
|
+
return {...template, path: template.match};
|
|
36
|
+
}
|
|
30
37
|
return template;
|
|
31
38
|
});
|
|
32
39
|
this.templates.forEach((template, i, templates) => {
|
|
@@ -62,7 +62,7 @@ import JSONPathTransformer from './JSONPathTransformer.js';
|
|
|
62
62
|
* JSONPath
|
|
63
63
|
* @property {(path: string) => number} [specificityPriorityResolver]
|
|
64
64
|
* Priority resolver function
|
|
65
|
-
* @property {import('./index.js').JSONPathTemplateObject<T>[]} templates
|
|
65
|
+
* @property {import('./index.js').JSONPathTemplateObject<T>[]} [templates]
|
|
66
66
|
*/
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -408,6 +408,48 @@ class JSONPathTransformerContext {
|
|
|
408
408
|
|
|
409
409
|
let templateObj;
|
|
410
410
|
if (!pathMatchedTemplates.length) {
|
|
411
|
+
// Check mode config for no-match behavior
|
|
412
|
+
const joiner = that._getJoiningTransformer();
|
|
413
|
+
const modeConfig = joiner._modeConfig;
|
|
414
|
+
if (modeConfig) {
|
|
415
|
+
const onNoMatch = modeConfig.onNoMatch ?? 'text-only-copy';
|
|
416
|
+
if (modeConfig.warningOnNoMatch) {
|
|
417
|
+
// eslint-disable-next-line no-console -- Warning as specified
|
|
418
|
+
console.warn(
|
|
419
|
+
'Warning: No template matches. ' +
|
|
420
|
+
'Mode is configured with warningOnNoMatch=true.'
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
if (onNoMatch === 'fail') {
|
|
424
|
+
throw new Error(
|
|
425
|
+
'No template matches. Mode is configured with onNoMatch="fail".'
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
if (onNoMatch === 'deep-skip') {
|
|
429
|
+
// Skip this node and its descendants entirely
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (onNoMatch === 'shallow-copy') {
|
|
433
|
+
// Output the value as-is without processing children
|
|
434
|
+
joiner.append(value);
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
if (onNoMatch === 'deep-copy') {
|
|
438
|
+
// Output the value and all descendants as-is
|
|
439
|
+
joiner.append(JSON.stringify(value));
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (onNoMatch === 'text-only-copy') {
|
|
443
|
+
// Output only text content (primitives)
|
|
444
|
+
if (typeof value === 'string' || typeof value === 'number' ||
|
|
445
|
+
typeof value === 'boolean') {
|
|
446
|
+
joiner.append(String(value));
|
|
447
|
+
}
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
// 'apply-templates', 'shallow-skip', or other:
|
|
451
|
+
// use default template rules
|
|
452
|
+
}
|
|
411
453
|
const dtr = JSONPathTransformer.DefaultTemplateRules;
|
|
412
454
|
if (propertyNamesMode) {
|
|
413
455
|
templateObj = dtr.transformPropertyNames;
|
|
@@ -446,6 +488,49 @@ class JSONPathTransformerContext {
|
|
|
446
488
|
return (aPriority > bPriority) ? -1 : 1;
|
|
447
489
|
});
|
|
448
490
|
|
|
491
|
+
// Check for multiple matches with same priority when mode is configured
|
|
492
|
+
const joiner = that._getJoiningTransformer();
|
|
493
|
+
const modeConfig = joiner._modeConfig;
|
|
494
|
+
if (modeConfig && pathMatchedTemplates.length > 1) {
|
|
495
|
+
// Check if top two templates have equal priority
|
|
496
|
+
const topPriority =
|
|
497
|
+
typeof pathMatchedTemplates[0].priority === 'number'
|
|
498
|
+
? pathMatchedTemplates[0].priority
|
|
499
|
+
: (that._config.specificityPriorityResolver &&
|
|
500
|
+
pathMatchedTemplates[0].path
|
|
501
|
+
? that._config.specificityPriorityResolver(
|
|
502
|
+
pathMatchedTemplates[0].path
|
|
503
|
+
)
|
|
504
|
+
/* c8 ignore next 2 -- defensive, templates without paths
|
|
505
|
+
are already filtered at line 388 */
|
|
506
|
+
: 0);
|
|
507
|
+
const secondPriority =
|
|
508
|
+
typeof pathMatchedTemplates[1].priority === 'number'
|
|
509
|
+
? pathMatchedTemplates[1].priority
|
|
510
|
+
: (that._config.specificityPriorityResolver &&
|
|
511
|
+
pathMatchedTemplates[1].path
|
|
512
|
+
? that._config.specificityPriorityResolver(
|
|
513
|
+
pathMatchedTemplates[1].path
|
|
514
|
+
)
|
|
515
|
+
/* c8 ignore next 2 -- defensive, templates without paths
|
|
516
|
+
are already filtered at line 388 */
|
|
517
|
+
: 0);
|
|
518
|
+
if (topPriority === secondPriority) {
|
|
519
|
+
if (modeConfig.onMultipleMatch === 'fail') {
|
|
520
|
+
throw new Error(
|
|
521
|
+
'Multiple templates match with equal priority. ' +
|
|
522
|
+
'Mode is configured with onMultipleMatch="fail".'
|
|
523
|
+
);
|
|
524
|
+
} else if (modeConfig.warningOnMultipleMatch !== false) {
|
|
525
|
+
// eslint-disable-next-line no-console -- Warning as specified
|
|
526
|
+
console.warn(
|
|
527
|
+
'Warning: Multiple templates match with equal priority. ' +
|
|
528
|
+
'Mode is configured with warningOnMultipleMatch=true.'
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
449
534
|
templateObj =
|
|
450
535
|
/** @type {import('./index.js').JSONPathTemplateObject<T>} */ (
|
|
451
536
|
pathMatchedTemplates.shift()
|
|
@@ -471,7 +556,18 @@ class JSONPathTransformerContext {
|
|
|
471
556
|
that._params = prevTemplateParams;
|
|
472
557
|
if (typeof ret !== 'undefined') {
|
|
473
558
|
// After the undefined check, ret is ResultType<T>
|
|
474
|
-
that._getJoiningTransformer()
|
|
559
|
+
const joiner = that._getJoiningTransformer();
|
|
560
|
+
// Close any open tag before appending template return value
|
|
561
|
+
/* c8 ignore start -- _openTagState only on StringJoiningTransformer,
|
|
562
|
+
defensive check for string output mode */
|
|
563
|
+
// @ts-expect-error -- _openTagState only on StringJoiningTransformer
|
|
564
|
+
if (joiner._openTagState) {
|
|
565
|
+
joiner.append('>');
|
|
566
|
+
// @ts-expect-error -- _openTagState only on StringJoiningTransformer
|
|
567
|
+
joiner._openTagState = false;
|
|
568
|
+
}
|
|
569
|
+
/* c8 ignore stop */
|
|
570
|
+
joiner.append(
|
|
475
571
|
/** @type {string|Node|*} */ (ret)
|
|
476
572
|
);
|
|
477
573
|
}
|
|
@@ -1757,6 +1853,22 @@ class JSONPathTransformerContext {
|
|
|
1757
1853
|
return this;
|
|
1758
1854
|
}
|
|
1759
1855
|
|
|
1856
|
+
/**
|
|
1857
|
+
* Configure mode behavior (similar to xsl:mode).
|
|
1858
|
+
* @param {{
|
|
1859
|
+
* onMultipleMatch?: "use-last"|"fail",
|
|
1860
|
+
* warningOnMultipleMatch?: boolean,
|
|
1861
|
+
* onNoMatch?: "shallow-copy"|"deep-copy"|"fail"|"apply-templates"|
|
|
1862
|
+
* "shallow-skip"|"deep-skip"|"text-only-copy",
|
|
1863
|
+
* warningOnNoMatch?: boolean
|
|
1864
|
+
* }} cfg - Mode configuration
|
|
1865
|
+
* @returns {this}
|
|
1866
|
+
*/
|
|
1867
|
+
mode (cfg) {
|
|
1868
|
+
this._getJoiningTransformer().mode(cfg);
|
|
1869
|
+
return this;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1760
1872
|
/**
|
|
1761
1873
|
* @param {string} name
|
|
1762
1874
|
* @param {import('./AbstractJoiningTransformer.js').
|
|
@@ -1778,14 +1890,30 @@ class JSONPathTransformerContext {
|
|
|
1778
1890
|
return this;
|
|
1779
1891
|
}
|
|
1780
1892
|
|
|
1893
|
+
/**
|
|
1894
|
+
* @param {string} stylesheetPrefix
|
|
1895
|
+
* @param {string} resultPrefix
|
|
1896
|
+
* @returns {this}
|
|
1897
|
+
*/
|
|
1898
|
+
namespaceAlias (stylesheetPrefix, resultPrefix) {
|
|
1899
|
+
this._getJoiningTransformer().namespaceAlias(
|
|
1900
|
+
stylesheetPrefix, resultPrefix
|
|
1901
|
+
);
|
|
1902
|
+
return this;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1781
1905
|
/**
|
|
1782
1906
|
* Create an element. Mirrors the joining transformer API so templates can
|
|
1783
1907
|
* call `this.element()`.
|
|
1784
1908
|
* @param {string} name - Element name
|
|
1785
|
-
* @param {Record<string, string
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1788
|
-
*
|
|
1909
|
+
* @param {Record<string, string>|any[]|
|
|
1910
|
+
* ((this: JSONPathTransformerContext<T>) => void)} [atts] -
|
|
1911
|
+
* Attributes object or children or callback
|
|
1912
|
+
* @param {any[]|
|
|
1913
|
+
* ((this: JSONPathTransformerContext<T>) => void)} [children] -
|
|
1914
|
+
* Child nodes or callback
|
|
1915
|
+
* @param {(this: JSONPathTransformerContext<T>) => void} [cb] -
|
|
1916
|
+
* Callback function
|
|
1789
1917
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
1790
1918
|
* @returns {this}
|
|
1791
1919
|
*/
|
|
@@ -471,14 +471,14 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
471
471
|
}
|
|
472
472
|
|
|
473
473
|
/**
|
|
474
|
-
* @param {string|Element}
|
|
474
|
+
* @param {string|Element} elem - Element name or element object
|
|
475
475
|
* @param {ElementAttributes} [atts] - Element attributes
|
|
476
476
|
* @param {any[]} [childNodes] - Child nodes
|
|
477
477
|
* @param {(this: StringJoiningTransformer) => void} [cb] - Callback function
|
|
478
478
|
* @param {string[]} [useAttributeSets] - Attribute set names to apply
|
|
479
479
|
* @returns {StringJoiningTransformer}
|
|
480
480
|
*/
|
|
481
|
-
element (
|
|
481
|
+
element (elem, atts, childNodes, cb, useAttributeSets) {
|
|
482
482
|
// If a parent element's start tag is still open, close it before
|
|
483
483
|
// starting a new element to ensure valid nesting.
|
|
484
484
|
if (this._openTagState) {
|
|
@@ -486,6 +486,24 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
486
486
|
this._openTagState = false;
|
|
487
487
|
}
|
|
488
488
|
|
|
489
|
+
/** @type {string} */
|
|
490
|
+
let elName;
|
|
491
|
+
if (typeof elem === 'object') {
|
|
492
|
+
/** @type {Record<string, string>} */
|
|
493
|
+
const objAtts = {};
|
|
494
|
+
/** @type {any} */
|
|
495
|
+
const elObj = elem;
|
|
496
|
+
[...elObj.attributes].forEach(function (att, i) {
|
|
497
|
+
objAtts[att.name] = att.value;
|
|
498
|
+
});
|
|
499
|
+
atts = Object.assign(objAtts, atts);
|
|
500
|
+
elName = elObj.nodeName;
|
|
501
|
+
} else {
|
|
502
|
+
elName = elem;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
elName = this._replaceNamespaceAliasInElement(elName);
|
|
506
|
+
|
|
489
507
|
if (!this.root) {
|
|
490
508
|
this.root = elName;
|
|
491
509
|
|
|
@@ -565,18 +583,6 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
565
583
|
return this;
|
|
566
584
|
}
|
|
567
585
|
|
|
568
|
-
if (typeof elName === 'object') {
|
|
569
|
-
/** @type {Record<string, string>} */
|
|
570
|
-
const objAtts = {};
|
|
571
|
-
/** @type {any} */
|
|
572
|
-
const elObj = elName;
|
|
573
|
-
[...elObj.attributes].forEach(function (att, i) {
|
|
574
|
-
objAtts[att.name] = att.value;
|
|
575
|
-
});
|
|
576
|
-
atts = Object.assign(objAtts, atts);
|
|
577
|
-
elName = elObj.nodeName;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
586
|
// Apply attribute sets if specified
|
|
581
587
|
if (useAttributeSets && useAttributeSets.length) {
|
|
582
588
|
const mergedAtts = {};
|
|
@@ -627,7 +633,10 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
627
633
|
* @returns {StringJoiningTransformer}
|
|
628
634
|
*/
|
|
629
635
|
namespace (prefix, namespaceURI) {
|
|
630
|
-
this.
|
|
636
|
+
const alias = this._getNamespaceAlias(prefix);
|
|
637
|
+
this.append(' ' + (
|
|
638
|
+
alias === '#default' ? 'xmlns' : 'xmlns:' + alias
|
|
639
|
+
) + '="' +
|
|
631
640
|
this._replaceCharacterMaps(namespaceURI) + '"');
|
|
632
641
|
return this;
|
|
633
642
|
}
|
|
@@ -644,8 +653,7 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
644
653
|
// Adds an attribute to the most recently opened start tag. Supports
|
|
645
654
|
// special objects for dataset and ordered attributes ($a). Escapes '&'
|
|
646
655
|
// and '"' unless cfg.preEscapedAttributes or avoidAttEscape are set.
|
|
647
|
-
|
|
648
|
-
const that = this;
|
|
656
|
+
|
|
649
657
|
if (!this._openTagState) {
|
|
650
658
|
throw new Error(
|
|
651
659
|
'An attribute cannot be added after an opening tag has been closed ' +
|
|
@@ -659,8 +667,8 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
659
667
|
const valObj = /** @type {any} */ (val);
|
|
660
668
|
switch (name) {
|
|
661
669
|
case 'dataset': {
|
|
662
|
-
Object.keys(valObj).forEach(
|
|
663
|
-
|
|
670
|
+
Object.keys(valObj).forEach((att) => {
|
|
671
|
+
this.attribute(
|
|
664
672
|
'data-' + att.replaceAll(
|
|
665
673
|
camelCase, _makeDatasetAttribute
|
|
666
674
|
),
|
|
@@ -673,8 +681,8 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
673
681
|
case '$a': { // Ordered attributes
|
|
674
682
|
/** @type {unknown[][]} */
|
|
675
683
|
const valArr = /** @type {any} */ (val);
|
|
676
|
-
valArr.forEach(
|
|
677
|
-
|
|
684
|
+
valArr.forEach((attArr) => {
|
|
685
|
+
this.attribute(
|
|
678
686
|
String(attArr[0]),
|
|
679
687
|
/** @type {string|Record<string, unknown>} */ (attArr[1]),
|
|
680
688
|
false
|
|
@@ -695,7 +703,10 @@ class StringJoiningTransformer extends AbstractJoiningTransformer {
|
|
|
695
703
|
val = (this._cfg.preEscapedAttributes || avoidAttEscape)
|
|
696
704
|
? valStr
|
|
697
705
|
: valStr.replaceAll('&', '&').replaceAll('"', '"');
|
|
698
|
-
this.append(
|
|
706
|
+
this.append(
|
|
707
|
+
' ' + this._replaceNamespaceAliasInNamespaceDeclaration(name) + '="' +
|
|
708
|
+
this._replaceCharacterMaps(val) + '"'
|
|
709
|
+
);
|
|
699
710
|
return this;
|
|
700
711
|
}
|
|
701
712
|
|
package/src/XPathTransformer.js
CHANGED
|
@@ -34,6 +34,10 @@ class XPathTransformer {
|
|
|
34
34
|
if (Array.isArray(template)) {
|
|
35
35
|
return {path: template[0], template: template[1]};
|
|
36
36
|
}
|
|
37
|
+
// Normalize 'match' to 'path' for XSLT compatibility
|
|
38
|
+
if (template.match && !template.path) {
|
|
39
|
+
return {...template, path: template.match};
|
|
40
|
+
}
|
|
37
41
|
return template;
|
|
38
42
|
});
|
|
39
43
|
this.templates.forEach((template) => {
|