xslt-processor 4.2.1 → 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) {
@@ -2177,6 +2214,157 @@ function xmlGetAttribute(node, name) {
2177
2214
  }
2178
2215
  return value;
2179
2216
  }
2217
+ function nodeToJsonObject(node) {
2218
+ if (!node) {
2219
+ return null;
2220
+ }
2221
+ const nodeType = node.nodeType;
2222
+ if (nodeType === DOM_TEXT_NODE || nodeType === DOM_CDATA_SECTION_NODE) {
2223
+ const text = node.nodeValue ? node.nodeValue.trim() : "";
2224
+ return text.length > 0 ? text : null;
2225
+ }
2226
+ if (nodeType === DOM_COMMENT_NODE) {
2227
+ return null;
2228
+ }
2229
+ if (nodeType === DOM_DOCUMENT_NODE || nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
2230
+ const children = node.childNodes || [];
2231
+ const childObjects = [];
2232
+ for (let i = 0; i < children.length; i++) {
2233
+ const child = children[i];
2234
+ const childObj = nodeToJsonObject(child);
2235
+ if (childObj !== null) {
2236
+ childObjects.push(childObj);
2237
+ }
2238
+ }
2239
+ if (childObjects.length === 0) {
2240
+ return null;
2241
+ } else if (childObjects.length === 1) {
2242
+ return childObjects[0];
2243
+ } else {
2244
+ return childObjects;
2245
+ }
2246
+ }
2247
+ if (nodeType === DOM_ELEMENT_NODE) {
2248
+ const obj = {};
2249
+ const element = node;
2250
+ const hasAttributes = element.attributes && element.attributes.length > 0;
2251
+ if (hasAttributes) {
2252
+ for (let i = 0; i < element.attributes.length; i++) {
2253
+ const attr = element.attributes[i];
2254
+ obj["@" + attr.nodeName] = attr.nodeValue;
2255
+ }
2256
+ }
2257
+ const children = element.childNodes || [];
2258
+ let textContent = "";
2259
+ let hasElementChildren = false;
2260
+ const childElements = {};
2261
+ for (let i = 0; i < children.length; i++) {
2262
+ const child = children[i];
2263
+ const childType = child.nodeType;
2264
+ if (childType === DOM_TEXT_NODE || childType === DOM_CDATA_SECTION_NODE) {
2265
+ const text = child.nodeValue ? child.nodeValue.trim() : "";
2266
+ if (text.length > 0) {
2267
+ textContent += text;
2268
+ }
2269
+ } else if (childType === DOM_ELEMENT_NODE) {
2270
+ hasElementChildren = true;
2271
+ const childElement = child;
2272
+ const childName = childElement.localName || childElement.nodeName;
2273
+ const childObj = nodeToJsonObject(child);
2274
+ if (childObj !== null) {
2275
+ if (childElements[childName]) {
2276
+ if (!Array.isArray(childElements[childName])) {
2277
+ childElements[childName] = [childElements[childName]];
2278
+ }
2279
+ childElements[childName].push(childObj);
2280
+ } else {
2281
+ childElements[childName] = childObj;
2282
+ }
2283
+ }
2284
+ }
2285
+ }
2286
+ Object.assign(obj, childElements);
2287
+ if (!hasElementChildren && textContent.length > 0) {
2288
+ if (!hasAttributes && Object.keys(childElements).length === 0) {
2289
+ return textContent;
2290
+ } else {
2291
+ obj["#text"] = textContent;
2292
+ }
2293
+ }
2294
+ if (Object.keys(obj).length === 0) {
2295
+ return null;
2296
+ }
2297
+ return obj;
2298
+ }
2299
+ return null;
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
+ }
2336
+ function xmlToJson(node) {
2337
+ if (!node) {
2338
+ return "{}";
2339
+ }
2340
+ let rootElement = node;
2341
+ if (node.nodeType === DOM_DOCUMENT_NODE || node.nodeType === DOM_DOCUMENT_FRAGMENT_NODE) {
2342
+ const children = node.childNodes || [];
2343
+ for (let i = 0; i < children.length; i++) {
2344
+ if (children[i].nodeType === DOM_ELEMENT_NODE) {
2345
+ rootElement = children[i];
2346
+ break;
2347
+ }
2348
+ }
2349
+ }
2350
+ const element = rootElement;
2351
+ const rootName = element.localName || element.nodeName;
2352
+ const jsonObj = {};
2353
+ const elementContent = nodeToJsonObject(rootElement);
2354
+ if (elementContent === null) {
2355
+ jsonObj[rootName] = {};
2356
+ } else if (typeof elementContent === "object" && !Array.isArray(elementContent)) {
2357
+ jsonObj[rootName] = elementContent;
2358
+ } else {
2359
+ jsonObj[rootName] = elementContent;
2360
+ }
2361
+ try {
2362
+ const cleaned = JSON.parse(JSON.stringify(jsonObj));
2363
+ return JSON.stringify(cleaned);
2364
+ } catch (error) {
2365
+ return JSON.stringify(jsonObj);
2366
+ }
2367
+ }
2180
2368
 
