Migration Checker

xslt-migrate-check finds the XSLT a project depends on in the browser, the part that stops working when Chrome 158 removes native XSLT on 17 November 2026. It rates every finding, estimates the effort, and says what to install and which line to add. It reads files only; nothing leaves the machine. It has no dependencies and needs Node.js 20.19 or later.

Without Node.js, the same analysis runs on the website: drop the files, a folder or a zip on the online check; they are read in the browser and not uploaded.

npx xslt-migrate-check .
npx xslt-migrate-check . --html        # also writes xslt-migration-report.html
npx xslt-migrate-check . --fix         # writes migration.patch with the one-line changes

Then prove the switch: xslt-migrate-test, in the same package, runs every stylesheet on the browser's engine and on the library and compares the output.

The report

A sample project with two scripts (one already importing @tradik/xslt-processor), three XML pages with <?xml-stylesheet?>, ten XSLT 1.0 stylesheets (one with document(), one with msxsl:script, one with EXSLT common), one 2.0 and one 3.0 stylesheet:

xslt-migrate-check 0.3.0 — scanned 17 files in ./ (0.0 s)

Found 4 XSLTProcessor usages in 1 file
Found 12 XSL stylesheets (10 × XSLT 1.0, 1 × XSLT 2.0, 1 × XSLT 3.0)
Found 3 XML documents rendered with <?xml-stylesheet?>
Found 1 file already using @tradik/xslt-processor

Chrome 158 Migration Report

Risk: HIGH
  Chrome 158 (17 November 2026) stops running XSLT; these pages break then.

Native XSLTProcessor:      4 usages
xml-stylesheet:            3 files
Stylesheets:               12
Compatible automatically:  13
Manual review:             4

Recommended runtime:       @tradik/xslt-processor + @tradik/xslt3
Estimated migration difficulty: HIGH
  Some stylesheets need real work: 1 with MSXML extensions to rewrite, and 2 in XSLT 2.0/3.0 to test on @tradik/xslt3.

Found 17 XSLT usages

HIGH    public/catalog.xml  Uses <?xml-stylesheet?>
HIGH    public/invoice.xml  Uses <?xml-stylesheet?>
HIGH    public/orders.xml   Uses <?xml-stylesheet?>
HIGH    src/report.js       Uses native XSLTProcessor
HIGH    styles/legacy.xsl   Uses msxsl:script (MSXML); no browser runtime runs it
MEDIUM  styles/latest.xsl   Declares XSLT 3.0; needs @tradik/xslt3
MEDIUM  styles/modern.xsl   Declares XSLT 2.0; needs @tradik/xslt3
MEDIUM  styles/report.xsl   Uses document()
LOW     src/invoice.js      Already loads @tradik/xslt-processor
LOW     styles/nodeset.xsl  XSLT 1.0 with EXSLT common
LOW     styles/simple.xsl   Standard XSLT 1.0
...

The report then lists "What to do" (one numbered step per recommendation, see below) and the detail sections of 0.1.0: every XSLTProcessor line, the DOMParser calls within 20 lines of it (context, never a finding), the XML documents with their href, each stylesheet with its version and features, and the files already migrated.

  • Native XSLTProcessor: lines that use the API in scripts that do not load @tradik/xslt-processor yet.
  • Compatible automatically: LOW findings plus the HIGH ones the one-line migration fixes without touching code: XSLTProcessor in scripts, and <?xml-stylesheet?> documents whose stylesheet (when it is in the project) needs neither XSLT 2.0/3.0 nor MSXML.
  • Manual review: everything else.
  • Estimated migration difficulty: LOW when nothing needs review; MEDIUM when something does, but no stylesheet uses MSXML or XSLT 2.0/3.0; HIGH otherwise. The sentence under it says why.

Ratings

Every finding (one per file) gets a rating and a one-line reason.

