Skip to content

Commit 7854b62

Browse files
committed
refactor: wip
1 parent 52da218 commit 7854b62

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

packages/plugin-axe/src/lib/axe-core-polyfilled.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,45 @@
22
* Axe-core Polyfilled Import
33
*
44
* This file ensures the jsdom polyfill runs BEFORE axe-core is imported.
5-
* Due to ES module import hoisting, we must import the polyfill explicitly
6-
* at the top of this file, then import axe-core. This guarantees the correct
7-
* execution order: polyfill setup → axe-core import.
5+
* Uses dynamic import to avoid ES module hoisting issues.
6+
*
7+
* WHY THIS EXISTS:
8+
* axe-core has side effects on import - it expects global `window` and `document` objects
9+
* to be available when the module is loaded. In Node.js environments, these don't exist
10+
* by default. This polyfill creates a virtual DOM using JSDOM and sets these globals
11+
* before axe-core is imported.
12+
*
13+
* HOW IT WORKS:
14+
* 1. Top-level code sets up JSDOM polyfill (runs immediately)
15+
* 2. Axe-core is imported dynamically (runs after polyfill)
16+
* 3. Module exports a promise that resolves to axe-core
817
*
918
* IMPORT CHAIN:
10-
* 1. jsdom.polyfill.ts (sets globalThis.window and globalThis.document)
11-
* 2. This file (imports polyfill, then imports axe-core)
12-
* 3. safe-axe-core-import.ts (re-exports for clean imports)
19+
* 1. This file (sets up polyfill, then dynamically imports axe-core)
20+
* 2. safe-axe-core-import.ts (re-exports for clean imports)
1321
*
1422
* USAGE:
1523
* Do NOT import from this file directly. Use safe-axe-core-import.ts instead.
24+
*
25+
* @see https://github.com/dequelabs/axe-core/issues/3962
1626
*/
17-
// Import polyfill FIRST to ensure globals are set before axe-core loads
18-
// Now safe to import axe-core - globals exist due to polyfill import above
19-
import axe from 'axe-core';
20-
// eslint-disable-next-line import/no-unassigned-import
21-
import './jsdom.polyfill.js';
27+
import { JSDOM } from 'jsdom';
2228

23-
// Re-export axe default and all types used throughout the codebase
24-
export default axe;
29+
// Polyfill setup - runs immediately before any axe-core code
30+
const html = `<!DOCTYPE html>\n<html></html>`;
31+
const { window: jsdomWindow } = new JSDOM(html);
2532

33+
// Set globals for axe-core compatibility
34+
// eslint-disable-next-line functional/immutable-data
35+
globalThis.window = jsdomWindow as unknown as Window & typeof globalThis;
36+
// eslint-disable-next-line functional/immutable-data
37+
globalThis.document = jsdomWindow.document;
38+
39+
// Dynamic import ensures polyfill runs first
40+
// This cannot be a top-level await, so we export the promise
41+
const axePromise = import('axe-core');
42+
43+
// Re-export types (these are compile-time only, no runtime impact)
2644
export type {
2745
AxeResults,
2846
NodeResult,
@@ -32,3 +50,7 @@ export type {
3250
ImpactValue,
3351
CrossTreeSelector,
3452
} from 'axe-core';
53+
54+
// Export the axe instance synchronously by awaiting at the top level
55+
// Top-level await is supported in ES modules
56+
export default (await axePromise).default;

0 commit comments

Comments
 (0)