@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.
Files changed (122) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +102 -757
  3. package/bin/lib/decode.js +15 -0
  4. package/bin/lib/dom.js +177 -0
  5. package/bin/lib/loaders.js +127 -0
  6. package/bin/lib/options.js +17 -0
  7. package/bin/lib/output.js +114 -0
  8. package/bin/lib/paths.js +3 -3
  9. package/bin/lib/transform.js +124 -33
  10. package/bin/xslt.js +26 -27
  11. package/dist/xslt-processor.browser.js +8784 -2720
  12. package/dist/xslt-processor.browser.js.map +4 -4
  13. package/dist/xslt-processor.browser.min.js +13 -6
  14. package/dist/xslt-processor.browser.min.js.map +4 -4
  15. package/dist/xslt-processor.cjs +8789 -2723
  16. package/dist/xslt-processor.cjs.map +4 -4
  17. package/dist/xslt-processor.d.cts +380 -21
  18. package/dist/xslt-processor.d.ts +380 -21
  19. package/dist/xslt-processor.js +8770 -2722
  20. package/dist/xslt-processor.js.map +4 -4
  21. package/package.json +51 -11
  22. package/src/XSLTProcessor.js +343 -66
  23. package/src/async/abort.js +63 -0
  24. package/src/async/documentUris.js +128 -0
  25. package/src/async/loaders.js +134 -0
  26. package/src/async/preload.js +159 -0
  27. package/src/async/processor.js +206 -0
  28. package/src/async/stream.js +125 -0
  29. package/src/bridge/engine.js +221 -0
  30. package/src/bridge/loader.js +78 -0
  31. package/src/bridge/results.js +75 -0
  32. package/src/bridge/version.js +63 -0
  33. package/src/index.js +16 -4
  34. package/src/io/decode.js +140 -0
  35. package/src/io/readSource.js +167 -0
  36. package/src/xpath/axes.js +562 -0
  37. package/src/xpath/documentOrder.js +270 -0
  38. package/src/xpath/evaluator.js +475 -355
  39. package/src/xpath/index.js +8 -2
  40. package/src/xpath/namespaceNodes.js +172 -0
  41. package/src/xpath/nodeSetFunctions.js +169 -0
  42. package/src/xpath/parser.js +30 -5
  43. package/src/xpath/strings.js +183 -0
  44. package/src/xpath/tokenizer.js +37 -23
  45. package/src/xslt/attributeSets.js +95 -0
  46. package/src/xslt/avt.js +103 -0
  47. package/src/xslt/computedNames.js +91 -0
  48. package/src/xslt/copying.js +212 -0
  49. package/src/xslt/declarationNames.js +80 -0
  50. package/src/xslt/domParsing.js +95 -0
  51. package/src/xslt/elements.js +1 -1
  52. package/src/xslt/engine/bindings.js +195 -0
  53. package/src/xslt/engine/context.js +105 -0
  54. package/src/xslt/engine/controlFlow.js +145 -0
  55. package/src/xslt/engine/copyInstructions.js +133 -0
  56. package/src/xslt/engine/declarations.js +233 -0
  57. package/src/xslt/engine/functionSupport.js +103 -0
  58. package/src/xslt/engine/methods.js +33 -0
  59. package/src/xslt/engine/nodeConstruction.js +187 -0
  60. package/src/xslt/engine/numbering.js +104 -0
  61. package/src/xslt/engine/outputDeclaration.js +77 -0
  62. package/src/xslt/engine/sequenceConstructor.js +228 -0
  63. package/src/xslt/engine/stylesheetLoading.js +208 -0
  64. package/src/xslt/engine/templateInvocation.js +253 -0
  65. package/src/xslt/engine/templateRules.js +243 -0
  66. package/src/xslt/engine/textInstructions.js +171 -0
  67. package/src/xslt/engine/topLevel.js +130 -0
  68. package/src/xslt/engine/transformation.js +263 -0
  69. package/src/xslt/engine/workStack.js +245 -0
  70. package/src/xslt/engine.js +176 -2020
  71. package/src/xslt/exslt/arguments.js +99 -0
  72. package/src/xslt/exslt/calendar.js +120 -0
  73. package/src/xslt/exslt/common.js +44 -0
  74. package/src/xslt/exslt/dateCalc.js +261 -0
  75. package/src/xslt/exslt/dateFormat.js +150 -0
  76. package/src/xslt/exslt/dateParse.js +265 -0
  77. package/src/xslt/exslt/dates.js +259 -0
  78. package/src/xslt/exslt/duration.js +207 -0
  79. package/src/xslt/exslt/dynamic.js +59 -0
  80. package/src/xslt/exslt/index.js +59 -0
  81. package/src/xslt/exslt/math.js +177 -0
  82. package/src/xslt/exslt/sets.js +96 -0
  83. package/src/xslt/exslt/stringOps.js +163 -0
  84. package/src/xslt/exslt/strings.js +147 -0
  85. package/src/xslt/exslt/uri.js +92 -0
  86. package/src/xslt/formatNumber.js +22 -9
  87. package/src/xslt/forwardsCompatible.js +75 -0
  88. package/src/xslt/functions.js +94 -15
  89. package/src/xslt/index.js +7 -1
  90. package/src/xslt/keys.js +51 -28
  91. package/src/xslt/literalResult.js +63 -7
  92. package/src/xslt/matchScope.js +116 -0
  93. package/src/xslt/number.js +171 -78
  94. package/src/xslt/numberFormat.js +124 -26
  95. package/src/xslt/outputNames.js +58 -0
  96. package/src/xslt/patternCompiler.js +175 -0
  97. package/src/xslt/patterns.js +324 -0
  98. package/src/xslt/qname.js +90 -0
  99. package/src/xslt/resultDocument.js +98 -0
  100. package/src/xslt/resultNamespaces.js +219 -0
  101. package/src/xslt/resultTree.js +143 -6
  102. package/src/xslt/serializer/baseWriter.js +173 -66
  103. package/src/xslt/serializer/chunks.js +120 -0
  104. package/src/xslt/serializer/constants.js +14 -0
  105. package/src/xslt/serializer/encoding.js +327 -0
  106. package/src/xslt/serializer/escape.js +49 -12
  107. package/src/xslt/serializer/frames.js +168 -0
  108. package/src/xslt/serializer/htmlDoctype.js +102 -0
  109. package/src/xslt/serializer/htmlEntities.js +77 -0
  110. package/src/xslt/serializer/htmlSerializer.js +123 -25
  111. package/src/xslt/serializer/settings.js +89 -13
  112. package/src/xslt/serializer/textSerializer.js +58 -10
  113. package/src/xslt/serializer/xhtmlDocument.js +103 -0
  114. package/src/xslt/serializer/xmlSerializer.js +113 -13
  115. package/src/xslt/serializer.js +50 -17
  116. package/src/xslt/sort.js +151 -0
  117. package/src/xslt/spaceNameTests.js +115 -0
  118. package/src/xslt/stylesheetChecks.js +206 -0
  119. package/src/xslt/stylesheetNamespaces.js +266 -0
  120. package/src/xslt/variables.js +152 -0
  121. package/src/xslt/whitespace.js +43 -27
  122. package/LICENSE +0 -29
