ava 3.12.0 → 3.15.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/eslint-plugin-helper.js +134 -29
- package/lib/api.js +14 -13
- package/lib/assert.js +11 -3
- package/lib/cli.js +19 -21
- package/lib/concordance-options.js +1 -1
- package/lib/fork.js +79 -23
- package/lib/globs.js +1 -5
- package/lib/ipc-flow-control.js +22 -25
- package/lib/load-config.js +125 -18
- package/lib/plugin-support/shared-worker-loader.js +252 -0
- package/lib/plugin-support/shared-workers.js +140 -0
- package/lib/reporters/default.js +33 -2
- package/lib/reporters/tap.js +12 -2
- package/lib/run-status.js +5 -0
- package/lib/runner.js +53 -9
- package/lib/snapshot-manager.js +67 -9
- package/lib/test.js +20 -12
- package/lib/worker/ipc.js +171 -26
- package/lib/worker/plugin.js +121 -0
- package/lib/worker/subprocess.js +24 -1
- package/package.json +35 -31
- package/plugin.d.ts +79 -0
- package/plugin.js +9 -0
- package/readme.md +2 -7
package/eslint-plugin-helper.js
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
let isMainThread = true;
|
|
3
|
+
let supportsWorkers = false;
|
|
4
|
+
try {
|
|
5
|
+
({isMainThread} = require('worker_threads'));
|
|
6
|
+
supportsWorkers = true;
|
|
7
|
+
} catch {}
|
|
8
|
+
|
|
3
9
|
const {classify, hasExtension, isHelperish, matches, normalizeFileForMatching, normalizeGlobs, normalizePatterns} = require('./lib/globs');
|
|
4
|
-
const loadConfig = require('./lib/load-config');
|
|
5
|
-
const providerManager = require('./lib/provider-manager');
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
let resolveGlobs;
|
|
12
|
+
let resolveGlobsSync;
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
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');
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
let providers;
|
|
18
|
-
if (configCache.has(projectDir)) {
|
|
19
|
-
({conf, providers} = configCache.get(projectDir));
|
|
20
|
-
} else {
|
|
21
|
-
conf = loadConfig({resolveFrom: projectDir});
|
|
19
|
+
const configCache = new Map();
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
const collectProviders = ({conf, projectDir}) => {
|
|
22
|
+
const providers = [];
|
|
24
23
|
if (Reflect.has(conf, 'babel')) {
|
|
25
24
|
const {level, main} = providerManager.babel(projectDir);
|
|
26
25
|
providers.push({
|
|
@@ -39,12 +38,125 @@ function load(projectDir, overrides) {
|
|
|
39
38
|
});
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
|
|
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;
|
|
43
150
|
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const helperCache = new Map();
|
|
44
154
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
155
|
+
function load(projectDir, overrides) {
|
|
156
|
+
const cacheKey = `${JSON.stringify(overrides)}\n${projectDir}`;
|
|
157
|
+
if (helperCache.has(cacheKey)) {
|
|
158
|
+
return helperCache.get(cacheKey);
|
|
159
|
+
}
|
|
48
160
|
|
|
49
161
|
let helperPatterns = [];
|
|
50
162
|
if (overrides && overrides.helpers !== undefined) {
|
|
@@ -55,14 +167,7 @@ function load(projectDir, overrides) {
|
|
|
55
167
|
helperPatterns = normalizePatterns(overrides.helpers);
|
|
56
168
|
}
|
|
57
169
|
|
|
58
|
-
const globs =
|
|
59
|
-
cwd: projectDir,
|
|
60
|
-
...normalizeGlobs({
|
|
61
|
-
extensions,
|
|
62
|
-
files: overrides && overrides.files ? overrides.files : conf.files,
|
|
63
|
-
providers
|
|
64
|
-
})
|
|
65
|
-
};
|
|
170
|
+
const globs = resolveGlobsSync(projectDir, overrides && overrides.extensions, overrides && overrides.files);
|
|
66
171
|
|
|
67
172
|
const classifyForESLint = file => {
|
|
68
173
|
const {isTest} = classify(file, globs);
|
package/lib/api.js
CHANGED
|
@@ -17,6 +17,7 @@ const RunStatus = require('./run-status');
|
|
|
17
17
|
const fork = require('./fork');
|
|
18
18
|
const serializeError = require('./serialize-error');
|
|
19
19
|
const {getApplicableLineNumbers} = require('./line-numbers');
|
|
20
|
+
const sharedWorkers = require('./plugin-support/shared-workers');
|
|
20
21
|
|
|
21
22
|
function resolveModules(modules) {
|
|
22
23
|
return arrify(modules).map(name => {
|
|
@@ -110,21 +111,15 @@ class Api extends Emittery {
|
|
|
110
111
|
}
|
|
111
112
|
};
|
|
112
113
|
|
|
113
|
-
let cacheDir;
|
|
114
114
|
let testFiles;
|
|
115
115
|
try {
|
|
116
|
-
cacheDir = this._createCacheDir();
|
|
117
116
|
testFiles = await globs.findTests({cwd: this.options.projectDir, ...apiOptions.globs});
|
|
118
117
|
if (selectedFiles.length === 0) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
filter: filter.map(({pattern}) => pattern),
|
|
125
|
-
testFiles
|
|
126
|
-
});
|
|
127
|
-
}
|
|
118
|
+
selectedFiles = filter.length === 0 ? testFiles : globs.applyTestFileFilter({
|
|
119
|
+
cwd: this.options.projectDir,
|
|
120
|
+
filter: filter.map(({pattern}) => pattern),
|
|
121
|
+
testFiles
|
|
122
|
+
});
|
|
128
123
|
}
|
|
129
124
|
} catch (error) {
|
|
130
125
|
selectedFiles = [];
|
|
@@ -192,7 +187,7 @@ class Api extends Emittery {
|
|
|
192
187
|
|
|
193
188
|
const {providers = []} = this.options;
|
|
194
189
|
const providerStates = (await Promise.all(providers.map(async ({type, main}) => {
|
|
195
|
-
const state = await main.compile({cacheDir, files: testFiles});
|
|
190
|
+
const state = await main.compile({cacheDir: this._createCacheDir(), files: testFiles});
|
|
196
191
|
return state === null ? null : {type, state};
|
|
197
192
|
}))).filter(state => state !== null);
|
|
198
193
|
|
|
@@ -206,6 +201,8 @@ class Api extends Emittery {
|
|
|
206
201
|
concurrency = 1;
|
|
207
202
|
}
|
|
208
203
|
|
|
204
|
+
const deregisteredSharedWorkers = [];
|
|
205
|
+
|
|
209
206
|
// Try and run each file, limited by `concurrency`.
|
|
210
207
|
await pMap(selectedFiles, async file => {
|
|
211
208
|
// No new files should be run once a test has timed out or failed,
|
|
@@ -231,6 +228,7 @@ class Api extends Emittery {
|
|
|
231
228
|
|
|
232
229
|
const worker = fork(file, options, apiOptions.nodeArguments);
|
|
233
230
|
runStatus.observeWorker(worker, file, {selectingLines: lineNumbers.length > 0});
|
|
231
|
+
deregisteredSharedWorkers.push(sharedWorkers.observeWorkerProcess(worker, runStatus));
|
|
234
232
|
|
|
235
233
|
pendingWorkers.add(worker);
|
|
236
234
|
worker.promise.then(() => {
|
|
@@ -238,8 +236,11 @@ class Api extends Emittery {
|
|
|
238
236
|
});
|
|
239
237
|
restartTimer();
|
|
240
238
|
|
|
241
|
-
|
|
239
|
+
await worker.promise;
|
|
242
240
|
}, {concurrency, stopOnError: false});
|
|
241
|
+
|
|
242
|
+
// Allow shared workers to clean up before the run ends.
|
|
243
|
+
await Promise.all(deregisteredSharedWorkers);
|
|
243
244
|
} catch (error) {
|
|
244
245
|
if (error && error.name === 'AggregateError') {
|
|
245
246
|
for (const err of error) {
|
package/lib/assert.js
CHANGED
|
@@ -87,8 +87,16 @@ function getErrorWithLongStackTrace() {
|
|
|
87
87
|
return err;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
function validateExpectations(assertion, expectations, numberArgs) { // eslint-disable-line complexity
|
|
90
|
+
function validateExpectations(assertion, expectations, numberArgs, experiments) { // eslint-disable-line complexity
|
|
91
91
|
if (numberArgs === 1 || expectations === null || expectations === undefined) {
|
|
92
|
+
if (experiments.disableNullExpectations && expectations === null) {
|
|
93
|
+
throw new AssertionError({
|
|
94
|
+
assertion,
|
|
95
|
+
message: `The second argument to \`t.${assertion}()\` must be an expectation object or \`undefined\``,
|
|
96
|
+
values: [formatWithLabel('Called with:', expectations)]
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
92
100
|
expectations = {};
|
|
93
101
|
} else if (
|
|
94
102
|
typeof expectations === 'function' ||
|
|
@@ -465,7 +473,7 @@ class Assertions {
|
|
|
465
473
|
}
|
|
466
474
|
|
|
467
475
|
try {
|
|
468
|
-
expectations = validateExpectations('throws', expectations, args.length);
|
|
476
|
+
expectations = validateExpectations('throws', expectations, args.length, experiments);
|
|
469
477
|
} catch (error) {
|
|
470
478
|
fail(error);
|
|
471
479
|
return;
|
|
@@ -531,7 +539,7 @@ class Assertions {
|
|
|
531
539
|
}
|
|
532
540
|
|
|
533
541
|
try {
|
|
534
|
-
expectations = validateExpectations('throwsAsync', expectations, args.length);
|
|
542
|
+
expectations = validateExpectations('throwsAsync', expectations, args.length, experiments);
|
|
535
543
|
} catch (error) {
|
|
536
544
|
fail(error);
|
|
537
545
|
return Promise.resolve();
|
package/lib/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ const arrify = require('arrify');
|
|
|
7
7
|
const yargs = require('yargs');
|
|
8
8
|
const readPkg = require('read-pkg');
|
|
9
9
|
const isCi = require('./is-ci');
|
|
10
|
-
const loadConfig = require('./load-config');
|
|
10
|
+
const {loadConfig} = require('./load-config');
|
|
11
11
|
|
|
12
12
|
function exit(message) {
|
|
13
13
|
console.error(`\n ${require('./chalk').get().red(figures.cross)} ${message}`);
|
|
@@ -83,7 +83,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
83
83
|
let confError = null;
|
|
84
84
|
try {
|
|
85
85
|
const {argv: {config: configFile}} = yargs.help(false);
|
|
86
|
-
conf = loadConfig({configFile});
|
|
86
|
+
conf = await loadConfig({configFile});
|
|
87
87
|
} catch (error) {
|
|
88
88
|
confError = error;
|
|
89
89
|
}
|
|
@@ -409,35 +409,33 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
409
409
|
workerArgv: argv['--']
|
|
410
410
|
});
|
|
411
411
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
stdStream: process.stderr,
|
|
424
|
-
watching: combined.watch,
|
|
425
|
-
verbose: debug !== null || combined.verbose || isCi || !process.stdout.isTTY
|
|
426
|
-
});
|
|
427
|
-
}
|
|
412
|
+
const reporter = combined.tap && !combined.watch && debug === null ? new TapReporter({
|
|
413
|
+
projectDir,
|
|
414
|
+
reportStream: process.stdout,
|
|
415
|
+
stdStream: process.stderr
|
|
416
|
+
}) : new DefaultReporter({
|
|
417
|
+
projectDir,
|
|
418
|
+
reportStream: process.stdout,
|
|
419
|
+
stdStream: process.stderr,
|
|
420
|
+
watching: combined.watch,
|
|
421
|
+
verbose: debug !== null || combined.verbose || isCi || !process.stdout.isTTY
|
|
422
|
+
});
|
|
428
423
|
|
|
429
424
|
api.on('run', plan => {
|
|
430
425
|
reporter.startRun(plan);
|
|
431
426
|
|
|
432
427
|
if (process.env.AVA_EMIT_RUN_STATUS_OVER_IPC === 'I\'ll find a payphone baby / Take some time to talk to you') {
|
|
433
|
-
|
|
428
|
+
const {controlFlow} = require('./ipc-flow-control');
|
|
429
|
+
const bufferedSend = controlFlow(process);
|
|
430
|
+
|
|
431
|
+
if (process.versions.node >= '12.16.0') {
|
|
434
432
|
plan.status.on('stateChange', evt => {
|
|
435
|
-
|
|
433
|
+
bufferedSend(evt);
|
|
436
434
|
});
|
|
437
435
|
} else {
|
|
438
436
|
const v8 = require('v8');
|
|
439
437
|
plan.status.on('stateChange', evt => {
|
|
440
|
-
|
|
438
|
+
bufferedSend([...v8.serialize(evt)]);
|
|
441
439
|
});
|
|
442
440
|
}
|
|
443
441
|
}
|
package/lib/fork.js
CHANGED
|
@@ -12,16 +12,57 @@ if (fs.realpathSync(__filename) !== __filename) {
|
|
|
12
12
|
// In case the test file imports a different AVA install,
|
|
13
13
|
// the presence of this variable allows it to require this one instead
|
|
14
14
|
const AVA_PATH = path.resolve(__dirname, '..');
|
|
15
|
+
const WORKER_PATH = require.resolve('./worker/subprocess');
|
|
16
|
+
|
|
17
|
+
class SharedWorkerChannel extends Emittery {
|
|
18
|
+
constructor({channelId, filename, initialData}, sendToFork) {
|
|
19
|
+
super();
|
|
20
|
+
|
|
21
|
+
this.id = channelId;
|
|
22
|
+
this.filename = filename;
|
|
23
|
+
this.initialData = initialData;
|
|
24
|
+
this.sendToFork = sendToFork;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
signalReady() {
|
|
28
|
+
this.sendToFork({
|
|
29
|
+
type: 'shared-worker-ready',
|
|
30
|
+
channelId: this.id
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
signalError() {
|
|
35
|
+
this.sendToFork({
|
|
36
|
+
type: 'shared-worker-error',
|
|
37
|
+
channelId: this.id
|
|
38
|
+
});
|
|
39
|
+
}
|
|
15
40
|
|
|
16
|
-
|
|
41
|
+
emitMessage({messageId, replyTo, serializedData}) {
|
|
42
|
+
this.emit('message', {
|
|
43
|
+
messageId,
|
|
44
|
+
replyTo,
|
|
45
|
+
serializedData
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
forwardMessageToFork({messageId, replyTo, serializedData}) {
|
|
50
|
+
this.sendToFork({
|
|
51
|
+
type: 'shared-worker-message',
|
|
52
|
+
channelId: this.id,
|
|
53
|
+
messageId,
|
|
54
|
+
replyTo,
|
|
55
|
+
serializedData
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
17
59
|
|
|
18
|
-
|
|
19
|
-
// FIXME: Fix this in api.js or cli.js.
|
|
20
|
-
const serializeOptions = useAdvanced ?
|
|
21
|
-
options => JSON.parse(JSON.stringify(options)) : // Use JSON serialization to remove non-clonable values.
|
|
22
|
-
options => options;
|
|
60
|
+
let forkCounter = 0;
|
|
23
61
|
|
|
24
62
|
module.exports = (file, options, execArgv = process.execArgv) => {
|
|
63
|
+
const forkId = `fork/${++forkCounter}`;
|
|
64
|
+
const sharedWorkerChannels = new Map();
|
|
65
|
+
|
|
25
66
|
let finished = false;
|
|
26
67
|
|
|
27
68
|
const emitter = new Emittery();
|
|
@@ -32,17 +73,17 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
32
73
|
};
|
|
33
74
|
|
|
34
75
|
options = {
|
|
35
|
-
file,
|
|
36
76
|
baseDir: process.cwd(),
|
|
77
|
+
file,
|
|
78
|
+
forkId,
|
|
37
79
|
...options
|
|
38
80
|
};
|
|
39
81
|
|
|
40
|
-
const subprocess = childProcess.fork(
|
|
82
|
+
const subprocess = childProcess.fork(WORKER_PATH, options.workerArgv, {
|
|
41
83
|
cwd: options.projectDir,
|
|
42
84
|
silent: true,
|
|
43
85
|
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables, AVA_PATH},
|
|
44
|
-
execArgv
|
|
45
|
-
serialization: useAdvanced ? 'advanced' : 'json'
|
|
86
|
+
execArgv
|
|
46
87
|
});
|
|
47
88
|
|
|
48
89
|
subprocess.stdout.on('data', chunk => {
|
|
@@ -73,15 +114,25 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
73
114
|
return;
|
|
74
115
|
}
|
|
75
116
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
switch (message.ava.type) {
|
|
118
|
+
case 'ready-for-options':
|
|
119
|
+
send({type: 'options', options});
|
|
120
|
+
break;
|
|
121
|
+
case 'shared-worker-connect': {
|
|
122
|
+
const channel = new SharedWorkerChannel(message.ava, send);
|
|
123
|
+
sharedWorkerChannels.set(channel.id, channel);
|
|
124
|
+
emitter.emit('connectSharedWorker', channel);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case 'shared-worker-message':
|
|
129
|
+
sharedWorkerChannels.get(message.ava.channelId).emitMessage(message.ava);
|
|
130
|
+
break;
|
|
131
|
+
case 'ping':
|
|
132
|
+
send({type: 'pong'});
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
emitStateChange(message.ava);
|
|
85
136
|
}
|
|
86
137
|
});
|
|
87
138
|
|
|
@@ -106,6 +157,10 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
106
157
|
});
|
|
107
158
|
|
|
108
159
|
return {
|
|
160
|
+
file,
|
|
161
|
+
forkId,
|
|
162
|
+
promise,
|
|
163
|
+
|
|
109
164
|
exit() {
|
|
110
165
|
forcedExit = true;
|
|
111
166
|
subprocess.kill();
|
|
@@ -115,11 +170,12 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
115
170
|
send({type: 'peer-failed'});
|
|
116
171
|
},
|
|
117
172
|
|
|
118
|
-
|
|
119
|
-
return emitter.on('
|
|
173
|
+
onConnectSharedWorker(listener) {
|
|
174
|
+
return emitter.on('connectSharedWorker', listener);
|
|
120
175
|
},
|
|
121
176
|
|
|
122
|
-
|
|
123
|
-
|
|
177
|
+
onStateChange(listener) {
|
|
178
|
+
return emitter.on('stateChange', listener);
|
|
179
|
+
}
|
|
124
180
|
};
|
|
125
181
|
};
|
package/lib/globs.js
CHANGED
|
@@ -82,11 +82,7 @@ function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: igno
|
|
|
82
82
|
filePatterns = defaultTestPatterns;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
ignoredByWatcherPatterns = [...defaultIgnoredByWatcherPatterns, ...normalizePatterns(ignoredByWatcherPatterns)];
|
|
87
|
-
} else {
|
|
88
|
-
ignoredByWatcherPatterns = [...defaultIgnoredByWatcherPatterns];
|
|
89
|
-
}
|
|
85
|
+
ignoredByWatcherPatterns = ignoredByWatcherPatterns ? [...defaultIgnoredByWatcherPatterns, ...normalizePatterns(ignoredByWatcherPatterns)] : [...defaultIgnoredByWatcherPatterns];
|
|
90
86
|
|
|
91
87
|
for (const {level, main} of providers) {
|
|
92
88
|
if (level >= providerManager.levels.pathRewrites) {
|
package/lib/ipc-flow-control.js
CHANGED
|
@@ -1,40 +1,37 @@
|
|
|
1
|
-
// Manage how quickly messages are delivered to the channel. In theory, we
|
|
2
|
-
// should be able to call `send()` until it returns `false` but this leads to
|
|
3
|
-
// crashes with advanced serialization, see
|
|
4
|
-
// <https://github.com/nodejs/node/issues/34797>.
|
|
5
|
-
//
|
|
6
|
-
// Even if that's fixed (and the Node.js versions with the fixes are the
|
|
7
|
-
// minimally supported versions) we need flow control based on `send()`'s return
|
|
8
|
-
// value.
|
|
9
|
-
|
|
10
|
-
const nowAndTimers = require('./now-and-timers');
|
|
11
|
-
|
|
12
1
|
function controlFlow(channel) {
|
|
13
|
-
let
|
|
2
|
+
let errored = false;
|
|
3
|
+
let deliverImmediately = true;
|
|
14
4
|
|
|
15
|
-
const
|
|
16
|
-
const deliverNext =
|
|
17
|
-
if (
|
|
18
|
-
|
|
5
|
+
const backlog = [];
|
|
6
|
+
const deliverNext = error => {
|
|
7
|
+
if (error !== null) {
|
|
8
|
+
errored = true;
|
|
19
9
|
}
|
|
20
10
|
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
return;
|
|
11
|
+
if (errored || !channel.connected) {
|
|
12
|
+
backlog.length = 0; // Free memory.
|
|
13
|
+
return; // We can't send.
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let ok = true;
|
|
17
|
+
while (ok && backlog.length > 0) { // Stop sending after backpressure.
|
|
18
|
+
ok = channel.send(backlog.shift(), deliverNext);
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
// Re-enable immediate delivery if there is no backpressure and the backlog
|
|
22
|
+
// has been cleared.
|
|
23
|
+
deliverImmediately = ok && backlog.length === 0;
|
|
27
24
|
};
|
|
28
25
|
|
|
29
26
|
return message => {
|
|
30
|
-
if (!channel.connected) {
|
|
27
|
+
if (errored || !channel.connected) {
|
|
31
28
|
return;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
if (deliverImmediately) {
|
|
32
|
+
deliverImmediately = channel.send(message, deliverNext);
|
|
33
|
+
} else {
|
|
34
|
+
backlog.push(message);
|
|
38
35
|
}
|
|
39
36
|
};
|
|
40
37
|
}
|