ava 3.15.0 → 4.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 (72) hide show
  1. package/entrypoints/cli.mjs +4 -0
  2. package/entrypoints/eslint-plugin-helper.cjs +109 -0
  3. package/entrypoints/main.cjs +2 -0
  4. package/entrypoints/main.mjs +1 -0
  5. package/entrypoints/plugin.cjs +2 -0
  6. package/entrypoints/plugin.mjs +4 -0
  7. package/index.d.ts +6 -816
  8. package/lib/api.js +108 -49
  9. package/lib/assert.js +255 -270
  10. package/lib/chalk.js +9 -14
  11. package/lib/cli.js +118 -112
  12. package/lib/code-excerpt.js +12 -17
  13. package/lib/concordance-options.js +29 -65
  14. package/lib/context-ref.js +3 -6
  15. package/lib/create-chain.js +32 -20
  16. package/lib/environment-variables.js +1 -4
  17. package/lib/eslint-plugin-helper-worker.js +73 -0
  18. package/lib/extensions.js +2 -2
  19. package/lib/fork.js +81 -84
  20. package/lib/glob-helpers.cjs +140 -0
  21. package/lib/globs.js +136 -163
  22. package/lib/{ipc-flow-control.js → ipc-flow-control.cjs} +1 -0
  23. package/lib/is-ci.js +4 -2
  24. package/lib/like-selector.js +7 -13
  25. package/lib/line-numbers.js +11 -18
  26. package/lib/load-config.js +56 -180
  27. package/lib/module-types.js +3 -7
  28. package/lib/node-arguments.js +4 -5
  29. package/lib/{now-and-timers.js → now-and-timers.cjs} +0 -0
  30. package/lib/parse-test-args.js +22 -11
  31. package/lib/pkg.cjs +2 -0
  32. package/lib/plugin-support/shared-worker-loader.js +45 -48
  33. package/lib/plugin-support/shared-workers.js +24 -46
  34. package/lib/provider-manager.js +20 -14
  35. package/lib/reporters/beautify-stack.js +6 -12
  36. package/lib/reporters/colors.js +40 -15
  37. package/lib/reporters/default.js +114 -364
  38. package/lib/reporters/format-serialized-error.js +7 -18
  39. package/lib/reporters/improper-usage-messages.js +8 -9
  40. package/lib/reporters/prefix-title.js +17 -15
  41. package/lib/reporters/tap.js +18 -25
  42. package/lib/run-status.js +29 -23
  43. package/lib/runner.js +157 -172
  44. package/lib/scheduler.js +53 -0
  45. package/lib/serialize-error.js +61 -64
  46. package/lib/snapshot-manager.js +271 -289
  47. package/lib/test.js +135 -291
  48. package/lib/watcher.js +69 -44
  49. package/lib/worker/base.js +208 -0
  50. package/lib/worker/channel.cjs +290 -0
  51. package/lib/worker/dependency-tracker.js +24 -23
  52. package/lib/worker/{ensure-forked.js → guard-environment.cjs} +5 -4
  53. package/lib/worker/line-numbers.js +58 -20
  54. package/lib/worker/main.cjs +12 -0
  55. package/lib/worker/{options.js → options.cjs} +0 -0
  56. package/lib/worker/{plugin.js → plugin.cjs} +30 -21
  57. package/lib/worker/state.cjs +5 -0
  58. package/lib/worker/utils.cjs +6 -0
  59. package/package.json +71 -68
  60. package/plugin.d.ts +51 -53
  61. package/readme.md +5 -13
  62. package/types/assertions.d.ts +327 -0
  63. package/types/subscribable.ts +6 -0
  64. package/types/test-fn.d.ts +231 -0
  65. package/types/try-fn.d.ts +58 -0
  66. package/cli.js +0 -11
  67. package/eslint-plugin-helper.js +0 -201
  68. package/index.js +0 -8
  69. package/lib/worker/ipc.js +0 -201
  70. package/lib/worker/main.js +0 -21
  71. package/lib/worker/subprocess.js +0 -266
  72. package/plugin.js +0 -9