2181
2369
  // src/dom/xml-parser.ts
2182
2370
  import he2 from "he";
@@ -2421,7 +2609,7 @@ var XmlParser = class {
2421
2609
  } else if (!tag && char === "<") {
2422
2610
  let text = xml.slice(start, i);
2423
2611
  if (text && parent !== root) {
2424
- domAppendChild(parent, domCreateTextNode(xmlDocument, text));
2612
+ domAppendChild(parent, domCreateTextNode(xmlDocument, he2.decode(text)));
2425
2613
  }
2426
2614
  if (xml.slice(i + 1, i + 4) === "!--") {
2427
2615
  let endTagIndex = xml.slice(i + 4).indexOf("-->");
@@ -2565,15 +2753,6 @@ var NodeConverter = class {
2565
2753
  adaptXNode(node) {
2566
2754
  if (!node) return null;
2567
2755
  const adapted = node;
2568
- if (!("namespaceURI" in adapted)) {
2569
- Object.defineProperty(adapted, "namespaceURI", {
2570
- get() {
2571
- return this.namespaceUri;
2572
- },
2573
- enumerable: true,
2574
- configurable: true
2575
- });
2576
- }
2577
2756
  if (!("textContent" in adapted)) {
2578
2757
  Object.defineProperty(adapted, "textContent", {
2579
2758
  get() {
@@ -3515,9 +3694,10 @@ var Xslt = class {
3515
3694
  cData: options.cData === true,
3516
3695
  escape: options.escape === true,
3517
3696
  selfClosingTags: options.selfClosingTags === true,
3697
+ outputMethod: options.outputMethod,
3518
3698
  parameters: options.parameters || []
3519
3699
  };
3520
- this.outputMethod = "xml";
3700
+ this.outputMethod = options.outputMethod || "xml";
3521
3701
  this.outputOmitXmlDeclaration = "no";
3522
3702
  this.stripSpacePatterns = [];
3523
3703
  this.preserveSpacePatterns = [];
@@ -3540,7 +3720,7 @@ var Xslt = class {
3540
3720
  * The exported entry point of the XSL-T processor.
3541
3721
  * @param xmlDoc The input document root, as DOM node.
3542
3722
  * @param stylesheet The stylesheet document root, as DOM node.
3543
- * @returns the processed document, as XML text in a string.
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).
3544
3724
  */
3545
3725
  xsltProcess(xmlDoc, stylesheet) {
3546
3726
  return __async(this, null, function* () {
@@ -3553,11 +3733,18 @@ var Xslt = class {
3553
3733
  }
3554
3734
  }
3555
3735
  yield this.xsltProcessContext(expressionContext, stylesheet, this.outputDocument);
3736
+ if (this.outputMethod === "json") {
3737
+ return xmlToJson(outputDocument);
3738
+ }
3739
+ let outputMethod = this.outputMethod;
3740
+ if (this.outputMethod === "adaptive") {
3741
+ outputMethod = detectAdaptiveOutputFormat(outputDocument);
3742
+ }
3556
3743
  const transformedOutputXml = xmlTransformedText(outputDocument, {
3557
3744
  cData: this.options.cData,
3558
3745
  escape: this.options.escape,
3559
3746
  selfClosingTags: this.options.selfClosingTags,
3560
- outputMethod: this.outputMethod
3747
+ outputMethod
3561
3748
  });
3562
3749
  return transformedOutputXml;
3563
3750
  });
@@ -3718,6 +3905,9 @@ var Xslt = class {
3718
3905
  for (let j = 0; j < modifiedContext.contextSize(); ++j) {
3719
3906
  const currentNode = modifiedContext.nodeList[j];
3720
3907
  if (currentNode.nodeType === DOM_TEXT_NODE) {
3908
+ if (!this.xsltPassText(currentNode)) {
3909
+ continue;
3910
+ }
3721
3911
  const textNodeContext = context.clone(
3722
3912
  [currentNode],
3723
3913
  0
@@ -4486,6 +4676,7 @@ var Xslt = class {
4486
4676
  xsltText(context, template, output) {
4487
4677
  const text = xmlValue(template);
4488
4678
  const node = domCreateTextNode(this.outputDocument, text);
4679
+ node.fromXslText = true;
4489
4680
  const disableOutputEscaping = template.childNodes.filter(
4490
4681
  (a) => a.nodeType === DOM_ATTRIBUTE_NODE && a.nodeName === "disable-output-escaping"
4491
4682
  );
@@ -4493,8 +4684,80 @@ var Xslt = class {
4493
4684
  node.escape = false;
4494
4685
  }
4495
4686
  const destinationTextNode = output || this.outputDocument;
4687
+ node.siblingPosition = destinationTextNode.childNodes.length;
4496
4688
  destinationTextNode.appendChild(node);
4497
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
+ }
4498
4761
  /**
4499
4762
  * Implements `<xsl:stylesheet>` and `<xsl:transform>`, and its corresponding
4500
4763
  * validations.
@@ -4505,24 +4768,7 @@ var Xslt = class {
4505
4768
  */
4506
4769
  xsltTransformOrStylesheet(context, template, output) {
4507
4770
  return __async(this, null, function* () {
4508
- for (let stylesheetAttribute of template.childNodes.filter((n) => n.nodeType === DOM_ATTRIBUTE_NODE)) {
4509
- switch (stylesheetAttribute.nodeName) {
4510
- case "version":
4511
- this.version = stylesheetAttribute.nodeValue;
4512
- if (!["1.0", "2.0", "3.0"].includes(this.version)) {
4513
- throw new Error(
4514
- `XSLT version not defined or invalid. Actual resolved version: ${this.version || "(none)"}.`
4515
- );
4516
- }
4517
- context.xsltVersion = this.version;
4518
- break;
4519
- default:
4520
- if (stylesheetAttribute.prefix === "xmlns") {
4521
- context.knownNamespaces[stylesheetAttribute.localName] = stylesheetAttribute.nodeValue;
4522
- }
4523
- break;
4524
- }
4525
- }
4771
+ this.validateStylesheetAttributes(template, context);
4526
4772
  let importsDone = false;
4527
4773
  for (const child of template.childNodes) {
4528
4774
  if (child.nodeType === DOM_ELEMENT_NODE) {
@@ -4562,16 +4808,22 @@ var Xslt = class {
4562
4808
  }
4563
4809
  }
4564
4810
  if (matchCandidates.length > 0) {
4565
- matchCandidates.sort((a, b) => {
4566
- if (a.priority.importPrecedence !== b.priority.importPrecedence) {
4567
- return b.priority.importPrecedence - a.priority.importPrecedence;
4568
- }
4569
- if (a.priority.effectivePriority !== b.priority.effectivePriority) {
4570
- return b.priority.effectivePriority - a.priority.effectivePriority;
4571
- }
4572
- return b.priority.documentOrder - a.priority.documentOrder;
4573
- });
4574
- 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
+ }
4575
4827
  const conflicts = matchCandidates.filter(
4576
4828
  (t) => t.priority.importPrecedence === winner.priority.importPrecedence && t.priority.effectivePriority === winner.priority.effectivePriority
4577
4829
  );
@@ -4709,7 +4961,11 @@ var Xslt = class {
4709
4961
  return __async(this, null, function* () {
4710
4962
  const contextClone = context.clone();
4711
4963
  for (let i = 0; i < template.childNodes.length; ++i) {
4712
- 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);
4713
4969
  }
4714
4970
  });
4715
4971
  }