v1.1.0
Minor release: new public API (setStylesheetLoader, setDocumentLoader, transformToString, engine), the xsl:output serializer and the XSLT 1.0 conformance fixes below. Versions 1.0.4-1.0.8 were tagged but never published to npm, so this is the first npm release after 1.0.3.
Added
-
XSLTProcessor.setStylesheetLoader(loader)- public API for configuring the loader used to resolvexsl:importandxsl:include. It can be called beforeimportStylesheet()(required, since the engine is created during import) or after it (the live engine is updated). Passing anything other than a function ornullthrows aTypeError. Returns the processor for chaining. -
XSLTProcessor.engine- read-only getter exposing the underlyingXsltEnginefor advanced usage. Returnsnulluntil a stylesheet has been imported. -
importStylesheet(style, stylesheetUri)- the optional second argument is now forwarded to the engine and used as the base URI when resolving relativexsl:import/xsl:includehrefs. -
TypeScript declarations for the new API, including an exported
StylesheetLoadertype (synchronous:(href, baseUri?) => Document | string). -
dist/xslt-processor.d.cts- CommonJS-flavoured declarations, wired through nestedtypesconditions inpackage.jsonexports, sorequire()consumers under TypeScriptnode16/nodenextresolution no longer get the ESM declarations for the CommonJS bundle ("Masquerading as ESM" reported by@arethetypeswrong/cli). Verified with TypeScript 7.0.2 instrictmode undernodenextandbundlerresolution. -
Release workflow -
publishjob using npm Trusted Publishing (OIDC) with provenance; runs onv*tags after tests and build, and refuses to publish when the tag does not matchpackage.json. Requires a one-time Trusted Publisher configuration on npmjs.com (documented in README). -
XSLT-defined XPath functions -
document(),key(),format-number(),current(),generate-id(),system-property(),function-available(),element-available()andunparsed-entity-uri(). Calling any of them previously raisedUnknown function: X, which madetransformToFragment()/transformToDocument()returnnull. -
XSLTProcessor.setDocumentLoader(loader)(andXsltEngine.setDocumentLoader(loader), plus adocumentLoaderengine option) - synchronous loader for the XSLTdocument()function, returning aDocument, an XML string ornull. Same validation and chaining contract assetStylesheetLoader(). Missing loader or anullresult yields an empty node-set instead of failing the transformation;document('')returns the stylesheet, node-set arguments are unioned, fragment identifiers are ignored and relative URIs resolve against the stylesheet URI. -
XPathEvaluator.registerFunctions(map)- extension hook used to register the XSLT function library, keepingsrc/xpatha pure XPath 1.0 implementation.XPathContextgained an optionalhostContextthat is carried through predicate evaluation so host functions such ascurrent()can reach the XSLT context. -
xsl:apply-imports- previously reported asUnknown XSLT element. Applies only templates of lower import precedence in the same mode, falling back to the built-in rules. -
xsl:strip-space/xsl:preserve-space- parsed since 1.0.0 but never applied. Whitespace-only text nodes are now removed from a copy of the source tree (the caller's document is never modified), honouring "most specific name test wins",xsl:preserve-spacewinning ties, andxml:space="preserve"on ancestors. -
New focused modules with full test suites:
src/xslt/functions.js,keys.js,formatNumber.js,number.js,numberFormat.js,whitespace.js,literalResult.js,resultTree.js,elements.jsanduri.js. -
CommonJS consumer smoke test (
tests/cjs-smoke.cjs) exercising the built bundle throughrequire()with jsdom, plus tests for the package entry point. -
Output serializer (
xsl:output, XSLT 1.0 section 16) - newsrc/xslt/serializer.jsexportingserializeResult(node, outputSettings)plus the focused modules insrc/xslt/serializer/(baseWriter,xmlSerializer,htmlSerializer,textSerializer,escape,indent,namespaces,settings,rawText,constants).method="xml"- XML declaration honoringencoding,versionandstandalone,omit-xml-declaration,doctype-public/doctype-system, minimal text and attribute escaping,<x/>for empty elements, namespace declarations emitted where first used and never twice, comments and processing instructions.method="html"- no XML declaration, HTML doctype, void elements written as<br>, minimized boolean attributes, unescapedscript/stylecontent,>-terminated processing instructions, original element and attribute name case, no namespace declarations.method="xhtml"- XML rules with void elements written as<br />.method="text"- concatenation of all descendant text nodes, unescaped.- Automatic default method detection:
htmlwhen the result document element ishtmlin no namespace,xmlotherwise. indent="yes"- newline plus two-space indentation for element-only content; mixed content,cdata-section-elementsand the HTMLpre/script/style/textareaelements are left untouched.cdata-section-elements- text children wrapped in<![CDATA[...]]>, split around any]]>terminator.disable-output-escaping="yes"onxsl:textandxsl:value-ofis now honored; text nodes can also be marked explicitly with the exportedmarkRawText()helper.
-
XSLTProcessor.transformToString(source)andXsltEngine.transformToString(sourceNode)- non-W3C convenience methods returning the serialized result.transformToFragment()andtransformToDocument()are unchanged. -
Public exports -
serializeResult,markRawText,isRawTextandresolveOutputSettingsare exported from the package entry point, andtransformToString/OutputSettingsare declared in the generated TypeScript declarations. -
CLI -
bin/xslt.jsnow serializes throughtransformToString()instead of re-indenting with a regular expression, and gained--indent,--method <m>and--no-declarationflags that override the stylesheetxsl:outputsettings. Helpers were extracted tobin/lib/options.jsandbin/lib/transform.js. -
Tests -
src/xslt/serializer.test.js,src/XSLTProcessor.serialization.test.jsandsrc/cli.test.js(119 new tests, 560 in total), including the<xsl:output method="xml" indent="yes"/>regression from DesignLiquido/xslt-processor#219.
Fixed
TypeError: Cannot read properties of undefined (reading 'setStylesheetLoader')(#6) - the README documentedprocessor.engine.setStylesheetLoader(...), butprocessor.enginewas undefined and the engine did not exist beforeimportStylesheet(). The documented workflow now works throughprocessor.setStylesheetLoader(...).- README - rewrote the "Using xsl:import and xsl:include" section: the previous example used
awaitinside a non-async callback and contained two unreachable "options". It now shows a correct synchronous loader, a browser pre-fetch pattern, and a Node.js filesystem example usingpath.resolve(path.dirname(baseUri), href). xsl:copylost attributes - the identity transform turned<i k="a">1</i>into<i>a1</i>. Attribute nodes were never matched by patterns such as@*|node()because attributes have noparentNode; patterns are now evaluated from theownerElement, so<xsl:copy>on an attribute copies the attribute instead of falling back to the built-in text rule. The identity transform now round-trips elements, attributes, text, comments and processing instructions exactly.- CDATA sections were invisible - the string-value of an element containing a CDATA section was empty. CDATA nodes now count as text everywhere: string-value, the
text()node test,xsl:value-of,xsl:copy-ofand the built-in text template. xsl:number level="any"always produced the same number (I, Iinstead ofI, II). Counting was rewritten forsingle,multipleandany, includingcount,fromand the1,01,a,A,i,Iformat tokens with prefixes, separators and suffixes.xsl:namespace-aliasproduced wrong output (<ax:stylesheet xmlns:ax="xsl"/>). Aliases are now resolved against the namespace declarations in scope, so literal result elements and their attributes are emitted in the result namespace with the result prefix (or the default namespace forresult-prefix="#default"), which makes stylesheet-generating stylesheets work.xsl:use-attribute-setson literal result elements was ignored. It now applies the same attribute sets as onxsl:element/xsl:copy(literal attributes still win), andxsl:*attributes (xsl:version,xsl:exclude-result-prefixes,xsl:extension-element-prefixes,xsl:use-attribute-sets) never leak into the result.transformToFragment(xmlDoc, htmlDocument)lower-cased names and injected the XHTML namespace (<bar xmlns="http://www.w3.org/1999/xhtml">for<BAR>). The result tree is now built in a neutral XML document and imported into the output document at the end, preserving names, namespaces anddisable-output-escapingmarkers while keeping the W3C behaviour that the fragment is owned by the output document.- XPath function lookup no longer resolves inherited
Object.prototypemembers, so expressions such asconstructor()reportUnknown functioninstead of invoking an object built-in. setParameter()broke every transformation - the processor stored{ value }inglobalParameters, but the engine only understood{ select }/{ node }definitions and calledprocessChildren(undefined), throwingCannot read properties of undefined (reading 'childNodes')(sotransformTo*returnednull). A value set beforeimportStylesheet()was silently overwritten by thexsl:paramdeclaration. External values are now merged into the declaration and always win over the declared default. This also fixes the CLI-p name=valueflag.- Union match patterns had the wrong default priority -
match="@*|node()"was treated as one "complex" pattern with priority 0.5, so the identity template beat everymatch="name"template (priority 0). Per XSLT 1.0 section 5.5 a union pattern is now registered as one template rule per alternative, each with its own default priority;calculatePriority()moved tosrc/xslt/templatePriority.jsand also recognises@name,@*,prefix:name,@prefix:*,child::/attribute::axes andprocessing-instruction('literal'). transformToDocument()/transformToString()failed in Node.js without a globaldocument(Document creation not available in this environment). The result document is now created from the DOM implementation of the source document when no globaldocumentexists, so jsdom/xmldom users no longer need to install a global.xsl:outputignoredversionandstandalone- both attributes are now parsed intooutputSettings(versiondefaults to1.0,standalonetonull).
Changed
-
reset()keeps the configured stylesheet loader (it is processor configuration, not stylesheet state); passnulltosetStylesheetLoader()to remove it. Documented in JSDoc and the README API table. -
Updated
VERSIONinsrc/index.jsto1.1.0. -
Fixed the package name in the generated declaration header (
@tradik/xslt-processor). -
xsl:include/xsl:importfailures are rethrown with{ cause }so the original loader/parser error and stack are preserved (ESLint 10preserve-caught-error). -
GitHub Actions bumped to
actions/checkout@v7,actions/setup-node@v7,actions/upload-artifact@v7,docker/setup-buildx-action@v4;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24enabled. -
README "Publishing to npm" section rewritten to match the actual workflow (it previously claimed an
NPM_TOKEN-based auto-publish that did not exist). -
XsltEngine.namespaceAliasesis now aNamespaceAliasMapkeyed by namespace URI instead of a plain prefix-to-prefix object (internal API; the previous shape never produced correct output). -
XsltEngine.countNumber()was replaced bycountXsltNumber()insrc/xslt/number.js;XsltEngine.formatNumber()andXsltEngine.toRoman()are kept as thin delegating wrappers. -
XsltEngine.resolveUri()delegates tosrc/xslt/uri.js, which also recognises URIs with any scheme (not justhttp:/https:) as absolute. -
system-property('xsl:version')returns the string"1"(previously the number1); XPath converts it for arithmetic and comparisons, so output is unchanged. -
Build targets raised from ES2020/Node 18 to ES2022/Node 20 (the code now uses
Object.hasOwnandArray.prototype.at; browser bundle needs Chrome 92+, Firefox 92+, Safari 15.4+). -
removeParameter()andclearParameters()now restore thexsl:paramdefault of the stylesheet instead of deleting the declaration (which made$namean undefined variable). New engine helperssetParameterValue(),clearParameterValue()andclearParameterValues()back this. -
-f, --formaton the CLI is now an alias of--indentand drives the real serializer instead of the previous naive re-indentation.
Security
- CLI path validation - input and output paths are resolved, canonicalized (
realpathSync, so symbolic links cannot escape), confined to a trusted base directory (the working directory, orXSLT_BASE_DIR) and validated (regular file / existing parent directory, no NUL bytes) before any filesystem access (SonarCloud S8707). Paths outside the base directory are rejected with a hint. - Workflows - every GitHub Action is pinned to a full commit SHA; the publish job installs with
npm ci --ignore-scripts. - Docker - the production image runs as the unprivileged
nodeuser. - js-yaml (transitive via
eslint) - GHSA-5p4m-2wfm-xmqj, vulnerable>= 4.0.0, < 4.3.1. Resolved by upgradingeslintto 10.x, which no longer pulls@eslint/eslintrc/js-yamlat all;npm auditreports 0 vulnerabilities. - brace-expansion - GHSA-mh99-v99m-4gvg / GHSA-rgw5-rvv9-x895 (DoS), resolved via
npm audit fix(now 5.0.9).
Dependencies
eslint^9.0.0->^10.10.0(flat config unchanged;@eslint/jsis now an explicit devDependency because ESLint 10 stopped bundling it).jsdom^25.0.0->^29.1.1,esbuild^0.28.0->^0.28.2,prettier^3.4.0->^3.9.6.- Supported Node.js:
engines.noderaised from>=18.0.0to>=20.19.0(Node.js 18 and 20 are end-of-life; jsdom 29 needs 20.19+). CI matrix is now Node.js 22, 24 and 26; Docker images usenode:26-alpine.