@@ -0,0 +1,290 @@
1
+ 'use strict';
2
+ const events = require('events');
3
+ const process = require('process');
4
+ const {MessageChannel, threadId} = require('worker_threads');
5
+
6
+ const timers = require('../now-and-timers.cjs');
7
+
8
+ const {isRunningInChildProcess, isRunningInThread} = require('./utils.cjs');
9
+
10
+ let pEvent = async (emitter, event, options) => {
11
+ // We need to import p-event, but import() is asynchronous. Buffer any events
12
+ // emitted in the meantime. Don't handle errors.
13
+ const buffer = [];
14
+ const addToBuffer = (...args) => buffer.push(args);
15
+ emitter.on(event, addToBuffer);
16
+
17
+ try {
18
+ ({pEvent} = await import('p-event')); // eslint-disable-line node/no-unsupported-features/es-syntax
19
+ } finally {
20
+ emitter.off(event, addToBuffer);
21
+ }
22
+
23
+ if (buffer.length === 0) {
24
+ return pEvent(emitter, event, options);
25
+ }
26
+
27
+ // Now replay buffered events.
28
+ const replayEmitter = new events.EventEmitter();
29
+ const promise = pEvent(replayEmitter, event, options);
30
+ for (const args of buffer) {
31
+ replayEmitter.emit(event, ...args);
32
+ }
33
+
34
+ const replay = (...args) => replayEmitter.emit(event, ...args);
35
+ emitter.on(event, replay);
36
+
37
+ try {
38
+ return await promise;
39
+ } finally {
40
+ emitter.off(event, replay);
41
+ }
42
+ };
43
+
44
+ const selectAvaMessage = type => message => message.ava && message.ava.type === type;
45
+
46
+ class RefCounter {
47
+ constructor() {
48
+ this.count = 0;
49
+ }
50
+
51
+ refAndTest() {
52
+ return ++this.count === 1;
53
+ }
54
+
55
+ testAndUnref() {
56
+ return this.count > 0 && --this.count === 0;
57
+ }
58
+ }
59
+
60
+ class MessagePortHandle {
61
+ constructor(port) {
62
+ this.counter = new RefCounter();
63
+ this.unreferenceable = false;
64
+ this.channel = port;
65
+ // Referencing the port does not immediately prevent the thread from
66
+ // exiting. Use a timer to keep a reference for at least a second.
67
+ this.workaroundTimer = timers.setTimeout(() => {}, 1000).unref();
68
+ }
69
+
70
+ forceUnref() {
71
+ if (this.unreferenceable) {
72
+ return;
73
+ }
74
+
75
+ this.unreferenceable = true;
76
+ this.workaroundTimer.unref();
77
+ this.channel.unref();
78
+ }
79
+
80
+ ref() {
81
+ if (!this.unreferenceable && this.counter.refAndTest()) {
82
+ this.workaroundTimer.refresh().ref();
83
+ this.channel.ref();
84
+ }
85
+ }
86
+
87
+ unref() {
88
+ if (!this.unreferenceable && this.counter.testAndUnref()) {
89
+ this.workaroundTimer.unref();
90
+ this.channel.unref();
91
+ }
92
+ }
93
+
94
+ send(evt, transferList) {
95
+ this.channel.postMessage({ava: evt}, transferList);
96
+ }
97
+ }
98
+
99
+ class IpcHandle {
100
+ constructor(bufferedSend) {
101
+ this.counter = new RefCounter();
102
+ this.channel = process;
103
+ this.sendRaw = bufferedSend;
104
+ }
105
+
106
+ ref() {
107
+ if (this.counter.refAndTest()) {
108
+ process.channel.ref();
109
+ }
110
+ }
111
+
112
+ unref() {
113
+ if (this.counter.testAndUnref()) {
114
+ process.channel.unref();
115
+ }
116
+ }
117
+
118
+ send(evt) {
119
+ this.sendRaw({ava: evt});
120
+ }
121
+ }
122
+
123
+ let handle;
124
+ if (isRunningInChildProcess) {
125
+ const {controlFlow} = require('../ipc-flow-control.cjs');
126
+ handle = new IpcHandle(controlFlow(process));
127
+ } else if (isRunningInThread) {
128
+ const {parentPort} = require('worker_threads');
129
+ handle = new MessagePortHandle(parentPort);
130
+ }
131
+
132
+ // The attaching of message listeners will cause the port to be referenced by
133
+ // Node.js. In order to keep track, explicitly reference before attaching.
134
+ handle.ref();
135
+
136
+ exports.options = pEvent(handle.channel, 'message', selectAvaMessage('options')).then(message => message.ava.options);
137
+ exports.peerFailed = pEvent(handle.channel, 'message', selectAvaMessage('peer-failed'));
138
+ exports.send = handle.send.bind(handle);
139
+ exports.unref = handle.unref.bind(handle);
140
+
141
+ let pendingPings = Promise.resolve();
142
+ async function flush() {
143
+ handle.ref();
144
+ const promise = pendingPings.then(async () => {
145
+ handle.send({type: 'ping'});
146
+ await pEvent(handle.channel, 'message', selectAvaMessage('pong'));
147
+ if (promise === pendingPings) {
148
+ handle.unref();
149
+ }
150
+ });
151
+ pendingPings = promise;
152
+ await promise;
153
+ }
154
+
155
+ exports.flush = flush;
156
+
157
+ let channelCounter = 0;
158
+ let messageCounter = 0;
159
+
160
+ const channelEmitters = new Map();
161
+ function createChannelEmitter(channelId) {
162
+ if (channelEmitters.size === 0) {
163
+ handle.channel.on('message', message => {
164
+ if (!message.ava) {
165
+ return;
166
+ }
167
+
168
+ const {channelId, type, ...payload} = message.ava;
169
+ if (type === 'shared-worker-error') {
170
+ const emitter = channelEmitters.get(channelId);
171
+ if (emitter !== undefined) {
172
+ emitter.emit(type, payload);
173
+ }
174
+ }
175
+ });
176
+ }
177
+
178
+ const emitter = new events.EventEmitter();
179
+ channelEmitters.set(channelId, emitter);
180
+ return [emitter, () => channelEmitters.delete(channelId)];
181
+ }
182
+
183
+ function registerSharedWorker(filename, initialData) {
184
+ const channelId = `${threadId}/channel/${++channelCounter}`;
185
+
186
+ const {port1: ourPort, port2: theirPort} = new MessageChannel();
187
+ const sharedWorkerHandle = new MessagePortHandle(ourPort);
188
+
189
+ const [channelEmitter, unsubscribe] = createChannelEmitter(channelId);
190
+
191
+ handle.send({
192
+ type: 'shared-worker-connect',
193
+ channelId,
194
+ filename,
195
+ initialData,
196
+ port: theirPort,
197
+ }, [theirPort]);
198
+
199
+ let currentlyAvailable = false;
200
+ let error = null;
201
+
202
+ // The attaching of message listeners will cause the port to be referenced by
203
+ // Node.js. In order to keep track, explicitly reference before attaching.
204
+ sharedWorkerHandle.ref();
205
+ const ready = pEvent(ourPort, 'message', ({type}) => type === 'ready').then(() => {
206
+ currentlyAvailable = error === null;
207
+ }).finally(() => {
208
+ // Once ready, it's up to user code to subscribe to messages, which (see
209
+ // below) causes us to reference the port.
210
+ sharedWorkerHandle.unref();
211
+ });
212
+
213
+ const messageEmitters = new Set();
214
+
215
+ // Errors are received over the test worker channel, not the message port
216
+ // dedicated to the shared worker.
217
+ pEvent(channelEmitter, 'shared-worker-error').then(() => {
218
+ unsubscribe();
219
+ sharedWorkerHandle.forceUnref();
220
+ error = new Error('The shared worker is no longer available');
221
+ currentlyAvailable = false;
222
+ for (const emitter of messageEmitters) {
223
+ emitter.emit('error', error);
224
+ }
225
+ });
226
+
227
+ ourPort.on('message', message => {
228
+ if (message.type === 'message') {
229
+ // Wait for a turn of the event loop, to allow new subscriptions to be set
230
+ // up in response to the previous message.
231
+ setImmediate(() => {
232
+ for (const emitter of messageEmitters) {
233
+ emitter.emit('message', message);
234
+ }
235
+ });
236
+ }
237
+ });
238
+
239
+ return {
240
+ forceUnref: () => sharedWorkerHandle.forceUnref(),
241
+ ready,
242
+ channel: {
243
+ available: ready,
244
+
245
+ get currentlyAvailable() {
246
+ return currentlyAvailable;
247
+ },
248
+
249
+ async * receive() {
250
+ if (error !== null) {
251
+ throw error;
252
+ }
253
+
254
+ const emitter = new events.EventEmitter();
255
+ messageEmitters.add(emitter);
256
+ try {
257
+ sharedWorkerHandle.ref();
258
+ for await (const [message] of events.on(emitter, 'message')) {
259
+ yield message;
260
+ }
261
+ } finally {
262
+ sharedWorkerHandle.unref();
263
+ messageEmitters.delete(emitter);
264
+ }
265
+ },
266
+
267
+ post(data, replyTo) {
268
+ if (error !== null) {
269
+ throw error;
270
+ }
271
+
272
+ if (!currentlyAvailable) {
273
+ throw new Error('Shared worker is not yet available');
274
+ }
275
+
276
+ const messageId = `${channelId}/message/${++messageCounter}`;
277
+ ourPort.postMessage({
278
+ type: 'message',
279
+ messageId,
280
+ replyTo,
281
+ data,
282
+ });
283
+
284
+ return messageId;
285
+ },
286
+ },
287
+ };
288
+ }
289
+
290
+ exports.registerSharedWorker = registerSharedWorker;
@@ -1,20 +1,19 @@
1
- /* eslint-disable node/no-deprecated-api */
2
- 'use strict';
3
- const ipc = require('./ipc');
1
+ import process from 'node:process';
2
+
3
+ import channel from './channel.cjs';
4
4
 
