ava 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/entrypoints/{eslint-plugin-helper.cjs → eslint-plugin-helper.js} +10 -13
  2. package/entrypoints/{internal.d.mts → internal.d.ts} +1 -1
  3. package/entrypoints/{main.d.mts → main.d.ts} +5 -5
  4. package/entrypoints/{main.mjs → main.js} +1 -1
  5. package/entrypoints/{plugin.d.cts → plugin.d.ts} +2 -2
  6. package/entrypoints/plugin.js +1 -0
  7. package/index.d.ts +2 -2
  8. package/lib/api-event-iterator.js +2 -2
  9. package/lib/api.js +6 -6
  10. package/lib/assert.js +6 -5
  11. package/lib/cli.js +7 -27
  12. package/lib/create-chain.js +68 -25
  13. package/lib/extensions.js +5 -7
  14. package/lib/fork.js +3 -3
  15. package/lib/{glob-helpers.cjs → glob-helpers.js} +16 -30
  16. package/lib/globs.js +3 -3
  17. package/lib/{ipc-flow-control.cjs → ipc-flow-control.js} +1 -4
  18. package/lib/load-config.js +2 -3
  19. package/lib/{now-and-timers.cjs → now-and-timers.js} +11 -8
  20. package/lib/pkg.js +1 -0
  21. package/lib/plugin-support/shared-worker-loader.js +2 -2
  22. package/lib/plugin-support/shared-workers.js +3 -3
  23. package/lib/provider-manager.js +6 -5
  24. package/lib/reporters/default.js +1 -1
  25. package/lib/reporters/improper-usage-messages.js +1 -1
  26. package/lib/reporters/tap.js +10 -4
  27. package/lib/runner.js +5 -5
  28. package/lib/scheduler.js +1 -1
  29. package/lib/snapshot-manager.js +2 -2
  30. package/lib/test.js +15 -8
  31. package/lib/watcher.js +10 -18
  32. package/lib/worker/base.js +17 -41
  33. package/lib/worker/{channel.cjs → channel.js} +21 -25
  34. package/lib/worker/completion-handlers.js +3 -3
  35. package/lib/worker/{guard-environment.cjs → guard-environment.js} +4 -5
  36. package/lib/worker/line-numbers.js +3 -7
  37. package/lib/worker/main.js +11 -0
  38. package/lib/worker/{options.cjs → options.js} +4 -5
  39. package/lib/worker/{plugin.cjs → plugin.js} +8 -10
  40. package/lib/worker/state.js +5 -0
  41. package/lib/worker/utils.js +5 -0
  42. package/package.json +28 -36
  43. package/plugin.d.ts +1 -1
  44. package/types/{test-fn.d.cts → test-fn.d.ts} +24 -3
  45. package/types/{try-fn.d.cts → try-fn.d.ts} +1 -2
  46. package/entrypoints/main.cjs +0 -2
  47. package/entrypoints/main.d.cts +0 -12
  48. package/entrypoints/plugin.cjs +0 -2
  49. package/entrypoints/plugin.d.mts +0 -6
  50. package/entrypoints/plugin.mjs +0 -4
  51. package/lib/module-types.js +0 -85
  52. package/lib/pkg.cjs +0 -2
  53. package/lib/slash.cjs +0 -36
  54. package/lib/worker/main.cjs +0 -12
  55. package/lib/worker/state.cjs +0 -6
  56. package/lib/worker/utils.cjs +0 -6
  57. /package/entrypoints/{cli.mjs → cli.js} +0 -0
  58. /package/types/{assertions.d.cts → assertions.d.ts} +0 -0
  59. /package/types/{shared-worker.d.cts → shared-worker.d.ts} +0 -0
  60. /package/types/{state-change-events.d.cts → state-change-events.d.ts} +0 -0
  61. /package/types/{subscribable.d.cts → subscribable.d.ts} +0 -0
@@ -2,7 +2,7 @@ import {EventEmitter, on} from 'node:events';
2
2
  import process from 'node:process';
3
3
  import {workerData, parentPort, threadId} from 'node:worker_threads';
4
4
 
5
- import pkg from '../pkg.cjs';
5
+ import pkg from '../pkg.js';
6
6
 
7
7
  // Used to forward messages received over the `parentPort` and any direct ports
8
8
  // to test workers. Every subscription adds a listener, so do not enforce any
