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
23 changes: 21 additions & 2 deletions src/xslt/xslt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,8 @@ export class Xslt {

for (const t of expandedTemplates) {
try {
// For initial template selection, evaluate patterns from document root
// without axis override to ensure consistent matching for all patterns
// For initial template selection, evaluate patterns from document root
// without axis override to ensure consistent matching for all patterns
const matchedNodes = this.xsltMatch(t.matchPattern, contextClone);
Expand Down Expand Up @@ -1440,13 +1442,30 @@ export class Xslt {

protected xsltValueOf(context: ExprContext, template: XNode, output?: XNode) {
const select = xmlGetAttribute(template, 'select');
const attribute = this.xPath.xPathEval(select, context);
const current = context.nodeList[context.position];

// First try evaluating in the current context. If that returns an
// empty result and the current node is the document node, try again
// evaluating against the document element (fallback), which helps
// with some templates written to expect either form.
let attribute = this.xPath.xPathEval(select, context);
if (
current &&
current.nodeName === '#document' &&
(attribute.stringValue() === '' || (attribute instanceof NodeSetValue && attribute.nodeSetValue().length === 0))
) {
const docChild = current.childNodes.find((c: XNode) => c.nodeName !== '#dtd-section');
if (docChild) {
const fallbackContext = context.clone([docChild], 0);
attribute = this.xPath.xPathEval(select, fallbackContext);
}
}

const value = attribute.stringValue();
const node = domCreateTextNode(this.outputDocument, value);
// Set siblingPosition to preserve insertion order during serialization
const targetOutput = output || this.outputDocument;
node.siblingPosition = targetOutput.childNodes.length;

targetOutput.appendChild(node);
}

Expand Down
25 changes: 25 additions & 0 deletions tests/xslt/value-of.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,29 @@ describe('xsl:value-of', () => {

assert.equal(outXmlString, `<h1>Fergie's Web Feed Preview</h1>`);
});

it('XSLT template with text on both sides (issue 109)', async () => {
const xmlString = `<root>
<test name="test1">This text lost</test>
</root>`;

const xsltString = `<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<span>X<xsl:value-of select="test/@name" />Y</span>
</xsl:template>
</xsl:stylesheet>`;

const expectedOutString = `<span>Xtest1Y</span>`;

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, expectedOutString);
});
});