From 354c78d4d78e3b6bbb2083df795100994c95e84d Mon Sep 17 00:00:00 2001 From: Leonel Sanches da Silva <53848829+leonelsanchesdasilva@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:23:35 -0700 Subject: [PATCH 1/3] Exposing `xmlTransformedText ` as requested at https://github.com/DesignLiquido/xslt-processor/issues/187. --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 668b563..c76a99a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export { XPath, ExprContext } from './xpath'; export { Xslt, XsltOptions } from './xslt'; -export { XmlParser, xmlEscapeText, XDocument, XNode, domDocumentToXDocument } from './dom'; +export { XmlParser, xmlEscapeText, xmlTransformedText, XDocument, XNode, domDocumentToXDocument } from './dom'; From 5beb6f9b9a7abe8adced472ca627335cab5c0c0c Mon Sep 17 00:00:00 2001 From: Leonel Sanches da Silva <53848829+leonelsanchesdasilva@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:24:12 -0700 Subject: [PATCH 2/3] Removing padding spaces to avoid problems with test snapshots, as reported at https://github.com/DesignLiquido/xslt-processor/issues/187. --- src/dom/xml-functions.ts | 2 +- tests/xml/xml.test.tsx | 16 +++++++++++++++- tests/xslt/issue-187.test.ts | 23 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/xslt/issue-187.test.ts diff --git a/src/dom/xml-functions.ts b/src/dom/xml-functions.ts index 0197d28..7082012 100644 --- a/src/dom/xml-functions.ts +++ b/src/dom/xml-functions.ts @@ -244,7 +244,7 @@ function xmlTransformedTextRecursive(node: XNode, buffer: string[], options: Xml } } else if (nodeType == DOM_COMMENT_NODE) { if (options.outputMethod !== 'text') { - buffer.push(``); + buffer.push(``); } } else if (nodeType === DOM_PROCESSING_INSTRUCTION_NODE) { if (options.outputMethod !== 'text') { diff --git a/tests/xml/xml.test.tsx b/tests/xml/xml.test.tsx index 8ea837c..6bbc490 100644 --- a/tests/xml/xml.test.tsx +++ b/tests/xml/xml.test.tsx @@ -1,6 +1,6 @@ import assert from 'assert'; -import { XmlParser, xmlText } from '../../src/dom'; +import { XmlParser, xmlText, xmlTransformedText } from '../../src/dom'; describe('General XML', () => { it('Self-closing tags disabled', () => { @@ -15,4 +15,18 @@ describe('General XML', () => { }); assert.equal(outXmlString, ''); }); + + it('preserves comment boundaries in transformed serialization', () => { + const xmlString = ''; + + const xmlParser = new XmlParser(); + const outXmlString = xmlTransformedText(xmlParser.xmlParse(xmlString), { + cData: true, + selfClosingTags: true, + escape: true, + outputMethod: 'xml' + }); + + assert.equal(outXmlString, xmlString); + }); }); diff --git a/tests/xslt/issue-187.test.ts b/tests/xslt/issue-187.test.ts new file mode 100644 index 0000000..9972625 --- /dev/null +++ b/tests/xslt/issue-187.test.ts @@ -0,0 +1,23 @@ +import { Xslt, XmlParser } from '../../src/index'; + +describe('Issue #187: Identity transformation adds spaces to comments', () => { + it('keeps comment content stable across repeated identity transforms', async () => { + const xslt = new Xslt(); + const xmlParser = new XmlParser(); + + const xmlInput = ''; + const stylesheet = xmlParser.xmlParse(` + + + +`); + + const firstInputDoc = xmlParser.xmlParse(xmlInput); + const firstPass = await xslt.xsltProcess(firstInputDoc, stylesheet); + expect(firstPass).toBe(xmlInput); + + const secondInputDoc = xmlParser.xmlParse(firstPass); + const secondPass = await xslt.xsltProcess(secondInputDoc, stylesheet); + expect(secondPass).toBe(firstPass); + }); +}); \ No newline at end of file From c72beeec43bf19a21e2f8f7fd9d6f572667f393a Mon Sep 17 00:00:00 2001 From: Leonel Sanches da Silva <53848829+leonelsanchesdasilva@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:52:28 -0700 Subject: [PATCH 3/3] Addressing Copilot's comments. --- src/dom/xml-parser.ts | 10 +++++----- tests/xslt/issue-187.test.ts | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/dom/xml-parser.ts b/src/dom/xml-parser.ts index 6db2e64..7a1425c 100644 --- a/src/dom/xml-parser.ts +++ b/src/dom/xml-parser.ts @@ -159,14 +159,14 @@ export class XmlParser { } if (htmlText.slice(i + 1, i + 4) === '!--') { let endTagIndex = htmlText.slice(i + 4).indexOf('-->'); - if (endTagIndex) { + if (endTagIndex >= 0) { let node = domCreateComment(xmlDocument, htmlText.slice(i + 4, i + endTagIndex + 4)); domAppendChild(parent, node); i += endTagIndex + 6; } } else if (htmlText.slice(i + 1, i + 9) === '!DOCTYPE') { let endTagIndex = htmlText.slice(i + 9).indexOf('>'); - if (endTagIndex) { + if (endTagIndex >= 0) { const dtdValue = htmlText.slice(i + 9, i + endTagIndex + 9).trimStart(); // TODO: Not sure if this is a good solution. // Trying to implement this: https://github.com/DesignLiquido/xslt-processor/issues/30 @@ -289,21 +289,21 @@ export class XmlParser { } if (xml.slice(i + 1, i + 4) === '!--') { let endTagIndex = xml.slice(i + 4).indexOf('-->'); - if (endTagIndex) { + if (endTagIndex >= 0) { let node = domCreateComment(xmlDocument, xml.slice(i + 4, i + endTagIndex + 4)); domAppendChild(parent, node); i += endTagIndex + 6; } } else if (xml.slice(i + 1, i + 9) === '![CDATA[') { let endTagIndex = xml.slice(i + 9).indexOf(']]>'); - if (endTagIndex) { + if (endTagIndex >= 0) { let node = domCreateCDATASection(xmlDocument, xml.slice(i + 9, i + endTagIndex + 9)); domAppendChild(parent, node); i += endTagIndex + 11; } } else if (xml.slice(i + 1, i + 9) === '!DOCTYPE') { // "!DOCTYPE" can be used in a XSLT template. let endTagIndex = xml.slice(i + 9).indexOf('>'); - if (endTagIndex) { + if (endTagIndex >= 0) { const dtdValue = xml.slice(i + 9, i + endTagIndex + 9).trimStart(); // TODO: Not sure if this is a good solution. // Trying to implement this: https://github.com/DesignLiquido/xslt-processor/issues/30 diff --git a/tests/xslt/issue-187.test.ts b/tests/xslt/issue-187.test.ts index 9972625..f95cc46 100644 --- a/tests/xslt/issue-187.test.ts +++ b/tests/xslt/issue-187.test.ts @@ -20,4 +20,24 @@ describe('Issue #187: Identity transformation adds spaces to comments', () => { const secondPass = await xslt.xsltProcess(secondInputDoc, stylesheet); expect(secondPass).toBe(firstPass); }); + + it('keeps empty comments stable across repeated identity transforms', async () => { + const xslt = new Xslt(); + const xmlParser = new XmlParser(); + + const xmlInput = ''; + const stylesheet = xmlParser.xmlParse(` + + + +`); + + const firstInputDoc = xmlParser.xmlParse(xmlInput); + const firstPass = await xslt.xsltProcess(firstInputDoc, stylesheet); + expect(firstPass).toBe(xmlInput); + + const secondInputDoc = xmlParser.xmlParse(firstPass); + const secondPass = await xslt.xsltProcess(secondInputDoc, stylesheet); + expect(secondPass).toBe(firstPass); + }); }); \ No newline at end of file