Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/dom/xml-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ function xmlElementLogicTrivial(node: XNode, buffer: string[], options: XmlOutpu
continue;
}

// In HTML output mode, skip namespace declarations (xmlns and xmlns:*)
if (options.outputMethod === 'html' &&
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a trailing whitespace after the && on the options.outputMethod === 'html' && line; this will fail the repo’s no-trailing-spaces ESLint rule. Remove the trailing space and let formatting place the line break without extra whitespace.

Suggested change
if (options.outputMethod === 'html' &&
if (options.outputMethod === 'html' &&

Copilot uses AI. Check for mistakes.
(attribute.nodeName === 'xmlns' || attribute.nodeName.startsWith('xmlns:'))) {
continue;
}
Comment on lines +320 to +324
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior to omit xmlns / xmlns:* attributes during HTML serialization isn’t covered by tests. Please add a regression test that serializing a node with a namespace declaration using outputMethod: 'html' omits the declaration (and ideally that outputMethod: 'xml' still includes it).

Copilot uses AI. Check for mistakes.

if (attribute.nodeName && attribute.nodeValue !== null && attribute.nodeValue !== undefined) {
buffer.push(` ${xmlFullNodeName(attribute)}="${xmlEscapeAttr(attribute.nodeValue)}"`);
}
Expand Down
6 changes: 6 additions & 0 deletions src/xslt/xslt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4979,6 +4979,12 @@
} else if (namespaceUri) {
domSetAttribute(newNode, `xmlns:${aliasPrefix}`, namespaceUri);
}
} else if (namespaceUri) {
const prefix = templatePrefix || (qualifiedName.includes(':') ? qualifiedName.split(':')[0] : null);

Check warning on line 4983 in src/xslt/xslt.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
const nsAttr = prefix ? `xmlns:${prefix}` : 'xmlns';
if (!this.isNamespaceDeclaredOnAncestor(output, nsAttr, namespaceUri)) {
domSetAttribute(newNode, nsAttr, namespaceUri);
}
}

// Apply attribute sets from use-attribute-sets attribute on literal elements
Expand Down
40 changes: 40 additions & 0 deletions tests/xslt/message-number-namespace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,46 @@ describe('Error messages for misplaced elements', () => {
});
});

describe('Namespace declarations on literal result elements (issue #165)', () => {
it('Namespace-prefixed literal result element includes xmlns declaration', async () => {
const xmlString = `<?xml version="1.0"?><doc/>`;

const xsltString = `<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:q="urn:Q" version="1.0">
<xsl:template match="/">
<q:text>Out</q:text>
</xsl:template>
</xsl:stylesheet>`;

const xsltClass = new Xslt();
const xmlParser = new XmlParser();
const xml = xmlParser.xmlParse(xmlString);
const xslt = xmlParser.xmlParse(xsltString);

const outXmlString = await xsltClass.xsltProcess(xml, xslt);
assert.equal(outXmlString, '<q:text xmlns:q="urn:Q">Out</q:text>');
});

it('Nested namespace-prefixed elements do not duplicate xmlns declaration', async () => {
const xmlString = `<?xml version="1.0"?><doc/>`;

const xsltString = `<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:q="urn:Q" version="1.0">
<xsl:template match="/">
<q:outer><q:inner>Text</q:inner></q:outer>
</xsl:template>
</xsl:stylesheet>`;

const xsltClass = new Xslt();
const xmlParser = new XmlParser();
const xml = xmlParser.xmlParse(xmlString);
const xslt = xmlParser.xmlParse(xsltString);

const outXmlString = await xsltClass.xsltProcess(xml, xslt);
assert.equal(outXmlString, '<q:outer xmlns:q="urn:Q"><q:inner>Text</q:inner></q:outer>');
});
});

describe('Modes and Multiple Template Sets', () => {
it('Template in non-default mode not selected if no mode specified', async () => {
const xmlString = `<?xml version="1.0"?>
Expand Down
Loading