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
|
@@ -1,37 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
const util = require('util'); // eslint-disable-line unicorn/import-style
|
|
3
|
-
const ansiStyles = require('ansi-styles');
|
|
4
|
-
const stripAnsi = require('strip-ansi');
|
|
5
|
-
const cloneDeepWith = require('lodash/cloneDeepWith');
|
|
6
|
-
const reactPlugin = require('@concordance/react');
|
|
7
|
-
const chalk = require('./chalk').get();
|
|
1
|
+
import {inspect} from 'node:util';
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const plugins = [avaReactPlugin];
|
|
3
|
+
import ansiStyles from 'ansi-styles';
|
|
4
|
+
import {Chalk} from 'chalk'; // eslint-disable-line unicorn/import-style
|
|
5
|
+
import stripAnsi from 'strip-ansi';
|
|
13
6
|
|
|
14
|
-
|
|
7
|
+
import {chalk} from './chalk.js';
|
|
8
|
+
|
|
9
|
+
const forceColor = new Chalk({level: Math.max(chalk.level, 1)});
|
|
15
10
|
|
|
16
11
|
const colorTheme = {
|
|
17
12
|
boolean: ansiStyles.yellow,
|
|
18
13
|
circular: forceColor.grey('[Circular]'),
|
|
19
14
|
date: {
|
|
20
15
|
invalid: forceColor.red('invalid'),
|
|
21
|
-
value: ansiStyles.blue
|
|
16
|
+
value: ansiStyles.blue,
|
|
22
17
|
},
|
|
23
18
|
diffGutters: {
|
|
24
19
|
actual: forceColor.red('-') + ' ',
|
|
25
20
|
expected: forceColor.green('+') + ' ',
|
|
26
|
-
padding: ' '
|
|
21
|
+
padding: ' ',
|
|
27
22
|
},
|
|
28
23
|
error: {
|
|
29
24
|
ctor: {open: ansiStyles.grey.open + '(', close: ')' + ansiStyles.grey.close},
|
|
30
|
-
name: ansiStyles.magenta
|
|
25
|
+
name: ansiStyles.magenta,
|
|
31
26
|
},
|
|
32
27
|
function: {
|
|
33
28
|
name: ansiStyles.blue,
|
|
34
|
-
stringTag: ansiStyles.magenta
|
|
29
|
+
stringTag: ansiStyles.magenta,
|
|
35
30
|
},
|
|
36
31
|
global: ansiStyles.magenta,
|
|
37
32
|
item: {after: forceColor.grey(',')},
|
|
@@ -45,44 +40,16 @@ const colorTheme = {
|
|
|
45
40
|
closeBracket: forceColor.grey('}'),
|
|
46
41
|
ctor: ansiStyles.magenta,
|
|
47
42
|
stringTag: {open: ansiStyles.magenta.open + '@', close: ansiStyles.magenta.close},
|
|
48
|
-
secondaryStringTag: {open: ansiStyles.grey.open + '@', close: ansiStyles.grey.close}
|
|
43
|
+
secondaryStringTag: {open: ansiStyles.grey.open + '@', close: ansiStyles.grey.close},
|
|
49
44
|
},
|
|
50
45
|
property: {
|
|
51
46
|
after: forceColor.grey(','),
|
|
52
47
|
keyBracket: {open: forceColor.grey('['), close: forceColor.grey(']')},
|
|
53
|
-
valueFallback: forceColor.grey('…')
|
|
54
|
-
},
|
|
55
|
-
react: {
|
|
56
|
-
functionType: forceColor.grey('\u235F'),
|
|
57
|
-
openTag: {
|
|
58
|
-
start: forceColor.grey('<'),
|
|
59
|
-
end: forceColor.grey('>'),
|
|
60
|
-
selfClose: forceColor.grey('/'),
|
|
61
|
-
selfCloseVoid: ' ' + forceColor.grey('/')
|
|
62
|
-
},
|
|
63
|
-
closeTag: {
|
|
64
|
-
open: forceColor.grey('</'),
|
|
65
|
-
close: forceColor.grey('>')
|
|
66
|
-
},
|
|
67
|
-
tagName: ansiStyles.magenta,
|
|
68
|
-
attribute: {
|
|
69
|
-
separator: '=',
|
|
70
|
-
value: {
|
|
71
|
-
openBracket: forceColor.grey('{'),
|
|
72
|
-
closeBracket: forceColor.grey('}'),
|
|
73
|
-
string: {
|
|
74
|
-
line: {open: forceColor.blue('"'), close: forceColor.blue('"'), escapeQuote: '"'}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
child: {
|
|
79
|
-
openBracket: forceColor.grey('{'),
|
|
80
|
-
closeBracket: forceColor.grey('}')
|
|
81
|
-
}
|
|
48
|
+
valueFallback: forceColor.grey('…'),
|
|
82
49
|
},
|
|
83
50
|
regexp: {
|
|
84
51
|
source: {open: ansiStyles.blue.open + '/', close: '/' + ansiStyles.blue.close},
|
|
85
|
-
flags: ansiStyles.yellow
|
|
52
|
+
flags: ansiStyles.yellow,
|
|
86
53
|
},
|
|
87
54
|
stats: {separator: forceColor.grey('---')},
|
|
88
55
|
string: {
|
|
@@ -94,45 +61,42 @@ const colorTheme = {
|
|
|
94
61
|
diff: {
|
|
95
62
|
insert: {
|
|
96
63
|
open: ansiStyles.bgGreen.open + ansiStyles.black.open,
|
|
97
|
-
close: ansiStyles.black.close + ansiStyles.bgGreen.close
|
|
64
|
+
close: ansiStyles.black.close + ansiStyles.bgGreen.close,
|
|
98
65
|
},
|
|
99
66
|
delete: {
|
|
100
67
|
open: ansiStyles.bgRed.open + ansiStyles.black.open,
|
|
101
|
-
close: ansiStyles.black.close + ansiStyles.bgRed.close
|
|
68
|
+
close: ansiStyles.black.close + ansiStyles.bgRed.close,
|
|
102
69
|
},
|
|
103
70
|
equal: ansiStyles.blue,
|
|
104
71
|
insertLine: {
|
|
105
72
|
open: ansiStyles.green.open,
|
|
106
|
-
close: ansiStyles.green.close
|
|
73
|
+
close: ansiStyles.green.close,
|
|
107
74
|
},
|
|
108
75
|
deleteLine: {
|
|
109
76
|
open: ansiStyles.red.open,
|
|
110
|
-
close: ansiStyles.red.close
|
|
111
|
-
}
|
|
112
|
-
}
|
|
77
|
+
close: ansiStyles.red.close,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
113
80
|
},
|
|
114
81
|
symbol: ansiStyles.yellow,
|
|
115
82
|
typedArray: {
|
|
116
|
-
bytes: ansiStyles.yellow
|
|
83
|
+
bytes: ansiStyles.yellow,
|
|
117
84
|
},
|
|
118
|
-
undefined: ansiStyles.yellow
|
|
85
|
+
undefined: ansiStyles.yellow,
|
|
119
86
|
};
|
|
120
87
|
|
|
121
|
-
const plainTheme =
|
|
122
|
-
if (typeof value === 'string') {
|
|
123
|
-
return stripAnsi(value);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
88
|
+
const plainTheme = JSON.parse(JSON.stringify(colorTheme), value => typeof value === 'string' ? stripAnsi(value) : value);
|
|
126
89
|
|
|
127
90
|
const theme = chalk.level > 0 ? colorTheme : plainTheme;
|
|
128
91
|
|
|
129
|
-
|
|
92
|
+
const concordanceOptions = {
|
|
130
93
|
// Use Node's object inspection depth, clamped to a minimum of 3
|
|
131
94
|
get maxDepth() {
|
|
132
|
-
return Math.max(3,
|
|
95
|
+
return Math.max(3, inspect.defaultOptions.depth);
|
|
133
96
|
},
|
|
134
|
-
|
|
135
|
-
theme
|
|
97
|
+
theme,
|
|
136
98
|
};
|
|
137
99
|
|
|
138
|
-
|
|
100
|
+
export default concordanceOptions;
|
|
101
|
+
|
|
102
|
+
export const snapshotManager = {theme: plainTheme};
|
package/lib/context-ref.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const clone = require('lodash/clone');
|
|
3
|
-
|
|
4
|
-
class ContextRef {
|
|
1
|
+
export default class ContextRef {
|
|
5
2
|
constructor() {
|
|
6
3
|
this.value = {};
|
|
7
4
|
}
|
|
@@ -18,7 +15,6 @@ class ContextRef {
|
|
|
18
15
|
return new LateBinding(this);
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
|
-
module.exports = ContextRef;
|
|
22
18
|
|
|
23
19
|
class LateBinding extends ContextRef {
|
|
24
20
|
constructor(ref) {
|
|
@@ -29,7 +25,8 @@ class LateBinding extends ContextRef {
|
|
|
29
25
|
|
|
30
26
|
get() {
|
|
31
27
|
if (!this.bound) {
|
|
32
|
-
this.
|
|
28
|
+
const value = this.ref.get();
|
|
29
|
+
this.set(value !== null && typeof value === 'object' ? {...value} : value);
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
return super.get();
|
package/lib/create-chain.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
1
|
const chainRegistry = new WeakMap();
|
|
3
2
|
|
|
4
3
|
function startChain(name, call, defaults) {
|
|
@@ -48,20 +47,16 @@ function createHookChain(hook, isAfterHook) {
|
|
|
48
47
|
// * `skip` must come at the end
|
|
49
48
|
// * no `only`
|
|
50
49
|
// * no repeating
|
|
51
|
-
extendChain(hook, 'cb', 'callback');
|
|
52
50
|
extendChain(hook, 'skip', 'skipped');
|
|
53
|
-
extendChain(hook.cb, 'skip', 'skipped');
|
|
54
51
|
if (isAfterHook) {
|
|
55
52
|
extendChain(hook, 'always');
|
|
56
|
-
extendChain(hook.always, 'cb', 'callback');
|
|
57
53
|
extendChain(hook.always, 'skip', 'skipped');
|
|
58
|
-
extendChain(hook.always.cb, 'skip', 'skipped');
|
|
59
54
|
}
|
|
60
55
|
|
|
61
56
|
return hook;
|
|
62
57
|
}
|
|
63
58
|
|
|
64
|
-
function createChain(fn, defaults, meta) {
|
|
59
|
+
export default function createChain(fn, defaults, meta) {
|
|
65
60
|
// Test chaining rules:
|
|
66
61
|
// * `serial` must come at the start
|
|
67
62
|
// * `only` and `skip` must come at the end
|
|
@@ -69,27 +64,15 @@ function createChain(fn, defaults, meta) {
|
|
|
69
64
|
// * `only` and `skip` cannot be chained together
|
|
70
65
|
// * no repeating
|
|
71
66
|
const root = startChain('test', fn, {...defaults, type: 'test'});
|
|
72
|
-
extendChain(root, 'cb', 'callback');
|
|
73
67
|
extendChain(root, 'failing');
|
|
74
68
|
extendChain(root, 'only', 'exclusive');
|
|
75
69
|
extendChain(root, 'serial');
|
|
76
70
|
extendChain(root, 'skip', 'skipped');
|
|
77
|
-
extendChain(root.cb, 'failing');
|
|
78
|
-
extendChain(root.cb, 'only', 'exclusive');
|
|
79
|
-
extendChain(root.cb, 'skip', 'skipped');
|
|
80
|
-
extendChain(root.cb.failing, 'only', 'exclusive');
|
|
81
|
-
extendChain(root.cb.failing, 'skip', 'skipped');
|
|
82
71
|
extendChain(root.failing, 'only', 'exclusive');
|
|
83
72
|
extendChain(root.failing, 'skip', 'skipped');
|
|
84
|
-
extendChain(root.serial, 'cb', 'callback');
|
|
85
73
|
extendChain(root.serial, 'failing');
|
|
86
74
|
extendChain(root.serial, 'only', 'exclusive');
|
|
87
75
|
extendChain(root.serial, 'skip', 'skipped');
|
|
88
|
-
extendChain(root.serial.cb, 'failing');
|
|
89
|
-
extendChain(root.serial.cb, 'only', 'exclusive');
|
|
90
|
-
extendChain(root.serial.cb, 'skip', 'skipped');
|
|
91
|
-
extendChain(root.serial.cb.failing, 'only', 'exclusive');
|
|
92
|
-
extendChain(root.serial.cb.failing, 'skip', 'skipped');
|
|
93
76
|
extendChain(root.serial.failing, 'only', 'exclusive');
|
|
94
77
|
extendChain(root.serial.failing, 'skip', 'skipped');
|
|
95
78
|
|
|
@@ -108,9 +91,38 @@ function createChain(fn, defaults, meta) {
|
|
|
108
91
|
root.todo = startChain('test.todo', fn, {...defaults, type: 'test', todo: true});
|
|
109
92
|
root.serial.todo = startChain('test.serial.todo', fn, {...defaults, serial: true, type: 'test', todo: true});
|
|
110
93
|
|
|
94
|
+
root.macro = options => {
|
|
95
|
+
if (typeof options === 'function') {
|
|
96
|
+
return Object.freeze({exec: options});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return Object.freeze({exec: options.exec, title: options.title});
|
|
100
|
+
};
|
|
101
|
+
|
|
111
102
|
root.meta = meta;
|
|
112
103
|
|
|
104
|
+
// Our type definition uses ESM syntax; when using CJS with VSCode, the
|
|
105
|
+
// auto-completion assumes the root is accessed through `require('ava').default`.
|
|
106
|
+
// Placate VSCode by adding a mostly hidden default property on the root.
|
|
107
|
+
// This is available through both CJS and ESM imports. We use a proxy so that
|
|
108
|
+
// we don't end up with root.default.default.default chains.
|
|
109
|
+
Object.defineProperty(root, 'default', {
|
|
110
|
+
configurable: false,
|
|
111
|
+
enumerable: false,
|
|
112
|
+
writable: false,
|
|
113
|
+
value: new Proxy(root, {
|
|
114
|
+
apply(target, thisArg, argumentsList) {
|
|
115
|
+
target.apply(thisArg, argumentsList);
|
|
116
|
+
},
|
|
117
|
+
get(target, prop) {
|
|
118
|
+
if (prop === 'default') {
|
|
119
|
+
throw new TypeError('Cannot access default.default');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return target[prop];
|
|
123
|
+
},
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
|
|
113
127
|
return root;
|
|
114
128
|
}
|
|
115
|
-
|
|
116
|
-
module.exports = createChain;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
function validateEnvironmentVariables(environmentVariables) {
|
|
1
|
+
export default function validateEnvironmentVariables(environmentVariables) {
|
|
3
2
|
if (!environmentVariables) {
|
|
4
3
|
return {};
|
|
5
4
|
}
|
|
@@ -12,5 +11,3 @@ function validateEnvironmentVariables(environmentVariables) {
|
|
|
12
11
|
|
|
13
12
|
return environmentVariables;
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
module.exports = validateEnvironmentVariables;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import v8 from 'node:v8';
|
|
2
|
+
import {parentPort, workerData} from 'node:worker_threads';
|
|
3
|
+
|
|
4
|
+
import normalizeExtensions from './extensions.js';
|
|
5
|
+
import {normalizeGlobs} from './globs.js';
|
|
6
|
+
import {loadConfig} from './load-config.js';
|
|
7
|
+
import providerManager from './provider-manager.js';
|
|
8
|
+
|
|
9
|
+
const MAX_DATA_LENGTH_EXCLUSIVE = 100 * 1024; // Allocate 100 KiB to exchange globs.
|
|
10
|
+
|
|
11
|
+
const configCache = new Map();
|
|
12
|
+
|
|
13
|
+
const collectProviders = async ({conf, projectDir}) => {
|
|
14
|
+
const providers = [];
|
|
15
|
+
if (Reflect.has(conf, 'typescript')) {
|
|
16
|
+
const {level, main} = await providerManager.typescript(projectDir);
|
|
17
|
+
providers.push({
|
|
18
|
+
level,
|
|
19
|
+
main: main({config: conf.typescript}),
|
|
20
|
+
type: 'typescript',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return providers;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFiles}) => {
|
|
28
|
+
const extensions = overrideExtensions
|
|
29
|
+
? normalizeExtensions(overrideExtensions)
|
|
30
|
+
: normalizeExtensions(conf.extensions, providers);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
cwd: projectDir,
|
|
34
|
+
...normalizeGlobs({
|
|
35
|
+
extensions,
|
|
36
|
+
files: overrideFiles ? overrideFiles : conf.files,
|
|
37
|
+
providers,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
|
|
43
|
+
if (!configCache.has(projectDir)) {
|
|
44
|
+
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(async conf => {
|
|
45
|
+
const providers = await collectProviders({conf, projectDir});
|
|
46
|
+
return {conf, providers};
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const {conf, providers} = await configCache.get(projectDir);
|
|
51
|
+
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const data = new Uint8Array(workerData.dataBuffer);
|
|
55
|
+
const sync = new Int32Array(workerData.syncBuffer);
|
|
56
|
+
|
|
57
|
+
const handleMessage = async ({projectDir, overrideExtensions, overrideFiles}) => {
|
|
58
|
+
let encoded;
|
|
59
|
+
try {
|
|
60
|
+
const globs = await resolveGlobs(projectDir, overrideExtensions, overrideFiles);
|
|
61
|
+
encoded = v8.serialize(globs);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
encoded = v8.serialize(error);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const byteLength = encoded.length < MAX_DATA_LENGTH_EXCLUSIVE ? encoded.copy(data) : MAX_DATA_LENGTH_EXCLUSIVE;
|
|
67
|
+
Atomics.store(sync, 0, byteLength);
|
|
68
|
+
Atomics.notify(sync, 0);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
parentPort.on('message', handleMessage);
|
|
72
|
+
handleMessage(workerData.firstMessage);
|
|
73
|
+
delete workerData.firstMessage;
|
package/lib/extensions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export default function resolveExtensions(configuredExtensions, providers = []) {
|
|
2
2
|
// Combine all extensions possible for testing. Remove duplicate extensions.
|
|
3
3
|
const duplicates = new Set();
|
|
4
4
|
const seen = new Set();
|
|
@@ -43,4 +43,4 @@ module.exports = (configuredExtensions, providers = []) => {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
return [...seen];
|
|
46
|
-
}
|
|
46
|
+
}
|
package/lib/fork.js
CHANGED
|
@@ -1,68 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const Emittery = require('emittery');
|
|
6
|
-
const {controlFlow} = require('./ipc-flow-control');
|
|
7
|
-
|
|
8
|
-
if (fs.realpathSync(__filename) !== __filename) {
|
|
9
|
-
console.warn('WARNING: `npm link ava` and the `--preserve-symlink` flag are incompatible. We have detected that AVA is linked via `npm link`, and that you are using either an early version of Node 6, or the `--preserve-symlink` flag. This breaks AVA. You should upgrade to Node 6.2.0+, avoid the `--preserve-symlink` flag, or avoid using `npm link ava`.');
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// In case the test file imports a different AVA install,
|
|
13
|
-
// the presence of this variable allows it to require this one instead
|
|
14
|
-
const AVA_PATH = path.resolve(__dirname, '..');
|
|
15
|
-
const WORKER_PATH = require.resolve('./worker/subprocess');
|
|
1
|
+
import childProcess from 'node:child_process';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
import {Worker} from 'node:worker_threads';
|
|
16
5
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
super();
|
|
6
|
+
import Emittery from 'emittery';
|
|
7
|
+
import {pEvent} from 'p-event';
|
|
20
8
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
this.initialData = initialData;
|
|
24
|
-
this.sendToFork = sendToFork;
|
|
25
|
-
}
|
|
9
|
+
import {controlFlow} from './ipc-flow-control.cjs';
|
|
10
|
+
import serializeError from './serialize-error.js';
|
|
26
11
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
signalError() {
|
|
35
|
-
this.sendToFork({
|
|
36
|
-
type: 'shared-worker-error',
|
|
37
|
-
channelId: this.id
|
|
38
|
-
});
|
|
39
|
-
}
|
|
12
|
+
let workerPath = new URL('worker/base.js', import.meta.url);
|
|
13
|
+
export function _testOnlyReplaceWorkerPath(replacement) {
|
|
14
|
+
workerPath = replacement;
|
|
15
|
+
}
|
|
40
16
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
17
|
+
const additionalExecArgv = ['--enable-source-maps'];
|
|
18
|
+
|
|
19
|
+
const createWorker = (options, execArgv) => {
|
|
20
|
+
let worker;
|
|
21
|
+
let postMessage;
|
|
22
|
+
let close;
|
|
23
|
+
if (options.workerThreads) {
|
|
24
|
+
worker = new Worker(workerPath, {
|
|
25
|
+
argv: options.workerArgv,
|
|
26
|
+
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables},
|
|
27
|
+
execArgv: [...execArgv, ...additionalExecArgv],
|
|
28
|
+
workerData: {
|
|
29
|
+
options,
|
|
30
|
+
},
|
|
31
|
+
trackUnmanagedFds: true,
|
|
32
|
+
stdin: true,
|
|
33
|
+
stdout: true,
|
|
34
|
+
stderr: true,
|
|
46
35
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
36
|
+
postMessage = worker.postMessage.bind(worker);
|
|
37
|
+
|
|
38
|
+
// Ensure we've seen this event before we terminate the worker thread, as a
|
|
39
|
+
// workaround for https://github.com/nodejs/node/issues/38418.
|
|
40
|
+
const starting = pEvent(worker, 'message', ({ava}) => ava && ava.type === 'starting');
|
|
41
|
+
|
|
42
|
+
close = async () => {
|
|
43
|
+
try {
|
|
44
|
+
await starting;
|
|
45
|
+
await worker.terminate();
|
|
46
|
+
} finally {
|
|
47
|
+
// No-op
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
} else {
|
|
51
|
+
worker = childProcess.fork(fileURLToPath(workerPath), options.workerArgv, {
|
|
52
|
+
cwd: options.projectDir,
|
|
53
|
+
silent: true,
|
|
54
|
+
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables},
|
|
55
|
+
execArgv: [...execArgv, ...additionalExecArgv],
|
|
56
56
|
});
|
|
57
|
+
postMessage = controlFlow(worker);
|
|
58
|
+
close = async () => worker.kill();
|
|
57
59
|
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let forkCounter = 0;
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
return {
|
|
62
|
+
worker,
|
|
63
|
+
postMessage,
|
|
64
|
+
close,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
65
67
|
|
|
68
|
+
export default function loadFork(file, options, execArgv = process.execArgv) {
|
|
66
69
|
let finished = false;
|
|
67
70
|
|
|
68
71
|
const emitter = new Emittery();
|
|
@@ -75,31 +78,22 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
75
78
|
options = {
|
|
76
79
|
baseDir: process.cwd(),
|
|
77
80
|
file,
|
|
78
|
-
|
|
79
|
-
...options
|
|
81
|
+
...options,
|
|
80
82
|
};
|
|
81
83
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
silent: true,
|
|
85
|
-
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables, AVA_PATH},
|
|
86
|
-
execArgv
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
subprocess.stdout.on('data', chunk => {
|
|
84
|
+
const {worker, postMessage, close} = createWorker(options, execArgv);
|
|
85
|
+
worker.stdout.on('data', chunk => {
|
|
90
86
|
emitStateChange({type: 'worker-stdout', chunk});
|
|
91
87
|
});
|
|
92
88
|
|
|
93
|
-
|
|
89
|
+
worker.stderr.on('data', chunk => {
|
|
94
90
|
emitStateChange({type: 'worker-stderr', chunk});
|
|
95
91
|
});
|
|
96
92
|
|
|
97
|
-
const bufferedSend = controlFlow(subprocess);
|
|
98
|
-
|
|
99
93
|
let forcedExit = false;
|
|
100
94
|
const send = evt => {
|
|
101
95
|
if (!finished && !forcedExit) {
|
|
102
|
-
|
|
96
|
+
postMessage({ava: evt});
|
|
103
97
|
}
|
|
104
98
|
};
|
|
105
99
|
|
|
@@ -109,7 +103,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
109
103
|
resolve();
|
|
110
104
|
};
|
|
111
105
|
|
|
112
|
-
|
|
106
|
+
worker.on('message', message => {
|
|
113
107
|
if (!message.ava) {
|
|
114
108
|
return;
|
|
115
109
|
}
|
|
@@ -119,15 +113,18 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
119
113
|
send({type: 'options', options});
|
|
120
114
|
break;
|
|
121
115
|
case 'shared-worker-connect': {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
116
|
+
const {channelId, filename, initialData, port} = message.ava;
|
|
117
|
+
emitter.emit('connectSharedWorker', {
|
|
118
|
+
filename,
|
|
119
|
+
initialData,
|
|
120
|
+
port,
|
|
121
|
+
signalError() {
|
|
122
|
+
send({type: 'shared-worker-error', channelId});
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
125
|
break;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
case 'shared-worker-message':
|
|
129
|
-
sharedWorkerChannels.get(message.ava.channelId).emitMessage(message.ava);
|
|
130
|
-
break;
|
|
131
128
|
case 'ping':
|
|
132
129
|
send({type: 'pong'});
|
|
133
130
|
break;
|
|
@@ -136,12 +133,12 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
136
133
|
}
|
|
137
134
|
});
|
|
138
135
|
|
|
139
|
-
|
|
140
|
-
emitStateChange({type: 'worker-failed', err});
|
|
136
|
+
worker.on('error', error => {
|
|
137
|
+
emitStateChange({type: 'worker-failed', err: serializeError('Worker error', false, error, file)});
|
|
141
138
|
finish();
|
|
142
139
|
});
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
worker.on('exit', (code, signal) => {
|
|
145
142
|
if (forcedExit) {
|
|
146
143
|
emitStateChange({type: 'worker-finished', forcedExit});
|
|
147
144
|
} else if (code > 0) {
|
|
@@ -158,12 +155,12 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
158
155
|
|
|
159
156
|
return {
|
|
160
157
|
file,
|
|
161
|
-
|
|
158
|
+
threadId: worker.threadId,
|
|
162
159
|
promise,
|
|
163
160
|
|
|
164
161
|
exit() {
|
|
165
162
|
forcedExit = true;
|
|
166
|
-
|
|
163
|
+
close();
|
|
167
164
|
},
|
|
168
165
|
|
|
169
166
|
notifyOfPeerFailure() {
|
|
@@ -176,6 +173,6 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
176
173
|
|
|
177
174
|
onStateChange(listener) {
|
|
178
175
|
return emitter.on('stateChange', listener);
|
|
179
|
-
}
|
|
176
|
+
},
|
|
180
177
|
};
|
|
181
|
-
}
|
|
178
|
+
}
|