5
5
  const seenDependencies = new Set();
6
6
  let newDependencies = [];
7
+
7
8
  function flush() {
8
9
  if (newDependencies.length === 0) {
9
10
  return;
10
11
  }
11
12
 
12
- ipc.send({type: 'dependencies', dependencies: newDependencies});
13
+ channel.send({type: 'dependencies', dependencies: newDependencies});
13
14
  newDependencies = [];
14
15
  }
15
16
 
16
- exports.flush = flush;
17
-
18
17
  function track(filename) {
19
18
  if (seenDependencies.has(filename)) {
20
19
  return;
@@ -28,20 +27,22 @@ function track(filename) {
28
27
  newDependencies.push(filename);
29
28
  }
30
29
 
31
- exports.track = track;
32
-
33
- function install(testPath) {
34
- for (const ext of Object.keys(require.extensions)) {
35
- const wrappedHandler = require.extensions[ext];
36
-
37
- require.extensions[ext] = (module, filename) => {
38
- if (filename !== testPath) {
39
- track(filename);
40
- }
41
-
42
- wrappedHandler(module, filename);
43
- };
44
- }
45
- }
46
-
47
- exports.install = install;
30
+ const tracker = {
31
+ flush,
32
+ track,
33
+ install(extensions, testPath) {
34
+ for (const ext of Object.keys(extensions)) {
35
+ const wrappedHandler = extensions[ext];
36
+
37
+ extensions[ext] = (module, filename) => {
38
+ if (filename !== testPath) {
39
+ track(filename);
40
+ }
41
+
42
+ wrappedHandler(module, filename);
43
+ };
44
+ }
45
+ },
46
+ };
47
+
48
+ export default tracker;
@@ -1,15 +1,16 @@
1
1
  'use strict';
2
2
  const path = require('path');
3
- const chalk = require('chalk'); // Use default Chalk instance.
3
+ const process = require('process');
4
+
5
+ const {isRunningInThread, isRunningInChildProcess} = require('./utils.cjs');
4
6
 
5
7
  // Check if the test is being run without AVA cli
6
- const isForked = typeof process.send === 'function';
7
- if (!isForked) {
8
+ if (!isRunningInChildProcess && !isRunningInThread) {
8
9
  if (process.argv[1]) {
9
10
  const fp = path.relative('.', process.argv[1]);
10
11
 
11
12
  console.log();
12
- console.error(`Test files must be run with the AVA CLI:\n\n ${chalk.grey.dim('$')} ${chalk.cyan('ava ' + fp)}\n`);
13
+ console.error(`Test files must be run with the AVA CLI:\n\n $ ava ${fp}\n`);
13
14
 
14
15
  process.exit(1); // eslint-disable-line unicorn/no-process-exit
15
16
  } else {
@@ -1,18 +1,27 @@
1
+ import * as fs from 'node:fs';
2
+ import {createRequire, findSourceMap} from 'node:module';
3
+ import {pathToFileURL} from 'node:url';
4
+
5
+ import callsites from 'callsites';
6
+
7
+ const require = createRequire(import.meta.url);
8
+
1
9
  function parse(file) {
2
- const fs = require('fs');
10
+ // Avoid loading these until we actually need to select tests by line number.
3
11
  const acorn = require('acorn');
4
12
  const walk = require('acorn-walk');
5
13
 
6
14
  const ast = acorn.parse(fs.readFileSync(file, 'utf8'), {
7
15
  ecmaVersion: 11,
8
- locations: true
16
+ locations: true,
17
+ sourceType: 'module',
9
18
  });
10
19
 
11
20
  const locations = [];
12
21
  walk.simple(ast, {
13
22
  CallExpression(node) {
14
23
  locations.push(node.loc);
15
- }
24
+ },
16
25
  });
17
26
 
18
27
  // Walking is depth-first, but we want to sort these breadth-first.
@@ -49,36 +58,65 @@ function findTest(locations, declaration) {
49
58
  return spans.pop();
50
59
  }
51
60
 
52
- const range = (start, end) => new Array(end - start + 1).fill(start).map((element, index) => element + index);
61
+ const range = (start, end) => Array.from({length: end - start + 1}).fill(start).map((element, index) => element + index);
53
62
 
54
- module.exports = ({file, lineNumbers = []}) => {
63
+ const translate = (sourceMap, pos) => {
64
+ if (sourceMap === undefined) {
65
+ return pos;
66
+ }
67
+
68
+ const entry = sourceMap.findEntry(pos.line - 1, pos.column); // Source maps are 0-based
69
+ return {
70
+ line: entry.originalLine + 1, // Readjust for Acorn.
71
+ column: entry.originalColumn,
72
+ };
73
+ };
74
+
75
+ export default function lineNumberSelection({file, lineNumbers = []}) {
55
76
  if (lineNumbers.length === 0) {
56
77
  return undefined;
57
78
  }
58
79
 
59
- // Avoid loading these until we actually need to select tests by line number.
60
- const callsites = require('callsites');
61
- const sourceMapSupport = require('source-map-support');
62
-
63
- const locations = parse(file);
64
80
  const selected = new Set(lineNumbers);
65
81
 
82
+ let locations = parse(file);
83
+ let lookedForSourceMap = false;
84
+ let sourceMap;
85
+
66
86
  return () => {
87
+ if (!lookedForSourceMap) {
88
+ lookedForSourceMap = true;
89
+
90
+ // The returned function is called *after* the file has been loaded.
91
+ // Source maps are not available before then.
92
+ sourceMap = findSourceMap(file);
93
+
94
+ if (sourceMap !== undefined) {
95
+ locations = locations.map(({start, end}) => ({
96
+ start: translate(sourceMap, start),
97
+ end: translate(sourceMap, end),
98
+ }));
99
+ }
100
+ }
101
+
67
102
  // Assume this is called from a test declaration, which is located in the file.
68
103
  // If not… don't select the test!
69
- const callSite = callsites().find(callSite => callSite.getFileName() === file);
104
+ const callSite = callsites().find(callSite => {
105
+ const current = callSite.getFileName();
106
+ if (file.startsWith('file://')) {
107
+ return current.startsWith('file://') ? file === current : file === pathToFileURL(current).toString();
108
+ }
109
+
110
+ return current.startsWith('file://') ? pathToFileURL(file).toString() === current : file === current;
111
+ });
70
112
  if (!callSite) {
71
113
  return false;
72
114
  }
73
115
 
74
- // FIXME: This assumes the callSite hasn't already been adjusted. It's likely
75
- // that if `source-map-support/register` has been loaded, this would result
76
- // in the wrong location.
77
- const sourceCallSite = sourceMapSupport.wrapCallSite(callSite);
78
- const start = {
79
- line: sourceCallSite.getLineNumber(),
80
- column: sourceCallSite.getColumnNumber() - 1 // Use 0-indexed columns.
81
- };
116
+ const start = translate(sourceMap, {
117
+ line: callSite.getLineNumber(), // 1-based
118
+ column: callSite.getColumnNumber() - 1, // Comes out as 1-based, Acorn wants 0-based
119
+ });
82
120
 
83
121
  const test = findTest(locations, start);
84
122
  if (!test) {
@@ -87,4 +125,4 @@ module.exports = ({file, lineNumbers = []}) => {
87
125
 
88
126
  return range(test.start.line, test.end.line).some(line => selected.has(line));
89
127
  };
90
- };
128
+ }
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+ require('./guard-environment.cjs'); // eslint-disable-line import/no-unassigned-import
3
+
4
+ const assert = require('assert');
5
+
6
+ const {flags, refs} = require('./state.cjs');
7
+
8
+ assert(refs.runnerChain);
9
+
10
+ flags.loadedMain = true;
11
+
12
+ module.exports = refs.runnerChain;
File without changes
@@ -1,18 +1,29 @@
1
- const v8 = require('v8');
2
1
  const pkg = require('../../package.json');
3
- const subprocess = require('./subprocess');
4
- const options = require('./options');
2
+
3
+ const {registerSharedWorker: register} = require('./channel.cjs');
4
+ const options = require('./options.cjs');
5
+ const {sharedWorkerTeardowns, waitForReady} = require('./state.cjs');
6
+
7
+ require('./guard-environment.cjs'); // eslint-disable-line import/no-unassigned-import
5
8
 
6
9
  const workers = new Map();
7
10
  const workerTeardownFns = new WeakMap();
8
11
 
9
12
  function createSharedWorker(filename, initialData, teardown) {
10
- const channel = subprocess.registerSharedWorker(filename, initialData, teardown);
13
+ const {channel, forceUnref, ready} = register(filename, initialData, teardown);
14
+ waitForReady.push(ready);
15
+ sharedWorkerTeardowns.push(async () => {
16
+ try {
17
+ await teardown();
18
+ } finally {
19
+ forceUnref();
20
+ }
21
+ });
11
22
 
12
23
  class ReceivedMessage {
13
- constructor(id, serializedData) {
24
+ constructor(id, data) {
14
25
  this.id = id;
15
- this.data = v8.deserialize(new Uint8Array(serializedData));
26
+ this.data = data;
16
27
  }
17
28
 
18
29
  reply(data) {
@@ -35,7 +46,7 @@ function createSharedWorker(filename, initialData, teardown) {
35
46
 
36
47
  let message = messageCache.get(evt);
37
48
  if (message === undefined) {
38
- message = new ReceivedMessage(evt.messageId, evt.serializedData);
49
+ message = new ReceivedMessage(evt.messageId, evt.data);
39
50
  messageCache.set(evt, message);
40
51
  }
41
52
 
@@ -44,19 +55,19 @@ function createSharedWorker(filename, initialData, teardown) {
44
55
  }
45
56
 
46
57
  function publishMessage(data, replyTo) {
47
- const id = channel.post([...v8.serialize(data)], replyTo);
58
+ const id = channel.post(data, replyTo);
48
59
 
49
60
  return {
50
61
  id,
51
62
  async * replies() {
52
63
  yield * receiveMessages(id);
53
- }
64
+ },
54
65
  };
55
66
  }
56
67
 
57
68
  return {
58
69
  available: channel.available,
59
- protocol: 'experimental',
70
+ protocol: 'ava-4',
60
71
 
61
72
  get currentlyAvailable() {
62
73
  return channel.currentlyAvailable;
@@ -68,30 +79,28 @@ function createSharedWorker(filename, initialData, teardown) {
68
79
 
69
80
  async * subscribe() {
70
81
  yield * receiveMessages();
71
- }
82
+ },
72
83
  };
73
84
  }
74
85
 
75
- const supportsSharedWorkers = process.versions.node >= '12.17.0';
76
-
77
86
  function registerSharedWorker({
78
87
  filename,
79
88
  initialData,
80
89
  supportedProtocols,
81
- teardown
90
+ teardown,
82
91
  }) {
83
- if (!options.get().experiments.sharedWorkers) {
84
- throw new Error('Shared workers are experimental. Opt in to them in your AVA configuration');
85
- }
92
+ const options_ = options.get();
86
93
 
87
- if (!supportsSharedWorkers) {
88
- throw new Error('Shared workers require Node.js 12.17 or newer');
94
+ if (!options_.workerThreads) {
95
+ throw new Error('Shared workers can be used only when worker threads are enabled');
89
96
  }
90
97
 
91
- if (!supportedProtocols.includes('experimental')) {
92
- throw new Error(`This version of AVA (${pkg.version}) does not support any of the desired shared worker protocols: ${supportedProtocols.join()}`);
98
+ if (!supportedProtocols.includes('ava-4')) {
99
+ throw new Error(`This version of AVA (${pkg.version}) does not support any of the desired shared worker protocols: ${supportedProtocols.join(',')}`);
93
100
  }
94
101
 
102
+ filename = String(filename); // Allow URL instances.
103
+
95
104
  let worker = workers.get(filename);
96
105
  if (worker === undefined) {
97
106
  worker = createSharedWorker(filename, initialData, async () => {
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+ exports.flags = {loadedMain: false};
3
+ exports.refs = {runnerChain: null};
4
+ exports.sharedWorkerTeardowns = [];
5
+ exports.waitForReady = [];
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+ const process = require('process');
3
+ const {isMainThread} = require('worker_threads');
4
+
5
+ exports.isRunningInThread = isMainThread === false;
6
+ exports.isRunningInChildProcess = typeof process.send === 'function';