@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.
Files changed (286) hide show
  1. package/LICENSE.md +28 -0
  2. package/README.md +98 -2
  3. package/package.json +43 -4
  4. package/src/errors.js +20 -0
  5. package/src/functions/aggregates.js +144 -0
  6. package/src/functions/arraySort.js +33 -0
  7. package/src/functions/arrays.js +181 -0
  8. package/src/functions/attributeTypes.js +58 -0
  9. package/src/functions/baseUris.js +82 -0
  10. package/src/functions/collations.js +127 -0
  11. package/src/functions/collections.js +84 -0
  12. package/src/functions/context.js +83 -0
  13. package/src/functions/core.js +167 -0
  14. package/src/functions/datetime.js +203 -0
  15. package/src/functions/deepEqual.js +132 -0
  16. package/src/functions/diagnostics.js +88 -0
  17. package/src/functions/focus.js +34 -0
  18. package/src/functions/format/dateComponents.js +176 -0
  19. package/src/functions/format/datePicture.js +115 -0
  20. package/src/functions/format/decimalFormat.js +65 -0
  21. package/src/functions/format/digitPattern.js +145 -0
  22. package/src/functions/format/formatDateTime.js +204 -0
  23. package/src/functions/format/formatInteger.js +109 -0
  24. package/src/functions/format/formatNumber.js +165 -0
  25. package/src/functions/format/fractionFormat.js +80 -0
  26. package/src/functions/format/germanWords.js +89 -0
  27. package/src/functions/format/numberLanguages.js +41 -0
  28. package/src/functions/format/numberPicture.js +202 -0
  29. package/src/functions/format/numberWords.js +155 -0
  30. package/src/functions/format/place.js +74 -0
  31. package/src/functions/format/timezoneFormat.js +89 -0
  32. package/src/functions/higherOrder.js +106 -0
  33. package/src/functions/ids.js +127 -0
  34. package/src/functions/json/index.js +17 -0
  35. package/src/functions/json/jsonChars.js +110 -0
  36. package/src/functions/json/jsonParser.js +203 -0
  37. package/src/functions/json/jsonStrings.js +116 -0
  38. package/src/functions/json/jsonToXml.js +116 -0
  39. package/src/functions/json/parseJson.js +119 -0
  40. package/src/functions/json/xmlRepresentation.js +103 -0
  41. package/src/functions/json/xmlToJson.js +147 -0
  42. package/src/functions/library.js +47 -0
  43. package/src/functions/mapArrayEqual.js +41 -0
  44. package/src/functions/maps.js +152 -0
  45. package/src/functions/math.js +73 -0
  46. package/src/functions/nodePaths.js +181 -0
  47. package/src/functions/nodes.js +199 -0
  48. package/src/functions/numeric.js +148 -0
  49. package/src/functions/options.js +58 -0
  50. package/src/functions/parseIetfDate.js +104 -0
  51. package/src/functions/parseXml.js +77 -0
  52. package/src/functions/qnames.js +68 -0
  53. package/src/functions/random.js +128 -0
  54. package/src/functions/regex/analyzeString.js +144 -0
  55. package/src/functions/regex/charClass.js +112 -0
  56. package/src/functions/regex/compile.js +81 -0
  57. package/src/functions/regex/escapes.js +109 -0
  58. package/src/functions/regex/parser.js +169 -0
  59. package/src/functions/regex/reader.js +37 -0
  60. package/src/functions/regex/regexFunctions.js +140 -0
  61. package/src/functions/regex/translate.js +102 -0
  62. package/src/functions/regex/unicodeBlocks.js +194 -0
  63. package/src/functions/registry.js +140 -0
  64. package/src/functions/sequences.js +173 -0
  65. package/src/functions/serializeFunction.js +53 -0
  66. package/src/functions/signatures.js +105 -0
  67. package/src/functions/sort.js +102 -0
  68. package/src/functions/stringMatch.js +45 -0
  69. package/src/functions/strings.js +194 -0
  70. package/src/functions/stringsCore.js +66 -0
  71. package/src/functions/support.js +106 -0
  72. package/src/functions/textDecoding.js +117 -0
  73. package/src/functions/ucaCollation.js +127 -0
  74. package/src/functions/unparsedText.js +71 -0
  75. package/src/functions/uri.js +166 -0
  76. package/src/functions/xqueryModule.js +24 -0
  77. package/src/index.js +38 -0
  78. package/src/items/array.js +84 -0
  79. package/src/items/function.js +45 -0
  80. package/src/items/map.js +120 -0
  81. package/src/items/mapKey.js +82 -0
  82. package/src/serialize/index.js +129 -0
  83. package/src/serialize/markup/content.js +74 -0
  84. package/src/serialize/markup/dialect.js +178 -0
  85. package/src/serialize/markup/htmlData.js +70 -0
  86. package/src/serialize/markup/namespaces.js +145 -0
  87. package/src/serialize/markup/prolog.js +91 -0
  88. package/src/serialize/markup/writer.js +183 -0
  89. package/src/serialize/methods/adaptive.js +131 -0
  90. package/src/serialize/methods/json.js +158 -0
  91. package/src/serialize/methods/text.js +43 -0
  92. package/src/serialize/output/buffer.js +48 -0
  93. package/src/serialize/output/encoding.js +146 -0
  94. package/src/serialize/output/expander.js +204 -0
  95. package/src/serialize/params/fromElement.js +141 -0
  96. package/src/serialize/params/fromMap.js +128 -0
  97. package/src/serialize/params/names.js +69 -0
  98. package/src/serialize/params/settings.js +153 -0
  99. package/src/serialize/params/values.js +163 -0
  100. package/src/serialize/sequence.js +111 -0
  101. package/src/xdm/arithmetic.js +155 -0
  102. package/src/xdm/atomic.js +142 -0
  103. package/src/xdm/binary.js +92 -0
  104. package/src/xdm/calendar.js +71 -0
  105. package/src/xdm/cast.js +176 -0
  106. package/src/xdm/compare.js +192 -0
  107. package/src/xdm/datetime.js +206 -0
  108. package/src/xdm/decimal.js +200 -0
  109. package/src/xdm/duration.js +128 -0
  110. package/src/xdm/generalCompare.js +152 -0
  111. package/src/xdm/index.js +47 -0
  112. package/src/xdm/lexical.js +126 -0
  113. package/src/xdm/nodes.js +170 -0
  114. package/src/xdm/numeric.js +122 -0
  115. package/src/xdm/qname.js +65 -0
  116. package/src/xdm/strings.js +52 -0
  117. package/src/xdm/temporalArithmetic.js +180 -0
  118. package/src/xdm/timeline.js +68 -0
  119. package/src/xdm/types.js +178 -0
  120. package/src/xpath/eval/atomics.js +45 -0
  121. package/src/xpath/eval/axes.js +212 -0
  122. package/src/xpath/eval/calls.js +168 -0
  123. package/src/xpath/eval/coercion.js +146 -0
  124. package/src/xpath/eval/compiler.js +71 -0
  125. package/src/xpath/eval/constructors.js +129 -0
  126. package/src/xpath/eval/decimalFormats.js +93 -0
  127. package/src/xpath/eval/descendantMemo.js +77 -0
  128. package/src/xpath/eval/documentOrder.js +142 -0
  129. package/src/xpath/eval/domNodes.js +174 -0
  130. package/src/xpath/eval/dynamicCall.js +27 -0
  131. package/src/xpath/eval/dynamicContext.js +161 -0
  132. package/src/xpath/eval/flow.js +115 -0
  133. package/src/xpath/eval/functionItems.js +194 -0
  134. package/src/xpath/eval/groupedChildren.js +88 -0
  135. package/src/xpath/eval/inline.js +71 -0
  136. package/src/xpath/eval/namespaceNodes.js +90 -0
  137. package/src/xpath/eval/nodeTests.js +164 -0
  138. package/src/xpath/eval/operators.js +190 -0
  139. package/src/xpath/eval/paths.js +163 -0
  140. package/src/xpath/eval/positional.js +81 -0
  141. package/src/xpath/eval/predicates.js +56 -0
  142. package/src/xpath/eval/primary.js +99 -0
  143. package/src/xpath/eval/resources.js +149 -0
  144. package/src/xpath/eval/scope.js +108 -0
  145. package/src/xpath/eval/sequenceType.js +193 -0
  146. package/src/xpath/eval/staticContext.js +142 -0
  147. package/src/xpath/eval/steps.js +98 -0
  148. package/src/xpath/eval/subtype.js +154 -0
  149. package/src/xpath/eval/typeExprs.js +147 -0
  150. package/src/xpath/eval/typeNames.js +101 -0
  151. package/src/xpath/eval/uris.js +21 -0
  152. package/src/xpath/eval/values.js +64 -0
  153. package/src/xpath/index.js +217 -0
  154. package/src/xpath/syntax/ast.js +130 -0
  155. package/src/xpath/syntax/chars.js +58 -0
  156. package/src/xpath/syntax/constructors.js +144 -0
  157. package/src/xpath/syntax/expressions.js +107 -0
  158. package/src/xpath/syntax/index.js +44 -0
  159. package/src/xpath/syntax/kindTests.js +134 -0
  160. package/src/xpath/syntax/lexer.js +220 -0
  161. package/src/xpath/syntax/operators.js +111 -0
  162. package/src/xpath/syntax/paths.js +102 -0
  163. package/src/xpath/syntax/postfix.js +91 -0
  164. package/src/xpath/syntax/primaries.js +149 -0
  165. package/src/xpath/syntax/steps.js +146 -0
  166. package/src/xpath/syntax/syntaxError.js +31 -0
  167. package/src/xpath/syntax/tokenStream.js +163 -0
  168. package/src/xpath/syntax/typeAst.js +76 -0
  169. package/src/xpath/syntax/typeOperators.js +127 -0
  170. package/src/xpath/syntax/types.js +158 -0
  171. package/src/xslt/api.js +153 -0
  172. package/src/xslt/compiler/acceptance.js +178 -0
  173. package/src/xslt/compiler/accumulatorDecl.js +146 -0
  174. package/src/xslt/compiler/attributeValues.js +174 -0
  175. package/src/xslt/compiler/attributes.js +211 -0
  176. package/src/xslt/compiler/avt.js +103 -0
  177. package/src/xslt/compiler/body.js +154 -0
  178. package/src/xslt/compiler/children.js +105 -0
  179. package/src/xslt/compiler/compatibility.js +130 -0
  180. package/src/xslt/compiler/components.js +179 -0
  181. package/src/xslt/compiler/contextItem.js +95 -0
  182. package/src/xslt/compiler/decimalFormats.js +132 -0
  183. package/src/xslt/compiler/declarationKinds.js +82 -0
  184. package/src/xslt/compiler/elementInfo.js +246 -0
  185. package/src/xslt/compiler/expressions.js +175 -0
  186. package/src/xslt/compiler/globalContextItem.js +92 -0
  187. package/src/xslt/compiler/globals.js +209 -0
  188. package/src/xslt/compiler/linker.js +134 -0
  189. package/src/xslt/compiler/miscDecl.js +171 -0
  190. package/src/xslt/compiler/modeDecl.js +88 -0
  191. package/src/xslt/compiler/moduleLoading.js +111 -0
  192. package/src/xslt/compiler/modules.js +158 -0
  193. package/src/xslt/compiler/outputDecl.js +209 -0
  194. package/src/xslt/compiler/overrideLinks.js +141 -0
  195. package/src/xslt/compiler/packageChecks.js +84 -0
  196. package/src/xslt/compiler/packageRegistry.js +95 -0
  197. package/src/xslt/compiler/packageTable.js +181 -0
  198. package/src/xslt/compiler/packageVersions.js +143 -0
  199. package/src/xslt/compiler/packages.js +138 -0
  200. package/src/xslt/compiler/registry.js +234 -0
  201. package/src/xslt/compiler/staticStage.js +197 -0
  202. package/src/xslt/compiler/staticVariables.js +93 -0
  203. package/src/xslt/compiler/stylesheet.js +205 -0
  204. package/src/xslt/compiler/tailPosition.js +74 -0
  205. package/src/xslt/compiler/templatesDecl.js +180 -0
  206. package/src/xslt/compiler/useAccumulators.js +76 -0
  207. package/src/xslt/compiler/useWhen.js +94 -0
  208. package/src/xslt/compiler/visibility.js +160 -0
  209. package/src/xslt/index.js +11 -0
  210. package/src/xslt/instructions/analyzeString.js +137 -0
  211. package/src/xslt/instructions/assert.js +72 -0
  212. package/src/xslt/instructions/attributeSets.js +115 -0
  213. package/src/xslt/instructions/conditional.js +99 -0
  214. package/src/xslt/instructions/control.js +123 -0
  215. package/src/xslt/instructions/copy.js +151 -0
  216. package/src/xslt/instructions/elements.js +161 -0
  217. package/src/xslt/instructions/evaluate.js +189 -0
  218. package/src/xslt/instructions/fork.js +35 -0
  219. package/src/xslt/instructions/grouping.js +106 -0
  220. package/src/xslt/instructions/index.js +94 -0
  221. package/src/xslt/instructions/iterate.js +182 -0
  222. package/src/xslt/instructions/literal.js +142 -0
  223. package/src/xslt/instructions/maps.js +72 -0
  224. package/src/xslt/instructions/merge.js +190 -0
  225. package/src/xslt/instructions/number.js +205 -0
  226. package/src/xslt/instructions/output.js +102 -0
  227. package/src/xslt/instructions/params.js +110 -0
  228. package/src/xslt/instructions/resultDocument.js +143 -0
  229. package/src/xslt/instructions/sort.js +114 -0
  230. package/src/xslt/instructions/sourceDocument.js +73 -0
  231. package/src/xslt/instructions/templates.js +153 -0
  232. package/src/xslt/instructions/text.js +151 -0
  233. package/src/xslt/instructions/try.js +186 -0
  234. package/src/xslt/instructions/variables.js +119 -0
  235. package/src/xslt/names.js +204 -0
  236. package/src/xslt/patterns/axisOrigins.js +121 -0
  237. package/src/xslt/patterns/compile.js +151 -0
  238. package/src/xslt/patterns/general.js +132 -0
  239. package/src/xslt/patterns/grammar.js +190 -0
  240. package/src/xslt/patterns/modes.js +153 -0
  241. package/src/xslt/patterns/paths.js +130 -0
  242. package/src/xslt/patterns/patternChecks.js +29 -0
  243. package/src/xslt/patterns/steps.js +162 -0
  244. package/src/xslt/processor.js +178 -0
  245. package/src/xslt/runtime/accumulatorFunctions.js +48 -0
  246. package/src/xslt/runtime/accumulators.js +174 -0
  247. package/src/xslt/runtime/apply.js +153 -0
  248. package/src/xslt/runtime/atomicKeys.js +36 -0
  249. package/src/xslt/runtime/availability.js +162 -0
  250. package/src/xslt/runtime/baseUri.js +64 -0
  251. package/src/xslt/runtime/compilerHook.js +27 -0
  252. package/src/xslt/runtime/conditional.js +110 -0
  253. package/src/xslt/runtime/context.js +106 -0
  254. package/src/xslt/runtime/copy.js +123 -0
  255. package/src/xslt/runtime/copyFunctions.js +71 -0
  256. package/src/xslt/runtime/copyOrigins.js +60 -0
  257. package/src/xslt/runtime/defaultMethod.js +42 -0
  258. package/src/xslt/runtime/documentContent.js +93 -0
  259. package/src/xslt/runtime/dynamicXPath.js +90 -0
  260. package/src/xslt/runtime/functionCache.js +65 -0
  261. package/src/xslt/runtime/functionCall.js +57 -0
  262. package/src/xslt/runtime/functions.js +191 -0
  263. package/src/xslt/runtime/groups.js +108 -0
  264. package/src/xslt/runtime/invocation.js +156 -0
  265. package/src/xslt/runtime/iteration.js +107 -0
  266. package/src/xslt/runtime/keys.js +141 -0
  267. package/src/xslt/runtime/lazy.js +97 -0
  268. package/src/xslt/runtime/locations.js +86 -0
  269. package/src/xslt/runtime/machine.js +137 -0
  270. package/src/xslt/runtime/merging.js +167 -0
  271. package/src/xslt/runtime/namespaceScope.js +141 -0
  272. package/src/xslt/runtime/numberFormat.js +89 -0
  273. package/src/xslt/runtime/numbering.js +168 -0
  274. package/src/xslt/runtime/params.js +59 -0
  275. package/src/xslt/runtime/properties.js +103 -0
  276. package/src/xslt/runtime/rawResults.js +82 -0
  277. package/src/xslt/runtime/sequenceReceiver.js +102 -0
  278. package/src/xslt/runtime/snapshot.js +66 -0
  279. package/src/xslt/runtime/sortKeys.js +119 -0
  280. package/src/xslt/runtime/streamAvailable.js +60 -0
  281. package/src/xslt/runtime/strip.js +102 -0
  282. package/src/xslt/runtime/transformFunction.js +169 -0
  283. package/src/xslt/runtime/transformation.js +168 -0
  284. package/src/xslt/runtime/transformationContext.js +85 -0
  285. package/src/xslt/runtime/treeReceiver.js +194 -0
  286. 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
+ }