@@ -10,6 +10,7 @@ const { findReporter } = require( "./find-reporter" );
1010
1111const DEBOUNCE_WATCH_LENGTH = 60 ;
1212const DEBOUNCE_RESTART_LENGTH = 200 - DEBOUNCE_WATCH_LENGTH ;
13+ const os = require ( "os" ) ;
1314
1415const changedPendingPurge = [ ] ;
1516
@@ -20,15 +21,27 @@ async function run( args, options ) {
2021 // Default to non-zero exit code to avoid false positives
2122 process . exitCode = 1 ;
2223
23- const files = utils . getFilesFromArgs ( args ) ;
24-
2524 QUnit = requireQUnit ( ) ;
2625
26+ let globalConfig = { } ;
27+
28+ // TODO: Enable mode where QUnit is not auto-injected, but other setup is
29+ // still done automatically.
30+ if ( global . QUnit && global . QUnit . config ) {
31+ globalConfig = global . QUnit . config ;
32+ }
33+ global . QUnit = QUnit ;
34+
35+ Object . keys ( globalConfig ) . forEach ( function ( key ) {
36+ QUnit . config [ key ] = globalConfig [ key ] ;
37+ } ) ;
38+
2739 if ( options . filter ) {
2840 QUnit . config . filter = options . filter ;
2941 }
3042
3143 const seed = options . seed ;
44+
3245 if ( seed ) {
3346 if ( seed === true ) {
3447 QUnit . config . seed = Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
@@ -39,65 +52,80 @@ async function run( args, options ) {
3952 console . log ( `Running tests with seed: ${ QUnit . config . seed } ` ) ;
4053 }
4154
42- // TODO: Enable mode where QUnit is not auto-injected, but other setup is
43- // still done automatically.
44- global . QUnit = QUnit ;
55+ if ( ! options . noReporter ) {
56+ findReporter ( options . reporter , QUnit . reporters ) . init ( QUnit ) ;
57+ }
4558
46- options . requires . forEach ( requireFromCWD ) ;
59+ if ( ! QUnit . config . isWorker ) {
60+ QUnit . config . maxThreads = os . cpus ( ) . length ;
61+ QUnit . config . workerType = "NodeWorker" ;
62+ QUnit . config . files = args ;
63+
64+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
65+ const nodeWorkerModule = await import ( "../workers/node-worker.mjs" ) ;
66+ const NodeWorker = nodeWorkerModule . default ;
67+
68+ QUnit . WorkerFactory . registerWorkerClass ( NodeWorker ) ;
69+
70+ } else {
71+ options . requires . forEach ( requireFromCWD ) ;
72+
73+ const files = utils . getFilesFromArgs ( args ) ;
74+
75+ for ( let i = 0 ; i < files . length ; i ++ ) {
76+ const filePath = path . resolve ( process . cwd ( ) , files [ i ] ) ;
77+ delete require . cache [ filePath ] ;
78+
79+ // Node.js 12.0.0 has node_module_version=72
80+ // https://nodejs.org/en/download/releases/
81+ const nodeVint = process . config . variables . node_module_version ;
4782
48- findReporter ( options . reporter , QUnit . reporters ) . init ( QUnit ) ;
49-
50- for ( let i = 0 ; i < files . length ; i ++ ) {
51- const filePath = path . resolve ( process . cwd ( ) , files [ i ] ) ;
52- delete require . cache [ filePath ] ;
53-
54- // Node.js 12.0.0 has node_module_version=72
55- // https://nodejs.org/en/download/releases/
56- const nodeVint = process . config . variables . node_module_version ;
57-
58- try {
59-
60- // QUnit supports passing ESM files to the 'qunit' command when used on
61- // Node.js 12 or later. The dynamic import() keyword supports both CommonJS files
62- // (.js, .cjs) and ESM files (.mjs), so we could simply use that unconditionally on
63- // newer Node versions, regardless of the given file path.
64- //
65- // But:
66- // - Node.js 12 emits a confusing "ExperimentalWarning" when using import(),
67- // even if just to load a non-ESM file. So we should try to avoid it on non-ESM.
68- // - This Node.js feature is still considered experimental so to avoid unexpected
69- // breakage we should continue using require(). Consider flipping once stable and/or
70- // as part of QUnit 3.0.
71- // - Plugins and CLI bootstrap scripts may be hooking into require.extensions to modify
72- // or transform code as it gets loaded. For compatibility with that, we should
73- // support that until at least QUnit 3.0.
74- // - File extensions are not sufficient to differentiate between CJS and ESM.
75- // Use of ".mjs" is optional, as a package may configure Node to default to ESM
76- // and optionally use ".cjs" for CJS files.
77- //
78- // https://nodejs.org/docs/v12.7.0/api/modules.html#modules_addenda_the_mjs_extension
79- // https://nodejs.org/docs/v12.7.0/api/esm.html#esm_code_import_code_expressions
80- // https://github.com/qunitjs/qunit/issues/1465
8183 try {
82- require ( filePath ) ;
83- } catch ( e ) {
84- if ( ( e . code === "ERR_REQUIRE_ESM" ||
85- ( e instanceof SyntaxError &&
86- e . message === "Cannot use import statement outside a module" ) ) &&
87- ( ! nodeVint || nodeVint >= 72 ) ) {
88-
89- // filePath is an absolute file path here (per path.resolve above).
90- // On Windows, Node.js enforces that absolute paths via ESM use valid URLs,
91- // e.g. file-protocol) https://github.com/qunitjs/qunit/issues/1667
92- await import ( url . pathToFileURL ( filePath ) ) ; // eslint-disable-line node/no-unsupported-features/es-syntax
93- } else {
94- throw e ;
84+
85+ // QUnit supports passing ESM files to the 'qunit' command when used on
86+ // Node.js 12 or later. The dynamic import() keyword supports both CommonJS files
87+ // (.js, .cjs) and ESM files (.mjs), so we could simply use that unconditionally on
88+ // newer Node versions, regardless of the given file path.
89+ //
90+ // But:
91+ // - Node.js 12 emits a confusing "ExperimentalWarning" when using import(),
92+ // even if just to load a non-ESM file. So we should try to avoid it on non-ESM.
93+ // - This Node.js feature is still considered experimental so to avoid unexpected
94+ // breakage we should continue using require(). Consider flipping once stable and/or
95+ // as part of QUnit 3.0.
96+ // - Plugins and CLI bootstrap scripts may be hooking into require.extensions to modify
97+ // or transform code as it gets loaded. For compatibility with that, we should
98+ // support that until at least QUnit 3.0.
99+ // - File extensions are not sufficient to differentiate between CJS and ESM.
100+ // Use of ".mjs" is optional, as a package may configure Node to default to ESM
101+ // and optionally use ".cjs" for CJS files.
102+ //
103+ // https://nodejs.org/docs/v12.7.0/api/modules.html#modules_addenda_the_mjs_extension
104+ // https://nodejs.org/docs/v12.7.0/api/esm.html#esm_code_import_code_expressions
105+ // https://github.com/qunitjs/qunit/issues/1465
106+ try {
107+ require ( filePath ) ;
108+ } catch ( e ) {
109+ if ( ( e . code === "ERR_REQUIRE_ESM" ||
110+ ( e instanceof SyntaxError &&
111+ e . message === "Cannot use import statement outside a module" ) ) &&
112+ ( ! nodeVint || nodeVint >= 72 ) ) {
113+
114+ // filePath is an absolute file path here (per path.resolve above).
115+ // On Windows, Node.js enforces that absolute paths via ESM use valid URLs,
116+ // e.g. file-protocol) https://github.com/qunitjs/qunit/issues/1667
117+ await import ( url . pathToFileURL ( filePath ) ) ; // eslint-disable-line node/no-unsupported-features/es-syntax
118+ } else {
119+ throw e ;
120+ }
95121 }
122+ } catch ( e ) {
123+ const error = new Error (
124+ `Failed to load file ${ files [ i ] } \n${ e . name } : ${ e . message } `
125+ ) ;
126+ error . stack = e . stack ;
127+ QUnit . onUncaughtException ( error ) ;
96128 }
97- } catch ( e ) {
98- const error = new Error ( `Failed to load file ${ files [ i ] } \n${ e . name } : ${ e . message } ` ) ;
99- error . stack = e . stack ;
100- QUnit . onUncaughtException ( error ) ;
101129 }
102130 }
103131
@@ -141,7 +169,9 @@ async function run( args, options ) {
141169 }
142170 } ) ;
143171
144- QUnit . start ( ) ;
172+ if ( ! QUnit . config . isWorker ) {
173+ QUnit . start ( ) ;
174+ }
145175}
146176
147177run . restart = function ( args ) {
0 commit comments