RatingWhen
HIGHXSLTProcessor, importStylesheet(, transformToFragment(, transformToDocument( (and setParameter( in the same file) in a script that does not load @tradik/xslt-processor; an XML, RSS, Atom or RDF document with <?xml-stylesheet?> of type text/xsl, application/xslt+xml or application/xml; HTML with type="text/xsl"; msxsl:script, or an MSXML function other than msxsl:node-set
MEDIUMdocument() (the browser runtime fetches each file, same origin only); version="2.0" or "3.0" (needs @tradik/xslt3); EXSLT outside what the library runs: the functions, regular-expressions and random modules, date:format-date, date:parse-date, dyn:map, exsl:document; EXSLT dynamic (dyn:evaluate is off by default); calls into other extension namespaces and extension-element-prefixes bound to them; xsl:include/xsl:import whose file is not in the project; disable-output-escaping when the project calls transformToFragment() (the output is parsed as HTML)
LOWStandard XSLT 1.0, also with EXSLT common, math, sets, strings, dates-and-times (see Conformance) or msxsl:node-set; a stylesheet without a version (runs as 1.0); files that already load @tradik/xslt-processor; stylesheets of a server-side project

msxsl:node-set rates LOW because the library implements it. Calls to the stylesheet's own functions (xsl:function, func:function, the prefix of an msxsl:script) and to W3C namespaces (fn:, xs:, math:, map:) are not extensions.

A project is server-side when no browser code uses XSLT and its package.json depends on @tradik/xslt-processor, xslt-processor, saxon-js, libxslt, xslt3 or xsltproc, or an npm script runs xsltproc; its stylesheets then rate LOW unless they use MSXML.

The project risk is the highest rating (NONE when nothing is found). Since 0.2.0 the levels are NONE, LOW, MEDIUM, HIGH; 0.1.0 rated any stylesheet-only project MEDIUM, 0.2.0 rates standard XSLT 1.0 LOW.

The libxslt conformance corpus (584 stylesheets) rates 499 LOW and 85 MEDIUM, none HIGH.

Recommendations

ProjectStep
XSLTProcessor in JavaScriptnpm install @tradik/xslt-processor, then import "@tradik/xslt-processor/polyfill"; once with a bundler, or the CDN script tag before your scripts
XML pages with <?xml-stylesheet?>Browser compatibility loader: <script xmlns="http://www.w3.org/1999/xhtml" src="https://cdn.jsdelivr.net/npm/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js"></script> as the first child of the document element
XSLT 2.0 / 3.0 stylesheetsnpm install @tradik/xslt-processor @tradik/xslt3 and new XSLTProcessor({ xsltVersion: "auto" }); new code without the W3C API can use @tradik/xslt3 alone
MSXML extensionsNo runtime runs them: msxsl:script becomes templates or xsl:function, msxsl:format-date becomes format-date() (2.0) or EXSLT date:
Stylesheets with no browser usageRender at build time with the xslt CLI (npx xslt data.xml template.xsl -o page.html)

The CDN script tag: <script src="https://cdn.jsdelivr.net/npm/@tradik/xslt-processor@1/dist/xslt-processor.browser.min.js"></script>.

A mixed project gets every step that applies, ordered by the number of files each asks you to change, the "(N files)" of the report. Since 0.3.0 the polyfill step counts only the scripts and pages that call XSLTProcessor (0.2.0 added the stylesheets they run); its explanation names the XSLT 1.0 stylesheets that run unchanged. The texts live in one module (packages/migrate-check/src/texts.js) shared by the terminal report, the HTML report and the website.

HTML report

--html writes xslt-migration-report.html in the current working directory; --html report.html (any name ending in .html or .htm) or --html=path chooses the file. The command prints where it went (on stderr when --json is also given, so stdout stays JSON).

The page has a header (project, date, tool version, risk badge), the summary table, "What to do" with the commands and the three one-line fixes, the findings table (rating, file:line, reason, fix) with a filter by rating and a button that reverses the order, and links to the how-to, this page and the package on npm. It is one file with inline CSS and a few lines of inline script, makes no external requests and uses no web fonts. Colours are the website tokens from the Style Guide, light or dark from prefers-color-scheme; text pairs meet WCAG 2.2 AA and ratings are always written out, never shown by colour alone. Every value from the scan is HTML-escaped. It is rendered from the same analysis object as --json.

Options

OptionEffect
[dir]Directory to scan (default: the current one)
--jsonMachine-readable output, nothing else on stdout
--html [file]Also write the HTML report
--fail-on low|medium|highExit 1 when the risk is at or above the level
--ignore <name>Skip directories with this name; repeatable, * allowed
--fixWrite migration.patch (see Automatic fixes)
--fix --write [--force]Apply the fixes in place
--help, --versionUsage and version

Exit codes: 0 normally, 1 when --fail-on triggers, 2 for a usage error, a directory that does not exist, an HTML file or patch that cannot be written, or --write on a working tree with uncommitted changes.

The JSON object keeps the 0.1.0 keys version, scannedFiles, durationMs, risk, usages, stylesheets, xmlDocuments, migrated, serverSide, needsXslt3, msxml, suggestion, and adds findings (file, line, kind, rating, reason, fix, automatic, issues, details), summary (the numbers of the report) and recommendations (id, title, why, commands, snippet, alternative, runtime, findings). Usages gain method; stylesheets gain exsltModules, unsupportedExslt, msxmlScript, msxmlFunctions, extensionFunctions, extensionNamespaces and includes (with found: true, false, or null for a URL).

Skipped by default: node_modules, .git, dist, build, out, coverage, vendor, .next, .nuxt, .svelte-kit, target, anything named with --ignore, files over 5 MB, and symbolic links.

Automatic fixes and the browser entry

--fix writes the mechanical part of the migration as migration.patch, and xslt-migrate-check/analyze runs the same analysis in a browser: both are in Migration fixes. Comparing the output of your stylesheets on the browser's engine and on this library is xslt-migrate-test.

In a pipeline

- run: npx xslt-migrate-check . --fail-on high

Once every page is migrated, the step keeps a new new XSLTProcessor() without the one-line script from slipping in. --fail-on medium also fails on stylesheets that need review; --fail-on low on any XSLT.

Where it lives

packages/migrate-check in the repository, published to npm as xslt-migrate-check from the same release tag as @tradik/xslt-processor (make publish-migrate-check, see Development).

This page is generated from docs/MIGRATE-CHECK.md in the repository. Corrections are welcome as a pull request to that file.