@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,74 @@
1
+ /**
2
+ * Helpers of the markup writer about content: the children of an element
3
+ * as written, whitespace, namespace URIs in declarations, and the check
4
+ * that a document type or standalone declaration has a document to go
5
+ * with.
6
+ *
7
+ * @module @tradik/xslt3/serialize/markup/content
8
+ */
9
+
10
+ import { XPathError } from "../../errors.js";
11
+ import { isContentTypeMeta } from "./prolog.js";
12
+
13
+ /**
14
+ * @param {string} text
15
+ * @returns {boolean} whether the text is XML whitespace only
16
+ */
17
+ export const isWhitespace = (text) => /^[ \t\r\n]*$/.test(text);
18
+
19
+ const URI_ESCAPES = { "&": "&amp;", "<": "&lt;", '"': "&quot;" };
20
+
21
+ /**
22
+ * @param {string} uri - A namespace URI
23
+ * @returns {string} the URI escaped for an attribute value
24
+ */
25
+ export const escapeNamespaceUri = (uri) =>
26
+ uri.replace(/[&<"]/g, (c) => URI_ESCAPES[c]);
27
+
28
+ /**
29
+ * The children of an element as written: adjacent character data merged
30
+ * into strings, other nodes kept.
31
+ * @param {Element} element
32
+ * @param {boolean} dropMeta - Leave out content type meta elements
33
+ * @returns {Array<string|Node>}
34
+ */
35
+ export function childList(element, dropMeta) {
36
+ const list = [];
37
+ for (let child = element.firstChild; child; child = child.nextSibling) {
38
+ const type = child.nodeType;
39
+ if (type === 3 || type === 4) {
40
+ const last = list.length - 1;
41
+ if (typeof list[last] === "string") list[last] += child.nodeValue;
42
+ else if (child.nodeValue) list.push(child.nodeValue);
43
+ } else if (type === 1 || type === 7 || type === 8) {
44
+ if (!(dropMeta && isContentTypeMeta(child))) list.push(child);
45
+ }
46
+ }
47
+ return list;
48
+ }
49
+
50
+ /**
51
+ * A document type declaration or a standalone declaration needs a
52
+ * well-formed document: one element and no text at the top level.
53
+ * @param {Array<string|Node>} entries
54
+ * @param {import("../params/settings.js").Settings} settings
55
+ * @throws {XPathError} SEPM0004 when the content is not a document
56
+ */
57
+ export function checkDocument(entries, settings) {
58
+ const { method, doctypeSystem, standalone } = settings;
59
+ if (
60
+ method === "html" ||
61
+ (doctypeSystem === undefined && standalone === "omit")
62
+ ) {
63
+ return;
64
+ }
65
+ const elements = entries.filter(
66
+ (e) => typeof e !== "string" && e.nodeType === 1,
67
+ );
68
+ if (elements.length > 1 || entries.some((e) => typeof e === "string")) {
69
+ throw new XPathError(
70
+ "SEPM0004",
71
+ "doctype-system and standalone need a single element and no text at the top level",
72
+ );
73
+ }
74
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * The differences between the xml, xhtml and html output methods
3
+ * (Serialization 3.1 sections 5, 6 and 7) for the markup writer: which
4
+ * elements are HTML elements, how empty elements, attributes and
5
+ * processing instructions are written, where CDATA sections, raw text and
6
+ * indentation are allowed.
7
+ *
8
+ * HTML elements are, for the html method, those in no namespace (and with
9
+ * HTML5 those in the XHTML namespace); for the xhtml method, those in the
10
+ * XHTML namespace (and with HTML5 those in no namespace). HTML5 output
11
+ * writes XHTML, SVG and MathML elements without a prefix.
12
+ *
13
+ * @module @tradik/xslt3/serialize/markup/dialect
14
+ */
15
+
16
+ import { XPathError } from "../../errors.js";
17
+ import { percentEncode } from "../../functions/uri.js";
18
+ import { clarkName } from "../params/values.js";
19
+ import {
20
+ BOOLEAN_ATTRIBUTES,
21
+ HTML5_UNPREFIXED,
22
+ INLINE_ELEMENTS,
23
+ isUriAttribute,
24
+ PRESERVING_ELEMENTS,
25
+ RAW_TEXT_ELEMENTS,
26
+ VOID_ELEMENTS,
27
+ XHTML_NAMESPACE,
28
+ } from "./htmlData.js";
29
+
30
+ /** escape-html-uri: %-escapes all but printable ASCII. */
31
+ const escapeUri = (value) =>
32
+ percentEncode(value.normalize("NFC"), (cp) => cp >= 32 && cp < 127);
33
+
34
+ /**
35
+ * Lower-cases the local names of a set of Clark names.
36
+ * @param {Set<string>} names
37
+ * @returns {Set<string>}
38
+ */
39
+ const lowerCased = (names) =>
40
+ new Set([...names].map((n) => n.replace(/\}.*$/, (s) => s.toLowerCase())));
41
+
42
+ /** Output method rules for the markup writer. */
43
+ export class Dialect {
44
+ /**
45
+ * @param {import("../params/settings.js").Settings} settings
46
+ */
47
+ constructor(settings) {
48
+ this.settings = settings;
49
+ this.method = settings.method;
50
+ this.html = this.method === "html";
51
+ this.htmlLike = this.html || this.method === "xhtml";
52
+ this.html5 = this.htmlLike && settings.htmlVersion === 5;
53
+ this.unprefixed = this.html5 ? HTML5_UNPREFIXED : new Set();
54
+ this.voids = VOID_ELEMENTS[settings.htmlVersion];
55
+ const names = (set) => (this.html ? lowerCased(set) : set);
56
+ this.cdataElements = names(settings.cdataSectionElements);
57
+ this.suppressed = names(settings.suppressIndentation);
58
+ }
59
+
60
+ /**
61
+ * Describes an element for the other hooks.
62
+ * @param {Element} element
63
+ * @param {import("./namespaces.js").FixedElement} fixed
64
+ * @returns {{fixed: object, isHtml: boolean, lower: string, key: string}}
65
+ */
66
+ describe(element, fixed) {
67
+ const ns = fixed.namespaceURI;
68
+ const local = element.localName ?? element.nodeName;
69
+ const lower = local.toLowerCase();
70
+ const primary = this.html ? "" : XHTML_NAMESPACE;
71
+ const secondary = this.html ? XHTML_NAMESPACE : "";
72
+ const isHtml =
73
+ this.htmlLike && (ns === primary || (this.html5 && ns === secondary));
74
+ const key = clarkName(ns, this.html ? lower : local);
75
+ return { fixed, isHtml, lower, key };
76
+ }
77
+
78
+ /**
79
+ * @param {object} info - From {@link Dialect#describe}
80
+ * @returns {string} the markup closing an empty element's start tag
81
+ */
82
+ emptyElement(info) {
83
+ if (!this.htmlLike) return "/>";
84
+ const isVoid = info.isHtml && this.voids.has(info.lower);
85
+ if (this.html) {
86
+ if (isVoid) return ">";
87
+ return info.isHtml ? `></${info.fixed.name}>` : "/>";
88
+ }
89
+ return isVoid ? " />" : `></${info.fixed.name}>`;
90
+ }
91
+
92
+ /**
93
+ * @param {object} info - The element
94
+ * @param {{name: string, localName: string, namespaceURI: string, value: string}} attribute
95
+ * @param {import("../output/expander.js").Expander} expander
96
+ * @returns {string} the attribute markup, with its leading space
97
+ */
98
+ attribute(info, attribute, expander) {
99
+ let { value } = attribute;
100
+ if (info.isHtml && attribute.namespaceURI === "") {
101
+ const lower = attribute.localName.toLowerCase();
102
+ if (
103
+ this.html &&
104
+ BOOLEAN_ATTRIBUTES.has(lower) &&
105
+ value.toLowerCase() === lower
106
+ ) {
107
+ return ` ${attribute.name}`;
108
+ }
109
+ if (
110
+ this.settings.escapeUriAttributes &&
111
+ isUriAttribute(info.lower, lower)
112
+ ) {
113
+ value = escapeUri(value);
114
+ }
115
+ }
116
+ return ` ${attribute.name}="${expander.attribute(value)}"`;
117
+ }
118
+
119
+ /**
120
+ * How the text children of an element are written.
121
+ * @param {object} info
122
+ * @returns {"escape"|"cdata"|"raw"}
123
+ */
124
+ textMode(info) {
125
+ if (this.html && info.isHtml) {
126
+ return RAW_TEXT_ELEMENTS.has(info.lower) ? "raw" : "escape";
127
+ }
128
+ return this.cdataElements.has(info.key) ? "cdata" : "escape";
129
+ }
130
+
131
+ /**
132
+ * Whether indentation is suppressed inside an element and its
133
+ * descendants.
134
+ * @param {object} info
135
+ * @returns {boolean}
136
+ */
137
+ suppressesIndent(info) {
138
+ return (
139
+ this.suppressed.has(info.key) ||
140
+ (info.isHtml && PRESERVING_ELEMENTS.has(info.lower))
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Whether the children (elements, comments, processing instructions)
146
+ * of an element may be indented: not when one is an inline element.
147
+ * @param {Array<string|Node>} children
148
+ * @returns {boolean}
149
+ */
150
+ allowsIndent(children) {
151
+ if (!this.htmlLike) return true;
152
+ return !children.some(
153
+ (child) =>
154
+ child.nodeType === 1 &&
155
+ INLINE_ELEMENTS.has((child.localName ?? child.nodeName).toLowerCase()),
156
+ );
157
+ }
158
+
159
+ /**
160
+ * @param {ProcessingInstruction} node
161
+ * @param {import("../output/expander.js").Expander} expander
162
+ * @returns {string} the processing instruction markup
163
+ * @throws {XPathError} SERE0015 for ">" in HTML processing instructions
164
+ */
165
+ processingInstruction(node, expander) {
166
+ const data = expander.unescaped(node.nodeValue ?? "");
167
+ const target = node.target ?? node.nodeName;
168
+ const space = data ? " " : "";
169
+ if (!this.html) return `<?${target}${space}${data}?>`;
170
+ if (data.includes(">")) {
171
+ throw new XPathError(
172
+ "SERE0015",
173
+ "A processing instruction in HTML output cannot contain >",
174
+ );
175
+ }
176
+ return `<?${target}${space}${data}>`;
177
+ }
178
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * What the html and xhtml output methods know about HTML elements and
3
+ * attributes (Serialization 3.1 sections 6 and 7, HTML 4.01 and the HTML
4
+ * Living Standard). Names are compared in lower case.
5
+ *
6
+ * @module @tradik/xslt3/serialize/markup/htmlData
7
+ */
8
+
9
+ /** The XHTML namespace. */
10
+ export const XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
11
+ /** Namespaces whose elements HTML5 output writes without a prefix. */
12
+ export const HTML5_UNPREFIXED = new Set([
13
+ XHTML_NAMESPACE,
14
+ "http://www.w3.org/2000/svg",
15
+ "http://www.w3.org/1998/Math/MathML",
16
+ ]);
17
+
18
+ const words = (text) => new Set(text.split(" "));
19
+
20
+ /** Elements with no content (void elements), by HTML version. */
21
+ export const VOID_ELEMENTS = {
22
+ 4: words(
23
+ "area base basefont br col embed frame hr img input isindex link meta param",
24
+ ),
25
+ 5: words(
26
+ "area base br col embed hr img input keygen link meta param source track wbr",
27
+ ),
28
+ };
29
+
30
+ /** Elements whose content is written without escaping (html method). */
31
+ export const RAW_TEXT_ELEMENTS = words("script style");
32
+
33
+ /** Elements inside which no whitespace is added. */
34
+ export const PRESERVING_ELEMENTS = words("pre script style textarea title");
35
+
36
+ /**
37
+ * Phrasing (inline) elements: whitespace added around them would show, so
38
+ * their parents are not indented.
39
+ */
40
+ export const INLINE_ELEMENTS = words(
41
+ "a abbr acronym audio b bdi bdo big br button canvas cite code data " +
42
+ "datalist del dfn em embed font i iframe img input ins kbd label map " +
43
+ "mark math meter noscript object output picture progress q ruby s samp " +
44
+ "select slot small span strike strong sub sup svg template textarea " +
45
+ "time tt u var video wbr",
46
+ );
47
+
48
+ /** Attributes written minimized when their value is their name. */
49
+ export const BOOLEAN_ATTRIBUTES = words(
50
+ "allowfullscreen async autofocus autoplay checked compact controls " +
51
+ "declare default defer disabled formnovalidate hidden inert ismap " +
52
+ "itemscope loop multiple muted nohref noresize noshade novalidate " +
53
+ "nowrap open playsinline readonly required reversed scoped seamless " +
54
+ "selected typemustmatch",
55
+ );
56
+
57
+ /** Attributes holding URIs, %-escaped with escape-uri-attributes. */
58
+ export const URI_ATTRIBUTES = words(
59
+ "action archive background cite classid codebase data datasrc " +
60
+ "formaction href icon longdesc manifest poster profile src usemap",
61
+ );
62
+
63
+ /**
64
+ * Whether an attribute holds a URI (`name` only on `a`).
65
+ * @param {string} element - Element local name, lower case
66
+ * @param {string} attribute - Attribute local name, lower case
67
+ * @returns {boolean}
68
+ */
69
+ export const isUriAttribute = (element, attribute) =>
70
+ URI_ATTRIBUTES.has(attribute) || (attribute === "name" && element === "a");
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Namespace fixup: the namespace declarations written on an element so
3
+ * that its name, its attributes' names and its in-scope namespaces are
4
+ * declared, whether or not the DOM holds `xmlns` attributes for them
5
+ * (createElementNS and setAttributeNS add none). A namespaced attribute
6
+ * without a usable prefix gets one (an existing one for its namespace, or
7
+ * ns0, ns1, ...).
8
+ *
9
+ * A scope is a Map from prefix ("" for the default namespace) to URI,
10
+ * shared by the elements that declare nothing and copied on change.
11
+ *
12
+ * @module @tradik/xslt3/serialize/markup/namespaces
13
+ */
14
+
15
+ import {
16
+ isNamespaceDeclaration,
17
+ XMLNS_NAMESPACE,
18
+ } from "../../xpath/eval/domNodes.js";
19
+ import {
20
+ inScopeNamespaces,
21
+ XML_NAMESPACE,
22
+ } from "../../xpath/eval/namespaceNodes.js";
23
+
24
+ export { XML_NAMESPACE };
25
+
26
+ /**
27
+ * @typedef {object} FixedElement
28
+ * @property {string} name - Output name of the element
29
+ * @property {string} namespaceURI - Its namespace, "" for none
30
+ * @property {Array<[string, string]>} declarations - [prefix, URI] to declare
31
+ * @property {Array<{name: string, localName: string, namespaceURI: string, value: string}>} attributes
32
+ * @property {Map<string, string>} scope - Scope of the children
33
+ */
34
+
35
+ /**
36
+ * @typedef {object} FixupOptions
37
+ * @property {boolean} top - The element is the first one written of its
38
+ * tree: its inherited namespaces are declared too
39
+ * @property {Set<string>} unprefixed - Namespaces whose elements are
40
+ * written without a prefix (HTML5 prefix normalization)
41
+ * @property {boolean} undeclare - Write `xmlns:p=""` undeclarations (XML
42
+ * 1.1 with undeclare-prefixes)
43
+ */
44
+
45
+ /**
46
+ * Bindings an element asks for, in order: inherited ones (top element),
47
+ * its own declarations, its name.
48
+ * @param {Element} element
49
+ * @param {string} prefix - Output prefix of the element
50
+ * @param {FixupOptions} options
51
+ * @returns {Map<string, string>}
52
+ */
53
+ function wantedBindings(element, prefix, options) {
54
+ const wanted = new Map();
55
+ const add = (p, uri) => {
56
+ if (p === "xml") return;
57
+ if (p !== "" && options.unprefixed.has(uri)) return;
58
+ wanted.set(p, uri);
59
+ };
60
+ if (options.top) {
61
+ for (const [p, uri] of inScopeNamespaces(element)) {
62
+ if (uri !== "" || p === "") add(p, uri);
63
+ }
64
+ }
65
+ for (const attribute of element.attributes) {
66
+ if (isNamespaceDeclaration(attribute)) {
67
+ const name = attribute.name;
68
+ add(name === "xmlns" ? "" : name.slice(6), attribute.value);
69
+ }
70
+ }
71
+ wanted.set(prefix, element.namespaceURI ?? "");
72
+ return wanted;
73
+ }
74
+
75
+ /**
76
+ * Chooses the prefix of a namespaced attribute.
77
+ * @param {Attr} attribute
78
+ * @param {string} uri - Its namespace
79
+ * @param {(prefix: string) => string|undefined} bound - Current binding
80
+ * @param {Map<string, string>} wanted
81
+ * @param {Map<string, string>} scope - Scope of the parent
82
+ * @returns {string} the prefix
83
+ */
84
+ function attributePrefix(attribute, uri, bound, wanted, scope) {
85
+ const own = attribute.prefix ?? "";
86
+ if (own !== "" && (bound(own) === undefined || bound(own) === uri)) {
87
+ return own;
88
+ }
89
+ for (const map of [wanted, scope]) {
90
+ for (const [p, u] of map) {
91
+ if (p !== "" && u === uri && bound(p) === uri) return p;
92
+ }
93
+ }
94
+ let n = 0;
95
+ while (bound(`ns${n}`) !== undefined) n++;
96
+ return `ns${n}`;
97
+ }
98
+
99
+ /**
100
+ * Computes the names and the namespace declarations of an element.
101
+ * @param {Element} element
102
+ * @param {Map<string, string>} scope - Scope of the parent
103
+ * @param {FixupOptions} options
104
+ * @returns {FixedElement}
105
+ */
106
+ export function fixupElement(element, scope, options) {
107
+ const namespaceURI = element.namespaceURI ?? "";
108
+ const localName = element.localName ?? element.nodeName;
109
+ const prefix = options.unprefixed.has(namespaceURI)
110
+ ? ""
111
+ : (element.prefix ?? "");
112
+ const wanted = wantedBindings(element, prefix, options);
113
+ const bound = (p) =>
114
+ wanted.has(p) ? wanted.get(p) : (scope.get(p) ?? (p ? undefined : ""));
115
+ const attributes = [];
116
+ for (const attribute of element.attributes) {
117
+ if (isNamespaceDeclaration(attribute)) continue;
118
+ const uri = attribute.namespaceURI ?? "";
119
+ const local = attribute.localName ?? attribute.name;
120
+ let name = local;
121
+ if (uri === XML_NAMESPACE) {
122
+ name = `xml:${local}`;
123
+ } else if (uri !== "" && uri !== XMLNS_NAMESPACE) {
124
+ const p = attributePrefix(attribute, uri, bound, wanted, scope);
125
+ if (bound(p) !== uri) wanted.set(p, uri);
126
+ name = `${p}:${local}`;
127
+ }
128
+ const value = attribute.value;
129
+ attributes.push({ name, localName: local, namespaceURI: uri, value });
130
+ }
131
+ const declarations = [];
132
+ let childScope = scope;
133
+ for (const [p, uri] of wanted) {
134
+ const current = scope.get(p) ?? (p ? undefined : "");
135
+ if (current === uri) continue;
136
+ if (uri === "" && p !== "") {
137
+ if (!options.undeclare || current === undefined) continue;
138
+ }
139
+ declarations.push([p, uri]);
140
+ if (childScope === scope) childScope = new Map(scope);
141
+ childScope.set(p, uri);
142
+ }
143
+ const name = prefix ? `${prefix}:${localName}` : localName;
144
+ return { name, namespaceURI, declarations, attributes, scope: childScope };
145
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Markup the serializer adds on its own: the XML declaration, the
3
+ * document type declaration and the content type `meta` element of
4
+ * include-content-type (Serialization 3.1 sections 5.1, 6, 7.1 and 7.4).
5
+ *
6
+ * @module @tradik/xslt3/serialize/markup/prolog
7
+ */
8
+
9
+ /**
10
+ * @param {import("../params/settings.js").Settings} settings
11
+ * @returns {string} the XML declaration, "" when omitted or not XML
12
+ */
13
+ export function xmlDeclaration(settings) {
14
+ const { method, omitXmlDeclaration, version, encoding, standalone } =
15
+ settings;
16
+ if (omitXmlDeclaration || (method !== "xml" && method !== "xhtml")) {
17
+ return "";
18
+ }
19
+ const standalonePart =
20
+ standalone === "omit" ? "" : ` standalone="${standalone}"`;
21
+ return `<?xml version="${version}" encoding="${encoding.name}"${standalonePart}?>`;
22
+ }
23
+
24
+ /**
25
+ * A system literal: in double quotes, or single quotes when it holds one.
26
+ * @param {string} value
27
+ * @returns {string}
28
+ */
29
+ const quoted = (value) => (value.includes('"') ? `'${value}'` : `"${value}"`);
30
+
31
+ /**
32
+ * The document type declaration written before the first element.
33
+ * @param {import("../params/settings.js").Settings} settings
34
+ * @param {string} name - Output name of the first element
35
+ * @returns {string} the declaration, "" for none
36
+ */
37
+ export function doctype(settings, name) {
38
+ const { method, htmlVersion } = settings;
39
+ // a zero-length doctype-system or doctype-public is absent (erratum E31)
40
+ const doctypePublic = settings.doctypePublic || undefined;
41
+ const doctypeSystem =
42
+ settings.doctypeSystem === undefined || settings.doctypeSystem === ""
43
+ ? undefined
44
+ : quoted(settings.doctypeSystem);
45
+ const html5Default =
46
+ htmlVersion === 5 && name.replace(/^.*:/, "").toLowerCase() === "html";
47
+ if (method === "html") {
48
+ if (doctypePublic !== undefined) {
49
+ const system = doctypeSystem === undefined ? "" : ` ${doctypeSystem}`;
50
+ return `<!DOCTYPE html PUBLIC "${doctypePublic}"${system}>`;
51
+ }
52
+ if (doctypeSystem !== undefined) {
53
+ return `<!DOCTYPE html SYSTEM ${doctypeSystem}>`;
54
+ }
55
+ return html5Default ? "<!DOCTYPE html>" : "";
56
+ }
57
+ if (doctypeSystem !== undefined) {
58
+ const external =
59
+ doctypePublic === undefined
60
+ ? `SYSTEM ${doctypeSystem}`
61
+ : `PUBLIC "${doctypePublic}" ${doctypeSystem}`;
62
+ return `<!DOCTYPE ${name} ${external}>`;
63
+ }
64
+ // xhtml with HTML5: the element name as written
65
+ return method === "xhtml" && html5Default
66
+ ? `<!DOCTYPE ${name.replace(/^.*:/, "")}>`
67
+ : "";
68
+ }
69
+
70
+ /**
71
+ * @param {import("../params/settings.js").Settings} settings
72
+ * @returns {string} the meta element declaring the content type
73
+ */
74
+ export function contentTypeMeta(settings) {
75
+ const end = settings.method === "html" ? ">" : " />";
76
+ const content = `${settings.mediaType}; charset=${settings.encoding.name}`;
77
+ return `<meta http-equiv="Content-Type" content="${content}"${end}`;
78
+ }
79
+
80
+ /**
81
+ * Whether a node is a meta element declaring the content type (replaced
82
+ * by the serializer's own with include-content-type).
83
+ * @param {Node} node
84
+ * @returns {boolean}
85
+ */
86
+ export function isContentTypeMeta(node) {
87
+ if (node.nodeType !== 1) return false;
88
+ if ((node.localName ?? node.nodeName).toLowerCase() !== "meta") return false;
89
+ const value = node.getAttribute("http-equiv");
90
+ return typeof value === "string" && value.toLowerCase() === "content-type";
91
+ }