An XSLT 1.0+ implementation written entirely in JavaScript.
THIS MODULE IS STILL A WORK IN PROGRESS
This is a partial implementation of XSLT 1.0 and XSLT 2.0. Among its features:
- Pure 100% JavaScript implementation requiring no C++, C#, or Java binding
- Support for XML namespaces
- Support for include/import
- Support for XSL-T functions
- Drop-in compatibility with xslt4node
- <xsl:apply-templates>
- <xsl:attribute>
- <xsl:call-template>
- <xsl:choose>
- <xsl:comment>
- <xsl:copy>
- <xsl:copy-of>
- <xsl:decimal-format>
- <xsl:element>
- <xsl:for-each>
- <xsl:function>
- <xsl:if>
- <xsl:import>
- <xsl:include>
- <xsl:otherwise>
- <xsl:output>
- <xsl:param>
- <xsl:preserve-space>
- <xsl:processing-instruction>
- <xsl:sort>
- <xsl:strip-space>
- <xsl:stylesheet>
- <xsl:transform>
- <xsl:template>
- <xsl:text>
- <xsl:value-of>
- <xsl:variable>
- <xsl:when>
- <xsl:with-param>
- function-available()
- current()
- document()
- format-number()
- replace()
- lower-case()
- upper-case()
- matches()
- generateId()
XSLTjs is based on AJAXSLT, originally developed by Steffen Meschkat at Google. That version was developed around 2005 as a stopgap solution for "fat web pages" at a time when XSLT processors weren't found in all web browsers.
This version is a substantial reworking of that original work for use in Node.js (other uses will need accommodation). Below is a partial list of enhancements:
- Substantial rewrite to ES2015 standards
- Formatting of JavaScript to SemiStandard.
- Replacement of internal implementations of XML DOM and XPath with XmlDOM and XPath modules available from NPM.
- Support for namespaces
- Support for extended (but still partial) set of XSLT functions used with XPath expressions.
- Support for extended set of XSLT elements.
- Drop-in compatibility with xslt4node.
At this point, features are being added as needed on our projects. Contributions are always welcome.
npm install xsltjs --save
As this XSLT engine is implemented entirely in JavaScript, special care should be taken to optimize performance. XSLT transformations can be quite performance intensive and involve a considerable amount of XPath expression processing. The time spent with the XPath processor can quickly add up.
Be aware that the transform engine is dramatically slower (as much as 10x slower) when connected to a debugger such as Chrome developer tools. If your configuration permits it, it is generally better, performance-wise, to open the Chrome developer tools after a run to view any diagnostics.
A debug option is provided as a transformSpec property or XSLT.process() method option (see below) that will report detailed execution counts and times via console.debug(). Measuring performance using this option is more realistic than using the developer tools profiler as the profiler interactions appear to overwhelm the computation. Executions times have been observed to be as much as 20x slower with the profiler running.
As most execution time is consumed by XPath processing and within templates. Optimization strategies can be employed for both:
-
There is an optimization for simple xPaths that are simply the qualified element name. Try to use simple xPaths of this form rather than more complex xPath expressions.
-
If the templates are being called too many times, try to use the mode attribute to segment out the templates. Note that the time reported is the number of attempts to call a template -- rather than the number of templates that are actually evaluated. For this reason, the total number of calls will be exaggerated.
Also, because only single template matches are considered, try to reorder the templates to place commonly called templates at the start of the transform and less commonly used ones at the back.
XSLT
.process(inputDoc, transformDoc, params, {
inputURL: inputURL,
transformURL: transformURL,
customFunctions: {
'http://www.xcential.com/schemas/xed': {
'setVar': function (arg1, arg2) {...},
'getVar': function () {...}
}
},
debug: debug
logger: electronLogger })
.then(
(resultXML) => {
return ...;
},
(exception) => {
return ...;
}
);
Using an xslt4node approach:
const transformSpec = {
source: inputXML|inputDoc,
xsltPath: transformPath, // Optional
xslt: transformXML|transformDoc, // Optional
result: string|function,
params: {
'docName': 'My Document',
'docDate': 'February 24, 2019'
},
customFunctions: {
'http://www.xcential.com/schemas/xed': {
'setVar': function (arg1, arg2) {...},
'getVar': function () {...}
}
},
debug: true|false // Controls whether debug messages will log
logger: electronLogger|null // Will log to console if not specified
};
XSLT.transform(transformSpec, (errorMessage, resultXML) => {
if (errorMessage) {
throw new Error(error);
} else if (resultXML) {
...
}
});
Note that customFunctions and debug are not part of the xslt4node API.
Custom functions can be used when necessary. To use your custom function, be sure to prefix the function name with the prefix corresponding to the namespace the function is declared within.
The first argument is always the xPathContext object. The remainder of the arguments are expressions relating to the arguments used in the transform. To access the value, the expression must first be evaluated as shown in the example below.
Any return value must be converted to an XPath type. Consult the xpath module (available via NPM) for further details.
formatDate = function (xPathContext, dateTextExpr) {
let dateText = dateTextExpr.evaluate(xPathContext).str;
let date = new Date(Date.parse(dateText));
let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
let formattedDate = months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear();
return new this.XPath.XString(formattedDate);
};
- 0.0.29
- Improved documentation for custom functions
- Improved handling of position() function
- Improved search for custom functions
- Corrects retrieval of value of 0 with variable/parameters (was converting to an empty string)
- Fixes problem with double xml declaration in some cases
- Improved management of context objects when traversing the transform document
- Corrects problems normalizing non-string values
- Corrects problems storing non-string values
- Improves chaining of variable namespaces
- Cleans up unused code
- Improved whitespace handling
- Adds disable-output-escape support to xsl:value-of
- 0.0.28
- Corrects problem with xsl:copy of comments.
- 0.0.27
- Corrects problem with xsl:copy of processing instructions.
- 0.0.26
- Corrects problem with xsl:output not always being seen.
- 0.0.25
- Removes WORK IN PROGRESS notice
- 0.0.24
- Adds workaround to strange problem in DOM processor where text nodes with a value of 'false' disappear.
- 0.0.23
- Replaces request with Fetch API calls (request seems problematic with promises using Node.js 12.0).
- 0.0.22
- Fixes issues with @omit-xml-declaration.
- Implements @disable-output-escaping.
- 0.0.21
- Provides mechanism to control where and what is logged.
- Better global state management
- 0.0.20
- Adds support for flags in XPath replace function.
- Adds support for xsl:strip-space and xsl:preserve-space.
- Further improves whitespace handling.
- 0.0.19
- Improves whitespace handling.
- Improves context management of variables.
- 0.0.18
- Adds a mechanism to use custom JavaScript functions.
- 0.0.17
- Fixes problem when xPath position() function is used standalone rather than as part of a predicate.
- 0.0.16
- Fixes problem with root handling.
- 0.0.15
- Minor documentation update.
- 0.0.14
- Fixes problems with excessive aync/await usage.
- Fixes problem xPath processing namespaces.
- 0.0.13
- Adds workaround to apparent bug in xpath module's support for namespace axes.
- Better handling of root node.
- 0.0.12
- Bug fixes for xsl:comment and include/imports.
- 0.0.11
- Cleanup.
- 0.0.10
- Improved XPath handling.
- 0.0.9
- Lots of performance optimizations.
- Addition of debug performance metrics.
- Bug fixes.
- 0.0.8
- Corrects problem with nodeList naming.
- 0.0.7
- Documentation corrections.
- 0.0.6
- Implements include/import.
- Bug fixes.
- 0.0.5
- Implements functions.
- Bug fixes.
- 0.0.1
- Work in progress.
Grant Vergottini- @grantcv1 - grant.vergottini@xcential.com
Distributed under the MIT license. See LICENSE
for more information.
https://github.com/Xcential-Corporation/XSLTjs
- Fork it (https://github.com/yourname/yourproject/fork)
- Create your feature branch (
git checkout -b feature/fooBar
) - Commit your changes (
git commit -am 'Add some fooBar'
) - Push to the branch (
git push origin feature/fooBar
) - Create a new Pull Request