22 * Axe-core Polyfilled Import
33 *
44 * This file ensures the jsdom polyfill runs BEFORE axe-core is imported.
5- * Uses dynamic import to avoid ES module hoisting issues .
5+ * Static imports are evaluated in order, so the polyfill import must come first .
66 *
77 * WHY THIS EXISTS:
88 * axe-core has side effects on import - it expects global `window` and `document` objects
99 * to be available when the module is loaded. In Node.js environments, these don't exist
1010 * by default. This polyfill creates a virtual DOM using JSDOM and sets these globals
1111 * before axe-core is imported.
1212 *
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
13+ * IMPORT ORDER IS CRITICAL:
14+ * 1. jsdom.polyfill.ts is imported first (sets up globalThis.window and globalThis.document)
15+ * 2. axe-core is imported second (now globals exist)
1716 *
1817 * IMPORT CHAIN:
19- * 1. This file (sets up polyfill, then dynamically imports axe-core)
18+ * 1. This file (imports polyfill, then imports axe-core)
2019 * 2. safe-axe-core-import.ts (re-exports for clean imports)
2120 *
2221 * USAGE:
2322 * Do NOT import from this file directly. Use safe-axe-core-import.ts instead.
2423 *
2524 * @see https://github.com/dequelabs/axe-core/issues/3962
2625 */
27- import { JSDOM } from 'jsdom' ;
26+ // CRITICAL: This import MUST come before axe-core import
27+ // It sets up globalThis.window and globalThis.document as side effects
28+ // eslint-disable-next-line import/no-unassigned-import
29+ // Now safe to import axe-core - globals exist due to polyfill above
30+ // This import MUST come after the polyfill import
31+ import axe from 'axe-core' ;
32+ import './jsdom.polyfill.js' ;
2833
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 ) ;
34+ // Re-export axe as default
35+ export default axe ;
3236
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)
37+ // Re-export all types used throughout the codebase
4438export type {
4539 AxeResults ,
4640 NodeResult ,
@@ -50,7 +44,3 @@ export type {
5044 ImpactValue ,
5145 CrossTreeSelector ,
5246} 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