@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,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional content construction at run time (XSLT 3.0 section 8.4):
|
|
3
|
+
* vacuous and deemed-empty items, the sequence constructors that contain
|
|
4
|
+
* xsl:on-empty or xsl:on-non-empty, and the filter of
|
|
5
|
+
* xsl:where-populated.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/runtime/conditional
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { isArray, isAtomic, isMap } from "../../xdm/atomic.js";
|
|
11
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
12
|
+
import { SequenceReceiver } from "./sequenceReceiver.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The members of an array, flattened.
|
|
16
|
+
* @param {object} array
|
|
17
|
+
* @returns {Array}
|
|
18
|
+
*/
|
|
19
|
+
const flatten = (array) =>
|
|
20
|
+
array.members
|
|
21
|
+
.flat()
|
|
22
|
+
.flatMap((item) => (isArray(item) ? flatten(item) : [item]));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether an item is vacuous: a zero-length text node, a document node
|
|
26
|
+
* without children, an atomic value whose string is empty, an array of
|
|
27
|
+
* vacuous items.
|
|
28
|
+
* @param {*} item
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
export function isVacuous(item) {
|
|
32
|
+
if (isAtomic(item)) return canonicalString(item) === "";
|
|
33
|
+
if (isArray(item)) return flatten(item).every(isVacuous);
|
|
34
|
+
const type = item?.nodeType;
|
|
35
|
+
if (type === 3 || type === 4) return item.nodeValue === "";
|
|
36
|
+
return (type === 9 || type === 11) && !item.firstChild;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether xsl:where-populated drops an item: a document or element node
|
|
41
|
+
* without children, another node whose string value is empty, an atomic
|
|
42
|
+
* value whose string is empty, an empty map, an array of such items.
|
|
43
|
+
* @param {*} item
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
export function isDeemedEmpty(item) {
|
|
47
|
+
if (isAtomic(item)) return canonicalString(item) === "";
|
|
48
|
+
if (isMap(item)) return item.size === 0;
|
|
49
|
+
if (isArray(item)) return flatten(item).every(isDeemedEmpty);
|
|
50
|
+
const type = item?.nodeType;
|
|
51
|
+
if (type === 1 || type === 9 || type === 11) return !item.firstChild;
|
|
52
|
+
if (type === undefined) return false;
|
|
53
|
+
const value = type === 2 ? item.value : item.nodeValue;
|
|
54
|
+
return (value ?? "") === "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Runs a step and collects the items it produces.
|
|
59
|
+
* @param {Function} step
|
|
60
|
+
* @param {object} xc
|
|
61
|
+
* @param {import("./machine.js").Machine} machine
|
|
62
|
+
* @returns {Array}
|
|
63
|
+
*/
|
|
64
|
+
export function stepItems(step, xc, machine) {
|
|
65
|
+
const out = new SequenceReceiver(xc.tx.scratch);
|
|
66
|
+
machine.runBody([step], xc, out);
|
|
67
|
+
return out.items;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Wraps a body that contains xsl:on-empty or xsl:on-non-empty (steps
|
|
72
|
+
* whose `conditional` is "empty" or "non-empty"): the other steps run
|
|
73
|
+
* first, collected; the xsl:on-non-empty steps run when one of them
|
|
74
|
+
* gave a non-vacuous item, the xsl:on-empty step when none did.
|
|
75
|
+
* @param {import("./machine.js").Body} body
|
|
76
|
+
* @returns {Function} a step running the whole body
|
|
77
|
+
*/
|
|
78
|
+
export function conditionalBody(body) {
|
|
79
|
+
return (xc, out, machine) => {
|
|
80
|
+
const results = [];
|
|
81
|
+
let context = xc;
|
|
82
|
+
let populated = false;
|
|
83
|
+
let onEmpty = null;
|
|
84
|
+
for (const step of body) {
|
|
85
|
+
if (step.binding) {
|
|
86
|
+
context = step.binding(context, machine);
|
|
87
|
+
} else if (step.conditional === "empty") {
|
|
88
|
+
onEmpty = { step, xc: context };
|
|
89
|
+
} else if (step.conditional === "non-empty") {
|
|
90
|
+
results.push({ step, xc: context });
|
|
91
|
+
} else {
|
|
92
|
+
const items = stepItems(step, context, machine);
|
|
93
|
+
populated ||= !items.every(isVacuous);
|
|
94
|
+
results.push(items);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const items = [];
|
|
98
|
+
if (!populated && onEmpty) {
|
|
99
|
+
items.push(...stepItems(onEmpty.step, onEmpty.xc, machine));
|
|
100
|
+
} else {
|
|
101
|
+
for (const result of results) {
|
|
102
|
+
if (Array.isArray(result)) items.push(...result);
|
|
103
|
+
else if (populated) {
|
|
104
|
+
items.push(...stepItems(result.step, result.xc, machine));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const item of items) out.item(item);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XSLT context of an instruction (XSLT 3.0 section 5.4.3): the focus,
|
|
3
|
+
* the variables in scope, the current mode and template rule, the current
|
|
4
|
+
* group and grouping key, the captured groups of xsl:analyze-string, the
|
|
5
|
+
* tunnel parameters, and the transformation it belongs to. Contexts are
|
|
6
|
+
* immutable: instructions derive new ones.
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xslt/runtime/context
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {object} XsltContext
|
|
13
|
+
* @property {object} tx - The transformation (see transformation.js)
|
|
14
|
+
* @property {*} item - Context item, undefined when absent
|
|
15
|
+
* @property {number} position
|
|
16
|
+
* @property {number} size
|
|
17
|
+
* @property {{value: Array, next: object}|null} env - Variable values
|
|
18
|
+
* @property {object|null} mode - Current mode
|
|
19
|
+
* @property {object|null} rule - Current template rule
|
|
20
|
+
* @property {Array|undefined} group - Current group
|
|
21
|
+
* @property {Array|undefined} groupKey - Current grouping key
|
|
22
|
+
* @property {string[]|undefined} regex - Captured groups
|
|
23
|
+
* @property {Map<string, Array>|null} tunnel - Tunnel parameters
|
|
24
|
+
* @property {boolean} temporary - Temporary output state
|
|
25
|
+
* @property {object|null} dyn - Dynamic context of XPath, made on demand
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A context with some fields changed.
|
|
30
|
+
* @param {XsltContext} xc
|
|
31
|
+
* @param {Partial<XsltContext>} changes
|
|
32
|
+
* @returns {XsltContext}
|
|
33
|
+
*/
|
|
34
|
+
export function derive(xc, changes) {
|
|
35
|
+
const next = { ...xc, ...changes };
|
|
36
|
+
next.dyn = null;
|
|
37
|
+
return next;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A context with a new focus.
|
|
42
|
+
* @param {XsltContext} xc
|
|
43
|
+
* @param {*} item
|
|
44
|
+
* @param {number} position
|
|
45
|
+
* @param {number} size
|
|
46
|
+
* @returns {XsltContext}
|
|
47
|
+
*/
|
|
48
|
+
export const withFocus = (xc, item, position, size) =>
|
|
49
|
+
derive(xc, { item, position, size });
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A context with a variable bound innermost.
|
|
53
|
+
* @param {XsltContext} xc
|
|
54
|
+
* @param {Array} value
|
|
55
|
+
* @returns {XsltContext}
|
|
56
|
+
*/
|
|
57
|
+
export const withVariable = (xc, value) =>
|
|
58
|
+
derive(xc, { env: { value, next: xc.env } });
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The XPath dynamic context of an XSLT context: the dynamic context of
|
|
62
|
+
* the transformation with the XSLT context attached (read by current(),
|
|
63
|
+
* current-group(), regex-group()...).
|
|
64
|
+
* @param {XsltContext} xc
|
|
65
|
+
* @returns {object}
|
|
66
|
+
*/
|
|
67
|
+
export function dynamicContextOf(xc) {
|
|
68
|
+
let dyn = xc.dyn;
|
|
69
|
+
if (!dyn) {
|
|
70
|
+
dyn = Object.create(xc.tx.dyn);
|
|
71
|
+
dyn.xc = xc;
|
|
72
|
+
xc.dyn = dyn;
|
|
73
|
+
}
|
|
74
|
+
return dyn;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Evaluates a compiled expression in an XSLT context.
|
|
79
|
+
* @param {{run: Function}} expr
|
|
80
|
+
* @param {XsltContext} xc
|
|
81
|
+
* @returns {Array} the result sequence
|
|
82
|
+
*/
|
|
83
|
+
export function evaluate(expr, xc) {
|
|
84
|
+
const dyn = dynamicContextOf(xc);
|
|
85
|
+
useStaticContext(dyn, expr.sc);
|
|
86
|
+
return expr.run({
|
|
87
|
+
item: xc.item,
|
|
88
|
+
position: xc.position,
|
|
89
|
+
size: xc.size,
|
|
90
|
+
env: xc.env,
|
|
91
|
+
dyn,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Makes the static context of the expression about to run visible to
|
|
97
|
+
* the functions that need it (key(), system-property(), format-number(),
|
|
98
|
+
* doc() with a relative URI...).
|
|
99
|
+
* @param {object} dyn - XPath dynamic context
|
|
100
|
+
* @param {object} sc - Static context of the expression
|
|
101
|
+
*/
|
|
102
|
+
export function useStaticContext(dyn, sc) {
|
|
103
|
+
dyn.sc = sc;
|
|
104
|
+
dyn.staticBaseUri = sc.baseUri;
|
|
105
|
+
dyn.decimalFormats = sc.decimalFormats;
|
|
106
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep copies of nodes into a receiver (xsl:copy-of, nodes added to
|
|
3
|
+
* element content), with or without their namespace nodes. Iterative, so
|
|
4
|
+
* deep trees do not exhaust the call stack.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/runtime/copy
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
attributesOf,
|
|
11
|
+
isNamespaceDeclaration,
|
|
12
|
+
} from "../../xpath/eval/domNodes.js";
|
|
13
|
+
import { inScopeNamespaces } from "../../xpath/eval/namespaceNodes.js";
|
|
14
|
+
import { markCopied, markDocumentCopied } from "./baseUri.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Copies the namespace nodes of an element into a receiver.
|
|
18
|
+
* @param {Element} element
|
|
19
|
+
* @param {object} receiver
|
|
20
|
+
*/
|
|
21
|
+
export function copyNamespaceNodes(element, receiver) {
|
|
22
|
+
for (const [prefix, uri] of inScopeNamespaces(element)) {
|
|
23
|
+
if (prefix !== "xml" && uri !== "") receiver.namespace(prefix, uri);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Starts the copy of an element: its name, namespaces and attributes.
|
|
29
|
+
* @param {Element} element
|
|
30
|
+
* @param {object} receiver
|
|
31
|
+
* @param {boolean} copyNamespaces
|
|
32
|
+
* @param {boolean} [top] - The element is the root of the copy (its
|
|
33
|
+
* inherited namespaces are copied too)
|
|
34
|
+
* @returns {object} the receiver of the element's content
|
|
35
|
+
*/
|
|
36
|
+
export function startElementCopy(
|
|
37
|
+
element,
|
|
38
|
+
receiver,
|
|
39
|
+
copyNamespaces,
|
|
40
|
+
top = true,
|
|
41
|
+
) {
|
|
42
|
+
const content = receiver.element(
|
|
43
|
+
element.namespaceURI ?? "",
|
|
44
|
+
element.nodeName,
|
|
45
|
+
);
|
|
46
|
+
if (top) markCopied(element, content.parent);
|
|
47
|
+
if (copyNamespaces && top) copyNamespaceNodes(element, content);
|
|
48
|
+
else if (copyNamespaces) {
|
|
49
|
+
for (const attribute of element.attributes) {
|
|
50
|
+
if (!isNamespaceDeclaration(attribute)) continue;
|
|
51
|
+
const name = attribute.name;
|
|
52
|
+
content.namespace(name === "xmlns" ? "" : name.slice(6), attribute.value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const attribute of attributesOf(element)) {
|
|
56
|
+
content.attribute(
|
|
57
|
+
attribute.namespaceURI ?? "",
|
|
58
|
+
attribute.nodeName,
|
|
59
|
+
attribute.value,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return content;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Copies a node that is not an element or document.
|
|
67
|
+
* @param {Node} node
|
|
68
|
+
* @param {object} receiver
|
|
69
|
+
*/
|
|
70
|
+
export function copyLeaf(node, receiver) {
|
|
71
|
+
switch (node.nodeType) {
|
|
72
|
+
case 2:
|
|
73
|
+
receiver.attribute(node.namespaceURI ?? "", node.nodeName, node.value);
|
|
74
|
+
break;
|
|
75
|
+
case 3:
|
|
76
|
+
case 4:
|
|
77
|
+
receiver.text(node.nodeValue);
|
|
78
|
+
break;
|
|
79
|
+
case 7:
|
|
80
|
+
receiver.pi(node.target ?? node.nodeName, node.nodeValue);
|
|
81
|
+
break;
|
|
82
|
+
case 8:
|
|
83
|
+
receiver.comment(node.nodeValue);
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
// a namespace node
|
|
87
|
+
receiver.namespace(node.localName, node.nodeValue);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Copies a node with its descendants into a receiver; a document node
|
|
93
|
+
* contributes its children.
|
|
94
|
+
* @param {Node} node
|
|
95
|
+
* @param {object} receiver - Tree or sequence receiver
|
|
96
|
+
* @param {boolean} copyNamespaces - Copy the namespace nodes of elements
|
|
97
|
+
*/
|
|
98
|
+
export function copyNode(node, receiver, copyNamespaces) {
|
|
99
|
+
const stack = [[node, receiver]];
|
|
100
|
+
while (stack.length > 0) {
|
|
101
|
+
const [current, target] = stack.pop();
|
|
102
|
+
const type = current.nodeType;
|
|
103
|
+
let content;
|
|
104
|
+
if (type === 1) {
|
|
105
|
+
content = startElementCopy(
|
|
106
|
+
current,
|
|
107
|
+
target,
|
|
108
|
+
copyNamespaces,
|
|
109
|
+
current === node,
|
|
110
|
+
);
|
|
111
|
+
} else if (type === 9 || type === 11) {
|
|
112
|
+
content = target.document();
|
|
113
|
+
markDocumentCopied(current, content);
|
|
114
|
+
} else {
|
|
115
|
+
copyLeaf(current, target);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const children = current.childNodes;
|
|
119
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
120
|
+
if (children[i].nodeType !== 10) stack.push([children[i], content]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fn:copy-of and fn:snapshot (XSLT 3.0 sections 20.6 and 20.7): deep
|
|
3
|
+
* copies of nodes; a snapshot also copies the ancestors of each node.
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/xslt/runtime/copyFunctions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { isNode } from "../../xdm/atomic.js";
|
|
9
|
+
import { xsltError } from "../names.js";
|
|
10
|
+
import { setOrigin } from "./accumulators.js";
|
|
11
|
+
import { SequenceReceiver } from "./sequenceReceiver.js";
|
|
12
|
+
import { snapshotNode } from "./snapshot.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Copies the nodes of a sequence; other items are kept.
|
|
16
|
+
* @param {Array} items
|
|
17
|
+
* @param {object} context - XPath dynamic context
|
|
18
|
+
* @param {boolean} snapshot - fn:snapshot: copy the ancestors too
|
|
19
|
+
* @returns {Array}
|
|
20
|
+
*/
|
|
21
|
+
function copyItems(items, context, snapshot) {
|
|
22
|
+
const result = [];
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
if (!isNode(item)) {
|
|
25
|
+
result.push(item);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const out = new SequenceReceiver(context.xc.tx.scratch);
|
|
29
|
+
let copy;
|
|
30
|
+
if (snapshot) {
|
|
31
|
+
copy = snapshotNode(item, out, (c, o) => setOrigin(context.xc.tx, c, o));
|
|
32
|
+
} else {
|
|
33
|
+
out.copy(item, true);
|
|
34
|
+
copy = out.items.at(-1);
|
|
35
|
+
}
|
|
36
|
+
setOrigin(context.xc.tx, copy, item);
|
|
37
|
+
result.push(copy);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The context item of a focus function.
|
|
44
|
+
* @param {object} context
|
|
45
|
+
* @returns {Array}
|
|
46
|
+
* @throws {import("../../errors.js").XPathError} XPDY0002 when absent
|
|
47
|
+
*/
|
|
48
|
+
function contextItemOf(context) {
|
|
49
|
+
if (context.contextItem === undefined) {
|
|
50
|
+
throw xsltError("XPDY0002", "The context item is absent");
|
|
51
|
+
}
|
|
52
|
+
return [context.contextItem];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Function definitions. */
|
|
56
|
+
export const copyFunctions = ["copy-of", "snapshot"].flatMap((local) => [
|
|
57
|
+
{
|
|
58
|
+
local,
|
|
59
|
+
params: [],
|
|
60
|
+
returns: "item()*",
|
|
61
|
+
focus: true,
|
|
62
|
+
impl: (_, context) =>
|
|
63
|
+
copyItems(contextItemOf(context), context, local === "snapshot"),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
local,
|
|
67
|
+
params: ["item()*"],
|
|
68
|
+
returns: "item()*",
|
|
69
|
+
impl: ([items], context) => copyItems(items, context, local === "snapshot"),
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The original node a node of a copy stands for, for the accumulator
|
|
3
|
+
* values of copies (fn:copy-of, fn:snapshot, xsl:copy-of with
|
|
4
|
+
* copy-accumulators): copied nodes are recorded with their original, and
|
|
5
|
+
* a node inside a recorded copy is found by its child positions.
|
|
6
|
+
*
|
|
7
|
+
* Every accumulator-before()/accumulator-after() call asks, so the common
|
|
8
|
+
* case (a node of a source tree, in no copy) must stay cheap: the
|
|
9
|
+
* ancestors are first searched for a recorded copy, and child positions
|
|
10
|
+
* are only computed for nodes that are inside one. Positions are counted
|
|
11
|
+
* along previousSibling rather than with an indexOf over childNodes,
|
|
12
|
+
* which on jsdom goes through a live NodeList proxy per index.
|
|
13
|
+
*
|
|
14
|
+
* @module @tradik/xslt3/xslt/runtime/copyOrigins
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Position of a node among the children of its parent.
|
|
19
|
+
* @param {Node} node
|
|
20
|
+
* @returns {number}
|
|
21
|
+
*/
|
|
22
|
+
function positionOf(node) {
|
|
23
|
+
let position = 0;
|
|
24
|
+
for (let s = node.previousSibling; s; s = s.previousSibling) position++;
|
|
25
|
+
return position;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The nearest recorded copy containing a node (the node itself included).
|
|
30
|
+
* @param {WeakMap<Node, Node>} origins
|
|
31
|
+
* @param {Node} node
|
|
32
|
+
* @returns {Node|null}
|
|
33
|
+
*/
|
|
34
|
+
function recordedAncestor(origins, node) {
|
|
35
|
+
for (let current = node; current; current = current.parentNode) {
|
|
36
|
+
if (origins.has(current)) return current;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The node of the original tree a node of a copy stands for.
|
|
43
|
+
* @param {WeakMap<Node, Node>} origins - Copied nodes to original nodes
|
|
44
|
+
* @param {Node} node - Not an attribute (accumulators have no values
|
|
45
|
+
* for attributes)
|
|
46
|
+
* @returns {Node|null} the original, null when the node is not in a copy
|
|
47
|
+
*/
|
|
48
|
+
export function originalNode(origins, node) {
|
|
49
|
+
const copy = recordedAncestor(origins, node);
|
|
50
|
+
if (!copy) return null;
|
|
51
|
+
const path = [];
|
|
52
|
+
for (let current = node; current !== copy; current = current.parentNode) {
|
|
53
|
+
path.push(positionOf(current));
|
|
54
|
+
}
|
|
55
|
+
let original = origins.get(copy);
|
|
56
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
57
|
+
original = original.childNodes[path[i]];
|
|
58
|
+
}
|
|
59
|
+
return original;
|
|
60
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default output method of a result tree (XSLT 3.0 section 26.1): html
|
|
3
|
+
* for an `html` document element in no namespace (any case), xhtml for
|
|
4
|
+
* `html` in the XHTML namespace (xml when the principal stylesheet module
|
|
5
|
+
* is version 1.0 and the tree is the implicit principal result), else xml.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/runtime/defaultMethod
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The method a tree implies.
|
|
14
|
+
* @param {*} tree - Document or document fragment (other values: xml)
|
|
15
|
+
* @param {boolean} versionOne - XSLT 1.0 rules for an XHTML tree
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
function impliedMethod(tree, versionOne) {
|
|
19
|
+
for (let child = tree?.firstChild; child; child = child.nextSibling) {
|
|
20
|
+
if (child.nodeType === 3 && /\S/.test(child.nodeValue)) return "xml";
|
|
21
|
+
if (child.nodeType !== 1) continue;
|
|
22
|
+
const local = child.localName;
|
|
23
|
+
const uri = child.namespaceURI ?? "";
|
|
24
|
+
if (local.toLowerCase() === "html" && uri === "") return "html";
|
|
25
|
+
if (local === "html" && uri === XHTML_NS && !versionOne) return "xhtml";
|
|
26
|
+
return "xml";
|
|
27
|
+
}
|
|
28
|
+
return "xml";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Serialization parameters with the default method when they have none.
|
|
33
|
+
* @param {object} output - Parameters of the output definition
|
|
34
|
+
* @param {*} tree - The result
|
|
35
|
+
* @param {boolean} [versionOne] - The implicit principal result of a
|
|
36
|
+
* version 1.0 stylesheet
|
|
37
|
+
* @returns {object}
|
|
38
|
+
*/
|
|
39
|
+
export function withDefaultMethod(output, tree, versionOne = false) {
|
|
40
|
+
if (output.method !== undefined) return output;
|
|
41
|
+
return { ...output, method: impliedMethod(tree, versionOne) };
|
|
42
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content of a document node added to a tree (XSLT 3.0 section
|
|
3
|
+
* 5.7.1): its children go to the enclosing node, but attributes and
|
|
4
|
+
* namespaces are errors (XTDE0420).
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/runtime/documentContent
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { isAtomic } from "../../xdm/atomic.js";
|
|
10
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
11
|
+
import { xsltError } from "../names.js";
|
|
12
|
+
import { copyNode } from "./copy.js";
|
|
13
|
+
|
|
14
|
+
/** Receives the content of a document node inside a tree. */
|
|
15
|
+
export class DocumentContent {
|
|
16
|
+
/** @param {object} target - Receiver of the enclosing node's content */
|
|
17
|
+
constructor(target) {
|
|
18
|
+
this.target = target;
|
|
19
|
+
this.lastAtomic = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} uri
|
|
24
|
+
* @param {string} qname
|
|
25
|
+
* @returns {object} the receiver of the new element's content
|
|
26
|
+
*/
|
|
27
|
+
element(uri, qname) {
|
|
28
|
+
this.lastAtomic = false;
|
|
29
|
+
return this.target.element(uri, qname);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Raises XTDE0420. */
|
|
33
|
+
attribute() {
|
|
34
|
+
throw xsltError("XTDE0420", "A document node cannot have attributes");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Raises XTDE0420. */
|
|
38
|
+
namespace() {
|
|
39
|
+
throw xsltError("XTDE0420", "A document node cannot have namespaces");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** @param {string} text */
|
|
43
|
+
text(text) {
|
|
44
|
+
this.lastAtomic = false;
|
|
45
|
+
this.target.text(text);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @param {string} text */
|
|
49
|
+
comment(text) {
|
|
50
|
+
this.lastAtomic = false;
|
|
51
|
+
this.target.comment(text);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} target
|
|
56
|
+
* @param {string} data
|
|
57
|
+
*/
|
|
58
|
+
pi(target, data) {
|
|
59
|
+
this.lastAtomic = false;
|
|
60
|
+
this.target.pi(target, data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @param {*} value - An atomic value */
|
|
64
|
+
atomic(value) {
|
|
65
|
+
const text = canonicalString(value);
|
|
66
|
+
this.target.text(this.lastAtomic ? ` ${text}` : text);
|
|
67
|
+
this.lastAtomic = true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @param {*} item */
|
|
71
|
+
item(item) {
|
|
72
|
+
if (isAtomic(item)) this.atomic(item);
|
|
73
|
+
else if (typeof item?.nodeType === "number") this.copy(item, true);
|
|
74
|
+
else {
|
|
75
|
+
this.lastAtomic = false;
|
|
76
|
+
this.target.item(item);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {Node} node
|
|
82
|
+
* @param {boolean} copyNamespaces
|
|
83
|
+
*/
|
|
84
|
+
copy(node, copyNamespaces) {
|
|
85
|
+
copyNode(node, this, copyNamespaces);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** @returns {DocumentContent} itself */
|
|
89
|
+
document() {
|
|
90
|
+
this.lastAtomic = false;
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The target expressions of xsl:evaluate (XSLT 3.0 sections 10.4.1 and
|
|
3
|
+
* 10.4.2): compiled at run time against a static context built from the
|
|
4
|
+
* instruction (or from the namespace-context node), without the
|
|
5
|
+
* XSLT-defined functions and the stylesheet variables, and cached per
|
|
6
|
+
* instruction.
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xslt/runtime/dynamicXPath
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { FunctionLibrary } from "../../functions/registry.js";
|
|
12
|
+
import { compileNode, withinLimits } from "../../xpath/eval/compiler.js";
|
|
13
|
+
import { inScopeNamespaces } from "../../xpath/eval/namespaceNodes.js";
|
|
14
|
+
import { createStaticContext } from "../../xpath/eval/staticContext.js";
|
|
15
|
+
import { parseXPath } from "../../xpath/syntax/index.js";
|
|
16
|
+
import { xsltError } from "../names.js";
|
|
17
|
+
import { xsltFunctions } from "./functions.js";
|
|
18
|
+
|
|
19
|
+
/** @type {WeakMap<FunctionLibrary, FunctionLibrary>} */
|
|
20
|
+
const libraries = new WeakMap();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The functions of a target expression: those of the stylesheet less
|
|
24
|
+
* the XSLT-defined ones (key(), current-group(), system-property()...).
|
|
25
|
+
* @param {FunctionLibrary} library - The library of the stylesheet
|
|
26
|
+
* @returns {FunctionLibrary}
|
|
27
|
+
*/
|
|
28
|
+
function targetLibrary(library) {
|
|
29
|
+
let result = libraries.get(library);
|
|
30
|
+
if (!result) {
|
|
31
|
+
const xslt = new Set(xsltFunctions);
|
|
32
|
+
const hidden = (d) =>
|
|
33
|
+
d.visibility === "private" || d.visibility === "hidden";
|
|
34
|
+
result = new FunctionLibrary(
|
|
35
|
+
library.definitions.filter((d) => !xslt.has(d) && !hidden(d)),
|
|
36
|
+
);
|
|
37
|
+
libraries.set(library, result);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The namespaces of a namespace-context node: its in-scope namespaces,
|
|
44
|
+
* the default one as the default element namespace.
|
|
45
|
+
* @param {Node} node
|
|
46
|
+
* @returns {{namespaces: Map<string, string>, defaultElementNamespace: string}}
|
|
47
|
+
*/
|
|
48
|
+
export function nodeNamespaces(node) {
|
|
49
|
+
const bindings = node.nodeType === 1 ? inScopeNamespaces(node) : new Map();
|
|
50
|
+
const defaultElementNamespace = bindings.get("") ?? "";
|
|
51
|
+
bindings.delete("");
|
|
52
|
+
return { namespaces: bindings, defaultElementNamespace };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Compiles a target expression.
|
|
57
|
+
* @param {string} text
|
|
58
|
+
* @param {object} context - `{namespaces, defaultElementNamespace,
|
|
59
|
+
* baseUri, library, decimalFormats}`
|
|
60
|
+
* @param {string[]} keys - Clark names of the variables, outermost first
|
|
61
|
+
* @returns {(ctx: object) => Array} the evaluator
|
|
62
|
+
* @throws {import("../../errors.js").XPathError} XTDE3160 for a static
|
|
63
|
+
* error in the expression
|
|
64
|
+
*/
|
|
65
|
+
export function compileTarget(text, context, keys) {
|
|
66
|
+
try {
|
|
67
|
+
const sc = createStaticContext(
|
|
68
|
+
{
|
|
69
|
+
namespaces: context.namespaces,
|
|
70
|
+
defaultElementNamespace: context.defaultElementNamespace,
|
|
71
|
+
baseUri: context.baseUri,
|
|
72
|
+
decimalFormats: context.decimalFormats,
|
|
73
|
+
},
|
|
74
|
+
targetLibrary(context.library),
|
|
75
|
+
);
|
|
76
|
+
let vars = null;
|
|
77
|
+
for (const key of keys) vars = { key, next: vars };
|
|
78
|
+
const run = withinLimits(() => compileNode(parseXPath(text), { sc, vars }));
|
|
79
|
+
return Object.assign(run, { sc });
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// an undeclared variable keeps its code: the parameters vary per call
|
|
82
|
+
if (typeof error?.code !== "string" || error.code === "XPST0008") {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
throw xsltError(
|
|
86
|
+
"XTDE3160",
|
|
87
|
+
`Invalid expression "${text}" in xsl:evaluate: ${error.message}`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|