@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/bin/lib/transform.js
CHANGED
|
@@ -2,39 +2,44 @@
|
|
|
2
2
|
* XSLT Processor CLI - Transformation Helpers
|
|
3
3
|
*
|
|
4
4
|
* DOM environment setup, document parsing and the transformation itself.
|
|
5
|
-
* Output is serialized
|
|
6
|
-
* xsl:output settings of the
|
|
5
|
+
* Output is serialized in chunks (src/async/stream.js, the same serializer
|
|
6
|
+
* as XSLTProcessor#transformToString) so that the xsl:output settings of the
|
|
7
|
+
* stylesheet are honored and large results are written as they are produced.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
"use strict";
|
|
10
11
|
|
|
11
|
-
import { JSDOM } from "jsdom";
|
|
12
12
|
import { XSLTProcessor } from "../../src/XSLTProcessor.js";
|
|
13
|
+
import { transformToChunks } from "../../src/async/stream.js";
|
|
14
|
+
import { findParseError } from "../../src/xslt/domParsing.js";
|
|
15
|
+
import { XSLT_VERSION_MODES, usesXslt3 } from "../../src/bridge/version.js";
|
|
16
|
+
import {
|
|
17
|
+
createDocumentLoader,
|
|
18
|
+
createStylesheetLoader,
|
|
19
|
+
toBaseUri,
|
|
20
|
+
} from "./loaders.js";
|
|
21
|
+
import { installDomGlobals, loadDomEnvironment } from "./dom.js";
|
|
13
22
|
|
|
14
23
|
/**
|
|
15
|
-
* Create
|
|
24
|
+
* Create the DOM environment of the command line tool and expose it
|
|
25
|
+
* globally (see dom.js): jsdom, else @xmldom/xmldom; `XSLT_DOM=xmldom` or
|
|
26
|
+
* `XSLT_DOM=jsdom` picks one.
|
|
16
27
|
*
|
|
17
28
|
* The XSLT engine builds its result documents through the global `document`,
|
|
18
29
|
* so the globals have to be installed before transforming.
|
|
19
30
|
*
|
|
20
|
-
* @
|
|
31
|
+
* @param {string} [name] - DOM implementation, default `$XSLT_DOM`
|
|
32
|
+
* @returns {Promise<import("./dom.js").DomEnvironment>} The environment
|
|
33
|
+
* @throws {Error} When no DOM implementation is installed
|
|
21
34
|
*/
|
|
22
|
-
export function createDomEnvironment() {
|
|
23
|
-
|
|
24
|
-
contentType: "text/html",
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
globalThis.document = dom.window.document;
|
|
28
|
-
globalThis.DOMParser = dom.window.DOMParser;
|
|
29
|
-
globalThis.XMLSerializer = dom.window.XMLSerializer;
|
|
30
|
-
|
|
31
|
-
return dom;
|
|
35
|
+
export async function createDomEnvironment(name = process.env.XSLT_DOM) {
|
|
36
|
+
return installDomGlobals(await loadDomEnvironment(name));
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Parse an XML string, reporting parser errors as exceptions.
|
|
36
41
|
*
|
|
37
|
-
* @param {
|
|
42
|
+
* @param {import("./dom.js").DomEnvironment} dom - DOM environment
|
|
38
43
|
* @param {string} content - XML source text
|
|
39
44
|
* @param {string} label - Human readable document label used in errors
|
|
40
45
|
* @returns {Document} Parsed document
|
|
@@ -45,7 +50,7 @@ export function parseDocument(dom, content, label) {
|
|
|
45
50
|
"application/xml",
|
|
46
51
|
);
|
|
47
52
|
|
|
48
|
-
const error = doc
|
|
53
|
+
const error = findParseError(doc);
|
|
49
54
|
if (error) {
|
|
50
55
|
throw new Error(`Error parsing ${label}: ${error.textContent}`);
|
|
51
56
|
}
|
|
@@ -53,20 +58,29 @@ export function parseDocument(dom, content, label) {
|
|
|
53
58
|
return doc;
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
/** Output methods accepted by `--method`. */
|
|
62
|
+
export const OUTPUT_METHODS = Object.freeze(["xml", "html", "xhtml", "text"]);
|
|
63
|
+
|
|
56
64
|
/**
|
|
57
65
|
* Override the stylesheet xsl:output settings from the command line flags.
|
|
58
66
|
*
|
|
59
67
|
* @param {XSLTProcessor} processor - Processor with an imported stylesheet
|
|
60
68
|
* @param {object} values - Parsed command line option values
|
|
61
69
|
* @returns {object} The effective output settings
|
|
70
|
+
* @throws {Error} When `--method` is not one of {@link OUTPUT_METHODS}
|
|
62
71
|
*/
|
|
63
72
|
export function applyOutputOverrides(processor, values) {
|
|
64
|
-
const settings = processor.
|
|
73
|
+
const settings = processor.engine.outputSettings;
|
|
65
74
|
|
|
66
75
|
if (values.format || values.indent) {
|
|
67
76
|
settings.indent = "yes";
|
|
68
77
|
}
|
|
69
78
|
if (values.method) {
|
|
79
|
+
if (!OUTPUT_METHODS.includes(values.method)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Invalid --method "${values.method}": expected xml, html, xhtml or text`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
70
84
|
settings.method = values.method;
|
|
71
85
|
}
|
|
72
86
|
if (values["no-declaration"]) {
|
|
@@ -77,28 +91,90 @@ export function applyOutputOverrides(processor, values) {
|
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
/**
|
|
80
|
-
*
|
|
94
|
+
* The `xsltVersion` option selected by `--xslt-version`.
|
|
95
|
+
*
|
|
96
|
+
* @param {object} values - Parsed command line option values
|
|
97
|
+
* @returns {"1.0"|"auto"} The mode, "1.0" by default
|
|
98
|
+
* @throws {Error} When the flag is neither 1.0 nor auto
|
|
99
|
+
*/
|
|
100
|
+
export function xsltVersionOf(values) {
|
|
101
|
+
const mode = values["xslt-version"] ?? "1.0";
|
|
102
|
+
if (!XSLT_VERSION_MODES.includes(mode)) {
|
|
103
|
+
throw new Error(`Invalid --xslt-version "${mode}": expected 1.0 or auto`);
|
|
104
|
+
}
|
|
105
|
+
return mode;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Load @tradik/xslt3 (dynamic import) when `--xslt-version auto` is given
|
|
110
|
+
* and the stylesheet declares version 2.0 or more, so that the synchronous
|
|
111
|
+
* transformation can use it.
|
|
112
|
+
*
|
|
113
|
+
* @param {import("./dom.js").DomEnvironment} dom - DOM environment
|
|
114
|
+
* @param {string} xsltContent - XSLT stylesheet text
|
|
115
|
+
* @param {object} values - Parsed command line option values
|
|
116
|
+
* @returns {Promise<void>} Resolves once the engine is available
|
|
117
|
+
* @throws {Error} For an invalid flag, or when @tradik/xslt3 is missing
|
|
118
|
+
*/
|
|
119
|
+
export async function prepareXsltVersion(dom, xsltContent, values) {
|
|
120
|
+
const mode = xsltVersionOf(values);
|
|
121
|
+
if (mode === "auto") {
|
|
122
|
+
const xsltDoc = parseDocument(dom, xsltContent, "XSLT");
|
|
123
|
+
if (usesXslt3(xsltDoc, mode)) await XSLTProcessor.preload();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @typedef {Object} TransformationInputs
|
|
129
|
+
* @property {import("./dom.js").DomEnvironment} dom - DOM environment
|
|
130
|
+
* @property {string} xmlContent - XML source text
|
|
131
|
+
* @property {string} xsltContent - XSLT stylesheet text
|
|
132
|
+
* @property {Record<string, string>} params - Stylesheet parameters
|
|
133
|
+
* @property {object} values - Parsed command line option values
|
|
134
|
+
* @property {string} [xsltFile] - Canonical stylesheet path; enables
|
|
135
|
+
* xsl:include, xsl:import and document() relative to it
|
|
136
|
+
* @property {string} [baseDir] - Canonical base directory every loaded file
|
|
137
|
+
* is confined to (required together with xsltFile)
|
|
138
|
+
* @property {(message: string) => void} [warn] - Warning sink for document()
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @typedef {Object} StreamedTransformation
|
|
143
|
+
* @property {Iterator<string>} chunks - The serialized result in chunks of
|
|
144
|
+
* bounded size (see src/xslt/serializer/chunks.js), with character
|
|
145
|
+
* references for the characters the output encoding cannot represent
|
|
146
|
+
* @property {string} encoding - The effective `xsl:output` encoding, the
|
|
147
|
+
* encoding the result has to be written in
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Run a transformation; its result is serialized as the chunks are read,
|
|
152
|
+
* so the output is written without ever being held as one string.
|
|
81
153
|
*
|
|
82
|
-
* @param {
|
|
83
|
-
* @
|
|
84
|
-
* @
|
|
85
|
-
*
|
|
86
|
-
* @param {Record<string, string>} options.params - Stylesheet parameters
|
|
87
|
-
* @param {object} options.values - Parsed command line option values
|
|
88
|
-
* @returns {string} The serialized transformation result
|
|
154
|
+
* @param {TransformationInputs} inputs - Transformation inputs
|
|
155
|
+
* @returns {StreamedTransformation} The result chunks and their encoding
|
|
156
|
+
* @throws {Error} "Transformation failed" when the transformation fails
|
|
157
|
+
* (the cause is reported on stderr, as XSLTProcessor does)
|
|
89
158
|
*/
|
|
90
|
-
export function
|
|
159
|
+
export function streamTransformation({
|
|
91
160
|
dom,
|
|
92
161
|
xmlContent,
|
|
93
162
|
xsltContent,
|
|
94
163
|
params,
|
|
95
164
|
values,
|
|
165
|
+
xsltFile,
|
|
166
|
+
baseDir,
|
|
167
|
+
warn,
|
|
96
168
|
}) {
|
|
97
169
|
const xmlDoc = parseDocument(dom, xmlContent, "XML");
|
|
98
170
|
const xsltDoc = parseDocument(dom, xsltContent, "XSLT");
|
|
99
171
|
|
|
100
|
-
const processor = new XSLTProcessor();
|
|
101
|
-
|
|
172
|
+
const processor = new XSLTProcessor({ xsltVersion: xsltVersionOf(values) });
|
|
173
|
+
if (xsltFile) {
|
|
174
|
+
processor.setStylesheetLoader(createStylesheetLoader(baseDir));
|
|
175
|
+
processor.setDocumentLoader(createDocumentLoader(baseDir, warn));
|
|
176
|
+
}
|
|
177
|
+
processor.importStylesheet(xsltDoc, xsltFile && toBaseUri(xsltFile));
|
|
102
178
|
|
|
103
179
|
for (const [name, value] of Object.entries(params)) {
|
|
104
180
|
processor.setParameter(null, name, value);
|
|
@@ -106,10 +182,25 @@ export function runTransformation({
|
|
|
106
182
|
|
|
107
183
|
applyOutputOverrides(processor, values);
|
|
108
184
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
185
|
+
let chunks;
|
|
186
|
+
try {
|
|
187
|
+
chunks = transformToChunks(processor.engine, xmlDoc);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error("XSLT transformation error:", error);
|
|
190
|
+
throw new Error("Transformation failed", { cause: error });
|
|
112
191
|
}
|
|
192
|
+
return { chunks, encoding: processor.engine.outputSettings.encoding };
|
|
193
|
+
}
|
|
113
194
|
|
|
114
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Run a transformation and serialize its result to one string.
|
|
197
|
+
*
|
|
198
|
+
* @param {TransformationInputs} inputs - Transformation inputs
|
|
199
|
+
* @returns {{output: string, encoding: string}} The serialized result and
|
|
200
|
+
* its encoding
|
|
201
|
+
* @throws {Error} "Transformation failed" when the transformation fails
|
|
202
|
+
*/
|
|
203
|
+
export function runTransformation(inputs) {
|
|
204
|
+
const { chunks, encoding } = streamTransformation(inputs);
|
|
205
|
+
return { output: [...chunks].join(""), encoding };
|
|
115
206
|
}
|
package/bin/xslt.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
"use strict";
|
|
12
12
|
|
|
13
|
-
import { readFile
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
14
|
import { parseArgs } from "node:util";
|
|
15
15
|
import {
|
|
16
16
|
CLI_OPTIONS,
|
|
@@ -18,7 +18,13 @@ import {
|
|
|
18
18
|
printHelp,
|
|
19
19
|
printVersion,
|
|
20
20
|
} from "./lib/options.js";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
createDomEnvironment,
|
|
23
|
+
prepareXsltVersion,
|
|
24
|
+
streamTransformation,
|
|
25
|
+
} from "./lib/transform.js";
|
|
26
|
+
import { decodeXml } from "./lib/decode.js";
|
|
27
|
+
import { writeResult } from "./lib/output.js";
|
|
22
28
|
import {
|
|
23
29
|
resolveBaseDir,
|
|
24
30
|
resolveInputPath,
|
|
@@ -39,22 +45,6 @@ function readArguments() {
|
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
/**
|
|
43
|
-
* Write the transformation result to a file or to stdout.
|
|
44
|
-
*
|
|
45
|
-
* @param {string} output - Serialized transformation result
|
|
46
|
-
* @param {string|undefined} target - Validated absolute output path, if any
|
|
47
|
-
* @returns {Promise<void>} Resolves once the result has been written
|
|
48
|
-
*/
|
|
49
|
-
async function writeOutput(output, target) {
|
|
50
|
-
if (target) {
|
|
51
|
-
await writeFile(target, output, "utf-8");
|
|
52
|
-
console.error(`Output written to ${target}`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
console.log(output);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
48
|
/**
|
|
59
49
|
* CLI entry point.
|
|
60
50
|
*
|
|
@@ -81,9 +71,8 @@ async function main() {
|
|
|
81
71
|
process.exit(1);
|
|
82
72
|
}
|
|
83
73
|
|
|
84
|
-
const dom = createDomEnvironment();
|
|
85
|
-
|
|
86
74
|
try {
|
|
75
|
+
const dom = await createDomEnvironment();
|
|
87
76
|
const baseDir = resolveBaseDir();
|
|
88
77
|
const xmlFile = resolveInputPath(xmlPath, "XML", baseDir);
|
|
89
78
|
const xsltFile = resolveInputPath(xsltPath, "XSLT", baseDir);
|
|
@@ -91,24 +80,34 @@ async function main() {
|
|
|
91
80
|
? resolveOutputPath(args.values.output, baseDir)
|
|
92
81
|
: undefined;
|
|
93
82
|
|
|
94
|
-
const [
|
|
95
|
-
readFile(xmlFile
|
|
96
|
-
readFile(xsltFile
|
|
83
|
+
const [xmlBytes, xsltBytes] = await Promise.all([
|
|
84
|
+
readFile(xmlFile),
|
|
85
|
+
readFile(xsltFile),
|
|
97
86
|
]);
|
|
98
87
|
|
|
99
|
-
const
|
|
88
|
+
const xsltContent = decodeXml(xsltBytes, xsltFile);
|
|
89
|
+
// --xslt-version auto: load @tradik/xslt3 for a 2.0/3.0 stylesheet
|
|
90
|
+
await prepareXsltVersion(dom, xsltContent, args.values);
|
|
91
|
+
|
|
92
|
+
const { chunks, encoding } = streamTransformation({
|
|
100
93
|
dom,
|
|
101
|
-
xmlContent,
|
|
94
|
+
xmlContent: decodeXml(xmlBytes, xmlFile),
|
|
102
95
|
xsltContent,
|
|
103
96
|
params: parseParameters(args.values.param),
|
|
104
97
|
values: args.values,
|
|
98
|
+
xsltFile,
|
|
99
|
+
baseDir,
|
|
105
100
|
});
|
|
106
101
|
|
|
107
|
-
await
|
|
102
|
+
await writeResult(chunks, outputFile, { encoding });
|
|
108
103
|
} catch (err) {
|
|
109
104
|
console.error(`Error: ${err.message}`);
|
|
110
105
|
process.exit(1);
|
|
111
106
|
}
|
|
112
107
|
}
|
|
113
108
|
|
|
114
|
-
main()
|
|
109
|
+
main().catch((err) => {
|
|
110
|
+
// main() reports expected failures itself; this catches anything unexpected.
|
|
111
|
+
console.error(`Error: ${err?.message ?? err}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|