xslt-processor 4.0.0 → 4.1.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/README.md +48 -0
- package/index.d.mts +44 -0
- package/index.d.ts +44 -0
- package/index.js +105 -2
- package/index.js.map +1 -1
- package/index.mjs +105 -2
- package/index.mjs.map +1 -1
- package/package.json +1 -1
- package/umd/xslt-processor.global.js +3 -3
- package/umd/xslt-processor.global.js.map +1 -1
package/README.md
CHANGED
|
@@ -219,6 +219,54 @@ HTML per se is not strict XML. Because of that, starting on version 2.0.0, this
|
|
|
219
219
|
- Tags like `<hr>`, `<link>` and `<meta>` don't need to be closed. The output for these tags doesn't close them (adding a `/` before the tag closes, or a corresponding close tag);
|
|
220
220
|
- This rule doesn't apply for XHTML, which is strict XML.
|
|
221
221
|
|
|
222
|
+
### Whitespace Handling
|
|
223
|
+
|
|
224
|
+
This library supports `xsl:strip-space` and `xsl:preserve-space` for controlling whitespace in the input document.
|
|
225
|
+
|
|
226
|
+
#### `xsl:strip-space`
|
|
227
|
+
|
|
228
|
+
Use `<xsl:strip-space>` to remove whitespace-only text nodes from specified elements in the input document:
|
|
229
|
+
|
|
230
|
+
```xml
|
|
231
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
232
|
+
<!-- Strip whitespace from all elements -->
|
|
233
|
+
<xsl:strip-space elements="*"/>
|
|
234
|
+
|
|
235
|
+
<!-- Or strip from specific elements -->
|
|
236
|
+
<xsl:strip-space elements="book chapter section"/>
|
|
237
|
+
|
|
238
|
+
<!-- ... templates ... -->
|
|
239
|
+
</xsl:stylesheet>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The `elements` attribute accepts:
|
|
243
|
+
- `*` - matches all elements
|
|
244
|
+
- `name` - matches elements with the specified local name
|
|
245
|
+
- `prefix:*` - matches all elements in a namespace
|
|
246
|
+
- `prefix:name` - matches a specific element in a namespace
|
|
247
|
+
- Multiple patterns separated by whitespace (e.g., `"book chapter section"`)
|
|
248
|
+
|
|
249
|
+
#### `xsl:preserve-space`
|
|
250
|
+
|
|
251
|
+
Use `<xsl:preserve-space>` to preserve whitespace in specific elements, overriding `xsl:strip-space`:
|
|
252
|
+
|
|
253
|
+
```xml
|
|
254
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
255
|
+
<xsl:strip-space elements="*"/>
|
|
256
|
+
<!-- Preserve whitespace in pre and code elements -->
|
|
257
|
+
<xsl:preserve-space elements="pre code"/>
|
|
258
|
+
|
|
259
|
+
<!-- ... templates ... -->
|
|
260
|
+
</xsl:stylesheet>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### Precedence Rules
|
|
264
|
+
|
|
265
|
+
1. `xml:space="preserve"` attribute on an element takes highest precedence
|
|
266
|
+
2. `xsl:preserve-space` overrides `xsl:strip-space` for matching elements
|
|
267
|
+
3. `xsl:strip-space` applies to remaining matches
|
|
268
|
+
4. By default (no declarations), whitespace is preserved
|
|
269
|
+
|
|
222
270
|
## References
|
|
223
271
|
|
|
224
272
|
- XPath Specification: http://www.w3.org/TR/1999/REC-xpath-19991116
|
package/index.d.mts
CHANGED
|
@@ -570,6 +570,17 @@ declare class Xslt {
|
|
|
570
570
|
outputOmitXmlDeclaration: string;
|
|
571
571
|
version: string;
|
|
572
572
|
firstTemplateRan: boolean;
|
|
573
|
+
/**
|
|
574
|
+
* List of element name patterns from xsl:strip-space declarations.
|
|
575
|
+
* Whitespace-only text nodes inside matching elements will be stripped.
|
|
576
|
+
*/
|
|
577
|
+
stripSpacePatterns: string[];
|
|
578
|
+
/**
|
|
579
|
+
* List of element name patterns from xsl:preserve-space declarations.
|
|
580
|
+
* Whitespace-only text nodes inside matching elements will be preserved.
|
|
581
|
+
* preserve-space takes precedence over strip-space for conflicting patterns.
|
|
582
|
+
*/
|
|
583
|
+
preserveSpacePatterns: string[];
|
|
573
584
|
constructor(options?: Partial<XsltOptions>);
|
|
574
585
|
/**
|
|
575
586
|
* The exported entry point of the XSL-T processor.
|
|
@@ -704,6 +715,39 @@ declare class Xslt {
|
|
|
704
715
|
* @todo case-order is not implemented.
|
|
705
716
|
*/
|
|
706
717
|
protected xsltSort(context: ExprContext, template: XNode): void;
|
|
718
|
+
/**
|
|
719
|
+
* Implements `xsl:strip-space`.
|
|
720
|
+
* Collects element name patterns for which whitespace-only text nodes should be stripped.
|
|
721
|
+
* @param template The `<xsl:strip-space>` node.
|
|
722
|
+
*/
|
|
723
|
+
protected xsltStripSpace(template: XNode): void;
|
|
724
|
+
/**
|
|
725
|
+
* Implements `xsl:preserve-space`.
|
|
726
|
+
* Collects element name patterns for which whitespace-only text nodes should be preserved.
|
|
727
|
+
* preserve-space takes precedence over strip-space for matching elements.
|
|
728
|
+
* @param template The `<xsl:preserve-space>` node.
|
|
729
|
+
*/
|
|
730
|
+
protected xsltPreserveSpace(template: XNode): void;
|
|
731
|
+
/**
|
|
732
|
+
* Determines if a text node from the input document should be stripped.
|
|
733
|
+
* This applies xsl:strip-space and xsl:preserve-space rules to whitespace-only text nodes.
|
|
734
|
+
* @param textNode The text node to check.
|
|
735
|
+
* @returns True if the text node should be stripped (not included in output).
|
|
736
|
+
*/
|
|
737
|
+
protected shouldStripWhitespaceNode(textNode: XNode): boolean;
|
|
738
|
+
/**
|
|
739
|
+
* Matches an element name against a strip-space/preserve-space pattern.
|
|
740
|
+
* Supports:
|
|
741
|
+
* - "*" matches any element
|
|
742
|
+
* - "prefix:*" matches any element in a namespace
|
|
743
|
+
* - "name" matches elements with that local name
|
|
744
|
+
* - "prefix:name" matches elements with that QName
|
|
745
|
+
* @param elementName The local name of the element.
|
|
746
|
+
* @param pattern The pattern to match against.
|
|
747
|
+
* @param element The element node (for namespace checking).
|
|
748
|
+
* @returns True if the element matches the pattern.
|
|
749
|
+
*/
|
|
750
|
+
protected matchesNamePattern(elementName: string, pattern: string, element: XNode): boolean;
|
|
707
751
|
/**
|
|
708
752
|
* Implements `xsl:template`.
|
|
709
753
|
* @param context The Expression Context.
|
package/index.d.ts
CHANGED
|
@@ -570,6 +570,17 @@ declare class Xslt {
|
|
|
570
570
|
outputOmitXmlDeclaration: string;
|
|
571
571
|
version: string;
|
|
572
572
|
firstTemplateRan: boolean;
|
|
573
|
+
/**
|
|
574
|
+
* List of element name patterns from xsl:strip-space declarations.
|
|
575
|
+
* Whitespace-only text nodes inside matching elements will be stripped.
|
|
576
|
+
*/
|
|
577
|
+
stripSpacePatterns: string[];
|
|
578
|
+
/**
|
|
579
|
+
* List of element name patterns from xsl:preserve-space declarations.
|
|
580
|
+
* Whitespace-only text nodes inside matching elements will be preserved.
|
|
581
|
+
* preserve-space takes precedence over strip-space for conflicting patterns.
|
|
582
|
+
*/
|
|
583
|
+
preserveSpacePatterns: string[];
|
|
573
584
|
constructor(options?: Partial<XsltOptions>);
|
|
574
585
|
/**
|
|
575
586
|
* The exported entry point of the XSL-T processor.
|
|
@@ -704,6 +715,39 @@ declare class Xslt {
|
|
|
704
715
|
* @todo case-order is not implemented.
|
|
705
716
|
*/
|
|
706
717
|
protected xsltSort(context: ExprContext, template: XNode): void;
|
|
718
|
+
/**
|
|
719
|
+
* Implements `xsl:strip-space`.
|
|
720
|
+
* Collects element name patterns for which whitespace-only text nodes should be stripped.
|
|
721
|
+
* @param template The `<xsl:strip-space>` node.
|
|
722
|
+
*/
|
|
723
|
+
protected xsltStripSpace(template: XNode): void;
|
|
724
|
+
/**
|
|
725
|
+
* Implements `xsl:preserve-space`.
|
|
726
|
+
* Collects element name patterns for which whitespace-only text nodes should be preserved.
|
|
727
|
+
* preserve-space takes precedence over strip-space for matching elements.
|
|
728
|
+
* @param template The `<xsl:preserve-space>` node.
|
|
729
|
+
*/
|
|
730
|
+
protected xsltPreserveSpace(template: XNode): void;
|
|
731
|
+
/**
|
|
732
|
+
* Determines if a text node from the input document should be stripped.
|
|
733
|
+
* This applies xsl:strip-space and xsl:preserve-space rules to whitespace-only text nodes.
|
|
734
|
+
* @param textNode The text node to check.
|
|
735
|
+
* @returns True if the text node should be stripped (not included in output).
|
|
736
|
+
*/
|
|
737
|
+
protected shouldStripWhitespaceNode(textNode: XNode): boolean;
|
|
738
|
+
/**
|
|
739
|
+
* Matches an element name against a strip-space/preserve-space pattern.
|
|
740
|
+
* Supports:
|
|
741
|
+
* - "*" matches any element
|
|
742
|
+
* - "prefix:*" matches any element in a namespace
|
|
743
|
+
* - "name" matches elements with that local name
|
|
744
|
+
* - "prefix:name" matches elements with that QName
|
|
745
|
+
* @param elementName The local name of the element.
|
|
746
|
+
* @param pattern The pattern to match against.
|
|
747
|
+
* @param element The element node (for namespace checking).
|
|
748
|
+
* @returns True if the element matches the pattern.
|
|
749
|
+
*/
|
|
750
|
+
protected matchesNamePattern(elementName: string, pattern: string, element: XNode): boolean;
|
|
707
751
|
/**
|
|
708
752
|
* Implements `xsl:template`.
|
|
709
753
|
* @param context The Expression Context.
|
package/index.js
CHANGED
|
@@ -3548,6 +3548,8 @@ var Xslt = class {
|
|
|
3548
3548
|
};
|
|
3549
3549
|
this.outputMethod = "xml";
|
|
3550
3550
|
this.outputOmitXmlDeclaration = "no";
|
|
3551
|
+
this.stripSpacePatterns = [];
|
|
3552
|
+
this.preserveSpacePatterns = [];
|
|
3551
3553
|
this.decimalFormatSettings = {
|
|
3552
3554
|
decimalSeparator: ".",
|
|
3553
3555
|
groupingSeparator: ",",
|
|
@@ -3680,14 +3682,16 @@ var Xslt = class {
|
|
|
3680
3682
|
yield this.xsltVariable(context, template, false);
|
|
3681
3683
|
break;
|
|
3682
3684
|
case "preserve-space":
|
|
3683
|
-
|
|
3685
|
+
this.xsltPreserveSpace(template);
|
|
3686
|
+
break;
|
|
3684
3687
|
case "processing-instruction":
|
|
3685
3688
|
throw new Error(`not implemented: ${template.localName}`);
|
|
3686
3689
|
case "sort":
|
|
3687
3690
|
this.xsltSort(context, template);
|
|
3688
3691
|
break;
|
|
3689
3692
|
case "strip-space":
|
|
3690
|
-
|
|
3693
|
+
this.xsltStripSpace(template);
|
|
3694
|
+
break;
|
|
3691
3695
|
case "stylesheet":
|
|
3692
3696
|
case "transform":
|
|
3693
3697
|
yield this.xsltTransformOrStylesheet(context, template, output);
|
|
@@ -3847,6 +3851,9 @@ var Xslt = class {
|
|
|
3847
3851
|
return node;
|
|
3848
3852
|
}
|
|
3849
3853
|
if (source.nodeType == DOM_TEXT_NODE) {
|
|
3854
|
+
if (this.shouldStripWhitespaceNode(source)) {
|
|
3855
|
+
return null;
|
|
3856
|
+
}
|
|
3850
3857
|
let node = domCreateTextNode(this.outputDocument, source.nodeValue);
|
|
3851
3858
|
node.siblingPosition = destination.childNodes.length;
|
|
3852
3859
|
domAppendChild(destination, node);
|
|
@@ -4106,6 +4113,99 @@ var Xslt = class {
|
|
|
4106
4113
|
}
|
|
4107
4114
|
this.xPath.xPathSort(context, sort);
|
|
4108
4115
|
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Implements `xsl:strip-space`.
|
|
4118
|
+
* Collects element name patterns for which whitespace-only text nodes should be stripped.
|
|
4119
|
+
* @param template The `<xsl:strip-space>` node.
|
|
4120
|
+
*/
|
|
4121
|
+
xsltStripSpace(template) {
|
|
4122
|
+
const elements = xmlGetAttribute(template, "elements");
|
|
4123
|
+
if (elements) {
|
|
4124
|
+
const patterns = elements.trim().split(/\s+/);
|
|
4125
|
+
this.stripSpacePatterns.push(...patterns);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Implements `xsl:preserve-space`.
|
|
4130
|
+
* Collects element name patterns for which whitespace-only text nodes should be preserved.
|
|
4131
|
+
* preserve-space takes precedence over strip-space for matching elements.
|
|
4132
|
+
* @param template The `<xsl:preserve-space>` node.
|
|
4133
|
+
*/
|
|
4134
|
+
xsltPreserveSpace(template) {
|
|
4135
|
+
const elements = xmlGetAttribute(template, "elements");
|
|
4136
|
+
if (elements) {
|
|
4137
|
+
const patterns = elements.trim().split(/\s+/);
|
|
4138
|
+
this.preserveSpacePatterns.push(...patterns);
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
/**
|
|
4142
|
+
* Determines if a text node from the input document should be stripped.
|
|
4143
|
+
* This applies xsl:strip-space and xsl:preserve-space rules to whitespace-only text nodes.
|
|
4144
|
+
* @param textNode The text node to check.
|
|
4145
|
+
* @returns True if the text node should be stripped (not included in output).
|
|
4146
|
+
*/
|
|
4147
|
+
shouldStripWhitespaceNode(textNode) {
|
|
4148
|
+
if (!textNode.nodeValue || !textNode.nodeValue.match(/^\s*$/)) {
|
|
4149
|
+
return false;
|
|
4150
|
+
}
|
|
4151
|
+
if (this.stripSpacePatterns.length === 0) {
|
|
4152
|
+
return false;
|
|
4153
|
+
}
|
|
4154
|
+
const parentElement = textNode.parentNode;
|
|
4155
|
+
if (!parentElement || parentElement.nodeType !== DOM_ELEMENT_NODE) {
|
|
4156
|
+
return false;
|
|
4157
|
+
}
|
|
4158
|
+
let ancestor = parentElement;
|
|
4159
|
+
while (ancestor && ancestor.nodeType === DOM_ELEMENT_NODE) {
|
|
4160
|
+
const xmlspace = domGetAttributeValue(ancestor, "xml:space");
|
|
4161
|
+
if (xmlspace === "preserve") {
|
|
4162
|
+
return false;
|
|
4163
|
+
}
|
|
4164
|
+
if (xmlspace === "default") {
|
|
4165
|
+
break;
|
|
4166
|
+
}
|
|
4167
|
+
ancestor = ancestor.parentNode;
|
|
4168
|
+
}
|
|
4169
|
+
const parentName = parentElement.localName || parentElement.nodeName;
|
|
4170
|
+
for (const pattern of this.preserveSpacePatterns) {
|
|
4171
|
+
if (this.matchesNamePattern(parentName, pattern, parentElement)) {
|
|
4172
|
+
return false;
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
for (const pattern of this.stripSpacePatterns) {
|
|
4176
|
+
if (this.matchesNamePattern(parentName, pattern, parentElement)) {
|
|
4177
|
+
return true;
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
return false;
|
|
4181
|
+
}
|
|
4182
|
+
/**
|
|
4183
|
+
* Matches an element name against a strip-space/preserve-space pattern.
|
|
4184
|
+
* Supports:
|
|
4185
|
+
* - "*" matches any element
|
|
4186
|
+
* - "prefix:*" matches any element in a namespace
|
|
4187
|
+
* - "name" matches elements with that local name
|
|
4188
|
+
* - "prefix:name" matches elements with that QName
|
|
4189
|
+
* @param elementName The local name of the element.
|
|
4190
|
+
* @param pattern The pattern to match against.
|
|
4191
|
+
* @param element The element node (for namespace checking).
|
|
4192
|
+
* @returns True if the element matches the pattern.
|
|
4193
|
+
*/
|
|
4194
|
+
matchesNamePattern(elementName, pattern, element) {
|
|
4195
|
+
if (pattern === "*") {
|
|
4196
|
+
return true;
|
|
4197
|
+
}
|
|
4198
|
+
if (pattern.includes(":")) {
|
|
4199
|
+
const [prefix, localPart] = pattern.split(":");
|
|
4200
|
+
const elementPrefix = element.prefix || "";
|
|
4201
|
+
if (localPart === "*") {
|
|
4202
|
+
return elementPrefix === prefix;
|
|
4203
|
+
} else {
|
|
4204
|
+
return elementPrefix === prefix && elementName === localPart;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
return elementName === pattern;
|
|
4208
|
+
}
|
|
4109
4209
|
/**
|
|
4110
4210
|
* Implements `xsl:template`.
|
|
4111
4211
|
* @param context The Expression Context.
|
|
@@ -4310,6 +4410,9 @@ var Xslt = class {
|
|
|
4310
4410
|
*/
|
|
4311
4411
|
commonLogicTextNode(context, template, output) {
|
|
4312
4412
|
if (output) {
|
|
4413
|
+
if (this.shouldStripWhitespaceNode(template)) {
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4313
4416
|
let node = domCreateTextNode(this.outputDocument, template.nodeValue);
|
|
4314
4417
|
node.siblingPosition = output.childNodes.length;
|
|
4315
4418
|
domAppendChild(output, node);
|