package/README.md CHANGED
@@ -5,11 +5,14 @@
5
5
  [![GitHub forks](https://img.shields.io/github/forks/spagu/XSLT-Processor?style=social)](https://github.com/spagu/XSLT-Processor/network/members)
6
6
 
7
7
  [![CI](https://github.com/spagu/XSLT-Processor/actions/workflows/test.yml/badge.svg)](https://github.com/spagu/XSLT-Processor/actions/workflows/test.yml)
8
+ [![Browser tests](https://github.com/spagu/XSLT-Processor/actions/workflows/browser.yml/badge.svg)](https://github.com/spagu/XSLT-Processor/actions/workflows/browser.yml)
8
9
  [![Release](https://github.com/spagu/XSLT-Processor/actions/workflows/release.yml/badge.svg)](https://github.com/spagu/XSLT-Processor/actions/workflows/release.yml)
9
10
  [![npm version](https://img.shields.io/npm/v/@tradik/xslt-processor.svg)](https://www.npmjs.com/package/@tradik/xslt-processor)
10
11
  [![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
11
12
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.19.0-brightgreen.svg)](https://nodejs.org/)
12
- [![Test Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/spagu/XSLT-Processor)
13
+ [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=spagu_XSLT-Processor&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=spagu_XSLT-Processor)
14
+ [![Line Coverage](https://img.shields.io/badge/line%20coverage-100%25-brightgreen.svg)](docs/CONFORMANCE.md#test-coverage)
15
+ [![TypeScript](https://img.shields.io/badge/types-included-3178c6.svg?logo=typescript&logoColor=white)](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 deprecating native XSLTProcessor support:
21
- - **Chrome 143+**: XSLTProcessor starts showing deprecation warnings
22
- - **Chrome 164 (August 2027)**: Full removal of native XSLT support
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
- - **Full XSLT 1.0 Support**: Implements the complete W3C XSLT 1.0 specification
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**: Standalone implementation with no external 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
- - **WCAG 2.2 Compliant**: Designed with accessibility in mind
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
- ## Usage
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 now available globally
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.0.0` for exact version pinning.
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
- ### CLI Usage
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
- | Attribute | Behavior |
236
- |-----------|----------|
237
- | `method="xml"` | XML declaration, minimal escaping, empty elements as `<x/>` (default) |
238
- | `method="html"` | No XML declaration, void elements as `<br>`, minimized boolean attributes, unescaped `script`/`style` |
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
- `disable-output-escaping="yes"` on `xsl:text` and `xsl:value-of` is honored: the
249
- generated text is emitted verbatim, so `&lt;b&gt;` reaches the output as `<b>`.
168
+ ### XSLT 2.0 and 3.0 (opt-in)
250
169
 
251
- The serializer can also be used on its own, for example on a fragment produced
252
- by `transformToFragment`:
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
- import { serializeResult } from '@tradik/xslt-processor';
256
-
257
- serializeResult(fragment, { method: 'html', indent: 'yes' });
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
- // Get parameter
275
- const sortOrder = processor.getParameter(null, 'sortOrder');
276
-
277
- // Clear parameters
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
- ## Security Features
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
- ### Input Validation
190
+ ## CLI Usage
553
191
 
554
- - **AST Validation**: All AST nodes are validated before evaluation
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
- ### Custom Security Limits
559
-
560
- ```javascript
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
- # Run tests
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
- ### Docker
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
- # Run tests in container
677
- docker-compose run test
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
- ### Publishing to npm
211
+ # Transform XML with XSLT
212
+ xslt data.xml template.xsl
687
213
 
688
- The package is published to npm automatically by the `Release` workflow when a
689
- `v*` tag is pushed (or a GitHub release is published). Publishing uses npm
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
- **One-time prerequisite** (npmjs.com -> package `@tradik/xslt-processor` ->
694
- Settings -> Trusted Publisher): provider *GitHub Actions*, owner `spagu`,
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
- **Release process:**
220
+ # Format output with indentation
221
+ xslt data.xml template.xsl -f -o output.html
703
222
 
704
- ```bash
705
- # 1. Bump the version in package.json, package-lock.json, src/index.js and
706
- # add a CHANGELOG.md entry, then commit to main.
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
- # 2. Tag and push the tag; the workflow refuses to publish if the tag does
709
- # not match package.json.
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
- **Automated workflow:**
715
- 1. Runs lint, formatting check and tests on Node.js 22, 24 and 26
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
- This library provides a JavaScript polyfill for XSLTProcessor that works across all modern browsers.
234
+ ## Known Deviations
724
235
 
725
- ### Polyfill Support
236
+ The main differences from XSLT 1.0 / XPath 1.0 and from libxslt:
726
237
 
727
- | Browser | Minimum Version | ES Modules | Status |
728
- |---------|-----------------|------------|--------|
729
- | Chrome | 92+ | Yes | Fully Supported |
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
- ### Native XSLT Deprecation Timeline
242
+ Details and workarounds: [Known Deviations](docs/CONFORMANCE.md#known-deviations).
738
243
 
739
- | Browser | Deprecation Warning | Full Removal |
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
- ## W3C Standards Compliance
758
-
759
- This implementation follows these W3C specifications with comprehensive test coverage to ensure compliance.
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