xslt-processor 4.8.4 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ _A JavaScript XSLT processor without native library dependencies._
4
4
 
5
5
  <p align="center">
6
6
  <a href="https://github.com/DesignLiquido/xslt-processor/issues" target="_blank">
7
- <img src="https://img.shields.io/github/issues/Designliquido/xslt-processor" />
7
+ <img src="https://img.shields.io/github/issues/DesignLiquido/xslt-processor" />
8
8
  </a>
9
9
  <img src="https://img.shields.io/github/stars/Designliquido/xslt-processor" />
10
10
  <img src="https://img.shields.io/github/forks/Designliquido/xslt-processor" />
@@ -37,7 +37,7 @@ ohpm install xslt-processor
37
37
  yarn add xslt-processor
38
38
  ```
39
39
 
40
- Within your ES2015+ code, import the `Xslt` class, the `XmlParser` class and use this way:
40
+ Within your ES2015+ code, import the `Xslt` class, the `XmlParser` class and use it this way:
41
41
 
42
42
  ```js
43
43
  import { Xslt, XmlParser } from 'xslt-processor'
@@ -80,11 +80,44 @@ const xslt = new Xslt(options);
80
80
  - `cData` (`boolean`, default `true`): resolves CDATA elements in the output. Content under CDATA is resolved as text. This overrides `escape` for CDATA content.
