@tradik/xslt3 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +28 -0
- package/README.md +98 -2
- package/package.json +43 -4
- package/src/errors.js +20 -0
- package/src/functions/aggregates.js +144 -0
- package/src/functions/arraySort.js +33 -0
- package/src/functions/arrays.js +181 -0
- package/src/functions/attributeTypes.js +58 -0
- package/src/functions/baseUris.js +82 -0
- package/src/functions/collations.js +127 -0
- package/src/functions/collections.js +84 -0
- package/src/functions/context.js +83 -0
- package/src/functions/core.js +167 -0
- package/src/functions/datetime.js +203 -0
- package/src/functions/deepEqual.js +132 -0
- package/src/functions/diagnostics.js +88 -0
- package/src/functions/focus.js +34 -0
- package/src/functions/format/dateComponents.js +176 -0
- package/src/functions/format/datePicture.js +115 -0
- package/src/functions/format/decimalFormat.js +65 -0
- package/src/functions/format/digitPattern.js +145 -0
- package/src/functions/format/formatDateTime.js +204 -0
- package/src/functions/format/formatInteger.js +109 -0
- package/src/functions/format/formatNumber.js +165 -0
- package/src/functions/format/fractionFormat.js +80 -0
- package/src/functions/format/germanWords.js +89 -0
- package/src/functions/format/numberLanguages.js +41 -0
- package/src/functions/format/numberPicture.js +202 -0
- package/src/functions/format/numberWords.js +155 -0
- package/src/functions/format/place.js +74 -0
- package/src/functions/format/timezoneFormat.js +89 -0
- package/src/functions/higherOrder.js +106 -0
- package/src/functions/ids.js +127 -0
- package/src/functions/json/index.js +17 -0
- package/src/functions/json/jsonChars.js +110 -0
- package/src/functions/json/jsonParser.js +203 -0
- package/src/functions/json/jsonStrings.js +116 -0
- package/src/functions/json/jsonToXml.js +116 -0
- package/src/functions/json/parseJson.js +119 -0
- package/src/functions/json/xmlRepresentation.js +103 -0
- package/src/functions/json/xmlToJson.js +147 -0
- package/src/functions/library.js +47 -0
- package/src/functions/mapArrayEqual.js +41 -0
- package/src/functions/maps.js +152 -0
- package/src/functions/math.js +73 -0
- package/src/functions/nodePaths.js +181 -0
- package/src/functions/nodes.js +199 -0
- package/src/functions/numeric.js +148 -0
- package/src/functions/options.js +58 -0
- package/src/functions/parseIetfDate.js +104 -0
- package/src/functions/parseXml.js +77 -0
- package/src/functions/qnames.js +68 -0
- package/src/functions/random.js +128 -0
- package/src/functions/regex/analyzeString.js +144 -0
- package/src/functions/regex/charClass.js +112 -0
- package/src/functions/regex/compile.js +81 -0
- package/src/functions/regex/escapes.js +109 -0
- package/src/functions/regex/parser.js +169 -0
- package/src/functions/regex/reader.js +37 -0
- package/src/functions/regex/regexFunctions.js +140 -0
- package/src/functions/regex/translate.js +102 -0
- package/src/functions/regex/unicodeBlocks.js +194 -0
- package/src/functions/registry.js +140 -0
- package/src/functions/sequences.js +173 -0
- package/src/functions/serializeFunction.js +53 -0
- package/src/functions/signatures.js +105 -0
- package/src/functions/sort.js +102 -0
- package/src/functions/stringMatch.js +45 -0
- package/src/functions/strings.js +194 -0
- package/src/functions/stringsCore.js +66 -0
- package/src/functions/support.js +106 -0
- package/src/functions/textDecoding.js +117 -0
- package/src/functions/ucaCollation.js +127 -0
- package/src/functions/unparsedText.js +71 -0
- package/src/functions/uri.js +166 -0
- package/src/functions/xqueryModule.js +24 -0
- package/src/index.js +38 -0
- package/src/items/array.js +84 -0
- package/src/items/function.js +45 -0
- package/src/items/map.js +120 -0
- package/src/items/mapKey.js +82 -0
- package/src/serialize/index.js +129 -0
- package/src/serialize/markup/content.js +74 -0
- package/src/serialize/markup/dialect.js +178 -0
- package/src/serialize/markup/htmlData.js +70 -0
- package/src/serialize/markup/namespaces.js +145 -0
- package/src/serialize/markup/prolog.js +91 -0
- package/src/serialize/markup/writer.js +183 -0
- package/src/serialize/methods/adaptive.js +131 -0
- package/src/serialize/methods/json.js +158 -0
- package/src/serialize/methods/text.js +43 -0
- package/src/serialize/output/buffer.js +48 -0
- package/src/serialize/output/encoding.js +146 -0
- package/src/serialize/output/expander.js +204 -0
- package/src/serialize/params/fromElement.js +141 -0
- package/src/serialize/params/fromMap.js +128 -0
- package/src/serialize/params/names.js +69 -0
- package/src/serialize/params/settings.js +153 -0
- package/src/serialize/params/values.js +163 -0
- package/src/serialize/sequence.js +111 -0
- package/src/xdm/arithmetic.js +155 -0
- package/src/xdm/atomic.js +142 -0
- package/src/xdm/binary.js +92 -0
- package/src/xdm/calendar.js +71 -0
- package/src/xdm/cast.js +176 -0
- package/src/xdm/compare.js +192 -0
- package/src/xdm/datetime.js +206 -0
- package/src/xdm/decimal.js +200 -0
- package/src/xdm/duration.js +128 -0
- package/src/xdm/generalCompare.js +152 -0
- package/src/xdm/index.js +47 -0
- package/src/xdm/lexical.js +126 -0
- package/src/xdm/nodes.js +170 -0
- package/src/xdm/numeric.js +122 -0
- package/src/xdm/qname.js +65 -0
- package/src/xdm/strings.js +52 -0
- package/src/xdm/temporalArithmetic.js +180 -0
- package/src/xdm/timeline.js +68 -0
- package/src/xdm/types.js +178 -0
- package/src/xpath/eval/atomics.js +45 -0
- package/src/xpath/eval/axes.js +212 -0
- package/src/xpath/eval/calls.js +168 -0
- package/src/xpath/eval/coercion.js +146 -0
- package/src/xpath/eval/compiler.js +71 -0
- package/src/xpath/eval/constructors.js +129 -0
- package/src/xpath/eval/decimalFormats.js +93 -0
- package/src/xpath/eval/descendantMemo.js +77 -0
- package/src/xpath/eval/documentOrder.js +142 -0
- package/src/xpath/eval/domNodes.js +174 -0
- package/src/xpath/eval/dynamicCall.js +27 -0
- package/src/xpath/eval/dynamicContext.js +161 -0
- package/src/xpath/eval/flow.js +115 -0
- package/src/xpath/eval/functionItems.js +194 -0
- package/src/xpath/eval/groupedChildren.js +88 -0
- package/src/xpath/eval/inline.js +71 -0
- package/src/xpath/eval/namespaceNodes.js +90 -0
- package/src/xpath/eval/nodeTests.js +164 -0
- package/src/xpath/eval/operators.js +190 -0
- package/src/xpath/eval/paths.js +163 -0
- package/src/xpath/eval/positional.js +81 -0
- package/src/xpath/eval/predicates.js +56 -0
- package/src/xpath/eval/primary.js +99 -0
- package/src/xpath/eval/resources.js +149 -0
- package/src/xpath/eval/scope.js +108 -0
- package/src/xpath/eval/sequenceType.js +193 -0
- package/src/xpath/eval/staticContext.js +142 -0
- package/src/xpath/eval/steps.js +98 -0
- package/src/xpath/eval/subtype.js +154 -0
- package/src/xpath/eval/typeExprs.js +147 -0
- package/src/xpath/eval/typeNames.js +101 -0
- package/src/xpath/eval/uris.js +21 -0
- package/src/xpath/eval/values.js +64 -0
- package/src/xpath/index.js +217 -0
- package/src/xpath/syntax/ast.js +130 -0
- package/src/xpath/syntax/chars.js +58 -0
- package/src/xpath/syntax/constructors.js +144 -0
- package/src/xpath/syntax/expressions.js +107 -0
- package/src/xpath/syntax/index.js +44 -0
- package/src/xpath/syntax/kindTests.js +134 -0
- package/src/xpath/syntax/lexer.js +220 -0
- package/src/xpath/syntax/operators.js +111 -0
- package/src/xpath/syntax/paths.js +102 -0
- package/src/xpath/syntax/postfix.js +91 -0
- package/src/xpath/syntax/primaries.js +149 -0
- package/src/xpath/syntax/steps.js +146 -0
- package/src/xpath/syntax/syntaxError.js +31 -0
- package/src/xpath/syntax/tokenStream.js +163 -0
- package/src/xpath/syntax/typeAst.js +76 -0
- package/src/xpath/syntax/typeOperators.js +127 -0
- package/src/xpath/syntax/types.js +158 -0
- package/src/xslt/api.js +153 -0
- package/src/xslt/compiler/acceptance.js +178 -0
- package/src/xslt/compiler/accumulatorDecl.js +146 -0
- package/src/xslt/compiler/attributeValues.js +174 -0
- package/src/xslt/compiler/attributes.js +211 -0
- package/src/xslt/compiler/avt.js +103 -0
- package/src/xslt/compiler/body.js +154 -0
- package/src/xslt/compiler/children.js +105 -0
- package/src/xslt/compiler/compatibility.js +130 -0
- package/src/xslt/compiler/components.js +179 -0
- package/src/xslt/compiler/contextItem.js +95 -0
- package/src/xslt/compiler/decimalFormats.js +132 -0
- package/src/xslt/compiler/declarationKinds.js +82 -0
- package/src/xslt/compiler/elementInfo.js +246 -0
- package/src/xslt/compiler/expressions.js +175 -0
- package/src/xslt/compiler/globalContextItem.js +92 -0
- package/src/xslt/compiler/globals.js +209 -0
- package/src/xslt/compiler/linker.js +134 -0
- package/src/xslt/compiler/miscDecl.js +171 -0
- package/src/xslt/compiler/modeDecl.js +88 -0
- package/src/xslt/compiler/moduleLoading.js +111 -0
- package/src/xslt/compiler/modules.js +158 -0
- package/src/xslt/compiler/outputDecl.js +209 -0
- package/src/xslt/compiler/overrideLinks.js +141 -0
- package/src/xslt/compiler/packageChecks.js +84 -0
- package/src/xslt/compiler/packageRegistry.js +95 -0
- package/src/xslt/compiler/packageTable.js +181 -0
- package/src/xslt/compiler/packageVersions.js +143 -0
- package/src/xslt/compiler/packages.js +138 -0
- package/src/xslt/compiler/registry.js +234 -0
- package/src/xslt/compiler/staticStage.js +197 -0
- package/src/xslt/compiler/staticVariables.js +93 -0
- package/src/xslt/compiler/stylesheet.js +205 -0
- package/src/xslt/compiler/tailPosition.js +74 -0
- package/src/xslt/compiler/templatesDecl.js +180 -0
- package/src/xslt/compiler/useAccumulators.js +76 -0
- package/src/xslt/compiler/useWhen.js +94 -0
- package/src/xslt/compiler/visibility.js +160 -0
- package/src/xslt/index.js +11 -0
- package/src/xslt/instructions/analyzeString.js +137 -0
- package/src/xslt/instructions/assert.js +72 -0
- package/src/xslt/instructions/attributeSets.js +115 -0
- package/src/xslt/instructions/conditional.js +99 -0
- package/src/xslt/instructions/control.js +123 -0
- package/src/xslt/instructions/copy.js +151 -0
- package/src/xslt/instructions/elements.js +161 -0
- package/src/xslt/instructions/evaluate.js +189 -0
- package/src/xslt/instructions/fork.js +35 -0
- package/src/xslt/instructions/grouping.js +106 -0
- package/src/xslt/instructions/index.js +94 -0
- package/src/xslt/instructions/iterate.js +182 -0
- package/src/xslt/instructions/literal.js +142 -0
- package/src/xslt/instructions/maps.js +72 -0
- package/src/xslt/instructions/merge.js +190 -0
- package/src/xslt/instructions/number.js +205 -0
- package/src/xslt/instructions/output.js +102 -0
- package/src/xslt/instructions/params.js +110 -0
- package/src/xslt/instructions/resultDocument.js +143 -0
- package/src/xslt/instructions/sort.js +114 -0
- package/src/xslt/instructions/sourceDocument.js +73 -0
- package/src/xslt/instructions/templates.js +153 -0
- package/src/xslt/instructions/text.js +151 -0
- package/src/xslt/instructions/try.js +186 -0
- package/src/xslt/instructions/variables.js +119 -0
- package/src/xslt/names.js +204 -0
- package/src/xslt/patterns/axisOrigins.js +121 -0
- package/src/xslt/patterns/compile.js +151 -0
- package/src/xslt/patterns/general.js +132 -0
- package/src/xslt/patterns/grammar.js +190 -0
- package/src/xslt/patterns/modes.js +153 -0
- package/src/xslt/patterns/paths.js +130 -0
- package/src/xslt/patterns/patternChecks.js +29 -0
- package/src/xslt/patterns/steps.js +162 -0
- package/src/xslt/processor.js +178 -0
- package/src/xslt/runtime/accumulatorFunctions.js +48 -0
- package/src/xslt/runtime/accumulators.js +174 -0
- package/src/xslt/runtime/apply.js +153 -0
- package/src/xslt/runtime/atomicKeys.js +36 -0
- package/src/xslt/runtime/availability.js +162 -0
- package/src/xslt/runtime/baseUri.js +64 -0
- package/src/xslt/runtime/compilerHook.js +27 -0
- package/src/xslt/runtime/conditional.js +110 -0
- package/src/xslt/runtime/context.js +106 -0
- package/src/xslt/runtime/copy.js +123 -0
- package/src/xslt/runtime/copyFunctions.js +71 -0
- package/src/xslt/runtime/copyOrigins.js +60 -0
- package/src/xslt/runtime/defaultMethod.js +42 -0
- package/src/xslt/runtime/documentContent.js +93 -0
- package/src/xslt/runtime/dynamicXPath.js +90 -0
- package/src/xslt/runtime/functionCache.js +65 -0
- package/src/xslt/runtime/functionCall.js +57 -0
- package/src/xslt/runtime/functions.js +191 -0
- package/src/xslt/runtime/groups.js +108 -0
- package/src/xslt/runtime/invocation.js +156 -0
- package/src/xslt/runtime/iteration.js +107 -0
- package/src/xslt/runtime/keys.js +141 -0
- package/src/xslt/runtime/lazy.js +97 -0
- package/src/xslt/runtime/locations.js +86 -0
- package/src/xslt/runtime/machine.js +137 -0
- package/src/xslt/runtime/merging.js +167 -0
- package/src/xslt/runtime/namespaceScope.js +141 -0
- package/src/xslt/runtime/numberFormat.js +89 -0
- package/src/xslt/runtime/numbering.js +168 -0
- package/src/xslt/runtime/params.js +59 -0
- package/src/xslt/runtime/properties.js +103 -0
- package/src/xslt/runtime/rawResults.js +82 -0
- package/src/xslt/runtime/sequenceReceiver.js +102 -0
- package/src/xslt/runtime/snapshot.js +66 -0
- package/src/xslt/runtime/sortKeys.js +119 -0
- package/src/xslt/runtime/streamAvailable.js +60 -0
- package/src/xslt/runtime/strip.js +102 -0
- package/src/xslt/runtime/transformFunction.js +169 -0
- package/src/xslt/runtime/transformation.js +168 -0
- package/src/xslt/runtime/transformationContext.js +85 -0
- package/src/xslt/runtime/treeReceiver.js +194 -0
- package/src/xslt/runtime/values.js +92 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A transformation (XSLT 3.0 section 2.3): the dynamic context shared by
|
|
3
|
+
* every instruction, whitespace stripping of the source documents, the
|
|
4
|
+
* global variables evaluated on first use, and the results collected.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/runtime/transformation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { withinLimits } from "../../xpath/eval/compiler.js";
|
|
10
|
+
import { isEligibleMode } from "../compiler/packageChecks.js";
|
|
11
|
+
import { displayName, xsltError } from "../names.js";
|
|
12
|
+
import {
|
|
13
|
+
checkLibraryContextItems,
|
|
14
|
+
globalContextItem,
|
|
15
|
+
} from "../compiler/globalContextItem.js";
|
|
16
|
+
import { setInitialAccumulators } from "./accumulators.js";
|
|
17
|
+
import { withDefaultMethod } from "./defaultMethod.js";
|
|
18
|
+
import { finishTree, invoke } from "./invocation.js";
|
|
19
|
+
import { LazyEntry } from "./lazy.js";
|
|
20
|
+
import { Machine } from "./machine.js";
|
|
21
|
+
import { normalizeParams } from "./params.js";
|
|
22
|
+
import { isStrippedText } from "./strip.js";
|
|
23
|
+
import {
|
|
24
|
+
documentStripper,
|
|
25
|
+
transformationContext,
|
|
26
|
+
} from "./transformationContext.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The lazily evaluated entries of the global variables.
|
|
30
|
+
* @param {object} tx
|
|
31
|
+
* @param {Map<string, Array>} params - Supplied stylesheet parameters
|
|
32
|
+
* @returns {object|null} the environment (see xpath/eval/scope.js)
|
|
33
|
+
*/
|
|
34
|
+
function globalEnvironment(tx, params) {
|
|
35
|
+
let env = null;
|
|
36
|
+
for (const global of tx.stylesheet.globals) {
|
|
37
|
+
if (global.required && !params.has(global.key)) {
|
|
38
|
+
throw xsltError(
|
|
39
|
+
"XTDE0050",
|
|
40
|
+
`The parameter $${displayName(global.key)} is required`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const compute =
|
|
44
|
+
global.isParam && params.has(global.key)
|
|
45
|
+
? () => global.convert(params.get(global.key))
|
|
46
|
+
: () => global.value(tx.globalContext, tx.machine);
|
|
47
|
+
env = new LazyEntry(env, compute, global.key, true);
|
|
48
|
+
}
|
|
49
|
+
return env;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The document factory of a transformation: the option, else the DOM of
|
|
54
|
+
* the source, else the DOM of the stylesheet (a transformation without a
|
|
55
|
+
* source, from an initial template), else the global document.
|
|
56
|
+
* @param {object} options
|
|
57
|
+
* @param {object} stylesheet - Compiled stylesheet (its module tree)
|
|
58
|
+
* @returns {() => Document}
|
|
59
|
+
*/
|
|
60
|
+
function documentFactory(options, stylesheet) {
|
|
61
|
+
if (options.createDocument) return options.createDocument;
|
|
62
|
+
return () => {
|
|
63
|
+
const owner =
|
|
64
|
+
options.source?.ownerDocument ??
|
|
65
|
+
options.source ??
|
|
66
|
+
stylesheet.tree.root.ownerDocument;
|
|
67
|
+
const implementation =
|
|
68
|
+
owner?.implementation ?? globalThis.document?.implementation;
|
|
69
|
+
if (!implementation) {
|
|
70
|
+
throw xsltError(
|
|
71
|
+
"XPDY0130",
|
|
72
|
+
"No DOM to build results: pass createDocument",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return implementation.createDocument(null, null, null);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The initial mode: `#unnamed`, `#default` or a Clark name of a mode
|
|
81
|
+
* eligible as initial mode (XTDE0045).
|
|
82
|
+
* @param {object} stylesheet
|
|
83
|
+
* @param {string|undefined} requested - Option initialMode
|
|
84
|
+
* @returns {object} the mode
|
|
85
|
+
*/
|
|
86
|
+
function initialMode(stylesheet, requested) {
|
|
87
|
+
const token = requested?.replace(/^\{\}(?=#)/, "");
|
|
88
|
+
if (token === undefined || token === "#default") {
|
|
89
|
+
return stylesheet.mode(stylesheet.defaultModeName);
|
|
90
|
+
}
|
|
91
|
+
const name = token === "#unnamed" ? "" : token;
|
|
92
|
+
if (!isEligibleMode(stylesheet, name)) {
|
|
93
|
+
throw xsltError("XTDE0045", `The mode ${requested} is not eligible`);
|
|
94
|
+
}
|
|
95
|
+
return stylesheet.mode(name);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Runs a transformation.
|
|
100
|
+
* @param {object} stylesheet - Compiled stylesheet (StylesheetCompiler)
|
|
101
|
+
* @param {object} options - See CompiledStylesheet.transform
|
|
102
|
+
* @returns {{principal: Node|Array, secondary: Map<string, object>,
|
|
103
|
+
* messages: Array}}
|
|
104
|
+
*/
|
|
105
|
+
export function runTransformation(stylesheet, options) {
|
|
106
|
+
checkLibraryContextItems(stylesheet);
|
|
107
|
+
const createDocument = documentFactory(options, stylesheet);
|
|
108
|
+
let scratchDocument = null;
|
|
109
|
+
const strip = documentStripper();
|
|
110
|
+
// a source text node that stripping removes is no source (XPDY0002)
|
|
111
|
+
const source = isStrippedText(options.source ?? {}, stylesheet.spaceRules)
|
|
112
|
+
? undefined
|
|
113
|
+
: strip(options.source, stylesheet.spaceRules);
|
|
114
|
+
const tx = {
|
|
115
|
+
stylesheet,
|
|
116
|
+
options,
|
|
117
|
+
machine: new Machine(options.maxDepth),
|
|
118
|
+
scratch: () => (scratchDocument ??= createDocument()),
|
|
119
|
+
messages: [],
|
|
120
|
+
onMessage: options.onMessage,
|
|
121
|
+
resultUris: new Set(),
|
|
122
|
+
baseOutputUri: options.baseOutputUri,
|
|
123
|
+
secondary: new Map(),
|
|
124
|
+
keyIndexes: new Map(),
|
|
125
|
+
defaultMode: initialMode(stylesheet, options.initialMode),
|
|
126
|
+
principalOverride: null,
|
|
127
|
+
dynamicEvaluation: options.dynamicEvaluation !== false,
|
|
128
|
+
assertions: options.assertions !== false,
|
|
129
|
+
principalOutput: null,
|
|
130
|
+
dyn: transformationContext(stylesheet, options, strip, createDocument),
|
|
131
|
+
};
|
|
132
|
+
tx.addResult = (uri, fragment, output) => {
|
|
133
|
+
const tree = Array.isArray(fragment)
|
|
134
|
+
? fragment
|
|
135
|
+
: finishTree(fragment, createDocument);
|
|
136
|
+
const params = withDefaultMethod(output, tree);
|
|
137
|
+
if (uri === "") {
|
|
138
|
+
tx.principalOverride = tree;
|
|
139
|
+
tx.principalOutput = params;
|
|
140
|
+
} else tx.secondary.set(uri, { document: tree, output: params });
|
|
141
|
+
};
|
|
142
|
+
tx.globalContext = {
|
|
143
|
+
tx,
|
|
144
|
+
item: globalContextItem(
|
|
145
|
+
stylesheet.globalContextItem,
|
|
146
|
+
options.globalContextItem ?? source,
|
|
147
|
+
),
|
|
148
|
+
position: 1,
|
|
149
|
+
size: 1,
|
|
150
|
+
env: null,
|
|
151
|
+
mode: tx.defaultMode,
|
|
152
|
+
rule: null,
|
|
153
|
+
group: undefined,
|
|
154
|
+
groupKey: undefined,
|
|
155
|
+
regex: undefined,
|
|
156
|
+
tunnel: null,
|
|
157
|
+
temporary: false,
|
|
158
|
+
outputUri: options.baseOutputUri,
|
|
159
|
+
dyn: null,
|
|
160
|
+
};
|
|
161
|
+
setInitialAccumulators(tx, options, source);
|
|
162
|
+
tx.globalEnv = globalEnvironment(
|
|
163
|
+
tx,
|
|
164
|
+
normalizeParams(options.params, options.paramsAsUntyped),
|
|
165
|
+
);
|
|
166
|
+
tx.globalContext.env = tx.globalEnv;
|
|
167
|
+
return withinLimits(() => invoke(tx, options, source, createDocument));
|
|
168
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XPath dynamic context of a transformation: the resources it reads
|
|
3
|
+
* (documents, text, collections) with relative URIs resolved against the
|
|
4
|
+
* base URI of the calling expression, and the documents it loads stripped
|
|
5
|
+
* of whitespace by the xsl:strip-space rules of the package of that
|
|
6
|
+
* expression (XSLT 3.0 section 4.3).
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xslt/runtime/transformationContext
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createDynamicContext } from "../../xpath/eval/dynamicContext.js";
|
|
12
|
+
import { resolveUri } from "../../xpath/eval/uris.js";
|
|
13
|
+
import { stripDocument } from "./strip.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whitespace stripping of loaded documents, once per document and rule
|
|
17
|
+
* set, so that a document read twice by one package is one node.
|
|
18
|
+
* @returns {(document: Node, rules: object[]) => Node}
|
|
19
|
+
*/
|
|
20
|
+
export function documentStripper() {
|
|
21
|
+
const copies = new WeakMap();
|
|
22
|
+
return (document, rules) => {
|
|
23
|
+
if (document?.nodeType !== 9) return document;
|
|
24
|
+
let byRules = copies.get(document);
|
|
25
|
+
if (!byRules) copies.set(document, (byRules = new Map()));
|
|
26
|
+
let stripped = byRules.get(rules);
|
|
27
|
+
if (!stripped) {
|
|
28
|
+
stripped = stripDocument(document, rules);
|
|
29
|
+
byRules.set(rules, stripped);
|
|
30
|
+
}
|
|
31
|
+
return stripped;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Builds the dynamic context of a transformation.
|
|
37
|
+
* @param {object} stylesheet - The top-level package's compiler
|
|
38
|
+
* @param {object} options - See CompiledStylesheet.transform
|
|
39
|
+
* @param {(document: Node, rules: object[]) => Node} strip - From
|
|
40
|
+
* {@link documentStripper}
|
|
41
|
+
* @param {() => Document} createDocument
|
|
42
|
+
* @returns {object}
|
|
43
|
+
*/
|
|
44
|
+
export function transformationContext(
|
|
45
|
+
stylesheet,
|
|
46
|
+
options,
|
|
47
|
+
strip,
|
|
48
|
+
createDocument,
|
|
49
|
+
) {
|
|
50
|
+
const dyn = createDynamicContext(
|
|
51
|
+
stylesheet.sc,
|
|
52
|
+
{
|
|
53
|
+
documentLoader: options.documentLoader,
|
|
54
|
+
createDocument,
|
|
55
|
+
implicitTimezone: options.implicitTimezone,
|
|
56
|
+
currentDateTime: options.currentDateTime,
|
|
57
|
+
textLoader: options.textLoader,
|
|
58
|
+
xmlParser: options.xmlParser,
|
|
59
|
+
collections: options.collections,
|
|
60
|
+
},
|
|
61
|
+
options.source,
|
|
62
|
+
);
|
|
63
|
+
const { loadDocument, loadText, collection } = dyn;
|
|
64
|
+
/** @this {object} the dynamic context of the calling expression */
|
|
65
|
+
function rulesOf() {
|
|
66
|
+
return (this.sc?.owner ?? stylesheet).spaceRules ?? stylesheet.spaceRules;
|
|
67
|
+
}
|
|
68
|
+
// `this` is the dynamic context of the calling expression
|
|
69
|
+
dyn.loadDocument = function load(uri) {
|
|
70
|
+
const base = this.staticBaseUri;
|
|
71
|
+
const document = loadDocument(resolveUri(uri, base), base);
|
|
72
|
+
return strip(document, rulesOf.call(this));
|
|
73
|
+
};
|
|
74
|
+
dyn.loadText = function loadTextFrom(href, encoding) {
|
|
75
|
+
return loadText(href, encoding, this.staticBaseUri);
|
|
76
|
+
};
|
|
77
|
+
dyn.collection = function collectionFrom(href) {
|
|
78
|
+
const rules = rulesOf.call(this);
|
|
79
|
+
return collection(href, this.staticBaseUri).map((item) =>
|
|
80
|
+
strip(item, rules),
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
dyn.xc = null;
|
|
84
|
+
return dyn;
|
|
85
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The receiver that builds the content of a node in a result tree
|
|
3
|
+
* (XSLT 3.0 section 5.7.1, constructing complex content): adjacent
|
|
4
|
+
* atomic values become text separated by spaces, document nodes are
|
|
5
|
+
* replaced by their children, adjacent text is merged, attributes and
|
|
6
|
+
* namespaces must come before children, and namespace declarations are
|
|
7
|
+
* added where names need them.
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/xslt/runtime/treeReceiver
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { isArray, isAtomic, isFunctionItem } from "../../xdm/atomic.js";
|
|
13
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
14
|
+
import { xsltError } from "../names.js";
|
|
15
|
+
import { copyNode } from "./copy.js";
|
|
16
|
+
import { DocumentContent } from "./documentContent.js";
|
|
17
|
+
import { NamespaceScope, prefixOf } from "./namespaceScope.js";
|
|
18
|
+
|
|
19
|
+
export { prefixOf };
|
|
20
|
+
|
|
21
|
+
/** Builds the children (and attributes) of an element or document. */
|
|
22
|
+
export class TreeReceiver extends NamespaceScope {
|
|
23
|
+
/**
|
|
24
|
+
* @param {Node} parent - Element, document or document fragment
|
|
25
|
+
* @param {Map<string, string>} namespaces - In scope at the parent
|
|
26
|
+
*/
|
|
27
|
+
constructor(parent, namespaces) {
|
|
28
|
+
super(parent, namespaces);
|
|
29
|
+
this.doc = parent.nodeType === 9 ? parent : parent.ownerDocument;
|
|
30
|
+
this.hasChildren = false;
|
|
31
|
+
this.lastAtomic = false;
|
|
32
|
+
/** @type {boolean} the receiver of the top of a result tree */
|
|
33
|
+
this.resultTree = false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Appends a child node.
|
|
38
|
+
* @param {Node} node
|
|
39
|
+
*/
|
|
40
|
+
append(node) {
|
|
41
|
+
this.parent.appendChild(node);
|
|
42
|
+
this.hasChildren = true;
|
|
43
|
+
this.lastAtomic = false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Starts an element child.
|
|
48
|
+
* @param {string} uri - Namespace URI, "" for none
|
|
49
|
+
* @param {string} qname - Lexical name
|
|
50
|
+
* @returns {TreeReceiver} the receiver of its content (`.parent` is it)
|
|
51
|
+
*/
|
|
52
|
+
element(uri, qname) {
|
|
53
|
+
const element = this.doc.createElementNS(uri || null, qname);
|
|
54
|
+
this.append(element);
|
|
55
|
+
const content = new TreeReceiver(element, this.namespaces);
|
|
56
|
+
content.declare(prefixOf(qname), uri);
|
|
57
|
+
return content;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Checks that an attribute or namespace may be added now.
|
|
62
|
+
* @param {string} what
|
|
63
|
+
*/
|
|
64
|
+
checkAttributePosition(what) {
|
|
65
|
+
if (this.parent.nodeType !== 1) {
|
|
66
|
+
throw xsltError("XTDE0420", `A ${what} cannot be added to a document`);
|
|
67
|
+
}
|
|
68
|
+
if (this.hasChildren) {
|
|
69
|
+
throw xsltError(
|
|
70
|
+
"XTDE0410",
|
|
71
|
+
`A ${what} cannot be added after the children of an element`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Adds an attribute (replacing one of the same name); its prefix is
|
|
78
|
+
* changed when the element binds it to another namespace.
|
|
79
|
+
* @param {string} uri
|
|
80
|
+
* @param {string} qname
|
|
81
|
+
* @param {string} value
|
|
82
|
+
*/
|
|
83
|
+
attribute(uri, qname, value) {
|
|
84
|
+
this.checkAttributePosition("attribute");
|
|
85
|
+
let name = qname;
|
|
86
|
+
if (uri) {
|
|
87
|
+
let prefix = prefixOf(qname);
|
|
88
|
+
const local = qname.slice(prefix.length ? prefix.length + 1 : 0);
|
|
89
|
+
if (prefix === "" || !this.declare(prefix, uri)) {
|
|
90
|
+
prefix = this.prefixFor(uri, prefix);
|
|
91
|
+
}
|
|
92
|
+
name = `${prefix}:${local}`;
|
|
93
|
+
const existing = this.parent.getAttributeNodeNS(uri, local);
|
|
94
|
+
if (existing) this.parent.removeAttributeNode(existing);
|
|
95
|
+
}
|
|
96
|
+
this.parent.setAttributeNS(uri || null, name, value);
|
|
97
|
+
this.lastAtomic = false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Adds a namespace node.
|
|
102
|
+
* @param {string} prefix
|
|
103
|
+
* @param {string} uri
|
|
104
|
+
*/
|
|
105
|
+
namespace(prefix, uri) {
|
|
106
|
+
this.checkAttributePosition("namespace node");
|
|
107
|
+
if (prefix === "" && this.parent.namespaceURI === null && uri !== "") {
|
|
108
|
+
throw xsltError(
|
|
109
|
+
"XTDE0440",
|
|
110
|
+
"A default namespace cannot be added to an element in no namespace",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (
|
|
114
|
+
!this.declareExplicit(prefix, uri) &&
|
|
115
|
+
!(this.rebind(prefix, uri) && this.explicit.add(prefix))
|
|
116
|
+
) {
|
|
117
|
+
throw xsltError(
|
|
118
|
+
"XTDE0430",
|
|
119
|
+
`The prefix "${prefix}" is already bound to another namespace`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
this.lastAtomic = false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Adds text (merged with preceding text); zero-length text adds no node
|
|
127
|
+
* but separates atomic values.
|
|
128
|
+
* @param {string} text
|
|
129
|
+
*/
|
|
130
|
+
text(text) {
|
|
131
|
+
if (text === "") {
|
|
132
|
+
this.lastAtomic = false;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const last = this.parent.lastChild;
|
|
136
|
+
if (last && last.nodeType === 3) last.appendData(text);
|
|
137
|
+
else this.append(this.doc.createTextNode(text));
|
|
138
|
+
this.hasChildren = true;
|
|
139
|
+
this.lastAtomic = false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** @param {string} text */
|
|
143
|
+
comment(text) {
|
|
144
|
+
this.append(this.doc.createComment(text));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {string} target
|
|
149
|
+
* @param {string} data
|
|
150
|
+
*/
|
|
151
|
+
pi(target, data) {
|
|
152
|
+
this.append(this.doc.createProcessingInstruction(target, data));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @param {import("../../xdm/atomic.js").AtomicValue} value */
|
|
156
|
+
atomic(value) {
|
|
157
|
+
const text = canonicalString(value);
|
|
158
|
+
this.text(this.lastAtomic ? ` ${text}` : text);
|
|
159
|
+
this.lastAtomic = true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Adds an item of a sequence: nodes are copied, arrays flattened.
|
|
164
|
+
* @param {*} item
|
|
165
|
+
*/
|
|
166
|
+
item(item) {
|
|
167
|
+
if (isAtomic(item)) this.atomic(item);
|
|
168
|
+
else if (isArray(item)) {
|
|
169
|
+
for (const member of item.members) for (const x of member) this.item(x);
|
|
170
|
+
} else if (isFunctionItem(item)) {
|
|
171
|
+
// the top of a result tree is built by sequence normalization
|
|
172
|
+
// (XSLT 3.0 section 2.3.6), whose error is SENR0001
|
|
173
|
+
throw xsltError(
|
|
174
|
+
this.resultTree ? "SENR0001" : "XTDE0450",
|
|
175
|
+
"A function item cannot be added to a tree",
|
|
176
|
+
);
|
|
177
|
+
} else this.copy(item, true);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Copies a node and its descendants.
|
|
182
|
+
* @param {Node} node
|
|
183
|
+
* @param {boolean} copyNamespaces
|
|
184
|
+
*/
|
|
185
|
+
copy(node, copyNamespaces) {
|
|
186
|
+
copyNode(node, this, copyNamespaces);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** @returns {DocumentContent} the receiver of a document node's content */
|
|
190
|
+
document() {
|
|
191
|
+
this.lastAtomic = false;
|
|
192
|
+
return new DocumentContent(this);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Values computed from sequence constructors at run time: the sequence a
|
|
3
|
+
* body produces, temporary trees, simple content (XSLT 3.0 section
|
|
4
|
+
* 5.7.2) and attribute value templates.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/runtime/values
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { atomize } from "../../xdm/nodes.js";
|
|
10
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
11
|
+
import { derive, evaluate } from "./context.js";
|
|
12
|
+
import { SequenceReceiver } from "./sequenceReceiver.js";
|
|
13
|
+
import { TreeReceiver } from "./treeReceiver.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Runs a body and returns the sequence it produces.
|
|
17
|
+
* @param {import("./machine.js").Body} body
|
|
18
|
+
* @param {object} xc
|
|
19
|
+
* @param {import("./machine.js").Machine} machine
|
|
20
|
+
* @param {boolean} [temporary] - The body runs in temporary output state
|
|
21
|
+
* (XSLT 3.0 section 25.2: variables, functions, keys, sort keys...),
|
|
22
|
+
* not for the content of xsl:attribute, xsl:value-of, xsl:comment,
|
|
23
|
+
* xsl:message and the like
|
|
24
|
+
* @returns {Array}
|
|
25
|
+
*/
|
|
26
|
+
export function bodySequence(body, xc, machine, temporary = true) {
|
|
27
|
+
const out = new SequenceReceiver(xc.tx.scratch);
|
|
28
|
+
const context = temporary ? derive(xc, { temporary: true }) : xc;
|
|
29
|
+
machine.runBody(body, context, out);
|
|
30
|
+
return out.items;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Runs a body to build a temporary tree.
|
|
35
|
+
* @param {import("./machine.js").Body} body
|
|
36
|
+
* @param {object} xc
|
|
37
|
+
* @param {import("./machine.js").Machine} machine
|
|
38
|
+
* @returns {Node} its document node (a document fragment)
|
|
39
|
+
*/
|
|
40
|
+
export function temporaryTree(body, xc, machine) {
|
|
41
|
+
const fragment = xc.tx.scratch().createDocumentFragment();
|
|
42
|
+
const out = new TreeReceiver(fragment, new Map());
|
|
43
|
+
machine.runBody(body, derive(xc, { temporary: true }), out);
|
|
44
|
+
return fragment;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The string of simple content: zero-length text nodes dropped, adjacent
|
|
49
|
+
* text nodes merged, then the items atomized and joined.
|
|
50
|
+
* @param {Array} items
|
|
51
|
+
* @param {string} separator
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
export function simpleContent(items, separator) {
|
|
55
|
+
const parts = [];
|
|
56
|
+
let text = null;
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
if (item?.nodeType === 3) {
|
|
59
|
+
if (item.nodeValue !== "") text = (text ?? "") + item.nodeValue;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (text !== null) parts.push(text);
|
|
63
|
+
text = null;
|
|
64
|
+
for (const value of atomize([item])) parts.push(canonicalString(value));
|
|
65
|
+
}
|
|
66
|
+
if (text !== null) parts.push(text);
|
|
67
|
+
return parts.join(separator);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Evaluator of a compiled attribute value template.
|
|
72
|
+
* @param {Array<string|object>} parts - See ExpressionCompiler.avt
|
|
73
|
+
* @param {boolean} [compatible] - Backwards-compatible: first item only
|
|
74
|
+
* @returns {(xc: object) => string}
|
|
75
|
+
*/
|
|
76
|
+
export function avtEvaluator(parts, compatible = false) {
|
|
77
|
+
if (parts.length === 1 && typeof parts[0] === "string") {
|
|
78
|
+
const constant = parts[0];
|
|
79
|
+
return () => constant;
|
|
80
|
+
}
|
|
81
|
+
return (xc) => {
|
|
82
|
+
let result = "";
|
|
83
|
+
for (const part of parts) {
|
|
84
|
+
if (typeof part === "string") result += part;
|
|
85
|
+
else {
|
|
86
|
+
const value = evaluate(part, xc);
|
|
87
|
+
result += simpleContent(compatible ? value.slice(0, 1) : value, " ");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
};
|
|
92
|
+
}
|