Vendor axe-core, drop the Skypack CDN dependency#14677
Conversation
Pins AXE_JS in configuration and adds a download block to update-html-dependencies.ts (axe.min.js plus the MPL-2.0 LICENSE and LICENSE-3RD-PARTY.txt, which must ship alongside it). Files are vendored byte-for-byte from npm via unpkg; the /*! banner in axe.min.js carries the required Deque copyright and MPL notice.
Injects axe.min.js as a classic script ahead of axe-check.js (a deferred module), so window.axe is set before the checker runs. Replaces the pinned Skypack dynamic import, making the axe overlay work offline and removing the CDN request from every reader's browser. Same axe version (4.10.3), identical scan results. Also waits for the window load event before the first scan. Deferred modules run before DOMContentLoaded, but the dashboard layout runs on DOMContentLoaded and keeps <html> hidden until then, so an immediate scan sees only hidden elements and reports nothing. The Skypack import's network latency masked this pre-existing race; removing it exposed the bug (8 dashboard Playwright tests).
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This will be a really nice feature to have! |
The overlay is injected after the page scan, so it never audits itself and nothing else guards against new violations as the report UI grows. Scan it with the vendored axe-core build the page already loaded (window.axe), on the long-report page where the overlay is scrollable. Requires the vendored axe-core from #14677, which this branch is now stacked on. Verified the test fails with scrollable-region-focusable when the tabindex fix is removed.
There was a problem hiding this comment.
Looks good.
Some ideas to not forget about quarto-web PR
- Add to list of dependencies: https://quarto.org/license.html#dependencies and https://quarto.org/docs/faq/#what-are-quartos-dependencies - but probably the later should link to the former
|
Good catch, PR for docs at quarto-dev/quarto-web#2106 |
|
Cool ! Is this ready to merge ? Do we want this in v1.10 or should we release v1.10 and make this new feature for v1.11 first pre-release ? |
|
@cderv Ready to merge! Most of the PRs that I have queued up for after this are improving/fixing the The bigger site audit |
The overlay is injected after the page scan, so it never audits itself and nothing else guards against new violations as the report UI grows. Scan it with the vendored axe-core build the page already loaded (window.axe), on the long-report page where the overlay is scrollable. Requires the vendored axe-core from #14677, which this branch is now stacked on. Verified the test fails with scrollable-region-focusable when the tabindex fix is removed.
Description
Vendors axe-core as a bundled HTML resource, so the
axe:accessibility option stops loading it from the Skypack CDN in the reader's browser.What changes
configurationpinsAXE_JS=4.10.3— exactly the version the CDN import already pinned, so this PR changes where axe comes from, not which axe runs. Scan results are identical. A follow-up PR will bump to current axe-core (4.12.1), keeping "different source" and "different results" in separate diffs.update-html-dependencies.tsdownloadsaxe.min.jsplus axe-core'sLICENSE(MPL-2.0) andLICENSE-3RD-PARTY.txtfrom the npm package via unpkg —https://unpkg.com/axe-core@4.10.3/<file>, the sameupdateUnpkgDependencyhelper used for anchor-js, popper, and tippy — intosrc/resources/formats/html/axe/. To verify the committed bytes,curlthose three URLs and compare (or rerunquarto-bld update-html-dependencies). The files are vendored byte-for-byte: the/*!banner inaxe.min.jscarries the Deque copyright/MPL notice that must ship with every copy. Per-directory LICENSE files follow the existing convention (src/resources/formats/pdf/pdfjs/LICENSE).format-html-axe.tsinjectsaxe.min.jsas a classic script ahead of theaxe-check.jsmodule, sowindow.axeis set before the checker runs.axe-check.jsreplaces the pinned Skypack dynamic import withwindow.axe.Why
cdn.skypack.devfrom every reader's browser.quarto axescanner and the overlay will provably run the same axe version from the same file.A race this exposed (also fixed here):
quarto-dashboard.jskeeps<html>hidden until itsDOMContentLoadedhandler runs, but deferred modules execute before that event — so with the Skypack network latency gone, the first scan ran against a fully hidden document and dashboards reported no violations.axe-check.jsnow waits for the windowloadevent before the first scan. The race predates this PR (a sufficiently fast CDN response could have triggered it); vendoring just made it deterministic.Size: ~573 KB committed across three files (541 KB of it
axe.min.js; ~145 KB gzipped on the wire). Precedent: pdf.js, MathJax, and reveal.js are vendored the same way.Out of scope: any behavior change to scanning, report rendering, or options (#14607
standard, sort order, overlay polish, #14668 rescan) — those are follow-up PRs. This PR produces identical scan results, just sourced locally.Verification
axe-accessibility.spec.ts,axe-code-line-numbers.spec.ts): 27/27 pass on chromium.quarto-bld update-html-dependenciesrerun reproduces the committed axe files byte-for-byte.axe-conformance.test.tsunit tests: 9/9 pass.Checklist
I have (if applicable):
AI-assisted PR