@tradik/xslt-processor 1.1.1 → 1.3.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.md +1 -1
- package/README.md +102 -757
- package/bin/lib/decode.js +15 -0
- package/bin/lib/dom.js +177 -0
- package/bin/lib/loaders.js +127 -0
- package/bin/lib/options.js +17 -0
- package/bin/lib/output.js +114 -0
- package/bin/lib/paths.js +3 -3
- package/bin/lib/transform.js +124 -33
- package/bin/xslt.js +26 -27
- package/dist/xslt-processor.browser.js +8784 -2720
- package/dist/xslt-processor.browser.js.map +4 -4
- package/dist/xslt-processor.browser.min.js +13 -6
- package/dist/xslt-processor.browser.min.js.map +4 -4
- package/dist/xslt-processor.cjs +8789 -2723
- package/dist/xslt-processor.cjs.map +4 -4
- package/dist/xslt-processor.d.cts +380 -21
- package/dist/xslt-processor.d.ts +380 -21
- package/dist/xslt-processor.js +8770 -2722
- package/dist/xslt-processor.js.map +4 -4
- package/package.json +51 -11
- package/src/XSLTProcessor.js +343 -66
- package/src/async/abort.js +63 -0
- package/src/async/documentUris.js +128 -0
- package/src/async/loaders.js +134 -0
- package/src/async/preload.js +159 -0
- package/src/async/processor.js +206 -0
- package/src/async/stream.js +125 -0
- package/src/bridge/engine.js +221 -0
- package/src/bridge/loader.js +78 -0
- package/src/bridge/results.js +75 -0
- package/src/bridge/version.js +63 -0
- package/src/index.js +16 -4
- package/src/io/decode.js +140 -0
- package/src/io/readSource.js +167 -0
- package/src/xpath/axes.js +562 -0
- package/src/xpath/documentOrder.js +270 -0
- package/src/xpath/evaluator.js +475 -355
- package/src/xpath/index.js +8 -2
- package/src/xpath/namespaceNodes.js +172 -0
- package/src/xpath/nodeSetFunctions.js +169 -0
- package/src/xpath/parser.js +30 -5
- package/src/xpath/strings.js +183 -0
- package/src/xpath/tokenizer.js +37 -23
- package/src/xslt/attributeSets.js +95 -0
- package/src/xslt/avt.js +103 -0
- package/src/xslt/computedNames.js +91 -0
- package/src/xslt/copying.js +212 -0
- package/src/xslt/declarationNames.js +80 -0
- package/src/xslt/domParsing.js +95 -0
- package/src/xslt/elements.js +1 -1
- package/src/xslt/engine/bindings.js +195 -0
- package/src/xslt/engine/context.js +105 -0
- package/src/xslt/engine/controlFlow.js +145 -0
- package/src/xslt/engine/copyInstructions.js +133 -0
- package/src/xslt/engine/declarations.js +233 -0
- package/src/xslt/engine/functionSupport.js +103 -0
- package/src/xslt/engine/methods.js +33 -0
- package/src/xslt/engine/nodeConstruction.js +187 -0
- package/src/xslt/engine/numbering.js +104 -0
- package/src/xslt/engine/outputDeclaration.js +77 -0
- package/src/xslt/engine/sequenceConstructor.js +228 -0
- package/src/xslt/engine/stylesheetLoading.js +208 -0
- package/src/xslt/engine/templateInvocation.js +253 -0
- package/src/xslt/engine/templateRules.js +243 -0
- package/src/xslt/engine/textInstructions.js +171 -0
- package/src/xslt/engine/topLevel.js +130 -0
- package/src/xslt/engine/transformation.js +263 -0
- package/src/xslt/engine/workStack.js +245 -0
- package/src/xslt/engine.js +176 -2020
- package/src/xslt/exslt/arguments.js +99 -0
- package/src/xslt/exslt/calendar.js +120 -0
- package/src/xslt/exslt/common.js +44 -0
- package/src/xslt/exslt/dateCalc.js +261 -0
- package/src/xslt/exslt/dateFormat.js +150 -0
- package/src/xslt/exslt/dateParse.js +265 -0
- package/src/xslt/exslt/dates.js +259 -0
- package/src/xslt/exslt/duration.js +207 -0
- package/src/xslt/exslt/dynamic.js +59 -0
- package/src/xslt/exslt/index.js +59 -0
- package/src/xslt/exslt/math.js +177 -0
- package/src/xslt/exslt/sets.js +96 -0
- package/src/xslt/exslt/stringOps.js +163 -0
- package/src/xslt/exslt/strings.js +147 -0
- package/src/xslt/exslt/uri.js +92 -0
- package/src/xslt/formatNumber.js +22 -9
- package/src/xslt/forwardsCompatible.js +75 -0
- package/src/xslt/functions.js +94 -15
- package/src/xslt/index.js +7 -1
- package/src/xslt/keys.js +51 -28
- package/src/xslt/literalResult.js +63 -7
- package/src/xslt/matchScope.js +116 -0
- package/src/xslt/number.js +171 -78
- package/src/xslt/numberFormat.js +124 -26
- package/src/xslt/outputNames.js +58 -0
- package/src/xslt/patternCompiler.js +175 -0
- package/src/xslt/patterns.js +324 -0
- package/src/xslt/qname.js +90 -0
- package/src/xslt/resultDocument.js +98 -0
- package/src/xslt/resultNamespaces.js +219 -0
- package/src/xslt/resultTree.js +143 -6
- package/src/xslt/serializer/baseWriter.js +173 -66
- package/src/xslt/serializer/chunks.js +120 -0
- package/src/xslt/serializer/constants.js +14 -0
- package/src/xslt/serializer/encoding.js +327 -0
- package/src/xslt/serializer/escape.js +49 -12
- package/src/xslt/serializer/frames.js +168 -0
- package/src/xslt/serializer/htmlDoctype.js +102 -0
- package/src/xslt/serializer/htmlEntities.js +77 -0
- package/src/xslt/serializer/htmlSerializer.js +123 -25
- package/src/xslt/serializer/settings.js +89 -13
- package/src/xslt/serializer/textSerializer.js +58 -10
- package/src/xslt/serializer/xhtmlDocument.js +103 -0
- package/src/xslt/serializer/xmlSerializer.js +113 -13
- package/src/xslt/serializer.js +50 -17
- package/src/xslt/sort.js +151 -0
- package/src/xslt/spaceNameTests.js +115 -0
- package/src/xslt/stylesheetChecks.js +206 -0
- package/src/xslt/stylesheetNamespaces.js +266 -0
- package/src/xslt/variables.js +152 -0
- package/src/xslt/whitespace.js +43 -27
- package/LICENSE +0 -29
package/README.md
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
[](https://github.com/spagu/XSLT-Processor/network/members)
|
|
6
6
|
|
|
7
7
|
[](https://github.com/spagu/XSLT-Processor/actions/workflows/test.yml)
|
|
8
|
+
[](https://github.com/spagu/XSLT-Processor/actions/workflows/browser.yml)
|
|
8
9
|
[](https://github.com/spagu/XSLT-Processor/actions/workflows/release.yml)
|
|
9
10
|
[](https://www.npmjs.com/package/@tradik/xslt-processor)
|
|
10
11
|
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
11
12
|
[](https://nodejs.org/)
|
|
12
|
-
[](https://sonarcloud.io/summary/new_code?id=spagu_XSLT-Processor)
|
|
14
|
+
[](docs/CONFORMANCE.md#test-coverage)
|
|
15
|
+
[](docs/API.md#typescript)
|
|
13
16
|
|
|
14
17
|
> **Source Code:** [github.com/spagu/XSLT-Processor](https://github.com/spagu/XSLT-Processor)
|
|
15
18
|
|
|
@@ -17,22 +20,43 @@ JavaScript implementation of XSLTProcessor for browser environments and Node.js
|
|
|
17
20
|
|
|
18
21
|
## Background
|
|
19
22
|
|
|
20
|
-
Chrome and other browsers are
|
|
21
|
-
- **Chrome 143
|
|
22
|
-
- **Chrome
|
|
23
|
+
Chrome and other browsers are removing native XSLT ([Chrome's announcement](https://developer.chrome.com/docs/web-platform/deprecating-xslt)):
|
|
24
|
+
- **Chrome 143 (December 2025)**: `XSLTProcessor` and `<?xml-stylesheet type="text/xsl"?>` are deprecated, with warnings in the console
|
|
25
|
+
- **Chrome 158 (17 November 2026)**: XSLT stops working in stable Chrome, except for sites in the origin trial and browsers under the enterprise policy
|
|
26
|
+
- **Chrome 176 (17 August 2027)**: the origin trial and the enterprise policy end; XSLT is off everywhere
|
|
27
|
+
- Firefox and WebKit support the removal but have not announced dates
|
|
23
28
|
|
|
24
29
|
This library ensures your XSLT-based applications continue to work regardless of browser support.
|
|
25
30
|
|
|
26
31
|
## Features
|
|
27
32
|
|
|
28
33
|
- **1:1 Native API Compatibility**: Drop-in replacement for native `XSLTProcessor`
|
|
29
|
-
- **
|
|
34
|
+
- **XSLT 1.0**: Every element and function of the W3C XSLT 1.0 Recommendation, with the few gaps listed under [Known Deviations](docs/CONFORMANCE.md#known-deviations)
|
|
30
35
|
- **XPath 1.0 Engine**: Built-in XPath evaluator with all core functions
|
|
31
36
|
- **`xsl:output` Serialization**: `transformToString()` honors method, indent, doctype, CDATA sections and `disable-output-escaping`
|
|
32
|
-
- **Zero Dependencies**:
|
|
37
|
+
- **Zero Dependencies**: The library has no runtime dependencies; only the `xslt` command line tool needs `jsdom` (an optional peer dependency)
|
|
33
38
|
- **Multiple Formats**: ESM, CommonJS, and browser IIFE bundles
|
|
34
39
|
- **TypeScript Support**: Includes TypeScript declarations
|
|
35
|
-
|
|
40
|
+
|
|
41
|
+
## Documentation
|
|
42
|
+
|
|
43
|
+
The documentation is also published as a website with an interactive playground: <https://xslt-processor.tradik.com/> ([playground](https://xslt-processor.tradik.com/playground/): XSLT 1.0 transformations, and with the in-development `@tradik/xslt3` XSLT 3.0 stylesheets at [?mode=xslt3](https://xslt-processor.tradik.com/playground/?mode=xslt3) and XPath 3.1 expressions at [?mode=xpath](https://xslt-processor.tradik.com/playground/?mode=xpath); [blog](https://xslt-processor.tradik.com/blog/) with an [RSS feed](https://xslt-processor.tradik.com/blog/rss.xml)).
|
|
44
|
+
|
|
45
|
+
| Guide | Contents |
|
|
46
|
+
|-------|----------|
|
|
47
|
+
| [API Reference](docs/API.md) | `XSLTProcessor` methods, parameters, `xsl:output` serialization, module exports, `XsltEngine` options, TypeScript |
|
|
48
|
+
| [Loaders](docs/LOADERS.md) | `xsl:import`, `xsl:include` and the `document()` function |
|
|
49
|
+
| [Complete Example](docs/EXAMPLES.md) | A product list transformed into an HTML table (browser and CLI) |
|
|
50
|
+
| [Command Line Tool](docs/CLI.md) | `xslt` options, base directory, includes, input encodings |
|
|
51
|
+
| [Conformance](docs/CONFORMANCE.md) | W3C compliance tables, supported elements and functions, known deviations, test coverage |
|
|
52
|
+
| [Security Limits](docs/SECURITY-LIMITS.md) | XPath and XSLT limits, prototype pollution protection, input validation |
|
|
53
|
+
| [Browser Compatibility](docs/BROWSER-SUPPORT.md) | Minimum browser versions, native XSLT removal timeline, feature detection |
|
|
54
|
+
| [XSLT 2.0 and 3.0](docs/XSLT3.md) | In development: one engine for XSLT 3.0 and 2.0 in a separate package, `@tradik/xslt3`; design, scope and milestones |
|
|
55
|
+
| [Benchmarks](docs/BENCHMARKS.md) | 1.1.3 vs 1.2.0: speed-up, time and peak memory per scenario, with charts, tables and `npm run bench` to reproduce; XPath 1.0 of this package vs XPath 3.1 of @tradik/xslt3 (`npm run bench -- --suite xpath`); the XSLT 1.0 engine vs @tradik/xslt3 on the same stylesheets, idiomatic 2.0/3.0 rewrites and 3.0-only scenarios (`npm run bench -- --suite xslt`) |
|
|
56
|
+
| [Development](docs/DEVELOPMENT.md) | Setup, tests, Docker, Makefile, publishing to npm |
|
|
57
|
+
| [Style Guide](docs/STYLE-GUIDE.md) | Project colors with WCAG 2.2 contrast ratios |
|
|
58
|
+
|
|
59
|
+
All guides are listed in the [documentation index](docs/README.md).
|
|
36
60
|
|
|
37
61
|
## Installation
|
|
38
62
|
|
|
@@ -40,7 +64,10 @@ This library ensures your XSLT-based applications continue to work regardless of
|
|
|
40
64
|
npm install @tradik/xslt-processor
|
|
41
65
|
```
|
|
42
66
|
|
|
43
|
-
|
|
67
|
+
The library itself has no dependencies. The `xslt` command line tool also
|
|
68
|
+
needs `jsdom` (an optional peer dependency, `>=25`), see [CLI Usage](#cli-usage).
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
44
71
|
|
|
45
72
|
### Browser via CDN (Recommended)
|
|
46
73
|
|
|
@@ -54,7 +81,7 @@ Use a CDN for the easiest browser integration - no build step required:
|
|
|
54
81
|
<script src="https://unpkg.com/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js"></script>
|
|
55
82
|
|
|
56
83
|
<script>
|
|
57
|
-
// XSLTProcessor is
|
|
84
|
+
// XSLTProcessor is the native one, or this polyfill when native XSLT is unavailable
|
|
58
85
|
const processor = new XSLTProcessor();
|
|
59
86
|
|
|
60
87
|
// Load and transform XML
|
|
@@ -68,13 +95,20 @@ Use a CDN for the easiest browser integration - no build step required:
|
|
|
68
95
|
</script>
|
|
69
96
|
```
|
|
70
97
|
|
|
98
|
+
The bundle defines the global `XsltProcessorLib` (all [module exports](docs/API.md#module-exports))
|
|
99
|
+
and calls `installGlobal()`: `window.XSLTProcessor` is replaced only when the
|
|
100
|
+
browser has no working native implementation. To always use this
|
|
101
|
+
implementation, call `XsltProcessorLib.installGlobal(true)` or use
|
|
102
|
+
`new XsltProcessorLib.XSLTProcessor()`.
|
|
103
|
+
|
|
71
104
|
**CDN URLs:**
|
|
105
|
+
|
|
72
106
|
| CDN | URL |
|
|
73
107
|
|-----|-----|
|
|
74
108
|
| jsDelivr | `https://cdn.jsdelivr.net/npm/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js` |
|
|
75
109
|
| unpkg | `https://unpkg.com/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js` |
|
|
76
110
|
|
|
77
|
-
> **Tip:** Use `@1` for latest 1.x version, or `@1.
|
|
111
|
+
> **Tip:** Use `@1` for the latest 1.x version, or an exact version such as `@1.1.3` for pinning.
|
|
78
112
|
|
|
79
113
|
### Browser (Local Install)
|
|
80
114
|
|
|
@@ -124,784 +158,95 @@ const processor = new XSLTProcessor();
|
|
|
124
158
|
// ...
|
|
125
159
|
```
|
|
126
160
|
|
|
127
|
-
###
|
|
128
|
-
|
|
129
|
-
The package includes a command-line tool for transforming XML documents:
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
# Global installation
|
|
133
|
-
npm install -g @tradik/xslt-processor
|
|
134
|
-
|
|
135
|
-
# Transform XML with XSLT
|
|
136
|
-
xslt data.xml template.xsl
|
|
137
|
-
|
|
138
|
-
# Save output to file
|
|
139
|
-
xslt data.xml template.xsl -o result.html
|
|
140
|
-
|
|
141
|
-
# With parameters
|
|
142
|
-
xslt data.xml template.xsl -p title="My Page" -p count=10
|
|
143
|
-
|
|
144
|
-
# Format output with indentation
|
|
145
|
-
xslt data.xml template.xsl -f -o output.html
|
|
146
|
-
|
|
147
|
-
# Override the output method and drop the XML declaration
|
|
148
|
-
xslt data.xml template.xsl --method text
|
|
149
|
-
xslt data.xml template.xsl --no-declaration
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
The output is serialized according to the `xsl:output` element of the
|
|
153
|
-
stylesheet (see [Serializing output](#serializing-output-xsloutput)); the
|
|
154
|
-
options below override individual `xsl:output` settings.
|
|
155
|
-
|
|
156
|
-
All file arguments must live inside the current working directory (symbolic
|
|
157
|
-
links are resolved first). To work with files elsewhere, run the command from
|
|
158
|
-
that directory or point `XSLT_BASE_DIR` at it:
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
XSLT_BASE_DIR=/srv/data xslt /srv/data/in.xml /srv/data/t.xsl -o /srv/data/out.html
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
#### CLI Options
|
|
165
|
-
|
|
166
|
-
| Option | Description |
|
|
167
|
-
|--------|-------------|
|
|
168
|
-
| `-o, --output <file>` | Write output to file instead of stdout |
|
|
169
|
-
| `-p, --param <n>=<v>` | Set XSLT parameter (can be used multiple times) |
|
|
170
|
-
| `-f, --format` | Format output with indentation (same as `--indent`) |
|
|
171
|
-
| `--indent` | Override `xsl:output` to `indent="yes"` |
|
|
172
|
-
| `--method <m>` | Override the `xsl:output` method (`xml`, `html`, `xhtml`, `text`) |
|
|
173
|
-
| `--no-declaration` | Override `xsl:output` to omit the XML declaration |
|
|
174
|
-
| `-h, --help` | Show help message |
|
|
175
|
-
| `-v, --version` | Show version number |
|
|
176
|
-
|
|
177
|
-
## API Reference
|
|
178
|
-
|
|
179
|
-
### XSLTProcessor
|
|
180
|
-
|
|
181
|
-
#### Constructor
|
|
182
|
-
|
|
183
|
-
```javascript
|
|
184
|
-
const processor = new XSLTProcessor();
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
#### Methods
|
|
188
|
-
|
|
189
|
-
| Method | Description |
|
|
190
|
-
|--------|-------------|
|
|
191
|
-
| `importStylesheet(node, stylesheetUri?)` | Imports an XSLT stylesheet from a Document or Element node. The optional `stylesheetUri` is the base URI used to resolve relative `xsl:import`/`xsl:include` hrefs |
|
|
192
|
-
| `transformToFragment(source, output)` | Transforms XML and returns a DocumentFragment |
|
|
193
|
-
| `transformToDocument(source)` | Transforms XML and returns an XMLDocument |
|
|
194
|
-
| `transformToString(source)` | Transforms XML and returns the serialized result honoring `xsl:output` (non-W3C extension) |
|
|
195
|
-
| `setParameter(namespaceURI, localName, value)` | Sets an XSLT parameter |
|
|
196
|
-
| `getParameter(namespaceURI, localName)` | Gets an XSLT parameter value |
|
|
197
|
-
| `removeParameter(namespaceURI, localName)` | Removes an XSLT parameter |
|
|
198
|
-
| `clearParameters()` | Removes all parameters |
|
|
199
|
-
| `reset()` | Resets the processor, removing stylesheet and parameters (the stylesheet and document loaders are kept) |
|
|
200
|
-
| `setStylesheetLoader(loader)` | Sets the loader used to resolve `xsl:import`/`xsl:include`. Returns the processor for chaining |
|
|
201
|
-
| `setDocumentLoader(loader)` | Sets the loader used to resolve the XSLT `document()` function. Returns the processor for chaining |
|
|
202
|
-
|
|
203
|
-
#### Properties
|
|
204
|
-
|
|
205
|
-
| Property | Description |
|
|
206
|
-
|----------|-------------|
|
|
207
|
-
| `engine` | Read-only access to the underlying `XsltEngine` (advanced usage). It is `null` until `importStylesheet()` has been called |
|
|
208
|
-
|
|
209
|
-
### Serializing output (xsl:output)
|
|
210
|
-
|
|
211
|
-
`transformToString(source)` serializes the result tree according to the
|
|
212
|
-
`xsl:output` element of the stylesheet (XSLT 1.0 section 16). Unlike the DOM
|
|
213
|
-
based methods it returns ready to write markup, so the CLI and Node.js users do
|
|
214
|
-
not need a separate `XMLSerializer` or a hand rolled re-indenter.
|
|
215
|
-
|
|
216
|
-
```javascript
|
|
217
|
-
const xslt = parser.parseFromString(`<?xml version="1.0"?>
|
|
218
|
-
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
219
|
-
<xsl:output method="xml" indent="yes"/>
|
|
220
|
-
<xsl:template match="/"><BAR><QUX/></BAR></xsl:template>
|
|
221
|
-
</xsl:stylesheet>`, 'application/xml');
|
|
222
|
-
|
|
223
|
-
const processor = new XSLTProcessor();
|
|
224
|
-
processor.importStylesheet(xslt);
|
|
225
|
-
|
|
226
|
-
processor.transformToString(xmlDoc);
|
|
227
|
-
// <?xml version="1.0" encoding="UTF-8"?>
|
|
228
|
-
// <BAR>
|
|
229
|
-
// <QUX/>
|
|
230
|
-
// </BAR>
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Supported `xsl:output` attributes:
|
|
161
|
+
### Node.js
|
|
234
162
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
| `method="xhtml"` | XML rules with void elements written as `<br />` |
|
|
240
|
-
| `method="text"` | Concatenation of all text nodes, no escaping |
|
|
241
|
-
| `indent="yes"` | Newline plus two-space indentation for element-only content; mixed content and `pre`/`script`/`style`/`textarea` are left untouched |
|
|
242
|
-
| `encoding`, `version`, `standalone` | Written into the XML declaration |
|
|
243
|
-
| `omit-xml-declaration="yes"` | Suppresses the XML declaration |
|
|
244
|
-
| `doctype-public`, `doctype-system` | Emit a `<!DOCTYPE ...>` before the document element |
|
|
245
|
-
| `cdata-section-elements` | Text children of the listed elements are wrapped in `<![CDATA[...]]>`, split around any `]]>` |
|
|
246
|
-
| `media-type` | Parsed and exposed on the settings object |
|
|
163
|
+
Node.js has no DOM, so bring one such as `jsdom`. The
|
|
164
|
+
[filesystem loader example](docs/LOADERS.md#nodejs-example-filesystem-loader)
|
|
165
|
+
is a complete Node.js script, and the [API Reference](docs/API.md) covers every
|
|
166
|
+
method.
|
|
247
167
|
|
|
248
|
-
|
|
249
|
-
generated text is emitted verbatim, so `<b>` reaches the output as `<b>`.
|
|
168
|
+
### XSLT 2.0 and 3.0 (opt-in)
|
|
250
169
|
|
|
251
|
-
|
|
252
|
-
|
|
170
|
+
By default every stylesheet runs with the XSLT 1.0 engine, and a
|
|
171
|
+
`version="2.0"` stylesheet runs in forwards-compatible mode, as in Chrome.
|
|
172
|
+
With `xsltVersion: "auto"`, a stylesheet that declares version 2.0 or 3.0 is
|
|
173
|
+
run by [`@tradik/xslt3`](docs/XSLT3.md), an optional peer dependency loaded
|
|
174
|
+
with `import()` only when such a stylesheet is imported, so the XSLT 1.0
|
|
175
|
+
bundles do not grow by the size of the new engine:
|
|
253
176
|
|
|
254
177
|
```javascript
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
When `method` is absent (or `auto`), the output method is derived from the
|
|
261
|
-
result tree: `html` when the document element is `html` in no namespace, `xml`
|
|
262
|
-
otherwise.
|
|
263
|
-
|
|
264
|
-
### Parameters Example
|
|
265
|
-
|
|
266
|
-
```javascript
|
|
267
|
-
const processor = new XSLTProcessor();
|
|
268
|
-
processor.importStylesheet(xsltDoc);
|
|
269
|
-
|
|
270
|
-
// Set parameters
|
|
271
|
-
processor.setParameter(null, 'sortOrder', 'ascending');
|
|
272
|
-
processor.setParameter(null, 'itemsPerPage', 10);
|
|
178
|
+
// npm install @tradik/xslt3
|
|
179
|
+
const processor = new XSLTProcessor({ xsltVersion: "auto" });
|
|
180
|
+
const html = await processor.transformAsync(xmlText, { stylesheet: xsl20Text });
|
|
273
181
|
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
processor.clearParameters();
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### Using xsl:import and xsl:include
|
|
282
|
-
|
|
283
|
-
To use `xsl:import` and `xsl:include` elements in your stylesheets, configure a
|
|
284
|
-
stylesheet loader that tells the processor how to fetch external stylesheets.
|
|
285
|
-
|
|
286
|
-
Call `processor.setStylesheetLoader(...)` **before** `importStylesheet()` -
|
|
287
|
-
references are resolved while the main stylesheet is being compiled, so a loader
|
|
288
|
-
set afterwards is too late for that stylesheet (it still applies to the next
|
|
289
|
-
`importStylesheet()` call).
|
|
290
|
-
|
|
291
|
-
The loader is **synchronous**. It receives the resolved `href` and the `baseUri`
|
|
292
|
-
of the importing stylesheet, and must return either a `Document` or an XML
|
|
293
|
-
`string` (which is parsed automatically). Promises are not awaited, so pre-load
|
|
294
|
-
remote stylesheets before importing.
|
|
295
|
-
|
|
296
|
-
```javascript
|
|
297
|
-
import { XSLTProcessor } from '@tradik/xslt-processor';
|
|
298
|
-
|
|
299
|
-
const processor = new XSLTProcessor();
|
|
300
|
-
|
|
301
|
-
// Pre-loaded stylesheets, keyed by resolved URI
|
|
302
|
-
const stylesheets = {
|
|
303
|
-
'/styles/base.xsl': baseXslText,
|
|
304
|
-
'/styles/utils.xsl': utilsXslText,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
processor.setStylesheetLoader((href, baseUri) => {
|
|
308
|
-
// href: resolved URI of the xsl:import/xsl:include target
|
|
309
|
-
// baseUri: URI of the importing stylesheet (the stylesheetUri you passed in)
|
|
310
|
-
const xml = stylesheets[href];
|
|
311
|
-
if (!xml) {
|
|
312
|
-
throw new Error(`Unknown stylesheet: ${href} (from ${baseUri})`);
|
|
313
|
-
}
|
|
314
|
-
return xml; // a Document is also accepted
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
// The second argument is the base URI used to resolve relative hrefs
|
|
318
|
-
processor.importStylesheet(mainStylesheet, '/styles/main.xsl');
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
If you need remote stylesheets in the browser, fetch them first and hand the
|
|
322
|
-
loader a ready-made map:
|
|
323
|
-
|
|
324
|
-
```javascript
|
|
325
|
-
const hrefs = ['/styles/base.xsl', '/styles/utils.xsl'];
|
|
326
|
-
const texts = await Promise.all(
|
|
327
|
-
hrefs.map((href) => fetch(href).then((response) => response.text())),
|
|
328
|
-
);
|
|
329
|
-
const cache = Object.fromEntries(hrefs.map((href, i) => [href, texts[i]]));
|
|
330
|
-
|
|
331
|
-
processor.setStylesheetLoader((href) => cache[href]);
|
|
332
|
-
processor.importStylesheet(mainStylesheet, '/styles/main.xsl');
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
#### Node.js example (filesystem loader)
|
|
336
|
-
|
|
337
|
-
```javascript
|
|
338
|
-
import { readFileSync } from 'node:fs';
|
|
339
|
-
import path from 'node:path';
|
|
340
|
-
import { DOMParser } from '@xmldom/xmldom'; // or: new JSDOM(...).window.DOMParser
|
|
341
|
-
import { XSLTProcessor } from '@tradik/xslt-processor';
|
|
342
|
-
|
|
343
|
-
const parser = new DOMParser();
|
|
344
|
-
const mainPath = path.resolve('./styles/main.xsl');
|
|
345
|
-
|
|
346
|
-
const processor = new XSLTProcessor();
|
|
347
|
-
|
|
348
|
-
processor.setStylesheetLoader((href, baseUri) => {
|
|
349
|
-
const filePath = path.resolve(path.dirname(baseUri), href);
|
|
350
|
-
return readFileSync(filePath, 'utf8'); // returned XML string is parsed for you
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
const mainStylesheet = parser.parseFromString(
|
|
354
|
-
readFileSync(mainPath, 'utf8'),
|
|
355
|
-
'application/xml',
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
processor.importStylesheet(mainStylesheet, mainPath);
|
|
359
|
-
|
|
360
|
-
const xml = parser.parseFromString(readFileSync('./data.xml', 'utf8'), 'application/xml');
|
|
361
|
-
const result = processor.transformToDocument(xml);
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
#### Advanced: the engine accessor
|
|
365
|
-
|
|
366
|
-
After `importStylesheet()`, `processor.engine` exposes the underlying
|
|
367
|
-
`XsltEngine` for advanced inspection (output settings, compiled templates). It
|
|
368
|
-
is `null` before the first import, which is why the loader must be configured
|
|
369
|
-
through `processor.setStylesheetLoader(...)` rather than `processor.engine`.
|
|
370
|
-
|
|
371
|
-
### Using the document() function
|
|
372
|
-
|
|
373
|
-
The XSLT `document()` function loads additional XML documents at transformation
|
|
374
|
-
time. Configure a **synchronous** document loader with
|
|
375
|
-
`processor.setDocumentLoader(...)`; it receives the resolved URI and the base URI
|
|
376
|
-
and returns a `Document`, an XML `string` (parsed automatically) or `null`.
|
|
377
|
-
|
|
378
|
-
Semantics:
|
|
379
|
-
|
|
380
|
-
- `document('')` returns the stylesheet itself, per the XSLT 1.0 specification.
|
|
381
|
-
- A node-set argument loads one document per node and returns their union.
|
|
382
|
-
- Relative URIs are resolved against the `stylesheetUri` passed to
|
|
383
|
-
`importStylesheet()`; fragment identifiers are ignored.
|
|
384
|
-
- Without a loader, or when the loader returns `null`, `document()` evaluates to
|
|
385
|
-
an **empty node-set** instead of failing the transformation.
|
|
386
|
-
- Each resolved URI is loaded once and cached for the life of the processor.
|
|
387
|
-
|
|
388
|
-
```javascript
|
|
389
|
-
import { readFileSync } from 'node:fs';
|
|
390
|
-
import path from 'node:path';
|
|
391
|
-
import { XSLTProcessor } from '@tradik/xslt-processor';
|
|
392
|
-
|
|
393
|
-
const processor = new XSLTProcessor();
|
|
394
|
-
|
|
395
|
-
processor.setDocumentLoader((uri, baseUri) => {
|
|
396
|
-
const filePath = path.resolve(path.dirname(baseUri || '.'), uri);
|
|
397
|
-
try {
|
|
398
|
-
return readFileSync(filePath, 'utf8'); // XML string is parsed for you
|
|
399
|
-
} catch {
|
|
400
|
-
return null; // -> empty node-set, the transformation keeps going
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
processor.importStylesheet(mainStylesheet, '/styles/main.xsl');
|
|
405
|
-
const result = processor.transformToDocument(xmlDoc);
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
```xml
|
|
409
|
-
<xsl:template match="/">
|
|
410
|
-
<rate><xsl:value-of select="document('rates.xml')/rates/eur"/></rate>
|
|
411
|
-
</xsl:template>
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
#### Import vs Include Behavior
|
|
415
|
-
|
|
416
|
-
- **xsl:include**: Merges templates at the same precedence level. If multiple templates match, priority attribute decides.
|
|
417
|
-
- **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.
|
|
418
|
-
|
|
419
|
-
```xml
|
|
420
|
-
<!-- main.xsl -->
|
|
421
|
-
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
422
|
-
<xsl:import href="base.xsl"/> <!-- imported templates have lower precedence -->
|
|
423
|
-
<xsl:include href="utils.xsl"/> <!-- included templates have same precedence -->
|
|
424
|
-
|
|
425
|
-
<xsl:template match="item">
|
|
426
|
-
<!-- This template overrides the one from base.xsl -->
|
|
427
|
-
</xsl:template>
|
|
428
|
-
</xsl:stylesheet>
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
### Utility Functions
|
|
432
|
-
|
|
433
|
-
```javascript
|
|
434
|
-
import {
|
|
435
|
-
isNativeXSLTSupported,
|
|
436
|
-
installGlobal,
|
|
437
|
-
serializeResult,
|
|
438
|
-
markRawText
|
|
439
|
-
} from '@tradik/xslt-processor';
|
|
440
|
-
|
|
441
|
-
// Check if native XSLT is functional
|
|
442
|
-
if (!isNativeXSLTSupported()) {
|
|
443
|
-
console.log('Using JS implementation');
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Install as global XSLTProcessor
|
|
447
|
-
installGlobal(); // Only if native not available
|
|
448
|
-
installGlobal(true); // Force install
|
|
449
|
-
|
|
450
|
-
// Serialize any result tree with xsl:output settings
|
|
451
|
-
serializeResult(node, { method: 'xml', indent: 'yes' });
|
|
452
|
-
|
|
453
|
-
// Mark a text node so that it is emitted without escaping
|
|
454
|
-
markRawText(document.createTextNode('<b>raw</b>'));
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
## Complete Example
|
|
458
|
-
|
|
459
|
-
Here's a full example transforming a list of products into an HTML table:
|
|
460
|
-
|
|
461
|
-
**products.xml:**
|
|
462
|
-
```xml
|
|
463
|
-
<?xml version="1.0"?>
|
|
464
|
-
<products>
|
|
465
|
-
<product id="1">
|
|
466
|
-
<name>Widget</name>
|
|
467
|
-
<price>29.99</price>
|
|
468
|
-
<stock>150</stock>
|
|
469
|
-
</product>
|
|
470
|
-
<product id="2">
|
|
471
|
-
<name>Gadget</name>
|
|
472
|
-
<price>49.99</price>
|
|
473
|
-
<stock>75</stock>
|
|
474
|
-
</product>
|
|
475
|
-
</products>
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
**products.xsl:**
|
|
479
|
-
```xml
|
|
480
|
-
<?xml version="1.0"?>
|
|
481
|
-
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
482
|
-
<xsl:param name="title" select="'Product Catalog'"/>
|
|
483
|
-
|
|
484
|
-
<xsl:template match="/">
|
|
485
|
-
<html>
|
|
486
|
-
<head><title><xsl:value-of select="$title"/></title></head>
|
|
487
|
-
<body>
|
|
488
|
-
<h1><xsl:value-of select="$title"/></h1>
|
|
489
|
-
<table>
|
|
490
|
-
<tr><th>ID</th><th>Name</th><th>Price</th><th>Stock</th></tr>
|
|
491
|
-
<xsl:apply-templates select="products/product">
|
|
492
|
-
<xsl:sort select="name"/>
|
|
493
|
-
</xsl:apply-templates>
|
|
494
|
-
</table>
|
|
495
|
-
</body>
|
|
496
|
-
</html>
|
|
497
|
-
</xsl:template>
|
|
498
|
-
|
|
499
|
-
<xsl:template match="product">
|
|
500
|
-
<tr>
|
|
501
|
-
<td><xsl:value-of select="@id"/></td>
|
|
502
|
-
<td><xsl:value-of select="name"/></td>
|
|
503
|
-
<td>$<xsl:value-of select="price"/></td>
|
|
504
|
-
<td>
|
|
505
|
-
<xsl:choose>
|
|
506
|
-
<xsl:when test="stock > 100">In Stock</xsl:when>
|
|
507
|
-
<xsl:when test="stock > 0">Low Stock</xsl:when>
|
|
508
|
-
<xsl:otherwise>Out of Stock</xsl:otherwise>
|
|
509
|
-
</xsl:choose>
|
|
510
|
-
</td>
|
|
511
|
-
</tr>
|
|
512
|
-
</xsl:template>
|
|
513
|
-
</xsl:stylesheet>
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
**JavaScript:**
|
|
517
|
-
```javascript
|
|
518
|
-
import { XSLTProcessor } from '@tradik/xslt-processor';
|
|
519
|
-
|
|
520
|
-
const processor = new XSLTProcessor();
|
|
521
|
-
processor.importStylesheet(xsltDoc);
|
|
522
|
-
processor.setParameter(null, 'title', 'My Product List');
|
|
523
|
-
|
|
524
|
-
const result = processor.transformToFragment(xmlDoc, document);
|
|
525
|
-
document.body.appendChild(result);
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
**CLI:**
|
|
529
|
-
```bash
|
|
530
|
-
xslt products.xml products.xsl -p title="My Product List" -f -o catalog.html
|
|
182
|
+
// The synchronous W3C API needs the engine loaded first
|
|
183
|
+
await XSLTProcessor.preload();
|
|
184
|
+
processor.importStylesheet(xsl20Doc);
|
|
185
|
+
const fragment = processor.transformToFragment(xmlDoc, document);
|
|
531
186
|
```
|
|
532
187
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
The XPath evaluator includes comprehensive security hardening to prevent common attack vectors.
|
|
536
|
-
|
|
537
|
-
### DoS Prevention Limits
|
|
538
|
-
|
|
539
|
-
| Limit | Default | Description |
|
|
540
|
-
|-------|---------|-------------|
|
|
541
|
-
| `MAX_RECURSION_DEPTH` | 100 | Prevents stack overflow from deeply nested expressions |
|
|
542
|
-
| `MAX_RESULT_SIZE` | 10,000 | Prevents memory exhaustion from large result sets |
|
|
543
|
-
| `MAX_STRING_LENGTH` | 1,000,000 | Limits string processing to prevent memory issues |
|
|
544
|
-
|
|
545
|
-
### Prototype Pollution Protection
|
|
546
|
-
|
|
547
|
-
The following variable names are blocked:
|
|
548
|
-
- `__proto__`, `constructor`, `prototype`
|
|
549
|
-
- `__defineGetter__`, `__defineSetter__`
|
|
550
|
-
- `__lookupGetter__`, `__lookupSetter__`
|
|
188
|
+
The CLI flag is `--xslt-version auto`. Details: [Opt-in XSLT 2.0/3.0](docs/API.md#opt-in-xslt-2030).
|
|
551
189
|
|
|
552
|
-
|
|
190
|
+
## CLI Usage
|
|
553
191
|
|
|
554
|
-
|
|
555
|
-
- **Type Safety**: Strict type checking on all inputs
|
|
556
|
-
- **Safe Variable Lookup**: Uses `hasOwnProperty` to prevent prototype chain attacks
|
|
192
|
+
The package includes a command-line tool for transforming XML documents.
|
|
557
193
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
import { XPathEvaluator, XPathContext, parse } from '@tradik/xslt-processor';
|
|
562
|
-
|
|
563
|
-
const evaluator = new XPathEvaluator({
|
|
564
|
-
maxRecursionDepth: 50, // Lower for untrusted input
|
|
565
|
-
maxResultSize: 1000, // Limit result set size
|
|
566
|
-
maxStringLength: 10000 // Limit string operations
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
const ast = parse('//item');
|
|
570
|
-
const context = new XPathContext(xmlDoc);
|
|
571
|
-
const result = evaluator.evaluate(ast, context);
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
## XSLT Elements Supported
|
|
575
|
-
|
|
576
|
-
| Element | Status |
|
|
577
|
-
|---------|--------|
|
|
578
|
-
| `xsl:apply-templates` | Supported |
|
|
579
|
-
| `xsl:attribute` | Supported |
|
|
580
|
-
| `xsl:call-template` | Supported |
|
|
581
|
-
| `xsl:choose` / `when` / `otherwise` | Supported |
|
|
582
|
-
| `xsl:comment` | Supported |
|
|
583
|
-
| `xsl:copy` | Supported |
|
|
584
|
-
| `xsl:copy-of` | Supported |
|
|
585
|
-
| `xsl:element` | Supported |
|
|
586
|
-
| `xsl:for-each` | Supported |
|
|
587
|
-
| `xsl:if` | Supported |
|
|
588
|
-
| `xsl:message` | Supported |
|
|
589
|
-
| `xsl:number` | Supported |
|
|
590
|
-
| `xsl:output` | Supported |
|
|
591
|
-
| `xsl:param` | Supported |
|
|
592
|
-
| `xsl:processing-instruction` | Supported |
|
|
593
|
-
| `xsl:sort` | Supported |
|
|
594
|
-
| `xsl:template` | Supported |
|
|
595
|
-
| `xsl:text` | Supported |
|
|
596
|
-
| `xsl:value-of` | Supported |
|
|
597
|
-
| `xsl:variable` | Supported |
|
|
598
|
-
| `xsl:with-param` | Supported |
|
|
599
|
-
| `xsl:import` | Supported |
|
|
600
|
-
| `xsl:include` | Supported |
|
|
601
|
-
| `xsl:apply-imports` | Supported |
|
|
602
|
-
| `xsl:attribute-set` | Supported (also via `xsl:use-attribute-sets` on literal result elements) |
|
|
603
|
-
| `xsl:key` | Supported (see `key()`) |
|
|
604
|
-
| `xsl:decimal-format` | Supported (see `format-number()`) |
|
|
605
|
-
| `xsl:namespace-alias` | Supported |
|
|
606
|
-
| `xsl:strip-space` / `xsl:preserve-space` | Supported |
|
|
607
|
-
| `xsl:fallback` | Parsed, never instantiated (no extension elements) |
|
|
608
|
-
|
|
609
|
-
## XPath Functions Supported
|
|
610
|
-
|
|
611
|
-
### Node Set Functions
|
|
612
|
-
- `count()`, `id()`, `last()`, `local-name()`, `name()`, `namespace-uri()`, `position()`
|
|
613
|
-
|
|
614
|
-
### String Functions
|
|
615
|
-
- `concat()`, `contains()`, `normalize-space()`, `starts-with()`, `string()`, `string-length()`, `substring()`, `substring-after()`, `substring-before()`, `translate()`
|
|
616
|
-
|
|
617
|
-
### Boolean Functions
|
|
618
|
-
- `boolean()`, `false()`, `lang()`, `not()`, `true()`
|
|
619
|
-
|
|
620
|
-
### Number Functions
|
|
621
|
-
- `ceiling()`, `floor()`, `number()`, `round()`, `sum()`
|
|
622
|
-
|
|
623
|
-
### XSLT-Defined Functions
|
|
624
|
-
- `current()` - the XSLT current node, also inside predicates
|
|
625
|
-
- `document(object, base?)` - external documents, see `setDocumentLoader()`
|
|
626
|
-
- `element-available(name)`, `function-available(name)` - reflect the real element and function tables
|
|
627
|
-
- `format-number(number, pattern, decimalFormat?)` - full XSLT 1.0 picture strings, honouring `xsl:decimal-format`
|
|
628
|
-
- `generate-id(nodeSet?)` - stable identifier for the life of the transformation
|
|
629
|
-
- `key(name, value)` - `xsl:key` lookup, with lazily built per-document indexes
|
|
630
|
-
- `system-property(name)` - `xsl:version`, `xsl:vendor`, `xsl:vendor-url`
|
|
631
|
-
- `unparsed-entity-uri(name)` - always returns `''` (unparsed entities are not exposed by the DOM)
|
|
632
|
-
|
|
633
|
-
### Conformance Notes
|
|
634
|
-
- CDATA sections count as text everywhere (string-value, `text()`, `xsl:value-of`, `xsl:copy-of`)
|
|
635
|
-
- The identity transform `<xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template>` round-trips a document exactly
|
|
636
|
-
- `xsl:number` supports `level="single|multiple|any"` with `count`, `from` and the `1`, `01`, `a`, `A`, `i`, `I` format tokens
|
|
637
|
-
- The result tree is built in a neutral XML document and imported into the output
|
|
638
|
-
document at the end, so element names and namespaces survive an HTML owner document
|
|
639
|
-
|
|
640
|
-
## Development
|
|
641
|
-
|
|
642
|
-
### Prerequisites
|
|
643
|
-
|
|
644
|
-
- Node.js 22+ (native test runner; CI runs 22, 24 and 26)
|
|
645
|
-
- Docker (optional, for containerized testing)
|
|
646
|
-
|
|
647
|
-
### Setup
|
|
648
|
-
|
|
649
|
-
```bash
|
|
650
|
-
cd services/xslt-processor
|
|
651
|
-
npm install
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
### Commands
|
|
194
|
+
Without Node.js, use the standalone executable attached to every GitHub
|
|
195
|
+
release (Linux x64/arm64, macOS x64/arm64, Windows x64), installed with a
|
|
196
|
+
checksum check:
|
|
655
197
|
|
|
656
198
|
```bash
|
|
657
|
-
|
|
658
|
-
npm test
|
|
659
|
-
|
|
660
|
-
# Run tests with watch mode
|
|
661
|
-
npm run test:watch
|
|
662
|
-
|
|
663
|
-
# Build bundles
|
|
664
|
-
npm run build
|
|
665
|
-
|
|
666
|
-
# Lint code
|
|
667
|
-
npm run lint
|
|
668
|
-
|
|
669
|
-
# Format code
|
|
670
|
-
npm run format
|
|
199
|
+
curl -fsSL https://raw.githubusercontent.com/spagu/XSLT-Processor/main/scripts/install.sh | bash
|
|
671
200
|
```
|
|
672
201
|
|
|
673
|
-
|
|
202
|
+
See [Standalone executables](docs/CLI.md#standalone-executables) for manual
|
|
203
|
+
installation. With Node.js, the command line tool needs a DOM implementation, so install `jsdom` next to
|
|
204
|
+
the package. It is an optional peer dependency: library users do not need it.
|
|
205
|
+
Without it, `xslt` exits with an explanation instead of a stack trace.
|
|
674
206
|
|
|
675
207
|
```bash
|
|
676
|
-
#
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
# Development with hot reload
|
|
680
|
-
docker-compose run dev
|
|
681
|
-
|
|
682
|
-
# Build bundles
|
|
683
|
-
docker-compose run build
|
|
684
|
-
```
|
|
208
|
+
# Global installation
|
|
209
|
+
npm install -g @tradik/xslt-processor jsdom
|
|
685
210
|
|
|
686
|
-
|
|
211
|
+
# Transform XML with XSLT
|
|
212
|
+
xslt data.xml template.xsl
|
|
687
213
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
**Trusted Publishing** (OIDC): no `NPM_TOKEN` secret and no OTP are involved,
|
|
691
|
-
and every release carries provenance attestations.
|
|
214
|
+
# Save output to file
|
|
215
|
+
xslt data.xml template.xsl -o result.html
|
|
692
216
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
repository `XSLT-Processor`, workflow `release.yml`, environment left empty.
|
|
696
|
-
Alternatively add an `NPM_TOKEN` repository secret (granular access token
|
|
697
|
-
with publish rights and 2FA bypass); the publish step passes it as
|
|
698
|
-
`NODE_AUTH_TOKEN`. Without either, the `publish` job fails with `E404` and the
|
|
699
|
-
package must be published manually with
|
|
700
|
-
`npm publish --provenance --access public --otp=CODE`.
|
|
217
|
+
# With parameters
|
|
218
|
+
xslt data.xml template.xsl -p title="My Page" -p count=10
|
|
701
219
|
|
|
702
|
-
|
|
220
|
+
# Format output with indentation
|
|
221
|
+
xslt data.xml template.xsl -f -o output.html
|
|
703
222
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
223
|
+
# Override the output method and drop the XML declaration
|
|
224
|
+
xslt data.xml template.xsl --method text
|
|
225
|
+
xslt data.xml template.xsl --no-declaration
|
|
707
226
|
|
|
708
|
-
#
|
|
709
|
-
|
|
710
|
-
git tag v1.1.1
|
|
711
|
-
git push origin v1.1.1
|
|
227
|
+
# Run an XSLT 2.0/3.0 stylesheet with @tradik/xslt3 (npm install -g @tradik/xslt3)
|
|
228
|
+
xslt data.xml grouping.xsl --xslt-version auto
|
|
712
229
|
```
|
|
713
230
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
2. Builds the distribution bundles and verifies the package contents
|
|
717
|
-
3. Checks that the tag matches the `package.json` version
|
|
718
|
-
4. Uploads the `dist/` build artifacts to GitHub
|
|
719
|
-
5. Publishes to npm with provenance
|
|
720
|
-
|
|
721
|
-
## Browser Compatibility
|
|
231
|
+
See [Command Line Tool](docs/CLI.md) for all options, the base directory,
|
|
232
|
+
`xsl:include`/`document()` resolution and input encodings.
|
|
722
233
|
|
|
723
|
-
|
|
234
|
+
## Known Deviations
|
|
724
235
|
|
|
725
|
-
|
|
236
|
+
The main differences from XSLT 1.0 / XPath 1.0 and from libxslt:
|
|
726
237
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
| Firefox | 92+ | Yes | Fully Supported |
|
|
731
|
-
| Safari | 15.4+ | Yes | Fully Supported |
|
|
732
|
-
| Edge | 92+ | Yes | Fully Supported |
|
|
733
|
-
| Opera | 78+ | Yes | Fully Supported |
|
|
734
|
-
| Samsung Internet | 16+ | Yes | Fully Supported |
|
|
735
|
-
| Node.js | 20.19+ | Yes | Fully Supported (CI: 22, 24, 26) |
|
|
238
|
+
- `xsl:number` ignores `lang` and `letter-value`;
|
|
239
|
+
- `unparsed-entity-uri()` always returns `''`;
|
|
240
|
+
- numbers convert to strings in the XPath 1.0 form (`10000000000`), where libxslt writes `1e+10`.
|
|
736
241
|
|
|
737
|
-
|
|
242
|
+
Details and workarounds: [Known Deviations](docs/CONFORMANCE.md#known-deviations).
|
|
738
243
|
|
|
739
|
-
|
|
740
|
-
|---------|---------------------|--------------|
|
|
741
|
-
| Chrome | v143 (2026) | v164 (August 2027) |
|
|
742
|
-
| Edge | v143 (2026) | v164 (August 2027) |
|
|
743
|
-
| Other Chromium | v143 (2026) | v164 (August 2027) |
|
|
744
|
-
|
|
745
|
-
### Feature Detection
|
|
746
|
-
|
|
747
|
-
```javascript
|
|
748
|
-
import { isNativeXSLTSupported, installGlobal } from '@tradik/xslt-processor';
|
|
749
|
-
|
|
750
|
-
// Check native support and auto-install polyfill
|
|
751
|
-
if (!isNativeXSLTSupported()) {
|
|
752
|
-
installGlobal();
|
|
753
|
-
console.log('Using JavaScript XSLT polyfill');
|
|
754
|
-
}
|
|
755
|
-
```
|
|
244
|
+
## Contributing and Security
|
|
756
245
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
### Specifications Implemented
|
|
762
|
-
|
|
763
|
-
| Specification | Version | Status |
|
|
764
|
-
|---------------|---------|--------|
|
|
765
|
-
| [XPath 1.0](http://www.w3.org/TR/1999/REC-xpath-19991116) | W3C Recommendation, 16 November 1999 | Full Compliance |
|
|
766
|
-
| [XSLT 1.0](http://www.w3.org/TR/1999/REC-xslt-19991116) | W3C Recommendation, 16 November 1999 | Full Compliance |
|
|
767
|
-
| [DOM Level 3 Core](http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/) | W3C Recommendation, 7 April 2004 | Full Compliance |
|
|
768
|
-
|
|
769
|
-
### XSLT 1.0 Specification Compliance
|
|
770
|
-
|
|
771
|
-
| Section | Feature | Status | Notes |
|
|
772
|
-
|---------|---------|--------|-------|
|
|
773
|
-
| 2 | Stylesheet Structure | Supported | `xsl:stylesheet`, `xsl:transform` elements |
|
|
774
|
-
| 3 | Data Model | Supported | Seven node types per XPath data model |
|
|
775
|
-
| 5 | Template Rules | Supported | Pattern matching, priority calculation |
|
|
776
|
-
| 5.1 | Processing Model | Supported | Built-in templates for all node types |
|
|
777
|
-
| 5.2 | Patterns | Supported | All pattern syntax including predicates |
|
|
778
|
-
| 5.3 | Defining Template Rules | Supported | `match`, `name`, `priority`, `mode` attributes |
|
|
779
|
-
| 5.4 | Applying Template Rules | Supported | `xsl:apply-templates` with `select`, `mode` |
|
|
780
|
-
| 5.5 | Conflict Resolution | Supported | Import precedence and priority ordering |
|
|
781
|
-
| 6 | Named Templates | Supported | `xsl:call-template`, `xsl:with-param` |
|
|
782
|
-
| 7 | Creating Result Tree | Supported | Literal result elements, attribute value templates |
|
|
783
|
-
| 7.1.2 | Creating Elements | Supported | `xsl:element` with dynamic names/namespaces |
|
|
784
|
-
| 7.1.3 | Creating Attributes | Supported | `xsl:attribute` with dynamic names/namespaces |
|
|
785
|
-
| 7.2 | Creating Text | Supported | `xsl:value-of`, `xsl:text` |
|
|
786
|
-
| 7.3 | Creating PIs | Supported | `xsl:processing-instruction` |
|
|
787
|
-
| 7.4 | Creating Comments | Supported | `xsl:comment` |
|
|
788
|
-
| 7.5 | Copying | Supported | `xsl:copy`, `xsl:copy-of` |
|
|
789
|
-
| 7.6 | Attribute Sets | Supported | `xsl:attribute-set`, `use-attribute-sets` |
|
|
790
|
-
| 7.6.2 | Namespace Aliases | Supported | `xsl:namespace-alias` |
|
|
791
|
-
| 8 | Repetition | Supported | `xsl:for-each` |
|
|
792
|
-
| 9 | Conditional Processing | Supported | `xsl:if`, `xsl:choose`, `xsl:when`, `xsl:otherwise` |
|
|
793
|
-
| 10 | Sorting | Supported | `xsl:sort` with multiple keys, data-types, order |
|
|
794
|
-
| 11 | Variables/Parameters | Supported | `xsl:variable`, `xsl:param`, scoping rules |
|
|
795
|
-
| 11.1 | Result Tree Fragments | Supported | RTF handling as per spec |
|
|
796
|
-
| 12 | Additional Functions | Supported | `document()`, `key()`, `format-number()`, `current()`, `generate-id()`, `system-property()` |
|
|
797
|
-
| 12.3 | Number Formatting | Supported | `xsl:number` with all formatting options |
|
|
798
|
-
| 13 | Messages | Supported | `xsl:message` with `terminate` attribute |
|
|
799
|
-
| 14 | Extensions | Partial | `xsl:fallback` supported |
|
|
800
|
-
| 15 | Fallback | Supported | `xsl:fallback` element |
|
|
801
|
-
| 16 | Output | Supported | `xsl:output` honored by `transformToString()` / `serializeResult()` |
|
|
802
|
-
| 16.1 | XML Output Method | Supported | XML declaration (`encoding`, `version`, `standalone`), `omit-xml-declaration`, `doctype-public`/`doctype-system`, namespace declarations, `indent="yes"` for element-only content |
|
|
803
|
-
| 16.1 | CDATA Sections | Supported | `cdata-section-elements`, split around `]]>` |
|
|
804
|
-
| 16.2 | HTML Output Method | Supported | Void elements as `<br>`, minimized boolean attributes, unescaped `script`/`style`, no re-indent inside `pre`/`script`/`style`/`textarea` |
|
|
805
|
-
| 16.3 | Text Output Method | Supported | Concatenation of all text nodes, no escaping |
|
|
806
|
-
| 16.4 | Disabling Output Escaping | Supported | `disable-output-escaping` on `xsl:text` and `xsl:value-of` |
|
|
807
|
-
|
|
808
|
-
### XPath 1.0 Specification Compliance
|
|
809
|
-
|
|
810
|
-
| Section | Feature | Status | Notes |
|
|
811
|
-
|---------|---------|--------|-------|
|
|
812
|
-
| 2.1 | Location Steps | Supported | axis::node-test[predicate] |
|
|
813
|
-
| 2.2 | Axes | Supported | All 13 axes implemented |
|
|
814
|
-
| 2.3 | Node Tests | Supported | Name tests, `node()`, `text()`, `comment()`, `processing-instruction()` |
|
|
815
|
-
| 2.4 | Predicates | Supported | Position and boolean predicates |
|
|
816
|
-
| 2.5 | Abbreviated Syntax | Supported | `.`, `..`, `@`, `//` |
|
|
817
|
-
| 3.1 | Basics | Supported | Expression evaluation |
|
|
818
|
-
| 3.2 | Function Calls | Supported | All core functions |
|
|
819
|
-
| 3.3 | Node-sets | Supported | Union operator `\|` |
|
|
820
|
-
| 3.4 | Booleans | Supported | `and`, `or`, `not()` |
|
|
821
|
-
| 3.5 | Numbers | Supported | IEEE 754 double-precision |
|
|
822
|
-
| 3.6 | Strings | Supported | Unicode string handling |
|
|
823
|
-
| 3.7 | Lexical Structure | Supported | Full tokenization |
|
|
824
|
-
| 4.1 | Node Set Functions | Supported | `last()`, `position()`, `count()`, `id()`, `local-name()`, `namespace-uri()`, `name()` |
|
|
825
|
-
| 4.2 | String Functions | Supported | `string()`, `concat()`, `starts-with()`, `contains()`, `substring-before()`, `substring-after()`, `substring()`, `string-length()`, `normalize-space()`, `translate()` |
|
|
826
|
-
| 4.3 | Boolean Functions | Supported | `boolean()`, `not()`, `true()`, `false()`, `lang()` |
|
|
827
|
-
| 4.4 | Number Functions | Supported | `number()`, `sum()`, `floor()`, `ceiling()`, `round()` |
|
|
828
|
-
|
|
829
|
-
### XPath Axes Implementation
|
|
830
|
-
|
|
831
|
-
| Axis | Status | Description |
|
|
832
|
-
|------|--------|-------------|
|
|
833
|
-
| `child` | Supported | Children of context node |
|
|
834
|
-
| `descendant` | Supported | Descendants of context node |
|
|
835
|
-
| `parent` | Supported | Parent of context node |
|
|
836
|
-
| `ancestor` | Supported | Ancestors of context node |
|
|
837
|
-
| `following-sibling` | Supported | Following siblings |
|
|
838
|
-
| `preceding-sibling` | Supported | Preceding siblings |
|
|
839
|
-
| `following` | Supported | Nodes after context in document order |
|
|
840
|
-
| `preceding` | Supported | Nodes before context in document order |
|
|
841
|
-
| `attribute` | Supported | Attributes of context node |
|
|
842
|
-
| `namespace` | Supported | Namespace nodes |
|
|
843
|
-
| `self` | Supported | Context node itself |
|
|
844
|
-
| `descendant-or-self` | Supported | Context node and descendants |
|
|
845
|
-
| `ancestor-or-self` | Supported | Context node and ancestors |
|
|
846
|
-
|
|
847
|
-
### DOM Level 3 Core Compliance
|
|
848
|
-
|
|
849
|
-
| Interface | Status | Notes |
|
|
850
|
-
|-----------|--------|-------|
|
|
851
|
-
| `Node` | Supported | All node type constants |
|
|
852
|
-
| `Document` | Supported | `createElement`, `createTextNode`, `createComment`, etc. |
|
|
853
|
-
| `Element` | Supported | `getAttribute`, `setAttribute`, namespace methods |
|
|
854
|
-
| `Attr` | Supported | Attribute nodes with namespace support |
|
|
855
|
-
| `Text` | Supported | Text node handling |
|
|
856
|
-
| `Comment` | Supported | Comment nodes |
|
|
857
|
-
| `ProcessingInstruction` | Supported | PI nodes with target and data |
|
|
858
|
-
| `DocumentFragment` | Supported | Fragment handling in transforms |
|
|
859
|
-
| `NamedNodeMap` | Supported | Attribute collections |
|
|
860
|
-
| `NodeList` | Supported | Child node collections |
|
|
861
|
-
|
|
862
|
-
### Web API Compliance
|
|
863
|
-
|
|
864
|
-
This implementation provides full compatibility with the [MDN XSLTProcessor API](https://developer.mozilla.org/en-US/docs/Web/API/XSLTProcessor):
|
|
865
|
-
|
|
866
|
-
| Method | Status | Notes |
|
|
867
|
-
|--------|--------|-------|
|
|
868
|
-
| `importStylesheet(node)` | Supported | Accepts Document or Element |
|
|
869
|
-
| `transformToFragment(source, output)` | Supported | Returns DocumentFragment |
|
|
870
|
-
| `transformToDocument(source)` | Supported | Returns XMLDocument |
|
|
871
|
-
| `transformToString(source)` | Extension | Not part of the W3C API; returns the `xsl:output` serialized result |
|
|
872
|
-
| `setParameter(namespaceURI, localName, value)` | Supported | Full namespace support |
|
|
873
|
-
| `getParameter(namespaceURI, localName)` | Supported | Returns parameter value |
|
|
874
|
-
| `removeParameter(namespaceURI, localName)` | Supported | Removes single parameter |
|
|
875
|
-
| `clearParameters()` | Supported | Removes all parameters |
|
|
876
|
-
| `reset()` | Supported | Resets processor state |
|
|
877
|
-
|
|
878
|
-
### Test Coverage by Specification
|
|
879
|
-
|
|
880
|
-
| Specification | Tests | Coverage |
|
|
881
|
-
|---------------|-------|----------|
|
|
882
|
-
| XSLT 1.0 Elements | 82+ | 100% of supported elements |
|
|
883
|
-
| XPath 1.0 Functions | 50+ | 100% of core functions |
|
|
884
|
-
| XPath 1.0 Axes | 26+ | All 13 axes |
|
|
885
|
-
| DOM Level 3 | 20+ | Core interfaces |
|
|
886
|
-
| XSLTProcessor API | 39+ | All methods |
|
|
887
|
-
| Output Serialization | 79+ | `xsl:output`, CLI, `transformToString()` |
|
|
888
|
-
| Security | 34+ | DoS prevention, prototype pollution |
|
|
889
|
-
| **Total** | **560** | **100% line coverage** |
|
|
890
|
-
|
|
891
|
-
## Style Guide
|
|
892
|
-
|
|
893
|
-
### Colors
|
|
894
|
-
|
|
895
|
-
| Usage | Color | Hex |
|
|
896
|
-
|-------|-------|-----|
|
|
897
|
-
| Primary | Blue | `#2563eb` |
|
|
898
|
-
| Success | Green | `#16a34a` |
|
|
899
|
-
| Warning | Amber | `#d97706` |
|
|
900
|
-
| Error | Red | `#dc2626` |
|
|
901
|
-
| Text | Gray | `#1f2937` |
|
|
902
|
-
| Background | White | `#ffffff` |
|
|
903
|
-
|
|
904
|
-
All colors meet WCAG 2.2 AA contrast requirements for accessibility.
|
|
246
|
+
- [CONTRIBUTORS.md](CONTRIBUTORS.md) - how to contribute
|
|
247
|
+
- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
|
|
248
|
+
- [SECURITY.md](SECURITY.md) - reporting vulnerabilities and the threat model
|
|
249
|
+
- [CHANGELOG.md](CHANGELOG.md) - release history
|
|
905
250
|
|
|
906
251
|
## License
|
|
907
252
|
|