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.
- package/entrypoints/cli.mjs +4 -0
- package/entrypoints/eslint-plugin-helper.cjs +109 -0
- package/entrypoints/main.cjs +2 -0
- package/entrypoints/main.mjs +1 -0
- package/entrypoints/plugin.cjs +2 -0
- package/entrypoints/plugin.mjs +4 -0
- package/index.d.ts +6 -816
- package/lib/api.js +108 -49
- package/lib/assert.js +255 -270
- package/lib/chalk.js +9 -14
- package/lib/cli.js +118 -112
- package/lib/code-excerpt.js +12 -17
- package/lib/concordance-options.js +29 -65
- package/lib/context-ref.js +3 -6
- package/lib/create-chain.js +32 -20
- package/lib/environment-variables.js +1 -4
- package/lib/eslint-plugin-helper-worker.js +73 -0
- package/lib/extensions.js +2 -2
- package/lib/fork.js +81 -84
- package/lib/glob-helpers.cjs +140 -0
- package/lib/globs.js +136 -163
- package/lib/{ipc-flow-control.js → ipc-flow-control.cjs} +1 -0
- package/lib/is-ci.js +4 -2
- package/lib/like-selector.js +7 -13
- package/lib/line-numbers.js +11 -18
- package/lib/load-config.js +56 -180
- package/lib/module-types.js +3 -7
- package/lib/node-arguments.js +4 -5
- package/lib/{now-and-timers.js → now-and-timers.cjs} +0 -0
- package/lib/parse-test-args.js +22 -11
- package/lib/pkg.cjs +2 -0
- package/lib/plugin-support/shared-worker-loader.js +45 -48
- package/lib/plugin-support/shared-workers.js +24 -46
- package/lib/provider-manager.js +20 -14
- package/lib/reporters/beautify-stack.js +6 -12
- package/lib/reporters/colors.js +40 -15
- package/lib/reporters/default.js +114 -364
- package/lib/reporters/format-serialized-error.js +7 -18
- package/lib/reporters/improper-usage-messages.js +8 -9
- package/lib/reporters/prefix-title.js +17 -15
- package/lib/reporters/tap.js +18 -25
- package/lib/run-status.js +29 -23
- package/lib/runner.js +157 -172
- package/lib/scheduler.js +53 -0
- package/lib/serialize-error.js +61 -64
- package/lib/snapshot-manager.js +271 -289
- package/lib/test.js +135 -291
- package/lib/watcher.js +69 -44
- package/lib/worker/base.js +208 -0
- package/lib/worker/channel.cjs +290 -0
- package/lib/worker/dependency-tracker.js +24 -23
- package/lib/worker/{ensure-forked.js → guard-environment.cjs} +5 -4
- package/lib/worker/line-numbers.js +58 -20
- package/lib/worker/main.cjs +12 -0
- package/lib/worker/{options.js → options.cjs} +0 -0
- package/lib/worker/{plugin.js → plugin.cjs} +30 -21
- package/lib/worker/state.cjs +5 -0
- package/lib/worker/utils.cjs +6 -0
- package/package.json +71 -68
- package/plugin.d.ts +51 -53
- package/readme.md +5 -13
- package/types/assertions.d.ts +327 -0
- package/types/subscribable.ts +6 -0
- package/types/test-fn.d.ts +231 -0
- package/types/try-fn.d.ts +58 -0
- package/cli.js +0 -11
- package/eslint-plugin-helper.js +0 -201
- package/index.js +0 -8
- package/lib/worker/ipc.js +0 -201
- package/lib/worker/main.js +0 -21
- package/lib/worker/subprocess.js +0 -266
- 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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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
|
-
|
|
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 $
|
|
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
|
-
|
|
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) =>
|
|
61
|
+
const range = (start, end) => Array.from({length: end - start + 1}).fill(start).map((element, index) => element + index);
|
|
53
62
|
|
|
54
|
-
|
|
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 =>
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
4
|
-
const
|
|
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 =
|
|
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,
|
|
24
|
+
constructor(id, data) {
|
|
14
25
|
this.id = id;
|
|
15
|
-
this.data =
|
|
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.
|
|
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(
|
|
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: '
|
|
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
|
-
|
|
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 (!
|
|
88
|
-
throw new Error('Shared workers
|
|
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('
|
|
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 () => {
|