@tradik/xslt-processor 1.0.3 → 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 (131) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +110 -520
  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 +131 -0
  7. package/bin/lib/output.js +114 -0
  8. package/bin/lib/paths.js +186 -0
  9. package/bin/lib/transform.js +206 -0
  10. package/bin/xslt.js +73 -168
  11. package/dist/xslt-processor.browser.js +9564 -1585
  12. package/dist/xslt-processor.browser.js.map +4 -4
  13. package/dist/xslt-processor.browser.min.js +13 -2
  14. package/dist/xslt-processor.browser.min.js.map +4 -4
  15. package/dist/xslt-processor.cjs +9572 -1586
  16. package/dist/xslt-processor.cjs.map +4 -4
  17. package/dist/xslt-processor.d.cts +658 -0
  18. package/dist/xslt-processor.d.ts +459 -12
  19. package/dist/xslt-processor.js +9546 -1582
  20. package/dist/xslt-processor.js.map +4 -4
  21. package/package.json +71 -20
  22. package/src/XSLTProcessor.js +494 -48
  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 +26 -8
  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 +518 -357
  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 +57 -0
  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 +184 -1736
  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 +233 -0
  87. package/src/xslt/forwardsCompatible.js +75 -0
  88. package/src/xslt/functions.js +270 -0
  89. package/src/xslt/index.js +38 -1
  90. package/src/xslt/keys.js +164 -0
  91. package/src/xslt/literalResult.js +223 -0
  92. package/src/xslt/matchScope.js +116 -0
  93. package/src/xslt/number.js +271 -0
  94. package/src/xslt/numberFormat.js +253 -0
  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 +211 -0
  102. package/src/xslt/serializer/baseWriter.js +390 -0
  103. package/src/xslt/serializer/chunks.js +120 -0
  104. package/src/xslt/serializer/constants.js +92 -0
  105. package/src/xslt/serializer/encoding.js +327 -0
  106. package/src/xslt/serializer/escape.js +135 -0
  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 +239 -0
  111. package/src/xslt/serializer/indent.js +51 -0
  112. package/src/xslt/serializer/namespaces.js +68 -0
  113. package/src/xslt/serializer/rawText.js +41 -0
  114. package/src/xslt/serializer/settings.js +179 -0
  115. package/src/xslt/serializer/textSerializer.js +77 -0
  116. package/src/xslt/serializer/xhtmlDocument.js +103 -0
  117. package/src/xslt/serializer/xmlSerializer.js +227 -0
  118. package/src/xslt/serializer.js +90 -0
  119. package/src/xslt/sort.js +151 -0
  120. package/src/xslt/spaceNameTests.js +115 -0
  121. package/src/xslt/stylesheetChecks.js +206 -0
  122. package/src/xslt/stylesheetNamespaces.js +266 -0
  123. package/src/xslt/templatePriority.js +45 -0
  124. package/src/xslt/uri.js +68 -0
  125. package/src/xslt/variables.js +152 -0
  126. package/src/xslt/whitespace.js +200 -0
  127. package/LICENSE +0 -29
  128. package/src/XSLTProcessor.test.js +0 -930
  129. package/src/xpath/evaluator.test.js +0 -1852
  130. package/src/xpath/tokenizer.test.js +0 -224
  131. package/src/xslt/engine.test.js +0 -3130
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Streaming Output
3
+ *
4
+ * Serializes a transformation result as a WHATWG `ReadableStream<string>`.
5
+ * XSLT 1.0 builds the result tree in memory (templates can read anything
6
+ * they wrote into variables, and the whole source tree), so the tree is built
7
+ * first; what streams is the serialization: chunks are produced on demand
8
+ * (`pull`), one at a time, so the output is never held as one string and a
9
+ * consumer that reads slowly holds serialization back (backpressure).
10
+ *
11
+ * @module async/stream
12
+ */
13
+
14
+ import { serializeChunks } from "../xslt/serializer.js";
15
+ import { throwIfAborted } from "./abort.js";
16
+
17
+ /**
18
+ * Serialize the transformation of a source node in chunks, as
19
+ * `engine.transformToString` does in one piece.
20
+ *
21
+ * @param {import('../xslt/engine.js').XsltEngine|import('../bridge/engine.js').Xslt3Engine} engine -
22
+ * Engine with an imported stylesheet
23
+ * @param {Node} sourceNode - Source document or element
24
+ * @param {{chunkSize?: number}} [options] - Chunk size in UTF-16 code units
25
+ * @returns {Iterator<string>} The chunks; the result tree is built by this
26
+ * call, serialization happens as the iterator is consumed
27
+ *
28
+ * @example
29
+ * for (const chunk of transformToChunks(engine, xmlDoc)) out.write(chunk);
30
+ */
31
+ export function transformToChunks(engine, sourceNode, options = {}) {
32
+ // The @tradik/xslt3 engine (xsltVersion "auto") serializes on its own
33
+ if (engine.transformToChunks) {
34
+ return engine.transformToChunks(sourceNode, options);
35
+ }
36
+ const fragment = engine.buildResultTree(
37
+ sourceNode,
38
+ engine.createDocument(sourceNode),
39
+ );
40
+ return serializeChunks(fragment, engine.outputSettings, options);
41
+ }
42
+
43
+ /**
44
+ * Expose lazily produced chunks as a ReadableStream.
45
+ *
46
+ * @param {() => (Iterator<string>|Promise<Iterator<string>>)} start - Called
47
+ * once, on the first pull, to produce the chunk iterator (may be async, e.g.
48
+ * to read the input first)
49
+ * @param {{signal?: AbortSignal}} [options] - Aborting errors the stream with
50
+ * the abort reason and stops the serialization
51
+ * @returns {ReadableStream<string>} The stream; errors of `start` or of the
52
+ * serialization error the stream
53
+ * @throws {TypeError} When the runtime has no ReadableStream
54
+ *
55
+ * @example
56
+ * const stream = chunkStream(() => serializeChunks(doc, settings));
57
+ */
58
+ export function chunkStream(start, options = {}) {
59
+ const { signal } = options;
60
+ const Stream = globalThis.ReadableStream;
61
+ if (typeof Stream !== "function") {
62
+ throw new TypeError("ReadableStream is not available in this runtime");
63
+ }
64
+
65
+ let chunks = null;
66
+ const stop = () => chunks?.return?.();
67
+ let onAbort = null;
68
+
69
+ return new Stream(
70
+ {
71
+ start(controller) {
72
+ if (signal?.aborted) {
73
+ controller.error(signal.reason);
74
+ return;
75
+ }
76
+ onAbort = () => {
77
+ stop();
78
+ controller.error(signal.reason);
79
+ };
80
+ signal?.addEventListener("abort", onAbort, { once: true });
81
+ },
82
+ async pull(controller) {
83
+ try {
84
+ chunks ??= await start();
85
+ throwIfAborted(signal);
86
+ const { done, value } = chunks.next();
87
+ if (done) {
88
+ signal?.removeEventListener("abort", onAbort);
89
+ controller.close();
90
+ } else {
91
+ controller.enqueue(value);
92
+ }
93
+ } catch (error) {
94
+ signal?.removeEventListener("abort", onAbort);
95
+ throw error;
96
+ }
97
+ },
98
+ cancel() {
99
+ signal?.removeEventListener("abort", onAbort);
100
+ stop();
101
+ },
102
+ },
103
+ { highWaterMark: 1 },
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Transform a source node and stream the serialized result.
109
+ *
110
+ * @param {import('../xslt/engine.js').XsltEngine} engine - Engine with an imported stylesheet
111
+ * @param {Node} sourceNode - Source document or element
112
+ * @param {{signal?: AbortSignal, chunkSize?: number}} [options] - Options
113
+ * @returns {ReadableStream<string>} The serialized result
114
+ *
115
+ * @example
116
+ * const response = new Response(
117
+ * transformToStream(engine, xmlDoc).pipeThrough(new TextEncoderStream()),
118
+ * );
119
+ */
120
+ export function transformToStream(engine, sourceNode, options = {}) {
121
+ return chunkStream(
122
+ () => transformToChunks(engine, sourceNode, options),
123
+ options,
124
+ );
125
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Xslt3Engine: @tradik/xslt3 behind the interface of the XSLT 1.0 engine
3
+ *
4
+ * XSLTProcessor drives its engine through a small interface (loaders,
5
+ * parameters, importStylesheet, transformToFragment/Document/String,
6
+ * outputSettings). This class implements it with `compileStylesheet` and
7
+ * `CompiledStylesheet#transform` of @tradik/xslt3, so the W3C API, the
8
+ * asynchronous API and the command line tool run XSLT 2.0/3.0 stylesheets
9
+ * unchanged when `xsltVersion` is "auto".
10
+ *
11
+ * @module bridge/engine
12
+ */
13
+
14
+ import { parseXml, resolveDomParser } from "../xslt/domParsing.js";
15
+ import { transformationMethods } from "../xslt/engine/transformation.js";
16
+ import { loadedXslt3 } from "./loader.js";
17
+ import { camelCaseOutput, documentResult, fragmentResult } from "./results.js";
18
+ import { stylesheetVersion } from "./version.js";
19
+
20
+ /**
21
+ * A parameter value for @tradik/xslt3: node lists become arrays (sequences
22
+ * of nodes); other values are converted by @tradik/xslt3 (strings to
23
+ * xs:string, numbers to xs:double, booleans, nodes, arrays, Dates, maps).
24
+ *
25
+ * @param {*} value - Value given to setParameter
26
+ * @returns {*} The value to pass
27
+ */
28
+ function parameterValue(value) {
29
+ const isNodeList =
30
+ typeof value?.length === "number" && typeof value.item === "function";
31
+ return isNodeList ? Array.from(value) : value;
32
+ }
33
+
34
+ /** Runs XSLT 2.0/3.0 stylesheets with @tradik/xslt3. */
35
+ export class Xslt3Engine {
36
+ /**
37
+ * @param {object} xslt3 - The @tradik/xslt3 module
38
+ * @param {object} [options] - Options of XSLTProcessor
39
+ * @param {Function|null} [options.stylesheetLoader] - xsl:import/include loader
40
+ * @param {Function|null} [options.documentLoader] - doc()/document() loader
41
+ * @param {(() => Date)|null} [options.clock] - current-dateTime() clock
42
+ */
43
+ constructor(xslt3, options = {}) {
44
+ this.xslt3 = xslt3;
45
+ this.stylesheetLoader = options.stylesheetLoader ?? null;
46
+ this.documentLoader = options.documentLoader ?? null;
47
+ this.clock = options.clock ?? null;
48
+ /** @type {Map<string, *>} Parameters by `{uri}local` or `local` */
49
+ this.parameters = new Map();
50
+ this.compiled = null;
51
+ this.stylesheetDoc = null;
52
+ /** Output settings (camelCase); changes override the stylesheet's. */
53
+ this.outputSettings = {};
54
+ this.declaredOutput = {};
55
+ }
56
+
57
+ /** @param {Function|null} loader - xsl:import/xsl:include loader */
58
+ setStylesheetLoader(loader) {
59
+ this.stylesheetLoader = loader;
60
+ }
61
+
62
+ /** @param {Function|null} loader - doc()/document() loader */
63
+ setDocumentLoader(loader) {
64
+ this.documentLoader = loader;
65
+ }
66
+
67
+ /**
68
+ * @param {string} key - `{uri}local` or `local`
69
+ * @param {*} value - The value
70
+ */
71
+ setParameterValue(key, value) {
72
+ this.parameters.set(key, parameterValue(value));
73
+ }
74
+
75
+ /** @param {string} key - `{uri}local` or `local` */
76
+ clearParameterValue(key) {
77
+ this.parameters.delete(key);
78
+ }
79
+
80
+ /** Remove every parameter. */
81
+ clearParameterValues() {
82
+ this.parameters.clear();
83
+ }
84
+
85
+ /**
86
+ * Parse markup returned by a loader.
87
+ *
88
+ * @param {string} text - XML markup
89
+ * @returns {Document} The document
90
+ */
91
+ parseXmlString(text) {
92
+ return parseXml(text, resolveDomParser(null, this.stylesheetDoc));
93
+ }
94
+
95
+ /**
96
+ * A loader result as a document (markup is parsed).
97
+ *
98
+ * @param {Document|string|null} loaded - What a loader returned
99
+ * @returns {Document|null} The document
100
+ */
101
+ toDocument(loaded) {
102
+ return typeof loaded === "string" ? this.parseXmlString(loaded) : loaded;
103
+ }
104
+
105
+ /**
106
+ * Compile a stylesheet.
107
+ *
108
+ * @param {Node} style - Stylesheet document or root element
109
+ * @param {string} [stylesheetUri] - Its URI
110
+ * @returns {void}
111
+ * @throws {Error} The static errors of @tradik/xslt3 (XTSE...)
112
+ */
113
+ importStylesheet(style, stylesheetUri) {
114
+ this.stylesheetDoc = style.ownerDocument ?? style;
115
+ this.compiled = this.xslt3.compileStylesheet(style, {
116
+ baseUri: stylesheetUri,
117
+ // A module returned as markup is parsed with parseXml
118
+ loadStylesheet: (uri) => this.stylesheetLoader?.(uri),
119
+ parseXml: (text) => this.parseXmlString(text),
120
+ });
121
+ const declared = this.compiled.output;
122
+ this.declaredOutput = camelCaseOutput({ encoding: "UTF-8", ...declared });
123
+ this.outputSettings = { ...this.declaredOutput };
124
+ }
125
+
126
+ /**
127
+ * Run the stylesheet on a source.
128
+ *
129
+ * @param {Node} source - Source document or element
130
+ * @returns {{principal: Node, params: object}} The principal result and
131
+ * its serialization parameters
132
+ */
133
+ run(source) {
134
+ const loader = this.documentLoader;
135
+ const result = this.compiled.transform({
136
+ source,
137
+ params: this.parameters,
138
+ documentLoader: loader && ((uri) => this.toDocument(loader(uri))),
139
+ onMessage: (message) => console.log("XSLT Message:", message.textContent),
140
+ currentDateTime: this.clock?.(),
141
+ });
142
+ return { principal: result.principal, params: this.paramsOf(result) };
143
+ }
144
+
145
+ /**
146
+ * The serialization parameters of a result: those @tradik/xslt3 reports,
147
+ * with the output settings changed through `outputSettings` on top.
148
+ *
149
+ * @param {{output: object}} result - A transformation result
150
+ * @returns {Record<string, *>} camelCase parameters
151
+ */
152
+ paramsOf(result) {
153
+ const params = camelCaseOutput(result.output);
154
+ for (const [name, value] of Object.entries(this.outputSettings)) {
155
+ if (value !== this.declaredOutput[name]) params[name] = value;
156
+ }
157
+ return params;
158
+ }
159
+
160
+ /**
161
+ * @param {Node} source - Source document or element
162
+ * @param {Document} output - Document that owns the fragment
163
+ * @returns {DocumentFragment} The result fragment
164
+ */
165
+ transformToFragment(source, output) {
166
+ const { principal, params } = this.run(source);
167
+ return fragmentResult(principal, params, output, this.xslt3.serialize);
168
+ }
169
+
170
+ /**
171
+ * @param {Node} source - Source document or element
172
+ * @returns {Document} The result document
173
+ */
174
+ transformToDocument(source) {
175
+ const { principal, params } = this.run(source);
176
+ const doc = transformationMethods.createDocument(source);
177
+ return documentResult(principal, params, doc, this.xslt3.serialize);
178
+ }
179
+
180
+ /**
181
+ * @param {Node} source - Source document or element
182
+ * @returns {string} The serialized result
183
+ */
184
+ transformToString(source) {
185
+ const { principal, params } = this.run(source);
186
+ return this.xslt3.serialize(principal, params);
187
+ }
188
+
189
+ /**
190
+ * The serialized result in chunks (transformToStream, the CLI).
191
+ *
192
+ * @param {Node} source - Source document or element
193
+ * @param {{chunkSize?: number}} [options] - Chunk size
194
+ * @returns {Iterator<string>} The chunks
195
+ */
196
+ transformToChunks(source, options = {}) {
197
+ const { principal, params } = this.run(source);
198
+ return this.xslt3.serializeChunks(principal, params, options);
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Create the engine of an XSLT 2.0/3.0 stylesheet, with the @tradik/xslt3
204
+ * module loaded before (XSLTProcessor.preload or the asynchronous API).
205
+ *
206
+ * @param {Node} style - The stylesheet (for the error message)
207
+ * @param {object} options - See {@link Xslt3Engine}
208
+ * @returns {Xslt3Engine} The engine
209
+ * @throws {Error} When @tradik/xslt3 has not been loaded
210
+ */
211
+ export function createXslt3Engine(style, options) {
212
+ const xslt3 = loadedXslt3();
213
+ if (!xslt3) {
214
+ throw new Error(
215
+ `This XSLT ${stylesheetVersion(style).toFixed(1)} stylesheet needs @tradik/xslt3: ` +
216
+ 'call "await XSLTProcessor.preload()" before importStylesheet(), ' +
217
+ "or use importStylesheetAsync() / transformAsync()",
218
+ );
219
+ }
220
+ return new Xslt3Engine(xslt3, options);
221
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Loading @tradik/xslt3
3
+ *
4
+ * @tradik/xslt3 is an optional peer dependency: it is loaded with a dynamic
5
+ * `import()` the first time an XSLT 2.0/3.0 stylesheet needs it (or when
6
+ * `XSLTProcessor.preload()` is called), never statically, so the XSLT 1.0
7
+ * bundles do not contain it. Once loaded it is kept for the synchronous API.
8
+ *
9
+ * @module bridge/loader
10
+ */
11
+
12
+ /** What to do when @tradik/xslt3 is missing. */
13
+ export const XSLT3_MISSING =
14
+ "install @tradik/xslt3 to run XSLT 2.0/3.0 stylesheets (npm install @tradik/xslt3)";
15
+
16
+ /**
17
+ * The default importer. The specifier is a literal so that bundlers of the
18
+ * application can resolve it; the build of this package keeps it external.
19
+ *
20
+ * @returns {Promise<object>} The @tradik/xslt3 module
21
+ */
22
+ const importXslt3 = () => import("@tradik/xslt3");
23
+
24
+ let importer = importXslt3;
25
+ let loaded = null;
26
+ let pending = null;
27
+
28
+ /**
29
+ * Load @tradik/xslt3 once; concurrent calls share the import.
30
+ *
31
+ * @returns {Promise<object>} The @tradik/xslt3 module
32
+ * @throws {Error} "Cannot load @tradik/xslt3: install ..." when the import
33
+ * fails (the import error is the `cause`); a later call tries again
34
+ *
35
+ * @example
36
+ * const { compileStylesheet } = await loadXslt3();
37
+ */
38
+ export function loadXslt3() {
39
+ if (loaded) return Promise.resolve(loaded);
40
+ pending ??= Promise.resolve()
41
+ .then(() => importer())
42
+ .then(
43
+ (module) => {
44
+ loaded = module;
45
+ return module;
46
+ },
47
+ (error) => {
48
+ pending = null;
49
+ throw new Error(`Cannot load @tradik/xslt3: ${XSLT3_MISSING}`, {
50
+ cause: error,
51
+ });
52
+ },
53
+ );
54
+ return pending;
55
+ }
56
+
57
+ /**
58
+ * The module loaded by {@link loadXslt3}, for the synchronous API.
59
+ *
60
+ * @returns {object|null} The module, null before it has been loaded
61
+ */
62
+ export function loadedXslt3() {
63
+ return loaded;
64
+ }
65
+
66
+ /**
67
+ * Replace the importer of @tradik/xslt3 and forget the loaded module
68
+ * (tests: simulate a missing package without uninstalling it).
69
+ *
70
+ * @param {(() => Promise<object>)|null} [replacement] - The importer, or
71
+ * null for the default `import("@tradik/xslt3")`
72
+ * @returns {void}
73
+ */
74
+ export function setXslt3Importer(replacement) {
75
+ importer = replacement ?? importXslt3;
76
+ loaded = null;
77
+ pending = null;
78
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Shaping @tradik/xslt3 Results for the W3C API
3
+ *
4
+ * The principal result of @tradik/xslt3 is a document (a well-formed tree)
5
+ * or a document fragment. `transformToFragment` and `transformToDocument`
6
+ * return it shaped as the XSLT 1.0 engine shapes its own results (as
7
+ * Chrome does): html output parsed by the HTML parser of an HTML owner
8
+ * document, text output wrapped in a `pre` page, xml output as a document
9
+ * with the `doctype-public`/`doctype-system` of xsl:output.
10
+ *
11
+ * @module bridge/results
12
+ */
13
+
14
+ import {
15
+ importResultFragment,
16
+ isHtmlDocument,
17
+ parseHtmlFragment,
18
+ wrapTextResult,
19
+ } from "../xslt/resultTree.js";
20
+ import { fillXmlDocument, parseHtmlDocument } from "../xslt/resultDocument.js";
21
+
22
+ /**
23
+ * Serialization parameters by camelCase name (`omit-xml-declaration`
24
+ * becomes `omitXmlDeclaration`), the form the XSLT 1.0 output settings use.
25
+ *
26
+ * @param {Record<string, *>} output - Parameters of @tradik/xslt3
27
+ * @returns {Record<string, *>} The same parameters, camelCase names
28
+ *
29
+ * @example
30
+ * camelCaseOutput({ "omit-xml-declaration": "yes" }); // { omitXmlDeclaration: "yes" }
31
+ */
32
+ export function camelCaseOutput(output) {
33
+ return Object.fromEntries(
34
+ Object.entries(output).map(([name, value]) => [
35
+ name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()),
36
+ value,
37
+ ]),
38
+ );
39
+ }
40
+
41
+ /**
42
+ * The result as a fragment owned by `output`.
43
+ *
44
+ * @param {Node} principal - Principal result (document or fragment)
45
+ * @param {Record<string, *>} params - Its serialization parameters
46
+ * @param {Document} output - The owner document
47
+ * @param {(node: Node, params: object) => string} serialize - Serializer
48
+ * @returns {DocumentFragment} The fragment
49
+ */
50
+ export function fragmentResult(principal, params, output, serialize) {
51
+ if (isHtmlDocument(output) && params.method === "html") {
52
+ return parseHtmlFragment(serialize(principal, params), output);
53
+ }
54
+ return importResultFragment(principal, output, { htmlElements: true });
55
+ }
56
+
57
+ /**
58
+ * The result as a document.
59
+ *
60
+ * @param {Node} principal - Principal result (document or fragment)
61
+ * @param {Record<string, *>} params - Its serialization parameters
62
+ * @param {Document} doc - An empty document to fill
63
+ * @param {(node: Node, params: object) => string} serialize - Serializer
64
+ * @returns {Document} The result document
65
+ */
66
+ export function documentResult(principal, params, doc, serialize) {
67
+ if (params.method === "text") {
68
+ return wrapTextResult(doc, serialize(principal, params));
69
+ }
70
+ if (params.method === "html") {
71
+ const html = parseHtmlDocument(serialize(principal, params), doc);
72
+ if (html) return html;
73
+ }
74
+ return fillXmlDocument(doc, importResultFragment(principal, doc), params);
75
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * XSLT Version Selection
3
+ *
4
+ * The `xsltVersion` option of XSLTProcessor: "1.0" (the default) runs every
5
+ * stylesheet with the XSLT 1.0 engine, a stylesheet declaring a higher
6
+ * version in forwards-compatible mode (as Chrome does); "auto" hands a
7
+ * stylesheet whose effective version is 2.0 or more to @tradik/xslt3.
8
+ *
9
+ * @module bridge/version
10
+ */
11
+
12
+ import { declaredVersion } from "../xslt/forwardsCompatible.js";
13
+
14
+ /** Values accepted by the `xsltVersion` option. */
15
+ export const XSLT_VERSION_MODES = Object.freeze(["1.0", "auto"]);
16
+
17
+ /**
18
+ * Validate the `xsltVersion` option.
19
+ *
20
+ * @param {string|undefined|null} mode - The option value
21
+ * @returns {"1.0"|"auto"} The mode, "1.0" when not given
22
+ * @throws {RangeError} For any other value
23
+ *
24
+ * @example
25
+ * xsltVersionMode(undefined); // "1.0"
26
+ */
27
+ export function xsltVersionMode(mode) {
28
+ const value = mode ?? "1.0";
29
+ if (!XSLT_VERSION_MODES.includes(value)) {
30
+ throw new RangeError(
31
+ `Invalid xsltVersion "${value}": expected "1.0" or "auto"`,
32
+ );
33
+ }
34
+ return value;
35
+ }
36
+
37
+ /**
38
+ * The effective version of a stylesheet: the `version` attribute of
39
+ * `xsl:stylesheet`/`xsl:transform`, or the `xsl:version` attribute of a
40
+ * simplified stylesheet (literal result element).
41
+ *
42
+ * @param {Node} style - Stylesheet document or root element
43
+ * @returns {number} The version, NaN when missing or not a number
44
+ *
45
+ * @example
46
+ * stylesheetVersion(xslDoc); // 2 for <xsl:stylesheet version="2.0">
47
+ */
48
+ export function stylesheetVersion(style) {
49
+ const root = style.nodeType === 9 ? style.documentElement : style;
50
+ const version = root ? declaredVersion(root) : null;
51
+ return version ? Number(version.trim()) : NaN;
52
+ }
53
+
54
+ /**
55
+ * Whether a stylesheet is run by @tradik/xslt3 in the given mode.
56
+ *
57
+ * @param {Node} style - Stylesheet document or root element
58
+ * @param {"1.0"|"auto"} mode - The `xsltVersion` option
59
+ * @returns {boolean} True in "auto" mode for version 2.0 and above
60
+ */
61
+ export function usesXslt3(style, mode) {
62
+ return mode === "auto" && stylesheetVersion(style) >= 2;
63
+ }
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * xslt-processor
2
+ * @tradik/xslt-processor
3
3
  *
4
4
  * JavaScript implementation of XSLTProcessor for browser environments.
5
5
  *
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * @example
15
15
  * // ESM import
16
- * import { XSLTProcessor, installGlobal } from 'xslt-processor';
16
+ * import { XSLTProcessor, installGlobal } from '@tradik/xslt-processor';
17
17
  *
18
18
  * // Install as global replacement
19
19
  * installGlobal();
@@ -29,7 +29,7 @@ export {
29
29
  XSLTProcessor,
30
30
  isNativeXSLTSupported,
31
31
  installGlobal,
32
- default as default,
32
+ default,
33
33
  } from "./XSLTProcessor.js";
34
34
 
35
35
  // XPath module (for advanced users)
@@ -40,16 +40,36 @@ export {
40
40
  XPathEvaluator,
41
41
  XPathContext,
42
42
  XPathResultType,
43
+ XPathLimits,
43
44
  parse as parseXPath,
44
45
  } from "./xpath/index.js";
45
46
 
46
47
  // XSLT engine (for advanced users)
47
- export { XsltEngine, XsltContext } from "./xslt/index.js";
48
+ export {
49
+ XsltEngine,
50
+ XsltContext,
51
+ XSLT_MAX_RESULT_SIZE,
52
+ XSLT_MAX_EXPRESSION_DEPTH,
53
+ XSLT_MAX_TEMPLATE_DEPTH,
54
+ } from "./xslt/index.js";
55
+
56
+ // Output serializer (xsl:output, XSLT 1.0 section 16)
57
+ export {
58
+ serializeResult,
59
+ serializeChunks,
60
+ DEFAULT_CHUNK_SIZE,
61
+ markRawText,
62
+ isRawText,
63
+ resolveOutputSettings,
64
+ } from "./xslt/serializer.js";
65
+
66
+ // Streaming output on the engine side (advanced users)
67
+ export { transformToChunks, transformToStream } from "./async/stream.js";
48
68
 
49
69
  /**
50
70
  * Version information
51
71
  */
52
- export const VERSION = "1.0.0";
72
+ export const VERSION = "1.3.0";
53
73
 
54
74
  /**
55
75
  * Check if we're running in a browser environment
@@ -61,6 +81,4 @@ export const isBrowser =
61
81
  * Check if we're running in Node.js
62
82
  */
63
83
  export const isNode =
64
- typeof process !== "undefined" &&
65
- process.versions != null &&
66
- process.versions.node != null;
84
+ typeof process !== "undefined" && process.versions?.node != null;