@tradik/xslt-processor 1.0.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/LICENSE +29 -0
- package/LICENSE.md +28 -0
- package/README.md +634 -0
- package/bin/xslt.js +208 -0
- package/dist/xslt-processor.browser.js +3302 -0
- package/dist/xslt-processor.browser.js.map +7 -0
- package/dist/xslt-processor.browser.min.js +6 -0
- package/dist/xslt-processor.browser.min.js.map +7 -0
- package/dist/xslt-processor.cjs +3311 -0
- package/dist/xslt-processor.cjs.map +7 -0
- package/dist/xslt-processor.d.ts +211 -0
- package/dist/xslt-processor.js +3271 -0
- package/dist/xslt-processor.js.map +7 -0
- package/package.json +68 -0
- package/src/XSLTProcessor.js +368 -0
- package/src/XSLTProcessor.test.js +930 -0
- package/src/index.js +66 -0
- package/src/xpath/evaluator.js +1012 -0
- package/src/xpath/evaluator.test.js +1852 -0
- package/src/xpath/index.js +67 -0
- package/src/xpath/parser.js +595 -0
- package/src/xpath/tokenizer.js +383 -0
- package/src/xpath/tokenizer.test.js +224 -0
- package/src/xslt/engine.js +1812 -0
- package/src/xslt/engine.test.js +3130 -0
- package/src/xslt/index.js +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, XSLT-Processor Contributors
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, spagu
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
# @tradik/xslt-processor
|
|
2
|
+
|
|
3
|
+
[](https://github.com/spagu/XSLT-Processor/actions/workflows/test.yml)
|
|
4
|
+
[](https://github.com/spagu/XSLT-Processor/actions/workflows/release.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/@tradik/xslt-processor)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
[](https://github.com/spagu/XSLT-Processor)
|
|
9
|
+
|
|
10
|
+
JavaScript implementation of XSLTProcessor for browser environments and Node.js CLI. This package provides a complete implementation of the W3C XSLTProcessor API that can be used as a drop-in replacement for the native browser implementation.
|
|
11
|
+
|
|
12
|
+
## Background
|
|
13
|
+
|
|
14
|
+
Chrome and other browsers are deprecating native XSLTProcessor support:
|
|
15
|
+
- **Chrome 143+**: XSLTProcessor starts showing deprecation warnings
|
|
16
|
+
- **Chrome 164 (August 2027)**: Full removal of native XSLT support
|
|
17
|
+
|
|
18
|
+
This library ensures your XSLT-based applications continue to work regardless of browser support.
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **1:1 Native API Compatibility**: Drop-in replacement for native `XSLTProcessor`
|
|
23
|
+
- **Full XSLT 1.0 Support**: Implements the complete W3C XSLT 1.0 specification
|
|
24
|
+
- **XPath 1.0 Engine**: Built-in XPath evaluator with all core functions
|
|
25
|
+
- **Zero Dependencies**: Standalone implementation with no external dependencies
|
|
26
|
+
- **Multiple Formats**: ESM, CommonJS, and browser IIFE bundles
|
|
27
|
+
- **TypeScript Support**: Includes TypeScript declarations
|
|
28
|
+
- **WCAG 2.2 Compliant**: Designed with accessibility in mind
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @tradik/xslt-processor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### Browser (Auto-Install)
|
|
39
|
+
|
|
40
|
+
Include the browser bundle in your HTML - it automatically installs as global XSLTProcessor if native support is unavailable:
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<script src="node_modules/@tradik/xslt-processor/dist/xslt-processor.browser.min.js"></script>
|
|
44
|
+
<script>
|
|
45
|
+
// XSLTProcessor is now available globally
|
|
46
|
+
const processor = new XSLTProcessor();
|
|
47
|
+
// ...
|
|
48
|
+
</script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### ESM Module
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import { XSLTProcessor, installGlobal } from '@tradik/xslt-processor';
|
|
55
|
+
|
|
56
|
+
// Optional: Force install as global XSLTProcessor
|
|
57
|
+
installGlobal();
|
|
58
|
+
|
|
59
|
+
// Or use directly
|
|
60
|
+
const processor = new XSLTProcessor();
|
|
61
|
+
const parser = new DOMParser();
|
|
62
|
+
|
|
63
|
+
// Load and parse XSLT stylesheet
|
|
64
|
+
const xsltText = await fetch('template.xsl').then(r => r.text());
|
|
65
|
+
const xsltDoc = parser.parseFromString(xsltText, 'application/xml');
|
|
66
|
+
processor.importStylesheet(xsltDoc);
|
|
67
|
+
|
|
68
|
+
// Load and parse XML source
|
|
69
|
+
const xmlText = await fetch('data.xml').then(r => r.text());
|
|
70
|
+
const xmlDoc = parser.parseFromString(xmlText, 'application/xml');
|
|
71
|
+
|
|
72
|
+
// Transform
|
|
73
|
+
const fragment = processor.transformToFragment(xmlDoc, document);
|
|
74
|
+
document.getElementById('output').appendChild(fragment);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### CommonJS
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const { XSLTProcessor } = require('@tradik/xslt-processor');
|
|
81
|
+
|
|
82
|
+
const processor = new XSLTProcessor();
|
|
83
|
+
// ...
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### CLI Usage
|
|
87
|
+
|
|
88
|
+
The package includes a command-line tool for transforming XML documents:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Global installation
|
|
92
|
+
npm install -g @tradik/xslt-processor
|
|
93
|
+
|
|
94
|
+
# Transform XML with XSLT
|
|
95
|
+
xslt data.xml template.xsl
|
|
96
|
+
|
|
97
|
+
# Save output to file
|
|
98
|
+
xslt data.xml template.xsl -o result.html
|
|
99
|
+
|
|
100
|
+
# With parameters
|
|
101
|
+
xslt data.xml template.xsl -p title="My Page" -p count=10
|
|
102
|
+
|
|
103
|
+
# Format output with indentation
|
|
104
|
+
xslt data.xml template.xsl -f -o output.html
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### CLI Options
|
|
108
|
+
|
|
109
|
+
| Option | Description |
|
|
110
|
+
|--------|-------------|
|
|
111
|
+
| `-o, --output <file>` | Write output to file instead of stdout |
|
|
112
|
+
| `-p, --param <n>=<v>` | Set XSLT parameter (can be used multiple times) |
|
|
113
|
+
| `-f, --format` | Format output with indentation |
|
|
114
|
+
| `-h, --help` | Show help message |
|
|
115
|
+
| `-v, --version` | Show version number |
|
|
116
|
+
|
|
117
|
+
## API Reference
|
|
118
|
+
|
|
119
|
+
### XSLTProcessor
|
|
120
|
+
|
|
121
|
+
#### Constructor
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
const processor = new XSLTProcessor();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Methods
|
|
128
|
+
|
|
129
|
+
| Method | Description |
|
|
130
|
+
|--------|-------------|
|
|
131
|
+
| `importStylesheet(node)` | Imports an XSLT stylesheet from a Document or Element node |
|
|
132
|
+
| `transformToFragment(source, output)` | Transforms XML and returns a DocumentFragment |
|
|
133
|
+
| `transformToDocument(source)` | Transforms XML and returns an XMLDocument |
|
|
134
|
+
| `setParameter(namespaceURI, localName, value)` | Sets an XSLT parameter |
|
|
135
|
+
| `getParameter(namespaceURI, localName)` | Gets an XSLT parameter value |
|
|
136
|
+
| `removeParameter(namespaceURI, localName)` | Removes an XSLT parameter |
|
|
137
|
+
| `clearParameters()` | Removes all parameters |
|
|
138
|
+
| `reset()` | Resets the processor, removing stylesheet and parameters |
|
|
139
|
+
|
|
140
|
+
### Parameters Example
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const processor = new XSLTProcessor();
|
|
144
|
+
processor.importStylesheet(xsltDoc);
|
|
145
|
+
|
|
146
|
+
// Set parameters
|
|
147
|
+
processor.setParameter(null, 'sortOrder', 'ascending');
|
|
148
|
+
processor.setParameter(null, 'itemsPerPage', 10);
|
|
149
|
+
|
|
150
|
+
// Get parameter
|
|
151
|
+
const sortOrder = processor.getParameter(null, 'sortOrder');
|
|
152
|
+
|
|
153
|
+
// Clear parameters
|
|
154
|
+
processor.clearParameters();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Using xsl:import and xsl:include
|
|
158
|
+
|
|
159
|
+
To use `xsl:import` and `xsl:include` elements in your stylesheets, you need to configure a stylesheet loader that tells the processor how to fetch external stylesheets:
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
import { XSLTProcessor } from '@tradik/xslt-processor';
|
|
163
|
+
|
|
164
|
+
const processor = new XSLTProcessor();
|
|
165
|
+
|
|
166
|
+
// Configure stylesheet loader
|
|
167
|
+
processor.engine.setStylesheetLoader((href, baseUri) => {
|
|
168
|
+
// href: the href attribute from xsl:import/xsl:include
|
|
169
|
+
// baseUri: the URI of the importing stylesheet
|
|
170
|
+
|
|
171
|
+
// Option 1: Return a parsed Document
|
|
172
|
+
const response = await fetch(href);
|
|
173
|
+
const text = await response.text();
|
|
174
|
+
const parser = new DOMParser();
|
|
175
|
+
return parser.parseFromString(text, 'application/xml');
|
|
176
|
+
|
|
177
|
+
// Option 2: Return XML string (will be parsed automatically)
|
|
178
|
+
return await fetch(href).then(r => r.text());
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Now xsl:import and xsl:include will work
|
|
182
|
+
processor.importStylesheet(mainStylesheet, '/styles/main.xsl');
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Import vs Include Behavior
|
|
186
|
+
|
|
187
|
+
- **xsl:include**: Merges templates at the same precedence level. If multiple templates match, priority attribute decides.
|
|
188
|
+
- **xsl:import**: Imported templates have lower precedence than importing stylesheet. The importing stylesheet's templates always win over imported ones with the same match pattern.
|
|
189
|
+
|
|
190
|
+
```xml
|
|
191
|
+
<!-- main.xsl -->
|
|
192
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
193
|
+
<xsl:import href="base.xsl"/> <!-- imported templates have lower precedence -->
|
|
194
|
+
<xsl:include href="utils.xsl"/> <!-- included templates have same precedence -->
|
|
195
|
+
|
|
196
|
+
<xsl:template match="item">
|
|
197
|
+
<!-- This template overrides the one from base.xsl -->
|
|
198
|
+
</xsl:template>
|
|
199
|
+
</xsl:stylesheet>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Utility Functions
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
import { isNativeXSLTSupported, installGlobal } from '@tradik/xslt-processor';
|
|
206
|
+
|
|
207
|
+
// Check if native XSLT is functional
|
|
208
|
+
if (!isNativeXSLTSupported()) {
|
|
209
|
+
console.log('Using JS implementation');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Install as global XSLTProcessor
|
|
213
|
+
installGlobal(); // Only if native not available
|
|
214
|
+
installGlobal(true); // Force install
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Complete Example
|
|
218
|
+
|
|
219
|
+
Here's a full example transforming a list of products into an HTML table:
|
|
220
|
+
|
|
221
|
+
**products.xml:**
|
|
222
|
+
```xml
|
|
223
|
+
<?xml version="1.0"?>
|
|
224
|
+
<products>
|
|
225
|
+
<product id="1">
|
|
226
|
+
<name>Widget</name>
|
|
227
|
+
<price>29.99</price>
|
|
228
|
+
<stock>150</stock>
|
|
229
|
+
</product>
|
|
230
|
+
<product id="2">
|
|
231
|
+
<name>Gadget</name>
|
|
232
|
+
<price>49.99</price>
|
|
233
|
+
<stock>75</stock>
|
|
234
|
+
</product>
|
|
235
|
+
</products>
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**products.xsl:**
|
|
239
|
+
```xml
|
|
240
|
+
<?xml version="1.0"?>
|
|
241
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
242
|
+
<xsl:param name="title" select="'Product Catalog'"/>
|
|
243
|
+
|
|
244
|
+
<xsl:template match="/">
|
|
245
|
+
<html>
|
|
246
|
+
<head><title><xsl:value-of select="$title"/></title></head>
|
|
247
|
+
<body>
|
|
248
|
+
<h1><xsl:value-of select="$title"/></h1>
|
|
249
|
+
<table>
|
|
250
|
+
<tr><th>ID</th><th>Name</th><th>Price</th><th>Stock</th></tr>
|
|
251
|
+
<xsl:apply-templates select="products/product">
|
|
252
|
+
<xsl:sort select="name"/>
|
|
253
|
+
</xsl:apply-templates>
|
|
254
|
+
</table>
|
|
255
|
+
</body>
|
|
256
|
+
</html>
|
|
257
|
+
</xsl:template>
|
|
258
|
+
|
|
259
|
+
<xsl:template match="product">
|
|
260
|
+
<tr>
|
|
261
|
+
<td><xsl:value-of select="@id"/></td>
|
|
262
|
+
<td><xsl:value-of select="name"/></td>
|
|
263
|
+
<td>$<xsl:value-of select="price"/></td>
|
|
264
|
+
<td>
|
|
265
|
+
<xsl:choose>
|
|
266
|
+
<xsl:when test="stock > 100">In Stock</xsl:when>
|
|
267
|
+
<xsl:when test="stock > 0">Low Stock</xsl:when>
|
|
268
|
+
<xsl:otherwise>Out of Stock</xsl:otherwise>
|
|
269
|
+
</xsl:choose>
|
|
270
|
+
</td>
|
|
271
|
+
</tr>
|
|
272
|
+
</xsl:template>
|
|
273
|
+
</xsl:stylesheet>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**JavaScript:**
|
|
277
|
+
```javascript
|
|
278
|
+
import { XSLTProcessor } from '@tradik/xslt-processor';
|
|
279
|
+
|
|
280
|
+
const processor = new XSLTProcessor();
|
|
281
|
+
processor.importStylesheet(xsltDoc);
|
|
282
|
+
processor.setParameter(null, 'title', 'My Product List');
|
|
283
|
+
|
|
284
|
+
const result = processor.transformToFragment(xmlDoc, document);
|
|
285
|
+
document.body.appendChild(result);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**CLI:**
|
|
289
|
+
```bash
|
|
290
|
+
xslt products.xml products.xsl -p title="My Product List" -f -o catalog.html
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Security Features
|
|
294
|
+
|
|
295
|
+
The XPath evaluator includes comprehensive security hardening to prevent common attack vectors.
|
|
296
|
+
|
|
297
|
+
### DoS Prevention Limits
|
|
298
|
+
|
|
299
|
+
| Limit | Default | Description |
|
|
300
|
+
|-------|---------|-------------|
|
|
301
|
+
| `MAX_RECURSION_DEPTH` | 100 | Prevents stack overflow from deeply nested expressions |
|
|
302
|
+
| `MAX_RESULT_SIZE` | 10,000 | Prevents memory exhaustion from large result sets |
|
|
303
|
+
| `MAX_STRING_LENGTH` | 1,000,000 | Limits string processing to prevent memory issues |
|
|
304
|
+
|
|
305
|
+
### Prototype Pollution Protection
|
|
306
|
+
|
|
307
|
+
The following variable names are blocked:
|
|
308
|
+
- `__proto__`, `constructor`, `prototype`
|
|
309
|
+
- `__defineGetter__`, `__defineSetter__`
|
|
310
|
+
- `__lookupGetter__`, `__lookupSetter__`
|
|
311
|
+
|
|
312
|
+
### Input Validation
|
|
313
|
+
|
|
314
|
+
- **AST Validation**: All AST nodes are validated before evaluation
|
|
315
|
+
- **Type Safety**: Strict type checking on all inputs
|
|
316
|
+
- **Safe Variable Lookup**: Uses `hasOwnProperty` to prevent prototype chain attacks
|
|
317
|
+
|
|
318
|
+
### Custom Security Limits
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
import { XPathEvaluator, XPathContext, parse } from '@tradik/xslt-processor';
|
|
322
|
+
|
|
323
|
+
const evaluator = new XPathEvaluator({
|
|
324
|
+
maxRecursionDepth: 50, // Lower for untrusted input
|
|
325
|
+
maxResultSize: 1000, // Limit result set size
|
|
326
|
+
maxStringLength: 10000 // Limit string operations
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const ast = parse('//item');
|
|
330
|
+
const context = new XPathContext(xmlDoc);
|
|
331
|
+
const result = evaluator.evaluate(ast, context);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## XSLT Elements Supported
|
|
335
|
+
|
|
336
|
+
| Element | Status |
|
|
337
|
+
|---------|--------|
|
|
338
|
+
| `xsl:apply-templates` | Supported |
|
|
339
|
+
| `xsl:attribute` | Supported |
|
|
340
|
+
| `xsl:call-template` | Supported |
|
|
341
|
+
| `xsl:choose` / `when` / `otherwise` | Supported |
|
|
342
|
+
| `xsl:comment` | Supported |
|
|
343
|
+
| `xsl:copy` | Supported |
|
|
344
|
+
| `xsl:copy-of` | Supported |
|
|
345
|
+
| `xsl:element` | Supported |
|
|
346
|
+
| `xsl:for-each` | Supported |
|
|
347
|
+
| `xsl:if` | Supported |
|
|
348
|
+
| `xsl:message` | Supported |
|
|
349
|
+
| `xsl:number` | Supported |
|
|
350
|
+
| `xsl:output` | Supported |
|
|
351
|
+
| `xsl:param` | Supported |
|
|
352
|
+
| `xsl:processing-instruction` | Supported |
|
|
353
|
+
| `xsl:sort` | Supported |
|
|
354
|
+
| `xsl:template` | Supported |
|
|
355
|
+
| `xsl:text` | Supported |
|
|
356
|
+
| `xsl:value-of` | Supported |
|
|
357
|
+
| `xsl:variable` | Supported |
|
|
358
|
+
| `xsl:with-param` | Supported |
|
|
359
|
+
| `xsl:import` | Supported |
|
|
360
|
+
| `xsl:include` | Supported |
|
|
361
|
+
|
|
362
|
+
## XPath Functions Supported
|
|
363
|
+
|
|
364
|
+
### Node Set Functions
|
|
365
|
+
- `count()`, `id()`, `last()`, `local-name()`, `name()`, `namespace-uri()`, `position()`
|
|
366
|
+
|
|
367
|
+
### String Functions
|
|
368
|
+
- `concat()`, `contains()`, `normalize-space()`, `starts-with()`, `string()`, `string-length()`, `substring()`, `substring-after()`, `substring-before()`, `translate()`
|
|
369
|
+
|
|
370
|
+
### Boolean Functions
|
|
371
|
+
- `boolean()`, `false()`, `lang()`, `not()`, `true()`
|
|
372
|
+
|
|
373
|
+
### Number Functions
|
|
374
|
+
- `ceiling()`, `floor()`, `number()`, `round()`, `sum()`
|
|
375
|
+
|
|
376
|
+
## Development
|
|
377
|
+
|
|
378
|
+
### Prerequisites
|
|
379
|
+
|
|
380
|
+
- Node.js 25+ (for native test runner)
|
|
381
|
+
- Docker (optional, for containerized testing)
|
|
382
|
+
|
|
383
|
+
### Setup
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
cd services/xslt-processor
|
|
387
|
+
npm install
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Commands
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
# Run tests
|
|
394
|
+
npm test
|
|
395
|
+
|
|
396
|
+
# Run tests with watch mode
|
|
397
|
+
npm run test:watch
|
|
398
|
+
|
|
399
|
+
# Build bundles
|
|
400
|
+
npm run build
|
|
401
|
+
|
|
402
|
+
# Lint code
|
|
403
|
+
npm run lint
|
|
404
|
+
|
|
405
|
+
# Format code
|
|
406
|
+
npm run format
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Docker
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
# Run tests in container
|
|
413
|
+
docker-compose run test
|
|
414
|
+
|
|
415
|
+
# Development with hot reload
|
|
416
|
+
docker-compose run dev
|
|
417
|
+
|
|
418
|
+
# Build bundles
|
|
419
|
+
docker-compose run build
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Publishing to npm
|
|
423
|
+
|
|
424
|
+
The package is automatically published to npm when a GitHub release is created or a version tag is pushed.
|
|
425
|
+
|
|
426
|
+
**Prerequisites:**
|
|
427
|
+
1. Set up `NPM_TOKEN` secret in GitHub repository settings
|
|
428
|
+
2. Ensure version in `package.json` matches the release tag
|
|
429
|
+
|
|
430
|
+
**Release Process:**
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# 1. Update version in package.json
|
|
434
|
+
npm version patch # or minor, major
|
|
435
|
+
|
|
436
|
+
# 2. Push the tag
|
|
437
|
+
git push origin --tags
|
|
438
|
+
|
|
439
|
+
# 3. Create a GitHub release (or push triggers automatically)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Automated Workflow:**
|
|
443
|
+
1. Runs all tests and linting checks
|
|
444
|
+
2. Performs security audit with `npm audit`
|
|
445
|
+
3. Builds distribution bundles
|
|
446
|
+
4. Publishes to npm with provenance (supply chain security)
|
|
447
|
+
5. Uploads build artifacts to GitHub
|
|
448
|
+
|
|
449
|
+
## Browser Compatibility
|
|
450
|
+
|
|
451
|
+
This library provides a JavaScript polyfill for XSLTProcessor that works across all modern browsers.
|
|
452
|
+
|
|
453
|
+
### Polyfill Support
|
|
454
|
+
|
|
455
|
+
| Browser | Minimum Version | ES Modules | Status |
|
|
456
|
+
|---------|-----------------|------------|--------|
|
|
457
|
+
| Chrome | 90+ | Yes | Fully Supported |
|
|
458
|
+
| Firefox | 88+ | Yes | Fully Supported |
|
|
459
|
+
| Safari | 14+ | Yes | Fully Supported |
|
|
460
|
+
| Edge | 90+ | Yes | Fully Supported |
|
|
461
|
+
| Opera | 76+ | Yes | Fully Supported |
|
|
462
|
+
| Samsung Internet | 15+ | Yes | Fully Supported |
|
|
463
|
+
| Node.js | 25+ | Yes | Fully Supported |
|
|
464
|
+
|
|
465
|
+
### Native XSLT Deprecation Timeline
|
|
466
|
+
|
|
467
|
+
| Browser | Deprecation Warning | Full Removal |
|
|
468
|
+
|---------|---------------------|--------------|
|
|
469
|
+
| Chrome | v143 (2026) | v164 (August 2027) |
|
|
470
|
+
| Edge | v143 (2026) | v164 (August 2027) |
|
|
471
|
+
| Other Chromium | v143 (2026) | v164 (August 2027) |
|
|
472
|
+
|
|
473
|
+
### Feature Detection
|
|
474
|
+
|
|
475
|
+
```javascript
|
|
476
|
+
import { isNativeXSLTSupported, installGlobal } from '@tradik/xslt-processor';
|
|
477
|
+
|
|
478
|
+
// Check native support and auto-install polyfill
|
|
479
|
+
if (!isNativeXSLTSupported()) {
|
|
480
|
+
installGlobal();
|
|
481
|
+
console.log('Using JavaScript XSLT polyfill');
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## W3C Standards Compliance
|
|
486
|
+
|
|
487
|
+
This implementation follows these W3C specifications with comprehensive test coverage to ensure compliance.
|
|
488
|
+
|
|
489
|
+
### Specifications Implemented
|
|
490
|
+
|
|
491
|
+
| Specification | Version | Status |
|
|
492
|
+
|---------------|---------|--------|
|
|
493
|
+
| [XPath 1.0](http://www.w3.org/TR/1999/REC-xpath-19991116) | W3C Recommendation, 16 November 1999 | Full Compliance |
|
|
494
|
+
| [XSLT 1.0](http://www.w3.org/TR/1999/REC-xslt-19991116) | W3C Recommendation, 16 November 1999 | Full Compliance |
|
|
495
|
+
| [DOM Level 3 Core](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/) | W3C Recommendation, 7 April 2004 | Full Compliance |
|
|
496
|
+
|
|
497
|
+
### XSLT 1.0 Specification Compliance
|
|
498
|
+
|
|
499
|
+
| Section | Feature | Status | Notes |
|
|
500
|
+
|---------|---------|--------|-------|
|
|
501
|
+
| 2 | Stylesheet Structure | Supported | `xsl:stylesheet`, `xsl:transform` elements |
|
|
502
|
+
| 3 | Data Model | Supported | Seven node types per XPath data model |
|
|
503
|
+
| 5 | Template Rules | Supported | Pattern matching, priority calculation |
|
|
504
|
+
| 5.1 | Processing Model | Supported | Built-in templates for all node types |
|
|
505
|
+
| 5.2 | Patterns | Supported | All pattern syntax including predicates |
|
|
506
|
+
| 5.3 | Defining Template Rules | Supported | `match`, `name`, `priority`, `mode` attributes |
|
|
507
|
+
| 5.4 | Applying Template Rules | Supported | `xsl:apply-templates` with `select`, `mode` |
|
|
508
|
+
| 5.5 | Conflict Resolution | Supported | Import precedence and priority ordering |
|
|
509
|
+
| 6 | Named Templates | Supported | `xsl:call-template`, `xsl:with-param` |
|
|
510
|
+
| 7 | Creating Result Tree | Supported | Literal result elements, attribute value templates |
|
|
511
|
+
| 7.1.2 | Creating Elements | Supported | `xsl:element` with dynamic names/namespaces |
|
|
512
|
+
| 7.1.3 | Creating Attributes | Supported | `xsl:attribute` with dynamic names/namespaces |
|
|
513
|
+
| 7.2 | Creating Text | Supported | `xsl:value-of`, `xsl:text` |
|
|
514
|
+
| 7.3 | Creating PIs | Supported | `xsl:processing-instruction` |
|
|
515
|
+
| 7.4 | Creating Comments | Supported | `xsl:comment` |
|
|
516
|
+
| 7.5 | Copying | Supported | `xsl:copy`, `xsl:copy-of` |
|
|
517
|
+
| 7.6 | Attribute Sets | Supported | `xsl:attribute-set`, `use-attribute-sets` |
|
|
518
|
+
| 7.6.2 | Namespace Aliases | Supported | `xsl:namespace-alias` |
|
|
519
|
+
| 8 | Repetition | Supported | `xsl:for-each` |
|
|
520
|
+
| 9 | Conditional Processing | Supported | `xsl:if`, `xsl:choose`, `xsl:when`, `xsl:otherwise` |
|
|
521
|
+
| 10 | Sorting | Supported | `xsl:sort` with multiple keys, data-types, order |
|
|
522
|
+
| 11 | Variables/Parameters | Supported | `xsl:variable`, `xsl:param`, scoping rules |
|
|
523
|
+
| 11.1 | Result Tree Fragments | Supported | RTF handling as per spec |
|
|
524
|
+
| 12 | Additional Functions | Supported | `document()`, `key()`, `format-number()`, `current()`, `generate-id()`, `system-property()` |
|
|
525
|
+
| 12.3 | Number Formatting | Supported | `xsl:number` with all formatting options |
|
|
526
|
+
| 13 | Messages | Supported | `xsl:message` with `terminate` attribute |
|
|
527
|
+
| 14 | Extensions | Partial | `xsl:fallback` supported |
|
|
528
|
+
| 15 | Fallback | Supported | `xsl:fallback` element |
|
|
529
|
+
| 16 | Output | Supported | `xsl:output` with method, encoding, indent |
|
|
530
|
+
|
|
531
|
+
### XPath 1.0 Specification Compliance
|
|
532
|
+
|
|
533
|
+
| Section | Feature | Status | Notes |
|
|
534
|
+
|---------|---------|--------|-------|
|
|
535
|
+
| 2.1 | Location Steps | Supported | axis::node-test[predicate] |
|
|
536
|
+
| 2.2 | Axes | Supported | All 13 axes implemented |
|
|
537
|
+
| 2.3 | Node Tests | Supported | Name tests, `node()`, `text()`, `comment()`, `processing-instruction()` |
|
|
538
|
+
| 2.4 | Predicates | Supported | Position and boolean predicates |
|
|
539
|
+
| 2.5 | Abbreviated Syntax | Supported | `.`, `..`, `@`, `//` |
|
|
540
|
+
| 3.1 | Basics | Supported | Expression evaluation |
|
|
541
|
+
| 3.2 | Function Calls | Supported | All core functions |
|
|
542
|
+
| 3.3 | Node-sets | Supported | Union operator `\|` |
|
|
543
|
+
| 3.4 | Booleans | Supported | `and`, `or`, `not()` |
|
|
544
|
+
| 3.5 | Numbers | Supported | IEEE 754 double-precision |
|
|
545
|
+
| 3.6 | Strings | Supported | Unicode string handling |
|
|
546
|
+
| 3.7 | Lexical Structure | Supported | Full tokenization |
|
|
547
|
+
| 4.1 | Node Set Functions | Supported | `last()`, `position()`, `count()`, `id()`, `local-name()`, `namespace-uri()`, `name()` |
|
|
548
|
+
| 4.2 | String Functions | Supported | `string()`, `concat()`, `starts-with()`, `contains()`, `substring-before()`, `substring-after()`, `substring()`, `string-length()`, `normalize-space()`, `translate()` |
|
|
549
|
+
| 4.3 | Boolean Functions | Supported | `boolean()`, `not()`, `true()`, `false()`, `lang()` |
|
|
550
|
+
| 4.4 | Number Functions | Supported | `number()`, `sum()`, `floor()`, `ceiling()`, `round()` |
|
|
551
|
+
|
|
552
|
+
### XPath Axes Implementation
|
|
553
|
+
|
|
554
|
+
| Axis | Status | Description |
|
|
555
|
+
|------|--------|-------------|
|
|
556
|
+
| `child` | Supported | Children of context node |
|
|
557
|
+
| `descendant` | Supported | Descendants of context node |
|
|
558
|
+
| `parent` | Supported | Parent of context node |
|
|
559
|
+
| `ancestor` | Supported | Ancestors of context node |
|
|
560
|
+
| `following-sibling` | Supported | Following siblings |
|
|
561
|
+
| `preceding-sibling` | Supported | Preceding siblings |
|
|
562
|
+
| `following` | Supported | Nodes after context in document order |
|
|
563
|
+
| `preceding` | Supported | Nodes before context in document order |
|
|
564
|
+
| `attribute` | Supported | Attributes of context node |
|
|
565
|
+
| `namespace` | Supported | Namespace nodes |
|
|
566
|
+
| `self` | Supported | Context node itself |
|
|
567
|
+
| `descendant-or-self` | Supported | Context node and descendants |
|
|
568
|
+
| `ancestor-or-self` | Supported | Context node and ancestors |
|
|
569
|
+
|
|
570
|
+
### DOM Level 3 Core Compliance
|
|
571
|
+
|
|
572
|
+
| Interface | Status | Notes |
|
|
573
|
+
|-----------|--------|-------|
|
|
574
|
+
| `Node` | Supported | All node type constants |
|
|
575
|
+
| `Document` | Supported | `createElement`, `createTextNode`, `createComment`, etc. |
|
|
576
|
+
| `Element` | Supported | `getAttribute`, `setAttribute`, namespace methods |
|
|
577
|
+
| `Attr` | Supported | Attribute nodes with namespace support |
|
|
578
|
+
| `Text` | Supported | Text node handling |
|
|
579
|
+
| `Comment` | Supported | Comment nodes |
|
|
580
|
+
| `ProcessingInstruction` | Supported | PI nodes with target and data |
|
|
581
|
+
| `DocumentFragment` | Supported | Fragment handling in transforms |
|
|
582
|
+
| `NamedNodeMap` | Supported | Attribute collections |
|
|
583
|
+
| `NodeList` | Supported | Child node collections |
|
|
584
|
+
|
|
585
|
+
### Web API Compliance
|
|
586
|
+
|
|
587
|
+
This implementation provides full compatibility with the [MDN XSLTProcessor API](https://developer.mozilla.org/en-US/docs/Web/API/XSLTProcessor):
|
|
588
|
+
|
|
589
|
+
| Method | Status | Notes |
|
|
590
|
+
|--------|--------|-------|
|
|
591
|
+
| `importStylesheet(node)` | Supported | Accepts Document or Element |
|
|
592
|
+
| `transformToFragment(source, output)` | Supported | Returns DocumentFragment |
|
|
593
|
+
| `transformToDocument(source)` | Supported | Returns XMLDocument |
|
|
594
|
+
| `setParameter(namespaceURI, localName, value)` | Supported | Full namespace support |
|
|
595
|
+
| `getParameter(namespaceURI, localName)` | Supported | Returns parameter value |
|
|
596
|
+
| `removeParameter(namespaceURI, localName)` | Supported | Removes single parameter |
|
|
597
|
+
| `clearParameters()` | Supported | Removes all parameters |
|
|
598
|
+
| `reset()` | Supported | Resets processor state |
|
|
599
|
+
|
|
600
|
+
### Test Coverage by Specification
|
|
601
|
+
|
|
602
|
+
| Specification | Tests | Coverage |
|
|
603
|
+
|---------------|-------|----------|
|
|
604
|
+
| XSLT 1.0 Elements | 82+ | 100% of supported elements |
|
|
605
|
+
| XPath 1.0 Functions | 50+ | 100% of core functions |
|
|
606
|
+
| XPath 1.0 Axes | 26+ | All 13 axes |
|
|
607
|
+
| DOM Level 3 | 20+ | Core interfaces |
|
|
608
|
+
| XSLTProcessor API | 39+ | All methods |
|
|
609
|
+
| Security | 34+ | DoS prevention, prototype pollution |
|
|
610
|
+
| **Total** | **441** | **99.41% line coverage** |
|
|
611
|
+
|
|
612
|
+
## Style Guide
|
|
613
|
+
|
|
614
|
+
### Colors
|
|
615
|
+
|
|
616
|
+
| Usage | Color | Hex |
|
|
617
|
+
|-------|-------|-----|
|
|
618
|
+
| Primary | Blue | `#2563eb` |
|
|
619
|
+
| Success | Green | `#16a34a` |
|
|
620
|
+
| Warning | Amber | `#d97706` |
|
|
621
|
+
| Error | Red | `#dc2626` |
|
|
622
|
+
| Text | Gray | `#1f2937` |
|
|
623
|
+
| Background | White | `#ffffff` |
|
|
624
|
+
|
|
625
|
+
All colors meet WCAG 2.2 AA contrast requirements for accessibility.
|
|
626
|
+
|
|
627
|
+
## License
|
|
628
|
+
|
|
629
|
+
MIT License - see [LICENSE](../../LICENSE) for details.
|
|
630
|
+
|
|
631
|
+
## Related
|
|
632
|
+
|
|
633
|
+
- [MDN XSLTProcessor](https://developer.mozilla.org/en-US/docs/Web/API/XSLTProcessor)
|
|
634
|
+
- [libxslt](https://gitlab.gnome.org/GNOME/libxslt) - Reference implementation in C
|