xslt-processor 4.3.0 → 4.3.1

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/index.mjs CHANGED
@@ -706,12 +706,12 @@ var XPathStep = class extends XPathExpression {
706
706
  if (!nsUri) {
707
707
  return false;
708
708
  }
709
- const nodeLocalName = node.localName || node.nodeName;
709
+ const nodeLocalName2 = node.localName || node.nodeName && this.extractLocalName(node.nodeName);
710
710
  const nodeNsUri = node.namespaceURI || node.namespaceUri || "";
711
- return nodeLocalName === localName && nodeNsUri === nsUri;
711
+ return nodeLocalName2 === localName && nodeNsUri === nsUri;
712
712
  }
713
- const nodeName = node.localName || node.nodeName;
714
- return nodeName === testName;
713
+ const nodeLocalName = node.localName || this.extractLocalName(node.nodeName);
714
+ return nodeLocalName === testName;
715
715
  case "node-type":
716
716
  switch (this.nodeTest.nodeType) {
717
717
  case "node":
@@ -770,6 +770,17 @@ var XPathStep = class extends XPathExpression {
770
770
  if (Array.isArray(value)) return value.length > 0;
771
771
  return !!value;
772
772
  }
773
+ /**
774
+ * Extract the local name from a qualified name (e.g., "ns:name" -> "name", "name" -> "name")
775
+ */
776
+ extractLocalName(qname) {
777
+ if (!qname) return "";
778
+ const colonIndex = qname.indexOf(":");
779
+ if (colonIndex > 0) {
780
+ return qname.substring(colonIndex + 1);
781
+ }
782
+ return qname;
783
+ }
773
784
  };
774
785
 
775
786
  // src/xpath/lib/src/expressions/predicate-expression.ts
