ava 4.0.0-alpha.1 → 4.0.1
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/{eslint-plugin-helper.js → entrypoints/eslint-plugin-helper.cjs} +20 -7
- 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 -709
- package/lib/api.js +95 -46
- package/lib/assert.js +122 -173
- package/lib/chalk.js +9 -14
- package/lib/cli.js +105 -97
- package/lib/code-excerpt.js +12 -17
- package/lib/concordance-options.js +30 -31
- package/lib/context-ref.js +3 -6
- package/lib/create-chain.js +32 -4
- package/lib/environment-variables.js +1 -4
- package/lib/eslint-plugin-helper-worker.js +16 -26
- package/lib/extensions.js +2 -2
- package/lib/fork.js +42 -83
- 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 +10 -17
- package/lib/load-config.js +62 -56
- package/lib/module-types.js +3 -3
- 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 -43
- package/lib/provider-manager.js +20 -14
- package/lib/reporters/beautify-stack.js +6 -11
- package/lib/reporters/colors.js +40 -15
- package/lib/reporters/default.js +115 -350
- 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 +15 -16
- package/lib/run-status.js +25 -23
- package/lib/runner.js +138 -127
- package/lib/scheduler.js +42 -36
- package/lib/serialize-error.js +34 -34
- package/lib/snapshot-manager.js +83 -76
- package/lib/test.js +114 -195
- package/lib/watcher.js +65 -40
- package/lib/worker/base.js +48 -99
- package/lib/worker/channel.cjs +290 -0
- package/lib/worker/dependency-tracker.js +22 -22
- package/lib/worker/guard-environment.cjs +19 -0
- package/lib/worker/line-numbers.js +57 -19
- package/lib/worker/main.cjs +12 -0
- package/lib/worker/{options.js → options.cjs} +0 -0
- package/lib/worker/{plugin.js → plugin.cjs} +31 -16
- package/lib/worker/state.cjs +5 -0
- package/lib/worker/{utils.js → utils.cjs} +1 -1
- package/package.json +60 -68
- package/plugin.d.ts +51 -53
- package/readme.md +5 -12
- 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/index.js +0 -8
- package/lib/worker/channel.js +0 -218
- package/lib/worker/main.js +0 -20
- package/plugin.js +0 -9
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) {
|
|
@@ -57,7 +56,7 @@ function createHookChain(hook, isAfterHook) {
|
|
|
57
56
|
return hook;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
function createChain(fn, defaults, meta) {
|
|
59
|
+
export default function createChain(fn, defaults, meta) {
|
|
61
60
|
// Test chaining rules:
|
|
62
61
|
// * `serial` must come at the start
|
|
63
62
|
// * `only` and `skip` must come at the end
|
|
@@ -92,9 +91,38 @@ function createChain(fn, defaults, meta) {
|
|
|
92
91
|
root.todo = startChain('test.todo', fn, {...defaults, type: 'test', todo: true});
|
|
93
92
|
root.serial.todo = startChain('test.serial.todo', fn, {...defaults, serial: true, type: 'test', todo: true});
|
|
94
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
|
+
|
|
95
102
|
root.meta = meta;
|
|
96
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
|
+
|
|
97
127
|
return root;
|
|
98
128
|
}
|
|
99
|
-
|
|
100
|
-
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;
|
|
@@ -1,33 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const {parentPort, workerData} = require('worker_threads');
|
|
1
|
+
import v8 from 'node:v8';
|
|
2
|
+
import {parentPort, workerData} from 'node:worker_threads';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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';
|
|
9
8
|
|
|
10
9
|
const MAX_DATA_LENGTH_EXCLUSIVE = 100 * 1024; // Allocate 100 KiB to exchange globs.
|
|
11
10
|
|
|
12
11
|
const configCache = new Map();
|
|
13
12
|
|
|
14
|
-
const collectProviders = ({conf, projectDir}) => {
|
|
13
|
+
const collectProviders = async ({conf, projectDir}) => {
|
|
15
14
|
const providers = [];
|
|
16
|
-
if (Reflect.has(conf, 'babel')) {
|
|
17
|
-
const {level, main} = providerManager.babel(projectDir);
|
|
18
|
-
providers.push({
|
|
19
|
-
level,
|
|
20
|
-
main: main({config: conf.babel}),
|
|
21
|
-
type: 'babel'
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
15
|
if (Reflect.has(conf, 'typescript')) {
|
|
26
|
-
const {level, main} = providerManager.typescript(projectDir);
|
|
16
|
+
const {level, main} = await providerManager.typescript(projectDir);
|
|
27
17
|
providers.push({
|
|
28
18
|
level,
|
|
29
19
|
main: main({config: conf.typescript}),
|
|
30
|
-
type: 'typescript'
|
|
20
|
+
type: 'typescript',
|
|
31
21
|
});
|
|
32
22
|
}
|
|
33
23
|
|
|
@@ -35,24 +25,24 @@ const collectProviders = ({conf, projectDir}) => {
|
|
|
35
25
|
};
|
|
36
26
|
|
|
37
27
|
const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFiles}) => {
|
|
38
|
-
const extensions = overrideExtensions
|
|
39
|
-
normalizeExtensions(overrideExtensions)
|
|
40
|
-
normalizeExtensions(conf.extensions, providers);
|
|
28
|
+
const extensions = overrideExtensions
|
|
29
|
+
? normalizeExtensions(overrideExtensions)
|
|
30
|
+
: normalizeExtensions(conf.extensions, providers);
|
|
41
31
|
|
|
42
32
|
return {
|
|
43
33
|
cwd: projectDir,
|
|
44
34
|
...normalizeGlobs({
|
|
45
35
|
extensions,
|
|
46
36
|
files: overrideFiles ? overrideFiles : conf.files,
|
|
47
|
-
providers
|
|
48
|
-
})
|
|
37
|
+
providers,
|
|
38
|
+
}),
|
|
49
39
|
};
|
|
50
40
|
};
|
|
51
41
|
|
|
52
42
|
const resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
|
|
53
43
|
if (!configCache.has(projectDir)) {
|
|
54
|
-
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(conf => {
|
|
55
|
-
const providers = collectProviders({conf, projectDir});
|
|
44
|
+
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(async conf => {
|
|
45
|
+
const providers = await collectProviders({conf, projectDir});
|
|
56
46
|
return {conf, providers};
|
|
57
47
|
}));
|
|
58
48
|
}
|
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,96 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const Emittery = require('emittery');
|
|
7
|
-
const {controlFlow} = require('./ipc-flow-control');
|
|
8
|
-
|
|
9
|
-
if (fs.realpathSync(__filename) !== __filename) {
|
|
10
|
-
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`.');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// In case the test file imports a different AVA install,
|
|
14
|
-
// the presence of this variable allows it to require this one instead
|
|
15
|
-
const AVA_PATH = path.resolve(__dirname, '..');
|
|
16
|
-
const WORKER_PATH = require.resolve('./worker/base.js');
|
|
17
|
-
|
|
18
|
-
class SharedWorkerChannel extends Emittery {
|
|
19
|
-
constructor({channelId, filename, initialData}, sendToFork) {
|
|
20
|
-
super();
|
|
21
|
-
|
|
22
|
-
this.id = channelId;
|
|
23
|
-
this.filename = filename;
|
|
24
|
-
this.initialData = initialData;
|
|
25
|
-
this.sendToFork = sendToFork;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
signalReady() {
|
|
29
|
-
this.sendToFork({
|
|
30
|
-
type: 'shared-worker-ready',
|
|
31
|
-
channelId: this.id
|
|
32
|
-
});
|
|
33
|
-
}
|
|
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';
|
|
34
5
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
type: 'shared-worker-error',
|
|
38
|
-
channelId: this.id
|
|
39
|
-
});
|
|
40
|
-
}
|
|
6
|
+
import Emittery from 'emittery';
|
|
7
|
+
import {pEvent} from 'p-event';
|
|
41
8
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
messageId,
|
|
45
|
-
replyTo,
|
|
46
|
-
serializedData
|
|
47
|
-
});
|
|
48
|
-
}
|
|
9
|
+
import {controlFlow} from './ipc-flow-control.cjs';
|
|
10
|
+
import serializeError from './serialize-error.js';
|
|
49
11
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
channelId: this.id,
|
|
54
|
-
messageId,
|
|
55
|
-
replyTo,
|
|
56
|
-
serializedData
|
|
57
|
-
});
|
|
58
|
-
}
|
|
12
|
+
let workerPath = new URL('worker/base.js', import.meta.url);
|
|
13
|
+
export function _testOnlyReplaceWorkerPath(replacement) {
|
|
14
|
+
workerPath = replacement;
|
|
59
15
|
}
|
|
60
16
|
|
|
61
|
-
|
|
17
|
+
const additionalExecArgv = ['--enable-source-maps'];
|
|
62
18
|
|
|
63
19
|
const createWorker = (options, execArgv) => {
|
|
64
20
|
let worker;
|
|
65
21
|
let postMessage;
|
|
66
22
|
let close;
|
|
67
23
|
if (options.workerThreads) {
|
|
68
|
-
worker = new Worker(
|
|
24
|
+
worker = new Worker(workerPath, {
|
|
69
25
|
argv: options.workerArgv,
|
|
70
|
-
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables
|
|
71
|
-
execArgv,
|
|
26
|
+
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables},
|
|
27
|
+
execArgv: [...execArgv, ...additionalExecArgv],
|
|
72
28
|
workerData: {
|
|
73
|
-
options
|
|
29
|
+
options,
|
|
74
30
|
},
|
|
75
31
|
trackUnmanagedFds: true,
|
|
76
32
|
stdin: true,
|
|
77
33
|
stdout: true,
|
|
78
|
-
stderr: true
|
|
34
|
+
stderr: true,
|
|
79
35
|
});
|
|
80
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
|
+
|
|
81
42
|
close = async () => {
|
|
82
43
|
try {
|
|
44
|
+
await starting;
|
|
83
45
|
await worker.terminate();
|
|
84
46
|
} finally {
|
|
85
47
|
// No-op
|
|
86
48
|
}
|
|
87
49
|
};
|
|
88
50
|
} else {
|
|
89
|
-
worker = childProcess.fork(
|
|
51
|
+
worker = childProcess.fork(fileURLToPath(workerPath), options.workerArgv, {
|
|
90
52
|
cwd: options.projectDir,
|
|
91
53
|
silent: true,
|
|
92
|
-
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables
|
|
93
|
-
execArgv
|
|
54
|
+
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables},
|
|
55
|
+
execArgv: [...execArgv, ...additionalExecArgv],
|
|
94
56
|
});
|
|
95
57
|
postMessage = controlFlow(worker);
|
|
96
58
|
close = async () => worker.kill();
|
|
@@ -99,15 +61,11 @@ const createWorker = (options, execArgv) => {
|
|
|
99
61
|
return {
|
|
100
62
|
worker,
|
|
101
63
|
postMessage,
|
|
102
|
-
close
|
|
64
|
+
close,
|
|
103
65
|
};
|
|
104
66
|
};
|
|
105
67
|
|
|
106
|
-
|
|
107
|
-
// TODO: this can be changed to use `threadId` when using worker_threads
|
|
108
|
-
const forkId = `fork/${++forkCounter}`;
|
|
109
|
-
const sharedWorkerChannels = new Map();
|
|
110
|
-
|
|
68
|
+
export default function loadFork(file, options, execArgv = process.execArgv) {
|
|
111
69
|
let finished = false;
|
|
112
70
|
|
|
113
71
|
const emitter = new Emittery();
|
|
@@ -120,8 +78,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
120
78
|
options = {
|
|
121
79
|
baseDir: process.cwd(),
|
|
122
80
|
file,
|
|
123
|
-
|
|
124
|
-
...options
|
|
81
|
+
...options,
|
|
125
82
|
};
|
|
126
83
|
|
|
127
84
|
const {worker, postMessage, close} = createWorker(options, execArgv);
|
|
@@ -155,17 +112,19 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
155
112
|
case 'ready-for-options':
|
|
156
113
|
send({type: 'options', options});
|
|
157
114
|
break;
|
|
158
|
-
|
|
159
115
|
case 'shared-worker-connect': {
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
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
|
+
});
|
|
163
125
|
break;
|
|
164
126
|
}
|
|
165
127
|
|
|
166
|
-
case 'shared-worker-message':
|
|
167
|
-
sharedWorkerChannels.get(message.ava.channelId).emitMessage(message.ava);
|
|
168
|
-
break;
|
|
169
128
|
case 'ping':
|
|
170
129
|
send({type: 'pong'});
|
|
171
130
|
break;
|
|
@@ -175,7 +134,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
175
134
|
});
|
|
176
135
|
|
|
177
136
|
worker.on('error', error => {
|
|
178
|
-
emitStateChange({type: 'worker-failed', err: error});
|
|
137
|
+
emitStateChange({type: 'worker-failed', err: serializeError('Worker error', false, error, file)});
|
|
179
138
|
finish();
|
|
180
139
|
});
|
|
181
140
|
|
|
@@ -196,7 +155,7 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
196
155
|
|
|
197
156
|
return {
|
|
198
157
|
file,
|
|
199
|
-
|
|
158
|
+
threadId: worker.threadId,
|
|
200
159
|
promise,
|
|
201
160
|
|
|
202
161
|
exit() {
|
|
@@ -214,6 +173,6 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
214
173
|
|
|
215
174
|
onStateChange(listener) {
|
|
216
175
|
return emitter.on('stateChange', listener);
|
|
217
|
-
}
|
|
176
|
+
},
|
|
218
177
|
};
|
|
219
|
-
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const process = require('process');
|
|
4
|
+
|
|
5
|
+
const ignoreByDefault = require('ignore-by-default');
|
|
6
|
+
const picomatch = require('picomatch');
|
|
7
|
+
const slash = require('slash');
|
|
8
|
+
|
|
9
|
+
const defaultIgnorePatterns = [...ignoreByDefault.directories(), '**/node_modules'];
|
|
10
|
+
exports.defaultIgnorePatterns = defaultIgnorePatterns;
|
|
11
|
+
|
|
12
|
+
const defaultPicomatchIgnorePatterns = [
|
|
13
|
+
...defaultIgnorePatterns,
|
|
14
|
+
// Unlike globby(), picomatch needs a complete pattern when ignoring directories.
|
|
15
|
+
...defaultIgnorePatterns.map(pattern => `${pattern}/**/*`),
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const defaultMatchNoIgnore = picomatch(defaultPicomatchIgnorePatterns);
|
|
19
|
+
|
|
20
|
+
const matchingCache = new WeakMap();
|
|
21
|
+
const processMatchingPatterns = input => {
|
|
22
|
+
let result = matchingCache.get(input);
|
|
23
|
+
if (!result) {
|
|
24
|
+
const ignore = [...defaultPicomatchIgnorePatterns];
|
|
25
|
+
const patterns = input.filter(pattern => {
|
|
26
|
+
if (pattern.startsWith('!')) {
|
|
27
|
+
// Unlike globby(), picomatch needs a complete pattern when ignoring directories.
|
|
28
|
+
ignore.push(pattern.slice(1), `${pattern.slice(1)}/**/*`);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return true;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
result = {
|
|
36
|
+
match: picomatch(patterns, {ignore}),
|
|
37
|
+
matchNoIgnore: picomatch(patterns),
|
|
38
|
+
individualMatchers: patterns.map(pattern => ({pattern, match: picomatch(pattern, {ignore})})),
|
|
39
|
+
};
|
|
40
|
+
matchingCache.set(input, result);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
exports.processMatchingPatterns = processMatchingPatterns;
|
|
47
|
+
|
|
48
|
+
const matchesIgnorePatterns = (file, patterns) => {
|
|
49
|
+
const {matchNoIgnore} = processMatchingPatterns(patterns);
|
|
50
|
+
return matchNoIgnore(file) || defaultMatchNoIgnore(file);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function classify(file, {cwd, extensions, filePatterns, ignoredByWatcherPatterns}) {
|
|
54
|
+
file = normalizeFileForMatching(cwd, file);
|
|
55
|
+
return {
|
|
56
|
+
isIgnoredByWatcher: matchesIgnorePatterns(file, ignoredByWatcherPatterns),
|
|
57
|
+
isTest: hasExtension(extensions, file) && !isHelperish(file) && filePatterns.length > 0 && matches(file, filePatterns),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
exports.classify = classify;
|
|
62
|
+
|
|
63
|
+
const hasExtension = (extensions, file) => extensions.includes(path.extname(file).slice(1));
|
|
64
|
+
|
|
65
|
+
exports.hasExtension = hasExtension;
|
|
66
|
+
|
|
67
|
+
function isHelperish(file) { // Assume file has been normalized already.
|
|
68
|
+
// File names starting with an underscore are deemed "helpers".
|
|
69
|
+
if (path.basename(file).startsWith('_')) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// This function assumes the file has been normalized. If it couldn't be,
|
|
74
|
+
// don't check if it's got a parent directory that starts with an underscore.
|
|
75
|
+
// Deem it not a "helper".
|
|
76
|
+
if (path.isAbsolute(file)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If the file has a parent directory that starts with only a single
|
|
81
|
+
// underscore, it's deemed a "helper".
|
|
82
|
+
return path.dirname(file).split('/').some(dir => /^_(?:$|[^_])/.test(dir));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
exports.isHelperish = isHelperish;
|
|
86
|
+
|
|
87
|
+
function matches(file, patterns) {
|
|
88
|
+
const {match} = processMatchingPatterns(patterns);
|
|
89
|
+
return match(file);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
exports.matches = matches;
|
|
93
|
+
|
|
94
|
+
function normalizeFileForMatching(cwd, file) {
|
|
95
|
+
if (process.platform === 'win32') {
|
|
96
|
+
cwd = slash(cwd);
|
|
97
|
+
file = slash(file);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Note that if `file` is outside `cwd` we can't normalize it. If this turns
|
|
101
|
+
// out to be a real-world scenario we may have to make changes in calling code
|
|
102
|
+
// to make sure the file isn't even selected for matching.
|
|
103
|
+
if (!file.startsWith(cwd)) {
|
|
104
|
+
return file;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Assume `cwd` does *not* end in a slash.
|
|
108
|
+
return file.slice(cwd.length + 1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
exports.normalizeFileForMatching = normalizeFileForMatching;
|
|
112
|
+
|
|
113
|
+
function normalizePattern(pattern) {
|
|
114
|
+
// Always use `/` in patterns, harmonizing matching across platforms
|
|
115
|
+
if (process.platform === 'win32') {
|
|
116
|
+
pattern = slash(pattern);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (pattern.endsWith('/')) {
|
|
120
|
+
pattern = pattern.slice(0, -1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (pattern.startsWith('./')) {
|
|
124
|
+
return pattern.slice(2);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (pattern.startsWith('!./')) {
|
|
128
|
+
return `!${pattern.slice(3)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return pattern;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
exports.normalizePattern = normalizePattern;
|
|
135
|
+
|
|
136
|
+
function normalizePatterns(patterns) {
|
|
137
|
+
return patterns.map(pattern => normalizePattern(pattern));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
exports.normalizePatterns = normalizePatterns;
|