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
package/eslint-plugin-helper.js
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
let isMainThread = true;
|
|
3
|
-
let supportsWorkers = false;
|
|
4
|
-
try {
|
|
5
|
-
({isMainThread} = require('worker_threads'));
|
|
6
|
-
supportsWorkers = true;
|
|
7
|
-
} catch {}
|
|
8
|
-
|
|
9
|
-
const {classify, hasExtension, isHelperish, matches, normalizeFileForMatching, normalizeGlobs, normalizePatterns} = require('./lib/globs');
|
|
10
|
-
|
|
11
|
-
let resolveGlobs;
|
|
12
|
-
let resolveGlobsSync;
|
|
13
|
-
|
|
14
|
-
if (!supportsWorkers || !isMainThread) {
|
|
15
|
-
const normalizeExtensions = require('./lib/extensions');
|
|
16
|
-
const {loadConfig, loadConfigSync} = require('./lib/load-config');
|
|
17
|
-
const providerManager = require('./lib/provider-manager');
|
|
18
|
-
|
|
19
|
-
const configCache = new Map();
|
|
20
|
-
|
|
21
|
-
const collectProviders = ({conf, projectDir}) => {
|
|
22
|
-
const providers = [];
|
|
23
|
-
if (Reflect.has(conf, 'babel')) {
|
|
24
|
-
const {level, main} = providerManager.babel(projectDir);
|
|
25
|
-
providers.push({
|
|
26
|
-
level,
|
|
27
|
-
main: main({config: conf.babel}),
|
|
28
|
-
type: 'babel'
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (Reflect.has(conf, 'typescript')) {
|
|
33
|
-
const {level, main} = providerManager.typescript(projectDir);
|
|
34
|
-
providers.push({
|
|
35
|
-
level,
|
|
36
|
-
main: main({config: conf.typescript}),
|
|
37
|
-
type: 'typescript'
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return providers;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFiles}) => {
|
|
45
|
-
const extensions = overrideExtensions ?
|
|
46
|
-
normalizeExtensions(overrideExtensions) :
|
|
47
|
-
normalizeExtensions(conf.extensions, providers);
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
cwd: projectDir,
|
|
51
|
-
...normalizeGlobs({
|
|
52
|
-
extensions,
|
|
53
|
-
files: overrideFiles ? overrideFiles : conf.files,
|
|
54
|
-
providers
|
|
55
|
-
})
|
|
56
|
-
};
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
resolveGlobsSync = (projectDir, overrideExtensions, overrideFiles) => {
|
|
60
|
-
if (!configCache.has(projectDir)) {
|
|
61
|
-
const conf = loadConfigSync({resolveFrom: projectDir});
|
|
62
|
-
const providers = collectProviders({conf, projectDir});
|
|
63
|
-
configCache.set(projectDir, {conf, providers});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const {conf, providers} = configCache.get(projectDir);
|
|
67
|
-
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
|
|
71
|
-
if (!configCache.has(projectDir)) {
|
|
72
|
-
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(conf => { // eslint-disable-line promise/prefer-await-to-then
|
|
73
|
-
const providers = collectProviders({conf, projectDir});
|
|
74
|
-
return {conf, providers};
|
|
75
|
-
}));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const {conf, providers} = await configCache.get(projectDir);
|
|
79
|
-
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (supportsWorkers) {
|
|
84
|
-
const v8 = require('v8');
|
|
85
|
-
|
|
86
|
-
const MAX_DATA_LENGTH_EXCLUSIVE = 100 * 1024; // Allocate 100 KiB to exchange globs.
|
|
87
|
-
|
|
88
|
-
if (isMainThread) {
|
|
89
|
-
const {Worker} = require('worker_threads');
|
|
90
|
-
let data;
|
|
91
|
-
let sync;
|
|
92
|
-
let worker;
|
|
93
|
-
|
|
94
|
-
resolveGlobsSync = (projectDir, overrideExtensions, overrideFiles) => {
|
|
95
|
-
if (worker === undefined) {
|
|
96
|
-
const dataBuffer = new SharedArrayBuffer(MAX_DATA_LENGTH_EXCLUSIVE);
|
|
97
|
-
data = new Uint8Array(dataBuffer);
|
|
98
|
-
|
|
99
|
-
const syncBuffer = new SharedArrayBuffer(4);
|
|
100
|
-
sync = new Int32Array(syncBuffer);
|
|
101
|
-
|
|
102
|
-
worker = new Worker(__filename, {
|
|
103
|
-
workerData: {
|
|
104
|
-
dataBuffer,
|
|
105
|
-
syncBuffer,
|
|
106
|
-
firstMessage: {projectDir, overrideExtensions, overrideFiles}
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
worker.unref();
|
|
110
|
-
} else {
|
|
111
|
-
worker.postMessage({projectDir, overrideExtensions, overrideFiles});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
Atomics.wait(sync, 0, 0);
|
|
115
|
-
|
|
116
|
-
const byteLength = Atomics.exchange(sync, 0, 0);
|
|
117
|
-
if (byteLength === MAX_DATA_LENGTH_EXCLUSIVE) {
|
|
118
|
-
throw new Error('Globs are over 100 KiB and cannot be resolved');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const globsOrError = v8.deserialize(data.slice(0, byteLength));
|
|
122
|
-
if (globsOrError instanceof Error) {
|
|
123
|
-
throw globsOrError;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return globsOrError;
|
|
127
|
-
};
|
|
128
|
-
} else {
|
|
129
|
-
const {parentPort, workerData} = require('worker_threads');
|
|
130
|
-
const data = new Uint8Array(workerData.dataBuffer);
|
|
131
|
-
const sync = new Int32Array(workerData.syncBuffer);
|
|
132
|
-
|
|
133
|
-
const handleMessage = async ({projectDir, overrideExtensions, overrideFiles}) => {
|
|
134
|
-
let encoded;
|
|
135
|
-
try {
|
|
136
|
-
const globs = await resolveGlobs(projectDir, overrideExtensions, overrideFiles);
|
|
137
|
-
encoded = v8.serialize(globs);
|
|
138
|
-
} catch (error) {
|
|
139
|
-
encoded = v8.serialize(error);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const byteLength = encoded.length < MAX_DATA_LENGTH_EXCLUSIVE ? encoded.copy(data) : MAX_DATA_LENGTH_EXCLUSIVE;
|
|
143
|
-
Atomics.store(sync, 0, byteLength);
|
|
144
|
-
Atomics.notify(sync, 0);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
parentPort.on('message', handleMessage);
|
|
148
|
-
handleMessage(workerData.firstMessage);
|
|
149
|
-
delete workerData.firstMessage;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const helperCache = new Map();
|
|
154
|
-
|
|
155
|
-
function load(projectDir, overrides) {
|
|
156
|
-
const cacheKey = `${JSON.stringify(overrides)}\n${projectDir}`;
|
|
157
|
-
if (helperCache.has(cacheKey)) {
|
|
158
|
-
return helperCache.get(cacheKey);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let helperPatterns = [];
|
|
162
|
-
if (overrides && overrides.helpers !== undefined) {
|
|
163
|
-
if (!Array.isArray(overrides.helpers) || overrides.helpers.length === 0) {
|
|
164
|
-
throw new Error('The ’helpers’ override must be an array containing glob patterns.');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
helperPatterns = normalizePatterns(overrides.helpers);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const globs = resolveGlobsSync(projectDir, overrides && overrides.extensions, overrides && overrides.files);
|
|
171
|
-
|
|
172
|
-
const classifyForESLint = file => {
|
|
173
|
-
const {isTest} = classify(file, globs);
|
|
174
|
-
let isHelper = false;
|
|
175
|
-
if (!isTest && hasExtension(globs.extensions, file)) {
|
|
176
|
-
file = normalizeFileForMatching(projectDir, file);
|
|
177
|
-
isHelper = isHelperish(file) || (helperPatterns.length > 0 && matches(file, helperPatterns));
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return {isHelper, isTest};
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const helper = Object.freeze({
|
|
184
|
-
classifyFile: classifyForESLint,
|
|
185
|
-
classifyImport: importPath => {
|
|
186
|
-
if (hasExtension(globs.extensions, importPath)) {
|
|
187
|
-
// The importPath has one of the test file extensions: we can classify
|
|
188
|
-
// it directly.
|
|
189
|
-
return classifyForESLint(importPath);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Add the first extension. If multiple extensions are available, assume
|
|
193
|
-
// patterns are not biased to any particular extension.
|
|
194
|
-
return classifyForESLint(`${importPath}.${globs.extensions[0]}`);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
helperCache.set(cacheKey, helper);
|
|
198
|
-
return helper;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
exports.load = load;
|
package/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Ensure the same AVA install is loaded by the test file as by the test worker
|
|
4
|
-
if (process.env.AVA_PATH && process.env.AVA_PATH !== __dirname) {
|
|
5
|
-
module.exports = require(process.env.AVA_PATH);
|
|
6
|
-
} else {
|
|
7
|
-
module.exports = require('./lib/worker/main');
|
|
8
|
-
}
|
package/lib/worker/ipc.js
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const events = require('events');
|
|
3
|
-
const pEvent = require('p-event');
|
|
4
|
-
const {controlFlow} = require('../ipc-flow-control');
|
|
5
|
-
const {get: getOptions} = require('./options');
|
|
6
|
-
|
|
7
|
-
const selectAvaMessage = type => message => message.ava && message.ava.type === type;
|
|
8
|
-
|
|
9
|
-
exports.options = pEvent(process, 'message', selectAvaMessage('options')).then(message => message.ava.options);
|
|
10
|
-
exports.peerFailed = pEvent(process, 'message', selectAvaMessage('peer-failed'));
|
|
11
|
-
|
|
12
|
-
const bufferedSend = controlFlow(process);
|
|
13
|
-
function send(evt) {
|
|
14
|
-
bufferedSend({ava: evt});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
exports.send = send;
|
|
18
|
-
|
|
19
|
-
let refs = 1;
|
|
20
|
-
function ref() {
|
|
21
|
-
if (++refs === 1) {
|
|
22
|
-
process.channel.ref();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function unref() {
|
|
27
|
-
if (refs > 0 && --refs === 0) {
|
|
28
|
-
process.channel.unref();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
exports.unref = unref;
|
|
33
|
-
|
|
34
|
-
let pendingPings = Promise.resolve();
|
|
35
|
-
async function flush() {
|
|
36
|
-
ref();
|
|
37
|
-
const promise = pendingPings.then(async () => { // eslint-disable-line promise/prefer-await-to-then
|
|
38
|
-
send({type: 'ping'});
|
|
39
|
-
await pEvent(process, 'message', selectAvaMessage('pong'));
|
|
40
|
-
if (promise === pendingPings) {
|
|
41
|
-
unref();
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
pendingPings = promise;
|
|
45
|
-
await promise;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
exports.flush = flush;
|
|
49
|
-
|
|
50
|
-
let channelCounter = 0;
|
|
51
|
-
let messageCounter = 0;
|
|
52
|
-
|
|
53
|
-
const channelEmitters = new Map();
|
|
54
|
-
function createChannelEmitter(channelId) {
|
|
55
|
-
if (channelEmitters.size === 0) {
|
|
56
|
-
process.on('message', message => {
|
|
57
|
-
if (!message.ava) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const {channelId, type, ...payload} = message.ava;
|
|
62
|
-
if (
|
|
63
|
-
type === 'shared-worker-error' ||
|
|
64
|
-
type === 'shared-worker-message' ||
|
|
65
|
-
type === 'shared-worker-ready'
|
|
66
|
-
) {
|
|
67
|
-
const emitter = channelEmitters.get(channelId);
|
|
68
|
-
if (emitter !== undefined) {
|
|
69
|
-
emitter.emit(type, payload);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const emitter = new events.EventEmitter();
|
|
76
|
-
channelEmitters.set(channelId, emitter);
|
|
77
|
-
return [emitter, () => channelEmitters.delete(channelId)];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function registerSharedWorker(filename, initialData) {
|
|
81
|
-
const channelId = `${getOptions().forkId}/channel/${++channelCounter}`;
|
|
82
|
-
const [channelEmitter, unsubscribe] = createChannelEmitter(channelId);
|
|
83
|
-
|
|
84
|
-
let forcedUnref = false;
|
|
85
|
-
let refs = 0;
|
|
86
|
-
const forceUnref = () => {
|
|
87
|
-
if (forcedUnref) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
forcedUnref = true;
|
|
92
|
-
if (refs > 0) {
|
|
93
|
-
unref();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const refChannel = () => {
|
|
98
|
-
if (!forcedUnref && ++refs === 1) {
|
|
99
|
-
ref();
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const unrefChannel = () => {
|
|
104
|
-
if (!forcedUnref && refs > 0 && --refs === 0) {
|
|
105
|
-
unref();
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
send({
|
|
110
|
-
type: 'shared-worker-connect',
|
|
111
|
-
channelId,
|
|
112
|
-
filename,
|
|
113
|
-
initialData
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
let currentlyAvailable = false;
|
|
117
|
-
let error = null;
|
|
118
|
-
|
|
119
|
-
refChannel();
|
|
120
|
-
const ready = pEvent(channelEmitter, 'shared-worker-ready').then(() => { // eslint-disable-line promise/prefer-await-to-then
|
|
121
|
-
currentlyAvailable = error === null;
|
|
122
|
-
}).finally(unrefChannel);
|
|
123
|
-
|
|
124
|
-
const messageEmitters = new Set();
|
|
125
|
-
const handleMessage = message => {
|
|
126
|
-
// Wait for a turn of the event loop, to allow new subscriptions to be set
|
|
127
|
-
// up in response to the previous message.
|
|
128
|
-
setImmediate(() => {
|
|
129
|
-
for (const emitter of messageEmitters) {
|
|
130
|
-
emitter.emit('message', message);
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
channelEmitter.on('shared-worker-message', handleMessage);
|
|
136
|
-
|
|
137
|
-
pEvent(channelEmitter, 'shared-worker-error').then(() => { // eslint-disable-line promise/prefer-await-to-then
|
|
138
|
-
unsubscribe();
|
|
139
|
-
forceUnref();
|
|
140
|
-
|
|
141
|
-
error = new Error('The shared worker is no longer available');
|
|
142
|
-
currentlyAvailable = false;
|
|
143
|
-
for (const emitter of messageEmitters) {
|
|
144
|
-
emitter.emit('error', error);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
forceUnref,
|
|
150
|
-
ready,
|
|
151
|
-
channel: {
|
|
152
|
-
available: ready,
|
|
153
|
-
|
|
154
|
-
get currentlyAvailable() {
|
|
155
|
-
return currentlyAvailable;
|
|
156
|
-
},
|
|
157
|
-
|
|
158
|
-
async * receive() {
|
|
159
|
-
if (error !== null) {
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const emitter = new events.EventEmitter();
|
|
164
|
-
messageEmitters.add(emitter);
|
|
165
|
-
try {
|
|
166
|
-
refChannel();
|
|
167
|
-
for await (const [message] of events.on(emitter, 'message')) {
|
|
168
|
-
yield message;
|
|
169
|
-
}
|
|
170
|
-
} finally {
|
|
171
|
-
unrefChannel();
|
|
172
|
-
messageEmitters.delete(emitter);
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
post(serializedData, replyTo) {
|
|
177
|
-
if (error !== null) {
|
|
178
|
-
throw error;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (!currentlyAvailable) {
|
|
182
|
-
throw new Error('Shared worker is not yet available');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const messageId = `${channelId}/message/${++messageCounter}`;
|
|
186
|
-
send({
|
|
187
|
-
type: 'shared-worker-message',
|
|
188
|
-
channelId,
|
|
189
|
-
messageId,
|
|
190
|
-
replyTo,
|
|
191
|
-
serializedData
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
return messageId;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
exports.registerSharedWorker = registerSharedWorker;
|
|
201
|
-
|
package/lib/worker/main.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const runner = require('./subprocess').getRunner();
|
|
3
|
-
|
|
4
|
-
const makeCjsExport = () => {
|
|
5
|
-
function test(...args) {
|
|
6
|
-
return runner.chain(...args);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
return Object.assign(test, runner.chain);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// Support CommonJS modules by exporting a test function that can be fully
|
|
13
|
-
// chained. Also support ES module loaders by exporting __esModule and a
|
|
14
|
-
// default. Support `import * as ava from 'ava'` use cases by exporting a
|
|
15
|
-
// `test` member. Do all this whilst preventing `test.test.test() or
|
|
16
|
-
// `test.default.test()` chains, though in CommonJS `test.test()` is
|
|
17
|
-
// unavoidable.
|
|
18
|
-
module.exports = Object.assign(makeCjsExport(), {
|
|
19
|
-
__esModule: true,
|
|
20
|
-
default: runner.chain
|
|
21
|
-
});
|
package/lib/worker/subprocess.js
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const {pathToFileURL} = require('url');
|
|
3
|
-
const currentlyUnhandled = require('currently-unhandled')();
|
|
4
|
-
|
|
5
|
-
require('./ensure-forked'); // eslint-disable-line import/no-unassigned-import
|
|
6
|
-
|
|
7
|
-
const ipc = require('./ipc');
|
|
8
|
-
|
|
9
|
-
const supportsESM = async () => {
|
|
10
|
-
try {
|
|
11
|
-
await import('data:text/javascript,'); // eslint-disable-line node/no-unsupported-features/es-syntax
|
|
12
|
-
return true;
|
|
13
|
-
} catch {}
|
|
14
|
-
|
|
15
|
-
return false;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
ipc.send({type: 'ready-for-options'});
|
|
19
|
-
ipc.options.then(async options => {
|
|
20
|
-
require('./options').set(options);
|
|
21
|
-
require('../chalk').set(options.chalkOptions);
|
|
22
|
-
|
|
23
|
-
if (options.chalkOptions.level > 0) {
|
|
24
|
-
const {stdout, stderr} = process;
|
|
25
|
-
global.console = Object.assign(global.console, new console.Console({stdout, stderr, colorMode: true}));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const nowAndTimers = require('../now-and-timers');
|
|
29
|
-
const providerManager = require('../provider-manager');
|
|
30
|
-
const Runner = require('../runner');
|
|
31
|
-
const serializeError = require('../serialize-error');
|
|
32
|
-
const dependencyTracking = require('./dependency-tracker');
|
|
33
|
-
const lineNumberSelection = require('./line-numbers');
|
|
34
|
-
|
|
35
|
-
const sharedWorkerTeardowns = [];
|
|
36
|
-
|
|
37
|
-
async function exit(code) {
|
|
38
|
-
if (!process.exitCode) {
|
|
39
|
-
process.exitCode = code;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
dependencyTracking.flush();
|
|
43
|
-
await ipc.flush();
|
|
44
|
-
process.exit(); // eslint-disable-line unicorn/no-process-exit
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// TODO: Initialize providers here, then pass to lineNumberSelection() so they
|
|
48
|
-
// can be used to parse the test file.
|
|
49
|
-
let checkSelectedByLineNumbers;
|
|
50
|
-
try {
|
|
51
|
-
checkSelectedByLineNumbers = lineNumberSelection({
|
|
52
|
-
file: options.file,
|
|
53
|
-
lineNumbers: options.lineNumbers
|
|
54
|
-
});
|
|
55
|
-
} catch (error) {
|
|
56
|
-
ipc.send({type: 'line-number-selection-error', err: serializeError('Line number selection error', false, error, options.file)});
|
|
57
|
-
checkSelectedByLineNumbers = () => false;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const runner = new Runner({
|
|
61
|
-
checkSelectedByLineNumbers,
|
|
62
|
-
experiments: options.experiments,
|
|
63
|
-
failFast: options.failFast,
|
|
64
|
-
failWithoutAssertions: options.failWithoutAssertions,
|
|
65
|
-
file: options.file,
|
|
66
|
-
match: options.match,
|
|
67
|
-
projectDir: options.projectDir,
|
|
68
|
-
recordNewSnapshots: options.recordNewSnapshots,
|
|
69
|
-
runOnlyExclusive: options.runOnlyExclusive,
|
|
70
|
-
serial: options.serial,
|
|
71
|
-
snapshotDir: options.snapshotDir,
|
|
72
|
-
updateSnapshots: options.updateSnapshots
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
ipc.peerFailed.then(() => { // eslint-disable-line promise/prefer-await-to-then
|
|
76
|
-
runner.interrupt();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const attributedRejections = new Set();
|
|
80
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
81
|
-
if (runner.attributeLeakedError(reason)) {
|
|
82
|
-
attributedRejections.add(promise);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
runner.on('dependency', dependencyTracking.track);
|
|
87
|
-
runner.on('stateChange', state => ipc.send(state));
|
|
88
|
-
|
|
89
|
-
runner.on('error', error => {
|
|
90
|
-
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, error, runner.file)});
|
|
91
|
-
exit(1);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
runner.on('finish', async () => {
|
|
95
|
-
try {
|
|
96
|
-
const {cannotSave, touchedFiles} = runner.saveSnapshotState();
|
|
97
|
-
if (cannotSave) {
|
|
98
|
-
ipc.send({type: 'snapshot-error'});
|
|
99
|
-
} else if (touchedFiles) {
|
|
100
|
-
ipc.send({type: 'touched-files', files: touchedFiles});
|
|
101
|
-
}
|
|
102
|
-
} catch (error) {
|
|
103
|
-
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, error, runner.file)});
|
|
104
|
-
exit(1);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
await Promise.all(sharedWorkerTeardowns.map(fn => fn()));
|
|
110
|
-
} catch (error) {
|
|
111
|
-
ipc.send({type: 'uncaught-exception', err: serializeError('Shared worker teardown error', false, error, runner.file)});
|
|
112
|
-
exit(1);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
nowAndTimers.setImmediate(() => {
|
|
117
|
-
currentlyUnhandled()
|
|
118
|
-
.filter(rejection => !attributedRejections.has(rejection.promise))
|
|
119
|
-
.forEach(rejection => {
|
|
120
|
-
ipc.send({type: 'unhandled-rejection', err: serializeError('Unhandled rejection', true, rejection.reason, runner.file)});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
exit(0);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
process.on('uncaughtException', error => {
|
|
128
|
-
if (runner.attributeLeakedError(error)) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
ipc.send({type: 'uncaught-exception', err: serializeError('Uncaught exception', true, error, runner.file)});
|
|
133
|
-
exit(1);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
let accessedRunner = false;
|
|
137
|
-
exports.getRunner = () => {
|
|
138
|
-
accessedRunner = true;
|
|
139
|
-
return runner;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
exports.registerSharedWorker = (filename, initialData, teardown) => {
|
|
143
|
-
const {channel, forceUnref, ready} = ipc.registerSharedWorker(filename, initialData);
|
|
144
|
-
runner.waitForReady.push(ready);
|
|
145
|
-
sharedWorkerTeardowns.push(async () => {
|
|
146
|
-
try {
|
|
147
|
-
await teardown();
|
|
148
|
-
} finally {
|
|
149
|
-
forceUnref();
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
return channel;
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
// Store value to prevent required modules from modifying it.
|
|
156
|
-
const testPath = options.file;
|
|
157
|
-
|
|
158
|
-
// Install basic source map support.
|
|
159
|
-
const sourceMapSupport = require('source-map-support');
|
|
160
|
-
sourceMapSupport.install({
|
|
161
|
-
environment: 'node',
|
|
162
|
-
handleUncaughtExceptions: false
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const extensionsToLoadAsModules = Object.entries(options.moduleTypes)
|
|
166
|
-
.filter(([, type]) => type === 'module')
|
|
167
|
-
.map(([extension]) => extension);
|
|
168
|
-
|
|
169
|
-
// Install before processing options.require, so if helpers are added to the
|
|
170
|
-
// require configuration the *compiled* helper will be loaded.
|
|
171
|
-
const {projectDir, providerStates = []} = options;
|
|
172
|
-
const providers = providerStates.map(({type, state}) => {
|
|
173
|
-
if (type === 'babel') {
|
|
174
|
-
const provider = providerManager.babel(projectDir).worker({extensionsToLoadAsModules, state});
|
|
175
|
-
runner.powerAssert = provider.powerAssert;
|
|
176
|
-
return provider;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (type === 'typescript') {
|
|
180
|
-
return providerManager.typescript(projectDir).worker({extensionsToLoadAsModules, state});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return null;
|
|
184
|
-
}).filter(provider => provider !== null);
|
|
185
|
-
|
|
186
|
-
let requireFn = require;
|
|
187
|
-
let isESMSupported;
|
|
188
|
-
const load = async ref => {
|
|
189
|
-
for (const extension of extensionsToLoadAsModules) {
|
|
190
|
-
if (ref.endsWith(`.${extension}`)) {
|
|
191
|
-
if (typeof isESMSupported !== 'boolean') {
|
|
192
|
-
// Lazily determine support since this prints an experimental warning.
|
|
193
|
-
// eslint-disable-next-line no-await-in-loop
|
|
194
|
-
isESMSupported = await supportsESM();
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (isESMSupported) {
|
|
198
|
-
return import(pathToFileURL(ref)); // eslint-disable-line node/no-unsupported-features/es-syntax
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('ECMAScript Modules are not supported in this Node.js version.'))});
|
|
202
|
-
exit(1);
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
for (const provider of providers) {
|
|
208
|
-
if (provider.canLoad(ref)) {
|
|
209
|
-
return provider.load(ref, {requireFn});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return requireFn(ref);
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
for await (const ref of (options.require || [])) {
|
|
218
|
-
const mod = await load(ref);
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
if (Reflect.has(mod, Symbol.for('esm:package'))) {
|
|
222
|
-
requireFn = mod(module);
|
|
223
|
-
}
|
|
224
|
-
} catch {}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Install dependency tracker after the require configuration has been evaluated
|
|
228
|
-
// to make sure we also track dependencies with custom require hooks
|
|
229
|
-
dependencyTracking.install(testPath);
|
|
230
|
-
|
|
231
|
-
if (options.debug && options.debug.port !== undefined && options.debug.host !== undefined) {
|
|
232
|
-
// If an inspector was active when the main process started, and is
|
|
233
|
-
// already active for the worker process, do not open a new one.
|
|
234
|
-
const inspector = require('inspector'); // eslint-disable-line node/no-unsupported-features/node-builtins
|
|
235
|
-
if (!options.debug.active || inspector.url() === undefined) {
|
|
236
|
-
inspector.open(options.debug.port, options.debug.host, true);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (options.debug.break) {
|
|
240
|
-
debugger; // eslint-disable-line no-debugger
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
await load(testPath);
|
|
245
|
-
|
|
246
|
-
if (accessedRunner) {
|
|
247
|
-
// Unreference the IPC channel if the test file required AVA. This stops it
|
|
248
|
-
// from keeping the event loop busy, which means the `beforeExit` event can be
|
|
249
|
-
// used to detect when tests stall.
|
|
250
|
-
ipc.unref();
|
|
251
|
-
} else {
|
|
252
|
-
ipc.send({type: 'missing-ava-import'});
|
|
253
|
-
exit(1);
|
|
254
|
-
}
|
|
255
|
-
} catch (error) {
|
|
256
|
-
ipc.send({type: 'uncaught-exception', err: serializeError('Uncaught exception', true, error, runner.file)});
|
|
257
|
-
exit(1);
|
|
258
|
-
}
|
|
259
|
-
}).catch(error => {
|
|
260
|
-
// There shouldn't be any errors, but if there are we may not have managed
|
|
261
|
-
// to bootstrap enough code to serialize them. Re-throw and let the process
|
|
262
|
-
// crash.
|
|
263
|
-
setImmediate(() => {
|
|
264
|
-
throw error;
|
|
265
|
-
});
|
|
266
|
-
});
|
package/plugin.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
// Ensure the same AVA install is loaded by the test file as by the test worker
|
|
5
|
-
if (process.env.AVA_PATH && process.env.AVA_PATH !== __dirname) {
|
|
6
|
-
module.exports = require(path.join(process.env.AVA_PATH, 'plugin'));
|
|
7
|
-
} else {
|
|
8
|
-
module.exports = require('./lib/worker/plugin');
|
|
9
|
-
}
|