@@ -199,7 +199,7 @@ try {
199
199
 
200
200
  // Run possibly asynchronous release functions serially, in reverse
201
201
  // order. Any error will crash the worker.
202
- for await (const fn of [...teardownFns].reverse()) {
202
+ for await (const fn of [...teardownFns].toReversed()) {
203
203
  await fn();
204
204
  }
205
205
 
@@ -84,7 +84,7 @@ export async function observeWorkerProcess(fork, runStatus) {
84
84
  }
85
85
  };
86
86
 
87
- fork.promise.finally(() => { // eslint-disable-line promise/prefer-await-to-then
87
+ fork.promise.finally(() => {
88
88
  removeAllInstances();
89
89
  });
90
90
 
@@ -99,7 +99,7 @@ export async function observeWorkerProcess(fork, runStatus) {
99
99
  }
100
100
  };
101
101
 
102
- launched.statePromises.error.then(error => { // eslint-disable-line promise/prefer-await-to-then
102
+ launched.statePromises.error.then(error => {
103
103
  launched.worker.off('message', handleWorkerMessage);
104
104
  removeAllInstances();
105
105
  runStatus.emitStateChange({type: 'shared-worker-error', err: serializeError(error)});
@@ -118,7 +118,7 @@ export async function observeWorkerProcess(fork, runStatus) {
118
118
  port,
119
119
  }, [port]);
120
120
 
121
- fork.promise.finally(() => { // eslint-disable-line promise/prefer-await-to-then
121
+ fork.promise.finally(() => {
122
122
  launched.worker.postMessage({
123
123
  type: 'deregister-test-worker',
124
124
  id: fork.threadId,
@@ -1,14 +1,15 @@
1
1
  import * as globs from './globs.js';
2
- import pkg from './pkg.cjs';
2
+ import pkg from './pkg.js';
3
3
 
4
+ // Provides an integer representation of the protocol level. This is internal to a particular AVA installation, and
5
+ // allows other parts of AVA to assert minimum protocol levels for certain features without having to hardcode
6
+ // identifier strings. Integer values can be reused across protocols when older identifiers are removed.
4
7
  export const levels = {
5
- // As the protocol changes, comparing levels by integer allows AVA to be
6
- // compatible with different versions.
7
- ava6: 1,
8
+ ava8: 1,
8
9
  };
9
10
 
10
11
  const levelsByProtocol = Object.assign(Object.create(null), {
11
- 'ava-6': levels.ava6,
12
+ 'ava-8': levels.ava8,
12
13
  });
13
14
 
14
15
  async function load(providerModule, projectDir, selectProtocol = () => true) {
@@ -148,7 +148,7 @@ export default class Reporter {
148
148
  this.prefixTitle = (testFile, title) => prefixTitle(this.extensions, plan.filePathPrefix, testFile, title);
149
149
  }
150
150
 
151
- this.removePreviousListener = plan.status.on('stateChange', evt => {
151
+ this.removePreviousListener = plan.status.on('stateChange', ({data: evt}) => {
152
152
  this.consumeStateChange(evt);
153
153
  });
154
154
 
@@ -1,5 +1,5 @@
1
1
  import {chalk} from '../chalk.js';
2
- import pkg from '../pkg.cjs';
2
+ import pkg from '../pkg.js';
3
3
 
4
4
  export default function buildMessage(improperUsage) {
5
5
  if (!improperUsage) {
@@ -24,7 +24,13 @@ function dumpError({
24
24
  };
25
25
  }
26
26
 
27
- originalError.name = name; // Restore the original name.
27
+ if (originalError && Object.getOwnPropertyDescriptor(originalError, 'name')?.writable !== false) {
28
+ try {
29
+ originalError.name = name; // Restore the original name.
30
+ } catch {
31
+ // Ignore
32
+ }
33
+ }
28
34
 
29
35
  if (type === 'ava') {
30
36
  if (assertion) {
@@ -46,8 +52,6 @@ function dumpError({
46
52
 
47
53
  export default class TapReporter {
48
54
  constructor(options) {
49
- this.i = 0;
50
-
51
55
  this.extensions = options.extensions;
52
56
  this.stdStream = options.stdStream;
53
57
  this.reportStream = options.reportStream;
@@ -65,7 +69,7 @@ export default class TapReporter {
65
69
  this.prefixTitle = (testFile, title) => prefixTitle(this.extensions, plan.filePathPrefix, testFile, title);
66
70
  }
67
71
 
68
- plan.status.on('stateChange', evt => this.consumeStateChange(evt));
72
+ plan.status.on('stateChange', ({data: evt}) => this.consumeStateChange(evt));
69
73
 
70
74
  this.reportStream.write(supertap.start() + os.EOL);
71
75
  }
@@ -259,4 +263,6 @@ export default class TapReporter {
259
263
  }
260
264
  }
261
265
  }
266
+
267
+ i = 0;
262
268
  }
package/lib/runner.js CHANGED
@@ -10,7 +10,7 @@ import parseTestArgs from './parse-test-args.js';
10
10
  import serializeError from './serialize-error.js';
11
11
  import {load as loadSnapshots, determineSnapshotDir} from './snapshot-manager.js';
12
12
  import Runnable from './test.js';
13
- import {waitForReady} from './worker/state.cjs';
13
+ import {waitForReady} from './worker/state.js';
14
14
 
15
15
  const makeFileURL = file => file.startsWith('file://') ? file : pathToFileURL(file).toString();
16
16
 
@@ -254,7 +254,7 @@ export default class Runner extends Emittery {
254
254
  let waitForSerial = Promise.resolve();
255
255
  await runnables.reduce((previous, runnable) => { // eslint-disable-line unicorn/no-array-reduce
256
256
  if (runnable.metadata.serial || this.serial) {
257
- waitForSerial = previous.then(() => // eslint-disable-line promise/prefer-await-to-then
257
+ waitForSerial = previous.then(() =>
258
258
  // Serial runnables run as long as there was no previous failure, unless
259
259
  // the runnable should always be run.
260
260
  (allPassed || runnable.metadata.always) && runAndStoreResult(runnable));
@@ -263,7 +263,7 @@ export default class Runner extends Emittery {
263
263
 
264
264
  return Promise.all([
265
265
  previous,
266
- waitForSerial.then(() => // eslint-disable-line promise/prefer-await-to-then
266
+ waitForSerial.then(() =>
267
267
  // Concurrent runnables are kicked off after the previous serial
268
268
  // runnables have completed, as long as there was no previous failure
269
269
  // (or if the runnable should always be run). One concurrent runnable's
@@ -476,7 +476,7 @@ export default class Runner extends Emittery {
476
476
 
477
477
  // Note that the hooks and tests always begin running asynchronously.
478
478
  const beforePromise = this.runHooks(this.tasks.before, contextRef);
479
- const serialPromise = beforePromise.then(beforeHooksOk => { // eslint-disable-line promise/prefer-await-to-then
479
+ const serialPromise = beforePromise.then(beforeHooksOk => {
480
480
  // Don't run tests if a `before` hook failed.
481
481
  if (!beforeHooksOk) {
482
482
  return false;
@@ -498,7 +498,7 @@ export default class Runner extends Emittery {
498
498
  return this.runTest(task, contextRef.copy());
499
499
  }, true);
500
500
  });
501
- const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => { // eslint-disable-line promise/prefer-await-to-then
501
+ const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => {
502
502
  // Don't run tests if a `before` hook failed, or if `failFast` is enabled
503
503
  // and a previous serial test failed.
504
504
  if (!beforeHooksOk || (!serialOk && this.failFast)) {
package/lib/scheduler.js CHANGED
@@ -45,7 +45,7 @@ const scheduler = {
45
45
  return selectedFiles;
46
46
  }
47
47
 
48
- return [...selectedFiles].sort((f, s) => {
48
+ return selectedFiles.toSorted((f, s) => {
49
49
  if (failedTestFiles.includes(f) && failedTestFiles.includes(s)) {
50
50
  return 0;
51
51
  }
@@ -10,10 +10,10 @@ import cbor from 'cbor';
10
10
  import concordance from 'concordance';
11
11
  import indentString from 'indent-string';
12
12
  import memoize from 'memoize';
13
+ import slash from 'slash';
13
14
  import writeFileAtomic from 'write-file-atomic';
14
15
 
15
16
  import {snapshotManager as concordanceOptions} from './concordance-options.js';
16
- import slash from './slash.cjs';
17
17
 
18
18
  // Increment if encoding layout or Concordance serialization versions change. Previous AVA versions will not be able to
19
19
  // decode buffers generated by a newer version, so changing this value will require a major version bump of AVA itself.
@@ -160,7 +160,7 @@ class BufferBuilder {
160
160
  }
161
161
 
162
162
  function sortBlocks(blocksByTitle, blockIndices) {
163
- return [...blocksByTitle].sort(([aTitle], [bTitle]) => {
163
+ return [...blocksByTitle].toSorted(([aTitle], [bTitle]) => {
164
164
  const a = blockIndices.get(aTitle);
165
165
  const b = blockIndices.get(bTitle);
166
166
 
package/lib/test.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  AssertionError, Assertions, checkAssertionMessage, getAssertionStack,
7
7
  } from './assert.js';
8
8
  import concordanceOptions from './concordance-options.js';
9
- import nowAndTimers from './now-and-timers.cjs';
9
+ import * as nowAndTimers from './now-and-timers.js';
10
10
  import parseTestArgs from './parse-test-args.js';
11
11
 
12
12
  function isExternalAssertError(error) {
@@ -43,8 +43,8 @@ class ExecutionContext extends Assertions {
43
43
  test.countPassedAssertion();
44
44
  return true;
45
45
  },
46
- pending(promise) {
47
- test.addPendingAssertion(promise);
46
+ pending(promise, assertionStack) {
47
+ test.addPendingAssertion(promise, assertionStack);
48
48
  },
49
49
  fail(error) {
50
50
  return test.addFailedAssertion(error);
@@ -293,6 +293,7 @@ export default class Test {
293
293
  this.finishDueToTimeout = null;
294
294
  this.finishing = false;
295
295
  this.pendingAssertionCount = 0;
296
+ this.pendingAssertionMetadata = new Set();
296
297
  this.pendingAttemptCount = 0;
297
298
  this.planCount = null;
298
299
  this.startedAt = 0;
@@ -321,7 +322,7 @@ export default class Test {
321
322
  this.logs.push(text);
322
323
  }
323
324
 
324
- async addPendingAssertion(promise) {
325
+ async addPendingAssertion(promise, assertionStack) {
325
326
  if (this.finishing) {
326
327
  this.saveFirstError(new Error('Assertion started, but test has already finished'));
327
328
  }
@@ -332,6 +333,8 @@ export default class Test {
332
333
 
333
334
  this.assertCount++;
334
335
  this.pendingAssertionCount++;
336
+ const metadata = {assertionStack};
337
+ this.pendingAssertionMetadata.add(metadata);
335
338
  this.refreshTimeout();
336
339
 
337
340
  try {
@@ -340,6 +343,7 @@ export default class Test {
340
343
  // Ignore errors.
341
344
  } finally {
342
345
  this.pendingAssertionCount--;
346
+ this.pendingAssertionMetadata.delete(metadata);
343
347
  this.refreshTimeout();
344
348
  }
345
349
  }
@@ -474,7 +478,7 @@ export default class Test {
474
478
  }
475
479
 
476
480
  async runTeardowns() {
477
- const teardowns = [...this.teardowns].reverse();
481
+ const teardowns = this.teardowns.toReversed();
478
482
 
479
483
  for (const teardown of teardowns) {
480
484
  try {
@@ -505,7 +509,10 @@ export default class Test {
505
509
  }
506
510
 
507
511
  if (this.pendingAssertionCount > 0) {
508
- this.saveFirstError(new Error('Test finished, but an assertion is still pending'));
512
+ const [first] = this.pendingAssertionMetadata;
513
+ this.saveFirstError(new AssertionError('Test finished, but an assertion was not awaited', {
514
+ assertionStack: first?.assertionStack ?? '',
515
+ }));
509
516
  return;
510
517
  }
511
518
 
@@ -590,7 +597,7 @@ export default class Test {
590
597
  };
591
598
 
592
599
  promise
593
- .catch(error => { // eslint-disable-line promise/prefer-await-to-then
600
+ .catch(error => {
594
601
  if (this.testFailure !== null && error === this.testFailure) {
595
602
  return;
596
603
  }
@@ -607,7 +614,7 @@ export default class Test {
607
614
  }));
608
615
  }
609
616
  })
610
- .then(() => resolve(this.finish())); // eslint-disable-line promise/prefer-await-to-then
617
+ .then(() => resolve(this.finish()));
611
618
  });
612
619
  }
613
620
 
package/lib/watcher.js CHANGED
@@ -12,7 +12,6 @@ import {
12
12
  applyTestFileFilter, classify, buildIgnoreMatcher, findTests,
13
13
  normalizePattern,
14
14
  } from './globs.js';
15
- import {levels as providerLevels} from './provider-manager.js';
16
15
 
17
16
  const debug = createDebug('ava:watcher');
18
17
 
@@ -158,7 +157,6 @@ const promptForMatchPattern = async (reporter, lineReader, currentPattern) => {
158
157
  };
159
158
 
160
159
  export async function start({api, filter, globs, projectDir, providers, reporter, stdin, signal}) {
161
- providers = providers.filter(({level}) => level >= providerLevels.ava6);
162
160
  for await (const {files, testFileSelector, ...runtimeOptions} of plan({
163
161
  api,
164
162
  filter,
@@ -210,7 +208,7 @@ async function * plan({
210
208
  };
211
209
 
212
210
  // Begin a file trace in the background.
213
- fileTracer.update(findTests(cwdAndGlobs).then(testFiles => testFiles.map(path => ({ // eslint-disable-line promise/prefer-await-to-then
211
+ fileTracer.update(findTests(cwdAndGlobs).then(testFiles => testFiles.map(path => ({
214
212
  path: nodePath.relative(projectDir, path),
215
213
  isTest: true,
216
214
  exists: true,
@@ -231,8 +229,8 @@ async function * plan({
231
229
  };
232
230
 
233
231
  // Observe all test runs.
234
- api.on('run', ({status}) => {
235
- status.on('stateChange', evt => {
232
+ api.on('run', ({data: {status}}) => {
233
+ status.on('stateChange', ({data: evt}) => {
236
234
  switch (evt.type) {
237
235
  case 'accessed-snapshots': {
238
236
  fileTracer.addDependency(nodePath.relative(projectDir, evt.testFile), nodePath.relative(projectDir, evt.filename));
@@ -258,8 +256,11 @@ async function * plan({
258
256
  case 'uncaught-exception':
259
257
  case 'unhandled-rejection':
260
258
  case 'worker-failed': {
261
- const path = nodePath.relative(projectDir, evt.testFile);
262
- failureCounts.set(path, 1 + (failureCounts.get(path) ?? 0));
259
+ if (evt.testFile) {
260
+ const path = nodePath.relative(projectDir, evt.testFile);
261
+ failureCounts.set(path, 1 + (failureCounts.get(path) ?? 0));
262
+ }
263
+
263
264
  break;
264
265
  }
265
266
 
@@ -425,7 +426,7 @@ async function * plan({
425
426
  // If the file tracer is still analyzing dependencies, wait for that to
426
427
  // complete.
427
428
  if (fileTracer.busy !== null) {
428
- fileTracer.busy.then(() => debounce.refresh()); // eslint-disable-line promise/prefer-await-to-then
429
+ fileTracer.busy.then(() => debounce.refresh());
429
430
  takeCoverageForSelfTests?.();
430
431
  return;
431
432
  }
@@ -707,15 +708,10 @@ class FileTracer {
707
708
  #base;
708
709
  #cache = Object.create(null);
709
710
  #pendingTrace = null;
710
- #updateRunning;
711
- #signalUpdateRunning;
712
711
  #tree = new Tree();
713
712
 
714
713
  constructor({base}) {
715
714
  this.#base = base;
716
- this.#updateRunning = new Promise(resolve => {
717
- this.#signalUpdateRunning = resolve;
718
- });
719
715
  }
720
716
 
721
717
  get busy() {
@@ -761,12 +757,9 @@ class FileTracer {
761
757
  }
762
758
 
763
759
  update(changes) {
764
- const current = this.#update(changes).finally(() => { // eslint-disable-line promise/prefer-await-to-then
760
+ const current = this.#update(changes).finally(() => {
765
761
  if (this.#pendingTrace === current) {
766
762
  this.#pendingTrace = null;
767
- this.#updateRunning = new Promise(resolve => {
768
- this.#signalUpdateRunning = resolve;
769
- });
770
763
  }
771
764
  });
772
765
 
@@ -775,7 +768,6 @@ class FileTracer {
775
768
 
776
769
  async #update(changes) {
777
770
  await this.#pendingTrace; // Guard against race conditions.
778
- this.#signalUpdateRunning();
779
771
 
780
772
  let reuseCache = true;
781
773
  const knownTestFiles = new Set();
@@ -1,5 +1,4 @@
1
1
  import {mkdir} from 'node:fs/promises';
2
- import {createRequire} from 'node:module';
3
2
  import path from 'node:path';
4
3
  import process from 'node:process';
5
4
  import {pathToFileURL} from 'node:url';
@@ -9,17 +8,17 @@ import setUpCurrentlyUnhandled from 'currently-unhandled';
9
8
  import writeFileAtomic from 'write-file-atomic';
10
9
 
11
10
  import {set as setChalk} from '../chalk.js';
12
- import nowAndTimers from '../now-and-timers.cjs';
11
+ import {setImmediate} from '../now-and-timers.js';
13
12
  import providerManager from '../provider-manager.js';
14
13
  import Runner from '../runner.js';
15
14
  import serializeError from '../serialize-error.js';
16
15
 
17
- import channel from './channel.cjs';
16
+ import * as channel from './channel.js';
18
17
  import {runCompletionHandlers} from './completion-handlers.js';
19
18
  import lineNumberSelection from './line-numbers.js';
20
- import {set as setOptions} from './options.cjs';
21
- import {flags, refs, sharedWorkerTeardowns} from './state.cjs';
22
- import {isRunningInThread, isRunningInChildProcess} from './utils.cjs';
19
+ import {set as setOptions} from './options.js';
20
+ import {flags, refs, sharedWorkerTeardowns} from './state.js';
21
+ import {isRunningInThread, isRunningInChildProcess} from './utils.js';
23
22
 
24
23
  const currentlyUnhandled = setUpCurrentlyUnhandled();
25
24
  let runner;
@@ -88,14 +87,14 @@ const run = async options => {
88
87
 
89
88
  refs.runnerChain = runner.chain;
90
89
 
91
- channel.peerFailed.then(() => { // eslint-disable-line promise/prefer-await-to-then
90
+ channel.peerFailed.then(() => {
92
91
  runner.interrupt();
93
92
  });
94
93
 
95
- runner.on('accessed-snapshots', filename => channel.send({type: 'accessed-snapshots', filename}));
96
- runner.on('stateChange', state => channel.send(state));
94
+ runner.on('accessed-snapshots', ({data: filename}) => channel.send({type: 'accessed-snapshots', filename}));
95
+ runner.on('stateChange', ({data: state}) => channel.send(state));
97
96
 
98
- runner.on('error', error => {
97
+ runner.on('error', ({data: error}) => {
99
98
  channel.send({type: 'internal-error', err: serializeError(error)});
100
99
  forceExit();
101
100
  });
@@ -129,7 +128,7 @@ const run = async options => {
129
128
  await channel.workerFreed;
130
129
  channel.unref();
131
130
 
132
- nowAndTimers.setImmediate(() => {
131
+ setImmediate(() => {
133
132
  const unhandled = currentlyUnhandled();
134
133
  if (unhandled.length === 0) {
135
134
  return avaIsDone();
@@ -151,10 +150,6 @@ const run = async options => {
151
150
  // Store value to prevent required modules from modifying it.
152
151
  const testPath = options.file;
153
152
 
154
- const extensionsToLoadAsModules = Object.entries(options.moduleTypes)
155
- .filter(([, type]) => type === 'module')
156
- .map(([extension]) => extension);
157
-
158
153
  // Install before processing options.require, so if helpers are added to the
159
154
  // require configuration the *compiled* helper will be loaded.
160
155
  const {projectDir, providerStates = []} = options;
@@ -162,26 +157,18 @@ const run = async options => {
162
157
  await Promise.all(providerStates.map(async ({type, state, protocol}) => {
163
158
  if (type === 'typescript') {
164
159
  const provider = await providerManager.typescript(projectDir, {protocol});
165
- providers.push(provider.worker({extensionsToLoadAsModules, state}));
160
+ providers.push(provider.worker({state}));
166
161
  }
167
162
  }));
168
163
 
169
- const require = createRequire(import.meta.url);
170
164
  const load = async ref => {
171
165
  for (const provider of providers) {
172
166
  if (provider.canLoad(ref)) {
173
- return provider.load(ref, {requireFn: require});
174
- }
175
- }
176
-
177
- for (const extension of extensionsToLoadAsModules) {
178
- if (ref.endsWith(`.${extension}`)) {
179
- return import(pathToFileURL(ref));
167
+ return provider.load(ref);
180
168
  }
181
169
  }
182
170
 
183
- // We still support require() since it's more easily monkey-patched.
184
- return require(ref);
171
+ return import(pathToFileURL(ref));
185
172
  };
186
173
 
187
174
  const loadRequiredModule = async ref => {
@@ -189,21 +176,14 @@ const run = async options => {
189
176
  // dependency.
190
177
  for (const provider of providers) {
191
178
  if (provider.canLoad(ref)) {
192
- return provider.load(ref, {requireFn: require});
179
+ return provider.load(ref, {});
193
180
  }
194
181
  }
195
182
 
196
183
  // Try to load the module as a file, relative to the project directory.
197
- // Match load() behavior.
198
184
  const fullPath = path.resolve(projectDir, ref);
199
185
  try {
200
- for (const extension of extensionsToLoadAsModules) {
201
- if (fullPath.endsWith(`.${extension}`)) {
202
- return await import(pathToFileURL(fullPath)); // eslint-disable-line no-await-in-loop
203
- }
204
- }
205
-
206
- return require(fullPath);
186
+ return await import(pathToFileURL(fullPath));
207
187
  } catch (error) {
208
188
  // If the module could not be found, assume it's not a file but a dependency.
209
189
  if (error.code === 'ERR_MODULE_NOT_FOUND' || error.code === 'MODULE_NOT_FOUND') {
@@ -226,12 +206,8 @@ const run = async options => {
226
206
 
227
207
  try {
228
208
  for await (const [ref, ...args] of (options.require ?? [])) {
229
- const loadedModule = await loadRequiredModule(ref);
230
-
231
- if (typeof loadedModule === 'function') { // CJS module
232
- await loadedModule(...args);
233
- } else if (typeof loadedModule.default === 'function') { // ES module, or exports.default from CJS
234
- const {default: fn} = loadedModule;
209
+ const {default: fn} = await loadRequiredModule(ref);
210
+ if (typeof fn === 'function') {
235
211
  await fn(...args);
236
212
  }
237
213
  }
@@ -1,11 +1,11 @@
1
- 'use strict';
2
- const events = require('node:events');
3
- const process = require('node:process');
4
- const {MessageChannel, threadId} = require('node:worker_threads');
1
+ import events from 'node:events';
2
+ import process from 'node:process';
3
+ import {MessageChannel, parentPort, threadId} from 'node:worker_threads';
5
4
 
6
- const timers = require('../now-and-timers.cjs');
5
+ import {controlFlow} from '../ipc-flow-control.js';
6
+ import {setTimeout as setTimeoutTimer} from '../now-and-timers.js';
7
7
 
8
- const {isRunningInChildProcess, isRunningInThread} = require('./utils.cjs');
8
+ import {isRunningInChildProcess, isRunningInThread} from './utils.js';
9
9
 
10
10
  const selectAvaMessage = async (channel, type) => {
11
11
  for await (const [message] of events.on(channel, 'message')) {
@@ -16,10 +16,6 @@ const selectAvaMessage = async (channel, type) => {
16
16
  };
17
17
 
18
18
  class RefCounter {
19
- constructor() {
20
- this.count = 0;
21
- }
22
-
23
19
  refAndTest() {
24
20
  return ++this.count === 1;
25
21
  }
@@ -27,6 +23,8 @@ class RefCounter {
27
23
  testAndUnref() {
28
24
  return this.count > 0 && --this.count === 0;
29
25
  }
26
+
27
+ count = 0;
30
28
  }
31
29
 
32
30
  class MessagePortHandle {
@@ -36,7 +34,7 @@ class MessagePortHandle {
36
34
  this.channel = port;
37
35
  // Referencing the port does not immediately prevent the thread from
38
36
  // exiting. Use a timer to keep a reference for at least a second.
39
- this.workaroundTimer = timers.setTimeout(() => {}, 1000).unref();
37
+ this.workaroundTimer = setTimeoutTimer(() => {}, 1000).unref();
40
38
  }
41
39
 
42
40
  forceUnref() {
@@ -94,10 +92,8 @@ class IpcHandle {
94
92
 
95
93
  let handle;
96
94
  if (isRunningInChildProcess) {
97
- const {controlFlow} = require('../ipc-flow-control.cjs');
98
95
  handle = new IpcHandle(controlFlow(process));
99
96
  } else if (isRunningInThread) {
100
- const {parentPort} = require('node:worker_threads');
101
97
  handle = new MessagePortHandle(parentPort);
102
98
  }
103
99
 
@@ -105,12 +101,14 @@ if (isRunningInChildProcess) {
105
101
  // Node.js. In order to keep track, explicitly reference before attaching.
106
102
  handle.ref();
107
103
 
108
- exports.options = selectAvaMessage(handle.channel, 'options').then(message => message.ava.options);
109
- exports.peerFailed = selectAvaMessage(handle.channel, 'peer-failed');
110
- exports.workerFreed = selectAvaMessage(handle.channel, 'free-worker');
111
- exports.send = handle.send.bind(handle);
112
- exports.ref = handle.ref.bind(handle);
113
- exports.unref = handle.unref.bind(handle);
104
+ /* eslint-disable unicorn/prefer-top-level-await */
105
+ export const options = selectAvaMessage(handle.channel, 'options').then(message => message.ava.options);
106
+ export const peerFailed = selectAvaMessage(handle.channel, 'peer-failed');
107
+ export const workerFreed = selectAvaMessage(handle.channel, 'free-worker');
108
+ /* eslint-enable unicorn/prefer-top-level-await */
109
+ export const send = handle.send.bind(handle);
110
+ export const ref = handle.ref.bind(handle);
111
+ export const unref = handle.unref.bind(handle);
114
112
 
115
113
  let channelCounter = 0;
116
114
  let messageCounter = 0;
@@ -138,7 +136,7 @@ function createChannelEmitter(channelId) {
138
136
  return [emitter, () => channelEmitters.delete(channelId)];
139
137
  }
140
138
 
141
- function registerSharedWorker(filename, initialData) {
139
+ export function registerSharedWorker(filename, initialData) {
142
140
  const channelId = `${threadId}/channel/${++channelCounter}`;
143
141
 
144
142
  const {port1: ourPort, port2: theirPort} = new MessageChannel();
@@ -160,9 +158,9 @@ function registerSharedWorker(filename, initialData) {
160
158
  // The attaching of message listeners will cause the port to be referenced by
161
159
  // Node.js. In order to keep track, explicitly reference before attaching.
162
160
  sharedWorkerHandle.ref();
163
- const ready = selectAvaMessage(ourPort, 'ready').then(() => { // eslint-disable-line promise/prefer-await-to-then
161
+ const ready = selectAvaMessage(ourPort, 'ready').then(() => {
164
162
  currentlyAvailable = error === null;
165
- }).finally(() => { // eslint-disable-line promise/prefer-await-to-then
163
+ }).finally(() => {
166
164
  // Once ready, it's up to user code to subscribe to messages, which (see
167
165
  // below) causes us to reference the port.
168
166
  sharedWorkerHandle.unref();
@@ -172,7 +170,7 @@ function registerSharedWorker(filename, initialData) {
172
170
 
173
171
  // Errors are received over the test worker channel, not the message port
174
172
  // dedicated to the shared worker.
175
- events.once(channelEmitter, 'shared-worker-error').then(() => { // eslint-disable-line promise/prefer-await-to-then
173
+ events.once(channelEmitter, 'shared-worker-error').then(() => {
176
174
  unsubscribe();
177
175
  sharedWorkerHandle.forceUnref();
178
176
  error = new Error('The shared worker is no longer available');
@@ -244,5 +242,3 @@ function registerSharedWorker(filename, initialData) {
244
242
  },
245
243
  };
246
244
  }
247
-
248
- exports.registerSharedWorker = registerSharedWorker;
@@ -1,13 +1,13 @@
1
1
  import process from 'node:process';
2
2
 
3
- import state from './state.cjs';
3
+ import {completionHandlers} from './state.js';
4
4
 
5
5
  export function runCompletionHandlers() {
6
- for (const handler of state.completionHandlers) {
6
+ for (const handler of completionHandlers) {
7
7
  process.nextTick(() => handler());
8
8
  }
9
9
  }
10
10
 
11
11
  export function registerCompletionHandler(handler) {
12
- state.completionHandlers.push(handler);
12
+ completionHandlers.push(handler);
13
13
  }
@@ -1,8 +1,7 @@
1
- 'use strict';
2
- const path = require('node:path');
3
- const process = require('node:process');
1
+ import path from 'node:path';
2
+ import process from 'node:process';
4
3
 
5
- const {isRunningInThread, isRunningInChildProcess} = require('./utils.cjs');
4
+ import {isRunningInThread, isRunningInChildProcess} from './utils.js';
6
5
 
7
6
  // Check if the test is being run without AVA cli
8
7
  if (!isRunningInChildProcess && !isRunningInThread) {
@@ -14,6 +13,6 @@ if (!isRunningInChildProcess && !isRunningInThread) {
14
13
 
15
14
  process.exit(1); // eslint-disable-line unicorn/no-process-exit
16
15
  } else {
17
- throw new Error('The ’ava’ module can only be imported in test files');
16
+ throw new Error('The \u2018ava\u2019 module can only be imported in test files');
18
17
  }
19
18
  }