@@ -1054,7 +1065,7 @@ var XPathFunctionCall = class extends XPathExpression {
1054
1065
  namespaceUri(args, context) {
1055
1066
  var _a;
1056
1067
  const node = this.getNodeArg(args, context);
1057
- return (_a = node == null ? void 0 : node.namespaceURI) != null ? _a : "";
1068
+ return (_a = node == null ? void 0 : node.namespaceUri) != null ? _a : "";
1058
1069
  }
1059
1070
  nodeName(args, context) {
1060
1071
  var _a;
@@ -1524,6 +1535,7 @@ var _XNode = class _XNode {
1524
1535
  this.childNodes = [];
1525
1536
  this.visited = false;
1526
1537
  this.escape = true;
1538
+ this.fromXslText = false;
1527
1539
  this.siblingPosition = -1;
1528
1540
  this.init(type, name, opt_value, opt_owner, opt_namespace);
1529
1541
  }
@@ -2050,23 +2062,32 @@ function xmlTransformedTextRecursive(node, buffer, options) {
2050
2062
  const nodeType = node.nodeType;
2051
2063
  const nodeValue = node.nodeValue;
2052
2064
  if (nodeType === DOM_TEXT_NODE) {
2053
- if (node.nodeValue && node.nodeValue.trim() !== "") {
2065
+ const isFromXslText = node.fromXslText === true;
2066
+ if (node.nodeValue && (isFromXslText || node.nodeValue.trim() !== "")) {
2054
2067
  const finalText = node.escape && options.escape ? xmlEscapeText(node.nodeValue) : xmlUnescapeText(node.nodeValue);
2055
2068
  buffer.push(finalText);
2056
2069
  }
2057
2070
  } else if (nodeType === DOM_CDATA_SECTION_NODE) {
2058
- if (options.cData) {
2071
+ if (options.outputMethod === "text") {
2072
+ buffer.push(nodeValue);
2073
+ } else if (options.cData) {
2059
2074
  buffer.push(xmlEscapeText(nodeValue));
2060
2075
  } else {
2061
2076
  buffer.push(`<![CDATA[${nodeValue}]]>`);
2062
2077
  }
2063
2078
  } else if (nodeType == DOM_COMMENT_NODE) {
2064
- buffer.push(`<!-- ${nodeValue} -->`);
2079
+ if (options.outputMethod !== "text") {
2080
+ buffer.push(`<!-- ${nodeValue} -->`);
2081
+ }
2065
2082
  } else if (nodeType == DOM_ELEMENT_NODE) {
2066
- if (node.nodeName !== null && node.nodeName !== void 0) {
2067
- xmlElementLogicTrivial(node, buffer, options);
2083
+ if (options.outputMethod === "text") {
2084
+ xmlElementLogicTextOnly(node, buffer, options);
2068
2085
  } else {
2069
- xmlElementLogicMuted(node, buffer, options);
2086
+ if (node.nodeName !== null && node.nodeName !== void 0) {
2087
+ xmlElementLogicTrivial(node, buffer, options);
2088
+ } else {
2089
+ xmlElementLogicMuted(node, buffer, options);
2090
+ }
2070
2091
  }
2071
2092
  } else if (nodeType === DOM_DOCUMENT_NODE || nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
2072
2093
  let childNodes = node.firstChild ? [] : node.childNodes;
@@ -2154,6 +2175,22 @@ function xmlElementLogicMuted(node, buffer, options) {
2154
2175
  xmlTransformedTextRecursive(childNodes[i], buffer, options);
2155
2176
  }
2156
2177
  }
2178
+ function xmlElementLogicTextOnly(node, buffer, options) {
2179
+ let childNodes = [];
2180
+ if (node.firstChild) {
2181
+ let child = node.firstChild;
2182
+ while (child) {
2183
+ childNodes.push(child);
2184
+ child = child.nextSibling;
2185
+ }
2186
+ } else {
2187
+ childNodes = node.childNodes;
2188
+ }
2189
+ childNodes = childNodes.sort((a, b) => a.siblingPosition - b.siblingPosition);
2190
+ for (let i = 0; i < childNodes.length; ++i) {
2191
+ xmlTransformedTextRecursive(childNodes[i], buffer, options);
2192
+ }
2193
+ }
2157
2194
  function xmlFullNodeName(node) {
2158
2195
  const nodeName = node.nodeName;
2159
2196
  if (node.prefix && nodeName.indexOf(`${node.prefix}:`) != 0) {
@@ -2261,6 +2298,41 @@ function nodeToJsonObject(node) {
2261
2298
  }
2262
2299
  return null;
2263
2300
  }
2301
+ function detectAdaptiveOutputFormat(node) {
2302
+ if (!node) {
2303
+ return "xml";
2304
+ }
2305
+ const nodeType = node.nodeType;
2306
+ if (nodeType === DOM_DOCUMENT_NODE || nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
2307
+ const children = node.childNodes || [];
2308
+ let elementCount = 0;
2309
+ let textCount = 0;
2310
+ let hasSignificantText = false;
2311
+ for (let i = 0; i < children.length; i++) {
2312
+ const child = children[i];
2313
+ if (child.nodeType === DOM_ELEMENT_NODE) {
2314
+ elementCount++;
2315
+ } else if (child.nodeType === DOM_TEXT_NODE) {
2316
+ const text = child.nodeValue ? child.nodeValue.trim() : "";
2317
+ if (text.length > 0) {
2318
+ textCount++;
2319
+ hasSignificantText = true;
2320
+ }
2321
+ }
2322
+ }
2323
+ if (elementCount === 0 && hasSignificantText) {
2324
+ return "text";
2325
+ }
2326
+ return "xml";
2327
+ }
2328
+ if (nodeType === DOM_TEXT_NODE || nodeType === DOM_CDATA_SECTION_NODE) {
2329
+ const text = node.nodeValue ? node.nodeValue.trim() : "";
2330
+ if (text.length > 0) {
2331
+ return "text";
2332
+ }
2333
+ }
2334
+ return "xml";
2335
+ }
2264
2336
  function xmlToJson(node) {
2265
2337
  if (!node) {
2266
2338
  return "{}";
@@ -2537,7 +2609,7 @@ var XmlParser = class {
2537
2609
  } else if (!tag && char === "<") {
2538
2610
  let text = xml.slice(start, i);
2539
2611
  if (text && parent !== root) {
2540
- domAppendChild(parent, domCreateTextNode(xmlDocument, text));
2612
+ domAppendChild(parent, domCreateTextNode(xmlDocument, he2.decode(text)));
2541
2613
  }
2542
2614
  if (xml.slice(i + 1, i + 4) === "!--") {
2543
2615
  let endTagIndex = xml.slice(i + 4).indexOf("-->");
@@ -2681,15 +2753,6 @@ var NodeConverter = class {
2681
2753
  adaptXNode(node) {
2682
2754
  if (!node) return null;
2683
2755
  const adapted = node;
2684
- if (!("namespaceURI" in adapted)) {
2685
- Object.defineProperty(adapted, "namespaceURI", {
2686
- get() {
2687
- return this.namespaceUri;
2688
- },
2689
- enumerable: true,
2690
- configurable: true
2691
- });
2692
- }
2693
2756
  if (!("textContent" in adapted)) {
2694
2757
  Object.defineProperty(adapted, "textContent", {
2695
2758
  get() {
@@ -3657,7 +3720,7 @@ var Xslt = class {
3657
3720
  * The exported entry point of the XSL-T processor.
3658
3721
  * @param xmlDoc The input document root, as DOM node.
3659
3722
  * @param stylesheet The stylesheet document root, as DOM node.
3660
- * @returns the processed document, as XML text in a string, or JSON string if outputMethod is 'json'.
3723
+ * @returns the processed document, as XML text in a string, JSON string if outputMethod is 'json', or text if outputMethod is 'text' or 'adaptive' (with text content).
3661
3724
  */
3662
3725
  xsltProcess(xmlDoc, stylesheet) {
3663
3726
  return __async(this, null, function* () {
@@ -3673,11 +3736,15 @@ var Xslt = class {
3673
3736
  if (this.outputMethod === "json") {
3674
3737
  return xmlToJson(outputDocument);
3675
3738
  }
3739
+ let outputMethod = this.outputMethod;
3740
+ if (this.outputMethod === "adaptive") {
3741
+ outputMethod = detectAdaptiveOutputFormat(outputDocument);
3742
+ }
3676
3743
  const transformedOutputXml = xmlTransformedText(outputDocument, {
3677
3744
  cData: this.options.cData,
3678
3745
  escape: this.options.escape,
3679
3746
  selfClosingTags: this.options.selfClosingTags,
3680
- outputMethod: this.outputMethod
3747
+ outputMethod
3681
3748
  });
3682
3749
  return transformedOutputXml;
3683
3750
  });
@@ -3838,6 +3905,9 @@ var Xslt = class {
3838
3905
  for (let j = 0; j < modifiedContext.contextSize(); ++j) {
3839
3906
  const currentNode = modifiedContext.nodeList[j];
3840
3907
  if (currentNode.nodeType === DOM_TEXT_NODE) {
3908
+ if (!this.xsltPassText(currentNode)) {
3909
+ continue;
3910
+ }
3841
3911
  const textNodeContext = context.clone(
3842
3912
  [currentNode],
3843
3913
  0
@@ -4606,6 +4676,7 @@ var Xslt = class {
4606
4676
  xsltText(context, template, output) {
4607
4677
  const text = xmlValue(template);
4608
4678
  const node = domCreateTextNode(this.outputDocument, text);
4679
+ node.fromXslText = true;
4609
4680
  const disableOutputEscaping = template.childNodes.filter(
4610
4681
  (a) => a.nodeType === DOM_ATTRIBUTE_NODE && a.nodeName === "disable-output-escaping"
4611
4682
  );
@@ -4613,8 +4684,80 @@ var Xslt = class {
4613
4684
  node.escape = false;
4614
4685
  }
4615
4686
  const destinationTextNode = output || this.outputDocument;
4687
+ node.siblingPosition = destinationTextNode.childNodes.length;
4616
4688
  destinationTextNode.appendChild(node);
4617
4689
  }
4690
+ /**
4691
+ * Validates XSLT stylesheet/transform attributes.
4692
+ * According to XSLT specification, validates:
4693
+ * - Required version attribute
4694
+ * - Valid version values (1.0, 2.0, 3.0)
4695
+ * - Valid namespace declarations
4696
+ * - Valid values for optional attributes (extension-element-prefixes, exclude-result-prefixes)
4697
+ * @param stylesheetElement The `<xsl:stylesheet>` or `<xsl:transform>` element to validate.
4698
+ * @param context The Expression Context for namespace access.
4699
+ */
4700
+ validateStylesheetAttributes(stylesheetElement, context) {
4701
+ const attributes = stylesheetElement.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE);
4702
+ const validAttributes = ["version", "id", "extension-element-prefixes", "exclude-result-prefixes", "default-collation"];
4703
+ const validNamespaceAttributes = ["xmlns"];
4704
+ let versionFound = false;
4705
+ for (let attribute of attributes) {
4706
+ const nodeName = attribute.nodeName;
4707
+ const nodeValue = attribute.nodeValue;
4708
+ if (attribute.prefix === "xmlns") {
4709
+ context.knownNamespaces[attribute.localName] = nodeValue;
4710
+ continue;
4711
+ }
4712
+ if (nodeName === "xmlns") {
4713
+ context.knownNamespaces[""] = nodeValue;
4714
+ continue;
4715
+ }
4716
+ if (nodeName === "version") {
4717
+ versionFound = true;
4718
+ if (!["1.0", "2.0", "3.0"].includes(nodeValue)) {
4719
+ throw new Error(
4720
+ `XSLT version not defined or invalid. Actual resolved version: ${nodeValue || "(none)"}.`
4721
+ );
4722
+ }
4723
+ this.version = nodeValue;
4724
+ context.xsltVersion = nodeValue;
4725
+ continue;
4726
+ }
4727
+ if (nodeName === "extension-element-prefixes") {
4728
+ const prefixes = nodeValue.split(/\s+/);
4729
+ for (const prefix of prefixes) {
4730
+ if (prefix && !/^[a-zA-Z_:][\w:.-]*$/.test(prefix)) {
4731
+ throw new Error(`Invalid prefix in extension-element-prefixes: "${prefix}". Prefixes must be valid QNames.`);
4732
+ }
4733
+ }
4734
+ continue;
4735
+ }
4736
+ if (nodeName === "exclude-result-prefixes") {
4737
+ if (nodeValue !== "#all") {
4738
+ const prefixes = nodeValue.split(/\s+/);
4739
+ for (const prefix of prefixes) {
4740
+ if (prefix && !/^[a-zA-Z_:][\w:.-]*$/.test(prefix)) {
4741
+ throw new Error(`Invalid prefix in exclude-result-prefixes: "${prefix}". Prefixes must be valid QNames or "#all".`);
4742
+ }
4743
+ }
4744
+ }
4745
+ continue;
4746
+ }
4747
+ if (nodeName === "default-collation") {
4748
+ if (!nodeValue || nodeValue.trim().length === 0) {
4749
+ throw new Error("The default-collation attribute must contain a URI.");
4750
+ }
4751
+ continue;
4752
+ }
4753
+ if (nodeName === "id") {
4754
+ if (!/^[a-zA-Z_:][\w:.-]*$/.test(nodeValue)) {
4755
+ throw new Error(`Invalid id attribute value: "${nodeValue}". IDs must be valid NCNames.`);
4756
+ }
4757
+ continue;
4758
+ }
4759
+ }
4760
+ }
4618
4761
  /**
4619
4762
  * Implements `<xsl:stylesheet>` and `<xsl:transform>`, and its corresponding
4620
4763
  * validations.
@@ -4625,24 +4768,7 @@ var Xslt = class {
4625
4768
  */
4626
4769
  xsltTransformOrStylesheet(context, template, output) {
4627
4770
  return __async(this, null, function* () {
4628
- for (let stylesheetAttribute of template.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE)) {
4629
- switch (stylesheetAttribute.nodeName) {
4630
- case "version":
4631
- this.version = stylesheetAttribute.nodeValue;
4632
- if (!["1.0", "2.0", "3.0"].includes(this.version)) {
4633
- throw new Error(
4634
- `XSLT version not defined or invalid. Actual resolved version: ${this.version || "(none)"}.`
4635
- );
4636
- }
4637
- context.xsltVersion = this.version;
4638
- break;
4639
- default:
4640
- if (stylesheetAttribute.prefix === "xmlns") {
4641
- context.knownNamespaces[stylesheetAttribute.localName] = stylesheetAttribute.nodeValue;
4642
- }
4643
- break;
4644
- }
4645
- }
4771
+ this.validateStylesheetAttributes(template, context);
4646
4772
  let importsDone = false;
4647
4773
  for (const child of template.childNodes) {
4648
4774
  if (child.nodeType === DOM_ELEMENT_NODE) {
@@ -4682,16 +4808,22 @@ var Xslt = class {
4682
4808
  }
4683
4809
  }
4684
4810
  if (matchCandidates.length > 0) {
4685
- matchCandidates.sort((a, b) => {
4686
- if (a.priority.importPrecedence !== b.priority.importPrecedence) {
4687
- return b.priority.importPrecedence - a.priority.importPrecedence;
4688
- }
4689
- if (a.priority.effectivePriority !== b.priority.effectivePriority) {
4690
- return b.priority.effectivePriority - a.priority.effectivePriority;
4691
- }
4692
- return b.priority.documentOrder - a.priority.documentOrder;
4693
- });
4694
- const winner = matchCandidates[0];
4811
+ const rootPatternMatch = matchCandidates.find((c) => c.priority.matchPattern === "/");
4812
+ let winner;
4813
+ if (rootPatternMatch) {
4814
+ winner = rootPatternMatch;
4815
+ } else {
4816
+ matchCandidates.sort((a, b) => {
4817
+ if (a.priority.importPrecedence !== b.priority.importPrecedence) {
4818
+ return b.priority.importPrecedence - a.priority.importPrecedence;
4819
+ }
4820
+ if (a.priority.effectivePriority !== b.priority.effectivePriority) {
4821
+ return b.priority.effectivePriority - a.priority.effectivePriority;
4822
+ }
4823
+ return b.priority.documentOrder - a.priority.documentOrder;
4824
+ });
4825
+ winner = matchCandidates[0];
4826
+ }
4695
4827
  const conflicts = matchCandidates.filter(
4696
4828
  (t) => t.priority.importPrecedence === winner.priority.importPrecedence && t.priority.effectivePriority === winner.priority.effectivePriority
4697
4829
  );
@@ -4829,7 +4961,11 @@ var Xslt = class {
4829
4961
  return __async(this, null, function* () {
4830
4962
  const contextClone = context.clone();
4831
4963
  for (let i = 0; i < template.childNodes.length; ++i) {
4832
- yield this.xsltProcessContext(contextClone, template.childNodes[i], output);
4964
+ const child = template.childNodes[i];
4965
+ if (child.nodeType === DOM_ATTRIBUTE_NODE) {
4966
+ continue;
4967
+ }
4968
+ yield this.xsltProcessContext(contextClone, child, output);
4833
4969
  }
4834
4970
  });
4835
4971
  }