@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,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The markup writer of the xml, xhtml and html output methods: writes the
|
|
3
|
+
* normalized sequence (see sequence.js) as markup, with the method's rules
|
|
4
|
+
* from dialect.js.
|
|
5
|
+
*
|
|
6
|
+
* The walk is iterative: every element whose children are being written
|
|
7
|
+
* is a frame on an explicit stack, so a tree nested deeper than the
|
|
8
|
+
* JavaScript call stack allows is written like any other, and the output
|
|
9
|
+
* is handed out in chunks as it grows.
|
|
10
|
+
*
|
|
11
|
+
* @module @tradik/xslt3/serialize/markup/writer
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { Expander } from "../output/expander.js";
|
|
15
|
+
import {
|
|
16
|
+
checkDocument,
|
|
17
|
+
childList,
|
|
18
|
+
escapeNamespaceUri,
|
|
19
|
+
isWhitespace,
|
|
20
|
+
} from "./content.js";
|
|
21
|
+
import { Dialect } from "./dialect.js";
|
|
22
|
+
import { fixupElement, XML_NAMESPACE } from "./namespaces.js";
|
|
23
|
+
import { contentTypeMeta, doctype, xmlDeclaration } from "./prolog.js";
|
|
24
|
+
|
|
25
|
+
/** Indentation added per level. */
|
|
26
|
+
const INDENT = " ";
|
|
27
|
+
|
|
28
|
+
/** Writes markup for one serialization. */
|
|
29
|
+
export class MarkupWriter {
|
|
30
|
+
/**
|
|
31
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
32
|
+
* @param {import("../output/buffer.js").OutputBuffer} buffer
|
|
33
|
+
*/
|
|
34
|
+
constructor(settings, buffer) {
|
|
35
|
+
this.settings = settings;
|
|
36
|
+
this.buffer = buffer;
|
|
37
|
+
this.expander = new Expander(settings);
|
|
38
|
+
this.dialect = new Dialect(settings);
|
|
39
|
+
this.doctypePending = true;
|
|
40
|
+
this.fixup = {
|
|
41
|
+
top: true,
|
|
42
|
+
unprefixed: this.dialect.unprefixed,
|
|
43
|
+
undeclare: settings.undeclarePrefixes && settings.version === "1.1",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @param {string} text - Markup */
|
|
48
|
+
write(text) {
|
|
49
|
+
this.buffer.write(text);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Writes the normalized sequence.
|
|
54
|
+
* @param {Array<string|Node>} entries - From normalizeSequence
|
|
55
|
+
* @yields {string} chunks of output, when the buffer is full
|
|
56
|
+
* @returns {Generator<string, void, void>}
|
|
57
|
+
*/
|
|
58
|
+
*run(entries) {
|
|
59
|
+
checkDocument(entries, this.settings);
|
|
60
|
+
const declaration = xmlDeclaration(this.settings);
|
|
61
|
+
const indent = this.settings.indent;
|
|
62
|
+
const indentTop = indent && !entries.some((e) => typeof e === "string");
|
|
63
|
+
this.write(declaration && indent ? `${declaration}\n` : declaration);
|
|
64
|
+
const top = { depth: -1, scope: new Map(), preserve: false };
|
|
65
|
+
for (let i = 0; i < entries.length; i++) {
|
|
66
|
+
if (indentTop && i > 0) this.write("\n");
|
|
67
|
+
const entry = entries[i];
|
|
68
|
+
if (typeof entry === "string") this.write(this.expander.text(entry));
|
|
69
|
+
else if (entry.nodeType === 1) yield* this.tree(entry, top);
|
|
70
|
+
else this.leaf(entry, "escape");
|
|
71
|
+
if (this.buffer.full) yield this.buffer.take();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Writes an element and its descendants.
|
|
77
|
+
* @param {Element} element
|
|
78
|
+
* @param {object} top - The frame of the document
|
|
79
|
+
* @yields {string} chunks of output
|
|
80
|
+
* @returns {Generator<string, void, void>}
|
|
81
|
+
*/
|
|
82
|
+
*tree(element, top) {
|
|
83
|
+
const stack = [];
|
|
84
|
+
const root = this.openElement(element, top);
|
|
85
|
+
if (root) stack.push(root);
|
|
86
|
+
while (stack.length) {
|
|
87
|
+
const frame = stack[stack.length - 1];
|
|
88
|
+
if (frame.index === frame.children.length) {
|
|
89
|
+
stack.pop();
|
|
90
|
+
this.write(frame.end);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const child = frame.children[frame.index++];
|
|
94
|
+
if (frame.indent) this.write(`\n${INDENT.repeat(frame.depth + 1)}`);
|
|
95
|
+
if (typeof child === "object" && child.nodeType === 1) {
|
|
96
|
+
const opened = this.openElement(child, frame);
|
|
97
|
+
if (opened) stack.push(opened);
|
|
98
|
+
} else {
|
|
99
|
+
this.leaf(child, frame.textMode);
|
|
100
|
+
}
|
|
101
|
+
if (this.buffer.full) yield this.buffer.take();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Writes character data, a comment or a processing instruction.
|
|
107
|
+
* @param {string|Node} node
|
|
108
|
+
* @param {"escape"|"cdata"|"raw"} textMode
|
|
109
|
+
*/
|
|
110
|
+
leaf(node, textMode) {
|
|
111
|
+
const { expander } = this;
|
|
112
|
+
if (typeof node === "string") {
|
|
113
|
+
this.write(expander[textMode === "escape" ? "text" : textMode](node));
|
|
114
|
+
} else if (node.nodeType === 8) {
|
|
115
|
+
this.write(`<!--${expander.unescaped(node.nodeValue)}-->`);
|
|
116
|
+
} else {
|
|
117
|
+
this.write(this.dialect.processingInstruction(node, expander));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Writes the start tag of an element, or the whole element when it is
|
|
123
|
+
* empty.
|
|
124
|
+
* @param {Element} element
|
|
125
|
+
* @param {object} parent - Frame of the parent
|
|
126
|
+
* @returns {object|null} the frame of its children, null when empty
|
|
127
|
+
*/
|
|
128
|
+
openElement(element, parent) {
|
|
129
|
+
const { dialect, settings } = this;
|
|
130
|
+
this.fixup.top = parent.depth < 0;
|
|
131
|
+
const fixed = fixupElement(element, parent.scope, this.fixup);
|
|
132
|
+
const info = dialect.describe(element, fixed);
|
|
133
|
+
if (this.doctypePending) {
|
|
134
|
+
this.doctypePending = false;
|
|
135
|
+
const declaration = doctype(settings, fixed.name);
|
|
136
|
+
if (declaration) this.write(`${declaration}\n`);
|
|
137
|
+
}
|
|
138
|
+
let tag = `<${this.expander.unescaped(fixed.name)}`;
|
|
139
|
+
for (const [prefix, uri] of fixed.declarations) {
|
|
140
|
+
tag += ` xmlns${prefix ? `:${prefix}` : ""}="${escapeNamespaceUri(uri)}"`;
|
|
141
|
+
}
|
|
142
|
+
for (const attribute of fixed.attributes) {
|
|
143
|
+
tag += dialect.attribute(info, attribute, this.expander);
|
|
144
|
+
}
|
|
145
|
+
const head =
|
|
146
|
+
info.isHtml && info.lower === "head" && settings.includeContentType;
|
|
147
|
+
let children = childList(element, head);
|
|
148
|
+
const leading = head ? contentTypeMeta(settings) : "";
|
|
149
|
+
if (!children.length && !leading) {
|
|
150
|
+
this.write(tag + dialect.emptyElement(info));
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
this.write(`${tag}>`);
|
|
154
|
+
const space = element.getAttributeNS?.(XML_NAMESPACE, "space");
|
|
155
|
+
const preserve =
|
|
156
|
+
space === "preserve" || (space !== "default" && parent.preserve);
|
|
157
|
+
const suppressed = parent.suppressed || dialect.suppressesIndent(info);
|
|
158
|
+
const textMode = dialect.textMode(info);
|
|
159
|
+
const indent =
|
|
160
|
+
settings.indent &&
|
|
161
|
+
!preserve &&
|
|
162
|
+
!suppressed &&
|
|
163
|
+
textMode !== "raw" &&
|
|
164
|
+
children.some((child) => typeof child !== "string") &&
|
|
165
|
+
children.every((c) => typeof c !== "string" || isWhitespace(c)) &&
|
|
166
|
+
dialect.allowsIndent(children);
|
|
167
|
+
if (indent) children = children.filter((c) => typeof c !== "string");
|
|
168
|
+
const depth = parent.depth + 1;
|
|
169
|
+
const lineStart = indent ? `\n${INDENT.repeat(depth)}` : "";
|
|
170
|
+
if (leading) this.write(indent ? lineStart + INDENT + leading : leading);
|
|
171
|
+
return {
|
|
172
|
+
children,
|
|
173
|
+
index: 0,
|
|
174
|
+
indent,
|
|
175
|
+
depth,
|
|
176
|
+
scope: fixed.scope,
|
|
177
|
+
textMode,
|
|
178
|
+
preserve,
|
|
179
|
+
suppressed,
|
|
180
|
+
end: `${lineStart}</${fixed.name}>`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adaptive output method (Serialization 3.1 section 11): any
|
|
3
|
+
* sequence, written in a form close to XPath syntax. Items are separated
|
|
4
|
+
* by item-separator (a newline by default); nodes are written with the
|
|
5
|
+
* xml method, attributes as name="value", maps as map{key:value,...},
|
|
6
|
+
* arrays as [...], strings quoted, other atomic values as constructor
|
|
7
|
+
* calls (xs:date("...")), functions as name#arity.
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/serialize/methods/adaptive
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { canonicalString, itemKind } from "../../xdm/index.js";
|
|
13
|
+
|
|
14
|
+
/** Prefixes of the standard function namespaces. */
|
|
15
|
+
const FUNCTION_PREFIXES = {
|
|
16
|
+
"http://www.w3.org/2005/xpath-functions": "fn",
|
|
17
|
+
"http://www.w3.org/2005/xpath-functions/math": "math",
|
|
18
|
+
"http://www.w3.org/2005/xpath-functions/map": "map",
|
|
19
|
+
"http://www.w3.org/2005/xpath-functions/array": "array",
|
|
20
|
+
"http://www.w3.org/2001/XMLSchema": "xs",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Types written as quoted strings. */
|
|
24
|
+
const STRING_TYPES = new Set(["string", "anyURI", "untypedAtomic"]);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* An xs:double in exponential notation, e.g. 1.0e0 or -1.25e-3.
|
|
28
|
+
* @param {number} value
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
export function doubleText(value) {
|
|
32
|
+
if (Number.isNaN(value)) return "NaN";
|
|
33
|
+
if (!Number.isFinite(value)) return value > 0 ? "INF" : "-INF";
|
|
34
|
+
if (value === 0) return Object.is(value, -0) ? "-0.0e0" : "0.0e0";
|
|
35
|
+
const [mantissa, exponent] = value.toExponential().split("e");
|
|
36
|
+
const digits = mantissa.includes(".") ? mantissa : `${mantissa}.0`;
|
|
37
|
+
return `${digits}e${Number(exponent)}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {{namespaceURI: string, localName: string, prefix?: string}} name
|
|
42
|
+
* @returns {string} prefix:local for known namespaces, else Q{uri}local
|
|
43
|
+
*/
|
|
44
|
+
function functionName(name) {
|
|
45
|
+
const prefix = FUNCTION_PREFIXES[name.namespaceURI] ?? name.prefix;
|
|
46
|
+
if (prefix) return `${prefix}:${name.localName}`;
|
|
47
|
+
return name.namespaceURI
|
|
48
|
+
? `Q{${name.namespaceURI}}${name.localName}`
|
|
49
|
+
: name.localName;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Writes the adaptive form of items. */
|
|
53
|
+
export class AdaptiveWriter {
|
|
54
|
+
/**
|
|
55
|
+
* @param {import("../output/expander.js").Expander} expander
|
|
56
|
+
* @param {(node: Node) => string} serializeNode - The xml method
|
|
57
|
+
*/
|
|
58
|
+
constructor(expander, serializeNode) {
|
|
59
|
+
this.expander = expander;
|
|
60
|
+
this.serializeNode = serializeNode;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {Array} sequence
|
|
65
|
+
* @returns {string} one item as is, others in parentheses
|
|
66
|
+
*/
|
|
67
|
+
sequence(sequence) {
|
|
68
|
+
if (sequence.length === 1) return this.item(sequence[0]);
|
|
69
|
+
return `(${sequence.map((item) => this.item(item)).join(",")})`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {*} item
|
|
74
|
+
* @returns {string}
|
|
75
|
+
*/
|
|
76
|
+
item(item) {
|
|
77
|
+
switch (itemKind(item)) {
|
|
78
|
+
case "atomic":
|
|
79
|
+
return this.atomic(item);
|
|
80
|
+
case "node":
|
|
81
|
+
return this.node(item);
|
|
82
|
+
case "map": {
|
|
83
|
+
const entries = [...item.entries.values()].map(
|
|
84
|
+
({ key, value }) => `${this.atomic(key)}:${this.sequence(value)}`,
|
|
85
|
+
);
|
|
86
|
+
return `map{${entries.join(",")}}`;
|
|
87
|
+
}
|
|
88
|
+
case "array":
|
|
89
|
+
return `[${item.members.map((m) => this.sequence(m)).join(",")}]`;
|
|
90
|
+
default: {
|
|
91
|
+
const name = item.name
|
|
92
|
+
? functionName(item.name)
|
|
93
|
+
: "(anonymous-function)";
|
|
94
|
+
return `${name}#${item.arity}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {Node} node
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
node(node) {
|
|
104
|
+
if (node.nodeType === 2) {
|
|
105
|
+
const value = this.expander.attribute(node.value);
|
|
106
|
+
return `${node.name}="${value}"`;
|
|
107
|
+
}
|
|
108
|
+
if (node.nodeType === 13) {
|
|
109
|
+
const name = node.localName ? `xmlns:${node.localName}` : "xmlns";
|
|
110
|
+
return `${name}="${this.expander.attribute(node.nodeValue)}"`;
|
|
111
|
+
}
|
|
112
|
+
return this.serializeNode(node);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {import("../../xdm/atomic.js").AtomicValue} item
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
atomic(item) {
|
|
120
|
+
const primitive = item.type.primitive;
|
|
121
|
+
const name = primitive.localName;
|
|
122
|
+
if (STRING_TYPES.has(name)) return `"${item.value.replaceAll('"', '""')}"`;
|
|
123
|
+
if (name === "boolean") return item.value ? "true()" : "false()";
|
|
124
|
+
if (name === "decimal") return canonicalString(item);
|
|
125
|
+
if (name === "double") return doubleText(item.value);
|
|
126
|
+
if (name === "QName") {
|
|
127
|
+
return `Q{${item.value.namespaceURI}}${item.value.localName}`;
|
|
128
|
+
}
|
|
129
|
+
return `${primitive.prefixedName}("${canonicalString(item)}")`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JSON output method (Serialization 3.1 section 10): maps as objects,
|
|
3
|
+
* arrays as arrays, numbers, booleans and strings as themselves, other
|
|
4
|
+
* atomic values as strings, nodes as strings holding their serialization
|
|
5
|
+
* with json-node-output-method.
|
|
6
|
+
*
|
|
7
|
+
* Errors: SERE0020 for NaN and infinities, SERE0021 for function items,
|
|
8
|
+
* SERE0022 for duplicate keys (unless allow-duplicate-names), SERE0023
|
|
9
|
+
* for sequences of more than one item.
|
|
10
|
+
*
|
|
11
|
+
* @module @tradik/xslt3/serialize/methods/json
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { XPathError } from "../../errors.js";
|
|
15
|
+
import { canonicalString, itemKind } from "../../xdm/index.js";
|
|
16
|
+
|
|
17
|
+
const ESCAPES = {
|
|
18
|
+
'"': '\\"',
|
|
19
|
+
"\\": "\\\\",
|
|
20
|
+
"/": "\\/",
|
|
21
|
+
"\b": "\\b",
|
|
22
|
+
"\f": "\\f",
|
|
23
|
+
"\n": "\\n",
|
|
24
|
+
"\r": "\\r",
|
|
25
|
+
"\t": "\\t",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {number} unit - UTF-16 code unit
|
|
30
|
+
* @returns {string} a \u escape
|
|
31
|
+
*/
|
|
32
|
+
const unicodeEscape = (unit) =>
|
|
33
|
+
`\\u${unit.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
34
|
+
|
|
35
|
+
/** Writes JSON for one serialization. */
|
|
36
|
+
export class JsonWriter {
|
|
37
|
+
/**
|
|
38
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
39
|
+
* @param {import("../output/expander.js").Expander} expander - For
|
|
40
|
+
* character maps and normalization
|
|
41
|
+
* @param {(node: Node) => string} serializeNode - Serializes a node
|
|
42
|
+
* with json-node-output-method
|
|
43
|
+
*/
|
|
44
|
+
constructor(settings, expander, serializeNode) {
|
|
45
|
+
this.settings = settings;
|
|
46
|
+
this.expander = expander;
|
|
47
|
+
this.serializeNode = serializeNode;
|
|
48
|
+
const encodable = settings.encoding.encodable;
|
|
49
|
+
this.escapeChar = (char) => {
|
|
50
|
+
if (ESCAPES[char]) return ESCAPES[char];
|
|
51
|
+
const cp = char.codePointAt(0);
|
|
52
|
+
if (cp < 0xa0 || (encodable && !encodable(cp))) {
|
|
53
|
+
let escaped = "";
|
|
54
|
+
for (let i = 0; i < char.length; i++) {
|
|
55
|
+
escaped += unicodeEscape(char.charCodeAt(i));
|
|
56
|
+
}
|
|
57
|
+
return escaped;
|
|
58
|
+
}
|
|
59
|
+
return char;
|
|
60
|
+
};
|
|
61
|
+
const escaped = '["\\\\/\\u0000-\\u001F\\u007F-\\u009F]';
|
|
62
|
+
this.pattern = new RegExp(
|
|
63
|
+
encodable ? `${escaped}|[^\\u0000-\\u007F]` : escaped,
|
|
64
|
+
"gu",
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} text
|
|
70
|
+
* @returns {string} a JSON string literal
|
|
71
|
+
*/
|
|
72
|
+
string(text) {
|
|
73
|
+
const body = this.expander.mapped(text, (run) =>
|
|
74
|
+
run.replace(this.pattern, this.escapeChar),
|
|
75
|
+
);
|
|
76
|
+
return `"${body}"`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {Array} sequence - A value: at most one item
|
|
81
|
+
* @param {number} depth - Nesting depth, for indentation
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
sequence(sequence, depth) {
|
|
85
|
+
if (sequence.length > 1) {
|
|
86
|
+
throw new XPathError(
|
|
87
|
+
"SERE0023",
|
|
88
|
+
"The JSON output method cannot write a sequence of several items",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return sequence.length ? this.item(sequence[0], depth) : "null";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string[]} members - Serialized members
|
|
96
|
+
* @param {string} open
|
|
97
|
+
* @param {string} close
|
|
98
|
+
* @param {number} depth
|
|
99
|
+
* @returns {string} an object or array
|
|
100
|
+
*/
|
|
101
|
+
container(members, open, close, depth) {
|
|
102
|
+
if (!members.length) return open + close;
|
|
103
|
+
if (!this.settings.indent) return open + members.join(",") + close;
|
|
104
|
+
const inner = `\n${" ".repeat(depth + 1)}`;
|
|
105
|
+
return `${open}${inner}${members.join(`,${inner}`)}\n${" ".repeat(depth)}${close}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {*} item
|
|
110
|
+
* @param {number} depth
|
|
111
|
+
* @returns {string}
|
|
112
|
+
*/
|
|
113
|
+
item(item, depth) {
|
|
114
|
+
const kind = itemKind(item);
|
|
115
|
+
if (kind === "map") {
|
|
116
|
+
const seen = new Set();
|
|
117
|
+
const separator = this.settings.indent ? ": " : ":";
|
|
118
|
+
const members = [...item.entries.values()].map(({ key, value }) => {
|
|
119
|
+
const name = canonicalString(key);
|
|
120
|
+
if (seen.has(name) && !this.settings.allowDuplicateNames) {
|
|
121
|
+
throw new XPathError("SERE0022", `Duplicate key ${name} in JSON`);
|
|
122
|
+
}
|
|
123
|
+
seen.add(name);
|
|
124
|
+
return this.string(name) + separator + this.sequence(value, depth + 1);
|
|
125
|
+
});
|
|
126
|
+
return this.container(members, "{", "}", depth);
|
|
127
|
+
}
|
|
128
|
+
if (kind === "array") {
|
|
129
|
+
const members = item.members.map((m) => this.sequence(m, depth + 1));
|
|
130
|
+
return this.container(members, "[", "]", depth);
|
|
131
|
+
}
|
|
132
|
+
if (kind === "node") return this.string(this.serializeNode(item));
|
|
133
|
+
if (kind === "function") {
|
|
134
|
+
throw new XPathError(
|
|
135
|
+
"SERE0021",
|
|
136
|
+
"A function item cannot be written as JSON",
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return this.atomic(item);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {import("../../xdm/atomic.js").AtomicValue} item
|
|
144
|
+
* @returns {string}
|
|
145
|
+
*/
|
|
146
|
+
atomic(item) {
|
|
147
|
+
const primitive = item.type.primitive.localName;
|
|
148
|
+
if (primitive === "boolean") return String(item.value);
|
|
149
|
+
const text = canonicalString(item);
|
|
150
|
+
if (primitive === "double" || primitive === "float") {
|
|
151
|
+
if (!Number.isFinite(item.value)) {
|
|
152
|
+
throw new XPathError("SERE0020", `${text} cannot be written as JSON`);
|
|
153
|
+
}
|
|
154
|
+
return text;
|
|
155
|
+
}
|
|
156
|
+
return primitive === "decimal" ? text : this.string(text);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The text output method (Serialization 3.1 section 8): the string value
|
|
3
|
+
* of the normalized document, that is its text nodes in document order,
|
|
4
|
+
* without escaping. Character maps and Unicode normalization apply; a
|
|
5
|
+
* character the encoding cannot represent is a SERE0008 error.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/serialize/methods/text
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Expander } from "../output/expander.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Writes the text of the normalized sequence.
|
|
14
|
+
* @param {Array<string|Node>} entries - From normalizeSequence
|
|
15
|
+
* @param {import("../params/settings.js").Settings} settings
|
|
16
|
+
* @param {import("../output/buffer.js").OutputBuffer} buffer
|
|
17
|
+
* @yields {string} chunks of output, when the buffer is full
|
|
18
|
+
* @returns {Generator<string, void, void>}
|
|
19
|
+
*/
|
|
20
|
+
export function* writeText(entries, settings, buffer) {
|
|
21
|
+
const expander = new Expander(settings);
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (typeof entry === "string") {
|
|
24
|
+
buffer.write(expander.raw(entry));
|
|
25
|
+
} else if (entry.nodeType === 1) {
|
|
26
|
+
// descendant text nodes in document order, without recursion
|
|
27
|
+
const stack = [entry.firstChild];
|
|
28
|
+
while (stack.length) {
|
|
29
|
+
const node = stack.pop();
|
|
30
|
+
if (!node) continue;
|
|
31
|
+
stack.push(node.nextSibling);
|
|
32
|
+
const type = node.nodeType;
|
|
33
|
+
if (type === 3 || type === 4) {
|
|
34
|
+
buffer.write(expander.raw(node.nodeValue));
|
|
35
|
+
} else if (type === 1) {
|
|
36
|
+
stack.push(node.firstChild);
|
|
37
|
+
}
|
|
38
|
+
if (buffer.full) yield buffer.take();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (buffer.full) yield buffer.take();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The output buffer of the serializers: markup is appended piece by piece
|
|
3
|
+
* and handed out in chunks of about `chunkSize` UTF-16 code units, so a
|
|
4
|
+
* large result can be streamed without ever being held as one string.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/serialize/output/buffer
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Default chunk size of serializeChunks, in UTF-16 code units. */
|
|
10
|
+
export const DEFAULT_CHUNK_SIZE = 16384;
|
|
11
|
+
|
|
12
|
+
/** Collects output and releases it in chunks. */
|
|
13
|
+
export class OutputBuffer {
|
|
14
|
+
/**
|
|
15
|
+
* @param {number} [chunkSize] - Chunk size; Infinity keeps everything
|
|
16
|
+
* until the end
|
|
17
|
+
*/
|
|
18
|
+
constructor(chunkSize = DEFAULT_CHUNK_SIZE) {
|
|
19
|
+
this.chunkSize = chunkSize;
|
|
20
|
+
/** @type {string[]} */
|
|
21
|
+
this.parts = [];
|
|
22
|
+
this.length = 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @param {string} text - Markup to append */
|
|
26
|
+
write(text) {
|
|
27
|
+
if (text) {
|
|
28
|
+
this.parts.push(text);
|
|
29
|
+
this.length += text.length;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** @returns {boolean} whether a chunk is ready */
|
|
34
|
+
get full() {
|
|
35
|
+
return this.length >= this.chunkSize;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Takes the buffered output.
|
|
40
|
+
* @returns {string} everything written since the last take ("" if none)
|
|
41
|
+
*/
|
|
42
|
+
take() {
|
|
43
|
+
const chunk = this.parts.join("");
|
|
44
|
+
this.parts = [];
|
|
45
|
+
this.length = 0;
|
|
46
|
+
return chunk;
|
|
47
|
+
}
|
|
48
|
+
}
|