81
81
  - `escape` (`boolean`, default `true`): replaces symbols like `<`, `>`, `&` and `"` by the corresponding [HTML/XML entities](https://www.tutorialspoint.com/xml/xml_character_entities.htm). Can be overridden by `disable-output-escaping`, that also does the opposite, unescaping `&gt;` and `&lt;` by `<` and `>`, respectively.
82
82
  - `selfClosingTags` (`boolean`, default `true`): Self-closes tags that don't have inner elements, if `true`. For instance, `<test></test>` becomes `<test />`.
83
- - `outputMethod` (`string`, default `xml`): Specifies the default output method. if `<xsl:output>` is declared in your XSLT file, this will be overridden. Valid values: `xml`, `html`, `text`, `name`, `xhtml`, `json`, `adaptive`.
83
+ - `outputMethod` (`string`, default `xml`): Specifies the default output method. If `<xsl:output>` is declared in your XSLT file, this will be overridden. Valid values: `xml`, `html`, `text`, `xhtml`, `json`, `adaptive`.
84
84
  - `parameters` (`array`, default `[]`): external parameters that you want to use.
85
85
  - `name`: the parameter name;
86
86
  - `namespaceUri` (optional): the namespace;
87
87
  - `value`: the value.
88
+ - `fetchFunction` (`(uri: string) => Promise<string>`, optional): a custom function for loading external resources referenced by `<xsl:import>` and `<xsl:include>`. Receives the URI and must return the fetched content as a string. Defaults to the global `fetch` API. This is useful for:
89
+ - Denying external loading entirely;
90
+ - Loading from the local filesystem or other non-HTTP sources;
91
+ - Transforming or remapping URIs before fetching.
92
+
93
+ **`fetchFunction` examples:**
94
+
95
+ ```js
96
+ import { readFileSync } from 'fs';
97
+
98
+ // Deny all external loading
99
+ const xslt = new Xslt({
100
+ fetchFunction: async (uri) => {
101
+ throw new Error(`External loading is not allowed: ${uri}`);
102
+ }
103
+ });
104
+
105
+ // Load from local filesystem
106
+ const xslt = new Xslt({
107
+ fetchFunction: async (uri) => {
108
+ return readFileSync(uri, 'utf-8');
109
+ }
110
+ });
111
+
112
+ // Remap URIs before fetching
113
+ const xslt = new Xslt({
114
+ fetchFunction: async (uri) => {
115
+ const remapped = uri.replace('https://example.com/', '/local/stylesheets/');
116
+ const response = await fetch(remapped);
117
+ return response.text();
118
+ }
119
+ });
120
+ ```
88
121
 
89
122
  #### JSON Output Format
90
123
 
@@ -182,10 +215,10 @@ console.log(result2); // "<users><user>John</user></users>" (XML)
182
215
  You can simply add a tag like this:
183
216
 
184
217
  ```html
185
- <script type="application/javascript" src="https://www.unpkg.com/xslt-processor@latest/umd/xslt-processor.global.js"></script>
218
+ <script type="application/javascript" src="https://unpkg.com/xslt-processor@latest/umd/xslt-processor.global.js"></script>
186
219
  ```
187
220
 
188
- All the exports will live under `globalThis.XsltProcessor` and `window.XsltProcessor`. [See a usage example here](https://github.com/DesignLiquido/xslt-processor/blob/main/interactive-tests/xslt.html).
221
+ All the exports will live under `globalThis.XsltProcessor` and `window.XsltProcessor`. [See a usage example here](https://github.com/DesignLiquido/xslt-processor/blob/main/interactive-tests/xslt.html).
189
222
 
190
223
  ## XPath Parser
191
224
 
@@ -204,7 +237,7 @@ import { XPath } from 'xslt-processor'
204
237
  const xPath = new XPath();
205
238
  ```
206
239
 
207
- `XPath` class is an external dependency, [living in its own repository](https://github.com/DesignLiquido/xpath).
240
+ `XPath` class is an external dependency, [living in its own repository](https://github.com/DesignLiquido/xpath).
208
241
 
209
242
  ## Introduction
210
243
 
@@ -215,13 +248,40 @@ XSLT-processor builds on Google's [AJAXSLT](https://github.com/4031651/ajaxslt)
215
248
 
216
249
  This implementation of XSLT operates at the DOM level on its input documents. It internally uses a DOM implementation to create the output document, but usually returns the output document as text stream. The DOM to construct the output document can be supplied by the application, or else an internal minimal DOM implementation is used. This DOM comes with a minimal XML parser that can be used to generate a suitable DOM representation of the input documents if they are present as text.
217
250
 
251
+ ## Building from source
252
+
253
+ The XPath engine lives in a Git submodule at `src/xpath/lib`. A regular `git clone` does **not** fetch it automatically, so the build will fail unless the submodule is initialised.
254
+
255
+ ### Fresh clone
256
+
257
+ ```sh
258
+ git clone --recurse-submodules https://github.com/DesignLiquido/xslt-processor.git
259
+ cd xslt-processor
260
+ yarn install
261
+ yarn build
262
+ ```
263
+
264
+ ### Already cloned without submodules
265
+
266
+ ```sh
267
+ git submodule update --init --recursive
268
+ yarn install
269
+ yarn build
270
+ ```
271
+
272
+ ### Updating the submodule to the latest commit
273
+
274
+ ```sh
275
+ git submodule update --remote src/xpath/lib
276
+ ```
277
+
218
278
  ## Tests and usage examples
219
279
 
220
- New tests are written in Jest an can be run by calling: `yarn test`.
280
+ New tests are written in Jest and can be run by calling: `yarn test`.
221
281
 
222
282
  The files `xslt.html` and `xpath.html` in the directory `interactive-tests` are interactive tests. They can be run directly from the file system; no HTTP server is needed.
223
283
 
224
- Both interactive tests and automatic tests demonstrate the use of the library functions.
284
+ Both interactive tests and automatic tests demonstrate the use of the library functions.
225
285
 
226
286
  ## Conformance
227
287
 
@@ -233,9 +293,9 @@ So far, we have implemented XQuery functions for versions 1.0 and 2.0, but this
233
293
 
234
294
  The DOM implementation is minimal so as to support the XSLT processing, and not intended to be complete.
235
295
 
236
- The implementation is all agnostic about namespaces. It just expects XSLT elements to have tags that carry the `xsl:` prefix, but we disregard all namespace declaration for them.
296
+ The implementation is all agnostic about namespaces. It just expects XSLT elements to have tags that carry the `xsl:` prefix, but we disregard all namespace declarations for them.
237
297
 
238
- [There are a few nonstandard XPath functions](https://github.com/search?q=repo%3ADesignLiquido%2Fxslt-processor%20ext-&type=code).
298
+ [There are a few nonstandard XPath functions](https://github.com/search?q=repo%3ADesignLiquido%2Fxslt-processor%20ext-&type=code).
239
299
 
240
300
  ### HTML Conformance
241
301
 
package/index.d.mts CHANGED
@@ -172,6 +172,7 @@ type XsltOptions = {
172
172
  selfClosingTags: boolean;
173
173
  outputMethod?: 'xml' | 'html' | 'text' | 'xhtml' | 'json' | 'adaptive';
174
174
  parameters?: XsltParameter[];
175
+ fetchFunction?: (uri: string) => Promise<string>;
175
176
  };
176
177
 
177
178
  interface NodeValue {
@@ -719,6 +720,11 @@ declare class MatchResolver {
719
720
  private relativeXsltMatch;
720
721
  }
721
722
 
723
+ interface WhitespacePattern {
724
+ namespaceUri: string | null;
725
+ localName: string;
726
+ isWildcard: boolean;
727
+ }
722
728
  /**
723
729
  * The main class for XSL-T processing.
724
730
  *
@@ -755,6 +761,12 @@ declare class Xslt {
755
761
  options: XsltOptions;
756
762
  decimalFormatSettings: XsltDecimalFormatSettings;
757
763
  warningsCallback: (...args: any[]) => void;
764
+ /**
765
+ * Custom fetch function for loading external resources (e.g. xsl:import, xsl:include).
766
+ * Takes a URI and returns the fetched content as a string.
767
+ * Defaults to using the global `fetch` API.
768
+ */
769
+ fetchFunction: (uri: string) => Promise<string>;
758
770
  outputDocument: XDocument;
759
771
  outputMethod: 'xml' | 'html' | 'text' | 'name' | 'xhtml' | 'json' | 'adaptive';
760
772
  outputOmitXmlDeclaration: string;
@@ -775,13 +787,13 @@ declare class Xslt {
775
787
  * List of element name patterns from xsl:strip-space declarations.
776
788
  * Whitespace-only text nodes inside matching elements will be stripped.
777
789
  */
778
- stripSpacePatterns: string[];
790
+ stripSpacePatterns: WhitespacePattern[];
779
791
  /**
780
792
  * List of element name patterns from xsl:preserve-space declarations.
781
793
  * Whitespace-only text nodes inside matching elements will be preserved.
782
794
  * preserve-space takes precedence over strip-space for conflicting patterns.
783
795
  */
784
- preserveSpacePatterns: string[];
796
+ preserveSpacePatterns: WhitespacePattern[];
785
797
  /**
786
798
  * Namespace aliases from xsl:namespace-alias declarations.
787
799
  * Maps stylesheet namespace prefixes to result namespace prefixes.
@@ -1346,6 +1358,9 @@ declare class Xslt {
1346
1358
  * @todo case-order is not implemented.
1347
1359
  */
1348
1360
  protected xsltSort(context: ExprContext, template: XNode): void;
1361
+ private resolveNamespaceUriForPrefix;
1362
+ private isNamespaceDeclaredOnAncestor;
1363
+ private parseWhitespacePattern;
1349
1364
  /**
1350
1365
  * Implements `xsl:strip-space`.
1351
1366
  * Collects element name patterns for which whitespace-only text nodes should be stripped.
@@ -1378,7 +1393,7 @@ declare class Xslt {
1378
1393
  * @param element The element node (for namespace checking).
1379
1394
  * @returns True if the element matches the pattern.
1380
1395
  */
1381
- protected matchesNamePattern(elementName: string, pattern: string, element: XNode): boolean;
1396
+ protected matchesNamePattern(elementName: string, pattern: WhitespacePattern, element: XNode): boolean;
1382
1397
  /**
1383
1398
  * Implements `xsl:template`.
1384
1399
  * @param context The Expression Context.
package/index.d.ts CHANGED
@@ -172,6 +172,7 @@ type XsltOptions = {
172
172
  selfClosingTags: boolean;
173
173
  outputMethod?: 'xml' | 'html' | 'text' | 'xhtml' | 'json' | 'adaptive';
174
174
  parameters?: XsltParameter[];
175
+ fetchFunction?: (uri: string) => Promise<string>;
175
176
  };
176
177
 
177
178
  interface NodeValue {
@@ -719,6 +720,11 @@ declare class MatchResolver {
719
720
  private relativeXsltMatch;
720
721
  }
721
722
 
723
+ interface WhitespacePattern {
724
+ namespaceUri: string | null;
725
+ localName: string;
726
+ isWildcard: boolean;
727
+ }
722
728
  /**
723
729
  * The main class for XSL-T processing.
724
730
  *
@@ -755,6 +761,12 @@ declare class Xslt {
755
761
  options: XsltOptions;
756
762
  decimalFormatSettings: XsltDecimalFormatSettings;
757
763
  warningsCallback: (...args: any[]) => void;
764
+ /**
765
+ * Custom fetch function for loading external resources (e.g. xsl:import, xsl:include).
766
+ * Takes a URI and returns the fetched content as a string.
767
+ * Defaults to using the global `fetch` API.
768
+ */
769
+ fetchFunction: (uri: string) => Promise<string>;
758
770
  outputDocument: XDocument;
759
771
  outputMethod: 'xml' | 'html' | 'text' | 'name' | 'xhtml' | 'json' | 'adaptive';
760
772
  outputOmitXmlDeclaration: string;
@@ -775,13 +787,13 @@ declare class Xslt {
775
787
  * List of element name patterns from xsl:strip-space declarations.
776
788
  * Whitespace-only text nodes inside matching elements will be stripped.
777
789
  */
778
- stripSpacePatterns: string[];
790
+ stripSpacePatterns: WhitespacePattern[];
779
791
  /**
780
792
  * List of element name patterns from xsl:preserve-space declarations.
781
793
  * Whitespace-only text nodes inside matching elements will be preserved.
782
794
  * preserve-space takes precedence over strip-space for conflicting patterns.
783
795
  */
784
- preserveSpacePatterns: string[];
796
+ preserveSpacePatterns: WhitespacePattern[];
785
797
  /**
786
798
  * Namespace aliases from xsl:namespace-alias declarations.
787
799
  * Maps stylesheet namespace prefixes to result namespace prefixes.
@@ -1346,6 +1358,9 @@ declare class Xslt {
1346
1358
  * @todo case-order is not implemented.
1347
1359
  */
1348
1360
  protected xsltSort(context: ExprContext, template: XNode): void;
1361
+ private resolveNamespaceUriForPrefix;
1362
+ private isNamespaceDeclaredOnAncestor;
1363
+ private parseWhitespacePattern;
1349
1364
  /**
1350
1365
  * Implements `xsl:strip-space`.
1351
1366
  * Collects element name patterns for which whitespace-only text nodes should be stripped.
@@ -1378,7 +1393,7 @@ declare class Xslt {
1378
1393
  * @param element The element node (for namespace checking).
1379
1394
  * @returns True if the element matches the pattern.
1380
1395
  */
1381
- protected matchesNamePattern(elementName: string, pattern: string, element: XNode): boolean;
1396
+ protected matchesNamePattern(elementName: string, pattern: WhitespacePattern, element: XNode): boolean;
1382
1397
  /**
1383
1398
  * Implements `xsl:template`.
1384
1399
  * @param context The Expression Context.
package/index.js CHANGED
@@ -10973,6 +10973,9 @@ function xmlElementLogicTrivial(node, buffer, options) {
10973
10973
  if (!attribute) {
10974
10974
  continue;
10975
10975
  }
10976
+ if (options.outputMethod === "html" && attribute.nodeName === "xmlns" && attribute.nodeValue === "http://www.w3.org/1999/xhtml") {
10977
+ continue;
10978
+ }
10976
10979
  if (attribute.nodeName && attribute.nodeValue !== null && attribute.nodeValue !== void 0) {
10977
10980
  buffer.push(` ${xmlFullNodeName(attribute)}="${xmlEscapeAttr(attribute.nodeValue)}"`);
10978
10981
  }
@@ -13905,6 +13908,16 @@ var Xslt = class {
13905
13908
  this.firstTemplateRan = false;
13906
13909
  this.forwardsCompatible = false;
13907
13910
  this.warningsCallback = console.warn.bind(console);
13911
+ this.fetchFunction = options.fetchFunction || ((uri) => __async(this, null, function* () {
13912
+ const globalFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch : null;
13913
+ if (!globalFetch) {
13914
+ throw new Error(
13915
+ "No global fetch implementation available. Please provide options.fetchFunction or use a runtime that exposes globalThis.fetch."
13916
+ );
13917
+ }
13918
+ const response = yield globalFetch(uri);
13919
+ return response.text();
13920
+ }));
13908
13921
  this.streamingProcessor = new StreamingProcessor({
13909
13922
  xPath: this.xPath,
13910
13923
  version: ""
@@ -14475,7 +14488,40 @@ var Xslt = class {
14475
14488
  } = currentTemplateContext;
14476
14489
  const currentNode = context.nodeList[context.position];
14477
14490
  const top = template.ownerDocument.documentElement;
14478
- const allTemplates = collectAndExpandTemplates(top, currentMode, this.xPath, this.templateSourceMap);
14491
+ const stylesheetRoots = [];
14492
+ if (top) {
14493
+ stylesheetRoots.push(top);
14494
+ }
14495
+ this.importedStylesheets.forEach((doc) => {
14496
+ if (!doc) {
14497
+ return;
14498
+ }
14499
+ if (doc.nodeType === DOM_DOCUMENT_NODE) {
14500
+ const rootElement = doc.childNodes.find(
14501
+ (child) => child.nodeType === DOM_ELEMENT_NODE
14502
+ );
14503
+ if (rootElement) {
14504
+ stylesheetRoots.push(rootElement);
14505
+ }
14506
+ } else if (doc.nodeType === DOM_ELEMENT_NODE) {
14507
+ stylesheetRoots.push(doc);
14508
+ }
14509
+ });
14510
+ let allTemplates = [];
14511
+ let docOrderOffset = 0;
14512
+ for (const root2 of stylesheetRoots) {
14513
+ const templates = collectAndExpandTemplates(
14514
+ root2,
14515
+ currentMode,
14516
+ this.xPath,
14517
+ this.templateSourceMap
14518
+ );
14519
+ for (const templateEntry of templates) {
14520
+ templateEntry.documentOrder += docOrderOffset;
14521
+ }
14522
+ docOrderOffset += templates.length;
14523
+ allTemplates = allTemplates.concat(templates);
14524
+ }
14479
14525
  const importedTemplates = allTemplates.filter((t) => {
14480
14526
  const metadata2 = this.templateSourceMap.get(t.template);
14481
14527
  return metadata2 && metadata2.importDepth > currentDepth;
@@ -14536,6 +14582,19 @@ var Xslt = class {
14536
14582
  const value = xmlValueLegacyBehavior(documentFragment);
14537
14583
  if (output) {
14538
14584
  domSetAttribute(output, name, value);
14585
+ if (name.includes(":")) {
14586
+ const prefix = name.split(":")[0];
14587
+ if (prefix !== "xmlns") {
14588
+ const explicitNs = xmlGetAttribute(template, "namespace");
14589
+ const nsUri = explicitNs || this.resolveNamespaceUriForPrefix(template, prefix);
14590
+ if (nsUri) {
14591
+ const nsAttr = `xmlns:${prefix}`;
14592
+ if (!this.isNamespaceDeclaredOnAncestor(output, nsAttr, nsUri)) {
14593
+ domSetAttribute(output, nsAttr, nsUri);
14594
+ }
14595
+ }
14596
+ }
14597
+ }
14539
14598
  }
14540
14599
  });
14541
14600
  }
@@ -14611,7 +14670,11 @@ var Xslt = class {
14611
14670
  if (source.nodeType == DOM_ELEMENT_NODE) {
14612
14671
  let node = domCreateElement(this.outputDocument, source.nodeName);
14613
14672
  if (source.namespaceUri !== null && source.namespaceUri !== void 0) {
14614
- domSetAttribute(node, "xmlns", source.namespaceUri);
14673
+ const prefix = source.prefix || (source.nodeName.includes(":") ? source.nodeName.split(":")[0] : null);
14674
+ const nsAttr = prefix ? `xmlns:${prefix}` : "xmlns";
14675
+ if (!this.isNamespaceDeclaredOnAncestor(destination, nsAttr, source.namespaceUri)) {
14676
+ domSetAttribute(node, nsAttr, source.namespaceUri);
14677
+ }
14615
14678
  }
14616
14679
  node.siblingPosition = destination.childNodes.length;
14617
14680
  domAppendChild(destination, node);
@@ -14634,6 +14697,12 @@ var Xslt = class {
14634
14697
  domAppendChild(destination, node);
14635
14698
  } else if (source.nodeType == DOM_ATTRIBUTE_NODE) {
14636
14699
  domSetAttribute(destination, source.nodeName, source.nodeValue);
14700
+ if (source.prefix && source.namespaceUri && source.prefix !== "xmlns" && !source.nodeName.startsWith("xmlns")) {
14701
+ const nsAttr = `xmlns:${source.prefix}`;
14702
+ if (!this.isNamespaceDeclaredOnAncestor(destination, nsAttr, source.namespaceUri)) {
14703
+ domSetAttribute(destination, nsAttr, source.namespaceUri);
14704
+ }
14705
+ }
14637
14706
  }
14638
14707
  return null;
14639
14708
  }
@@ -15301,16 +15370,6 @@ var Xslt = class {
15301
15370
  xsltImportOrInclude(context, template, output, isImport) {
15302
15371
  return __async(this, null, function* () {
15303
15372
  const elementName = isImport ? "xsl:import" : "xsl:include";
15304
- const [major, minor] = process.versions.node.split(".").map(Number);
15305
- if (major <= 17 && minor < 5) {
15306
- throw new Error(`Your Node.js version does not support \`<${elementName}>\`. If possible, please update your Node.js version to at least version 17.5.0.`);
15307
- }
15308
- if (!global.globalThis.fetch) {
15309
- global.globalThis.fetch = fetch;
15310
- global.globalThis.Headers = Headers;
15311
- global.globalThis.Request = Request;
15312
- global.globalThis.Response = Response;
15313
- }
15314
15373
  const hrefAttributeFind = template.childNodes.filter((n) => n.nodeName === "href");
15315
15374
  if (hrefAttributeFind.length <= 0) {
15316
15375
  throw new Error(`<${elementName}> with no href attribute defined.`);
@@ -15320,8 +15379,7 @@ var Xslt = class {
15320
15379
  if (this.importedStylesheets.has(href)) {
15321
15380
  return;
15322
15381
  }
15323
- const fetchTest = yield global.globalThis.fetch(href);
15324
- const fetchResponse = yield fetchTest.text();
15382
+ const fetchResponse = yield this.fetchFunction(href);
15325
15383
  const includedXslt = this.xmlParser.xmlParse(fetchResponse);
15326
15384
  const currentDepth = this.styleSheetStack.length > 0 ? this.styleSheetStack[this.styleSheetStack.length - 1].importDepth : 0;
15327
15385
  const metadata = {
@@ -16354,6 +16412,17 @@ var Xslt = class {
16354
16412
  * @returns The formatted number string.
16355
16413
  */
16356
16414
  xsltFormatNumber(number, format, groupingSeparator, groupingSize) {
16415
+ if (format.match(/^0+1$/)) {
16416
+ const width = format.length;
16417
+ let result2 = number.toString().padStart(width, "0");
16418
+ if (groupingSeparator && groupingSize) {
16419
+ const size = parseInt(groupingSize, 10);
16420
+ if (size > 0 && !isNaN(size)) {
16421
+ result2 = this.applyGrouping(result2, groupingSeparator, size);
16422
+ }
16423
+ }
16424
+ return result2;
16425
+ }
16357
16426
  const formatChar = format.charAt(0);
16358
16427
  let result;
16359
16428
  switch (formatChar) {
@@ -16482,6 +16551,54 @@ var Xslt = class {
16482
16551
  }
16483
16552
  this.xPath.xPathSort(context, sort2);
16484
16553
  }
16554
+ resolveNamespaceUriForPrefix(node, prefix) {
16555
+ const attrName = prefix ? `xmlns:${prefix}` : "xmlns";
16556
+ let current = node;
16557
+ while (current) {
16558
+ const attributes = current.childNodes.filter(
16559
+ (child) => child.nodeType === DOM_ATTRIBUTE_NODE
16560
+ );
16561
+ for (const attribute of attributes) {
16562
+ if (attribute.nodeName === attrName) {
16563
+ return attribute.nodeValue;
16564
+ }
16565
+ if (prefix && attribute.prefix === "xmlns" && attribute.localName === prefix) {
16566
+ return attribute.nodeValue;
16567
+ }
16568
+ if (!prefix && attribute.nodeName === "xmlns") {
16569
+ return attribute.nodeValue;
16570
+ }
16571
+ }
16572
+ current = current.parentNode;
16573
+ }
16574
+ return null;
16575
+ }
16576
+ isNamespaceDeclaredOnAncestor(node, nsAttr, nsUri) {
16577
+ let current = node;
16578
+ while (current) {
16579
+ const value = domGetAttributeValue(current, nsAttr);
16580
+ if (value === nsUri) {
16581
+ return true;
16582
+ }
16583
+ current = current.parentNode;
16584
+ }
16585
+ return false;
16586
+ }
16587
+ parseWhitespacePattern(pattern, template) {
16588
+ if (pattern === "*") {
16589
+ return { namespaceUri: null, localName: "*", isWildcard: true };
16590
+ }
16591
+ if (pattern.includes(":")) {
16592
+ const [prefix, localPart] = pattern.split(":");
16593
+ const namespaceUri = this.resolveNamespaceUriForPrefix(template, prefix);
16594
+ return {
16595
+ namespaceUri: namespaceUri != null ? namespaceUri : null,
16596
+ localName: localPart || "*",
16597
+ isWildcard: localPart === "*"
16598
+ };
16599
+ }
16600
+ return { namespaceUri: null, localName: pattern, isWildcard: false };
16601
+ }
16485
16602
  /**
16486
16603
  * Implements `xsl:strip-space`.
16487
16604
  * Collects element name patterns for which whitespace-only text nodes should be stripped.
@@ -16491,7 +16608,9 @@ var Xslt = class {
16491
16608
  const elements = xmlGetAttribute(template, "elements");
16492
16609
  if (elements) {
16493
16610
  const patterns = elements.trim().split(/\s+/);
16494
- this.stripSpacePatterns.push(...patterns);
16611
+ for (const pattern of patterns) {
16612
+ this.stripSpacePatterns.push(this.parseWhitespacePattern(pattern, template));
16613
+ }
16495
16614
  }
16496
16615
  }
16497
16616
  /**
@@ -16504,7 +16623,9 @@ var Xslt = class {
16504
16623
  const elements = xmlGetAttribute(template, "elements");
16505
16624
  if (elements) {
16506
16625
  const patterns = elements.trim().split(/\s+/);
16507
- this.preserveSpacePatterns.push(...patterns);
16626
+ for (const pattern of patterns) {
16627
+ this.preserveSpacePatterns.push(this.parseWhitespacePattern(pattern, template));
16628
+ }
16508
16629
  }
16509
16630
  }
16510
16631
  /**
@@ -16561,19 +16682,19 @@ var Xslt = class {
16561
16682
  * @returns True if the element matches the pattern.
16562
16683
  */
16563
16684
  matchesNamePattern(elementName, pattern, element) {
16564
- if (pattern === "*") {
16565
- return true;
16566
- }
16567
- if (pattern.includes(":")) {
16568
- const [prefix, localPart] = pattern.split(":");
16569
- const elementPrefix = element.prefix || "";
16570
- if (localPart === "*") {
16571
- return elementPrefix === prefix;
16572
- } else {
16573
- return elementPrefix === prefix && elementName === localPart;
16685
+ var _a;
16686
+ const elementNamespace = (_a = element.namespaceUri) != null ? _a : this.resolveNamespaceUriForPrefix(element, element.prefix || null);
16687
+ if (pattern.namespaceUri !== null) {
16688
+ if (elementNamespace !== pattern.namespaceUri) {
16689
+ return false;
16574
16690
  }
16691
+ } else if (!pattern.isWildcard && elementNamespace) {
16692
+ return false;
16693
+ }
16694
+ if (pattern.isWildcard) {
16695
+ return true;
16575
16696
  }
16576
- return elementName === pattern;
16697
+ return elementName === pattern.localName;
16577
16698
  }
16578
16699
  /**
16579
16700
  * Implements `xsl:template`.
@@ -17459,9 +17580,38 @@ var Xslt = class {
17459
17580
  let elementContext = context;
17460
17581
  node = context.nodeList[context.position];
17461
17582
  let newNode;
17462
- newNode = domCreateElement(this.outputDocument, template.nodeName);
17583
+ let qualifiedName = template.nodeName;
17584
+ let namespaceUri = template.namespaceUri;
17585
+ const templatePrefix = template.prefix || "";
17586
+ const aliasPrefix = this.namespaceAliases.get(templatePrefix);
17587
+ if (aliasPrefix) {
17588
+ const localName = template.localName || template.nodeName;
17589
+ if (aliasPrefix === "#default") {
17590
+ qualifiedName = localName;
17591
+ namespaceUri = this.resolveNamespaceUriForPrefix(template, null);
17592
+ } else {
17593
+ qualifiedName = `${aliasPrefix}:${localName}`;
17594
+ namespaceUri = this.resolveNamespaceUriForPrefix(template, aliasPrefix);
17595
+ }
17596
+ }
17597
+ newNode = domCreateElement(this.outputDocument, qualifiedName);
17463
17598
  newNode.siblingPosition = (output || this.outputDocument).childNodes.length;
17464
17599
  domAppendChild(output || this.outputDocument, newNode);
17600
+ if (aliasPrefix) {
17601
+ if (aliasPrefix === "#default") {
17602
+ if (namespaceUri) {
17603
+ domSetAttribute(newNode, "xmlns", namespaceUri);
17604
+ }
17605
+ } else if (namespaceUri) {
17606
+ domSetAttribute(newNode, `xmlns:${aliasPrefix}`, namespaceUri);
17607
+ }
17608
+ } else if (namespaceUri) {
17609
+ const prefix = templatePrefix || (qualifiedName.includes(":") ? qualifiedName.split(":")[0] : null);
17610
+ const nsAttr = prefix ? `xmlns:${prefix}` : "xmlns";
17611
+ if (!this.isNamespaceDeclaredOnAncestor(output, nsAttr, namespaceUri)) {
17612
+ domSetAttribute(newNode, nsAttr, namespaceUri);
17613
+ }
17614
+ }
17465
17615
  const useAttributeSetsAttr = template.childNodes.find(
17466
17616
  (a) => (a == null ? void 0 : a.nodeType) === DOM_ATTRIBUTE_NODE && a.nodeName === "use-attribute-sets"
17467
17617
  );
@@ -17476,6 +17626,12 @@ var Xslt = class {
17476
17626
  const name = attribute.nodeName;
17477
17627
  const value = this.xsltAttributeValue(attribute.nodeValue, elementContext);
17478
17628
  domSetAttribute(newNode, name, value);
17629
+ if (attribute.prefix && attribute.namespaceUri && attribute.prefix !== "xmlns" && !attribute.nodeName.startsWith("xmlns")) {
17630
+ const nsAttr = `xmlns:${attribute.prefix}`;
17631
+ if (!this.isNamespaceDeclaredOnAncestor(newNode, nsAttr, attribute.namespaceUri)) {
17632
+ domSetAttribute(newNode, nsAttr, attribute.namespaceUri);
17633
+ }
17634
+ }
17479
17635
  }
17480
17636
  break;
17481
17637
  default: