Upgrade Ava to v4
This commit is contained in:
parent
9a40cc5274
commit
ce89f1b611
1153 changed files with 27264 additions and 95308 deletions
208
node_modules/ava/lib/worker/base.js
generated
vendored
Normal file
208
node_modules/ava/lib/worker/base.js
generated
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import {createRequire} from 'node:module';
|
||||
import process from 'node:process';
|
||||
import {pathToFileURL} from 'node:url';
|
||||
import {workerData} from 'node:worker_threads';
|
||||
|
||||
import setUpCurrentlyUnhandled from 'currently-unhandled';
|
||||
|
||||
import {set as setChalk} from '../chalk.js';
|
||||
import nowAndTimers from '../now-and-timers.cjs';
|
||||
import providerManager from '../provider-manager.js';
|
||||
import Runner from '../runner.js';
|
||||
import serializeError from '../serialize-error.js';
|
||||
|
||||
import channel from './channel.cjs';
|
||||
import dependencyTracking from './dependency-tracker.js';
|
||||
import lineNumberSelection from './line-numbers.js';
|
||||
import {set as setOptions} from './options.cjs';
|
||||
import {flags, refs, sharedWorkerTeardowns} from './state.cjs';
|
||||
import {isRunningInThread, isRunningInChildProcess} from './utils.cjs';
|
||||
|
||||
const currentlyUnhandled = setUpCurrentlyUnhandled();
|
||||
|
||||
const run = async options => {
|
||||
setOptions(options);
|
||||
setChalk(options.chalkOptions);
|
||||
|
||||
if (options.chalkOptions.level > 0) {
|
||||
const {stdout, stderr} = process;
|
||||
global.console = Object.assign(global.console, new console.Console({stdout, stderr, colorMode: true}));
|
||||
}
|
||||
|
||||
async function exit(code) {
|
||||
if (!process.exitCode) {
|
||||
process.exitCode = code;
|
||||
}
|
||||
|
||||
dependencyTracking.flush();
|
||||
await channel.flush();
|
||||
process.exit(); // eslint-disable-line unicorn/no-process-exit
|
||||
}
|
||||
|
||||
let checkSelectedByLineNumbers;
|
||||
try {
|
||||
checkSelectedByLineNumbers = lineNumberSelection({
|
||||
file: options.file,
|
||||
lineNumbers: options.lineNumbers,
|
||||
});
|
||||
} catch (error) {
|
||||
channel.send({type: 'line-number-selection-error', err: serializeError('Line number selection error', false, error, options.file)});
|
||||
checkSelectedByLineNumbers = () => false;
|
||||
}
|
||||
|
||||
const runner = new Runner({
|
||||
checkSelectedByLineNumbers,
|
||||
experiments: options.experiments,
|
||||
failFast: options.failFast,
|
||||
failWithoutAssertions: options.failWithoutAssertions,
|
||||
file: options.file,
|
||||
match: options.match,
|
||||
projectDir: options.projectDir,
|
||||
recordNewSnapshots: options.recordNewSnapshots,
|
||||
runOnlyExclusive: options.runOnlyExclusive,
|
||||
serial: options.serial,
|
||||
snapshotDir: options.snapshotDir,
|
||||
updateSnapshots: options.updateSnapshots,
|
||||
});
|
||||
|
||||
refs.runnerChain = runner.chain;
|
||||
|
||||
channel.peerFailed.then(() => {
|
||||
runner.interrupt();
|
||||
});
|
||||
|
||||
runner.on('dependency', dependencyTracking.track);
|
||||
runner.on('stateChange', state => channel.send(state));
|
||||
|
||||
runner.on('error', error => {
|
||||
channel.send({type: 'internal-error', err: serializeError('Internal runner error', false, error, runner.file)});
|
||||
exit(1);
|
||||
});
|
||||
|
||||
runner.on('finish', async () => {
|
||||
try {
|
||||
const {touchedFiles} = await runner.saveSnapshotState();
|
||||
if (touchedFiles) {
|
||||
channel.send({type: 'touched-files', files: touchedFiles});
|
||||
}
|
||||
} catch (error) {
|
||||
channel.send({type: 'internal-error', err: serializeError('Internal runner error', false, error, runner.file)});
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(sharedWorkerTeardowns.map(fn => fn()));
|
||||
} catch (error) {
|
||||
channel.send({type: 'uncaught-exception', err: serializeError('Shared worker teardown error', false, error, runner.file)});
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
nowAndTimers.setImmediate(() => {
|
||||
for (const rejection of currentlyUnhandled()) {
|
||||
channel.send({type: 'unhandled-rejection', err: serializeError('Unhandled rejection', true, rejection.reason, runner.file)});
|
||||
}
|
||||
|
||||
exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('uncaughtException', error => {
|
||||
channel.send({type: 'uncaught-exception', err: serializeError('Uncaught exception', true, error, runner.file)});
|
||||
exit(1);
|
||||
});
|
||||
|
||||
// Store value to prevent required modules from modifying it.
|
||||
const testPath = options.file;
|
||||
|
||||
const extensionsToLoadAsModules = Object.entries(options.moduleTypes)
|
||||
.filter(([, type]) => type === 'module')
|
||||
.map(([extension]) => extension);
|
||||
|
||||
// Install before processing options.require, so if helpers are added to the
|
||||
// require configuration the *compiled* helper will be loaded.
|
||||
const {projectDir, providerStates = []} = options;
|
||||
const providers = [];
|
||||
await Promise.all(providerStates.map(async ({type, state}) => {
|
||||
if (type === 'typescript') {
|
||||
const provider = await providerManager.typescript(projectDir);
|
||||
providers.push(provider.worker({extensionsToLoadAsModules, state}));
|
||||
}
|
||||
}));
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const load = async ref => {
|
||||
for (const provider of providers) {
|
||||
if (provider.canLoad(ref)) {
|
||||
return provider.load(ref, {requireFn: require});
|
||||
}
|
||||
}
|
||||
|
||||
for (const extension of extensionsToLoadAsModules) {
|
||||
if (ref.endsWith(`.${extension}`)) {
|
||||
return import(pathToFileURL(ref)); // eslint-disable-line node/no-unsupported-features/es-syntax
|
||||
}
|
||||
}
|
||||
|
||||
// We still support require() since it's more easily monkey-patched.
|
||||
return require(ref);
|
||||
};
|
||||
|
||||
try {
|
||||
for await (const ref of (options.require || [])) {
|
||||
await load(ref);
|
||||
}
|
||||
|
||||
// Install dependency tracker after the require configuration has been evaluated
|
||||
// to make sure we also track dependencies with custom require hooks
|
||||
dependencyTracking.install(require.extensions, testPath);
|
||||
|
||||
if (options.debug && options.debug.port !== undefined && options.debug.host !== undefined) {
|
||||
// If an inspector was active when the main process started, and is
|
||||
// already active for the worker process, do not open a new one.
|
||||
const {default: inspector} = await import('node:inspector'); // eslint-disable-line node/no-unsupported-features/es-syntax
|
||||
if (!options.debug.active || inspector.url() === undefined) {
|
||||
inspector.open(options.debug.port, options.debug.host, true);
|
||||
}
|
||||
|
||||
if (options.debug.break) {
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
}
|
||||
}
|
||||
|
||||
await load(testPath);
|
||||
|
||||
if (flags.loadedMain) {
|
||||
// Unreference the channel if the test file required AVA. This stops it
|
||||
// from keeping the event loop busy, which means the `beforeExit` event can be
|
||||
// used to detect when tests stall.
|
||||
channel.unref();
|
||||
} else {
|
||||
channel.send({type: 'missing-ava-import'});
|
||||
exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
channel.send({type: 'uncaught-exception', err: serializeError('Uncaught exception', true, error, runner.file)});
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
const onError = error => {
|
||||
// There shouldn't be any errors, but if there are we may not have managed
|
||||
// to bootstrap enough code to serialize them. Re-throw and let the process
|
||||
// crash.
|
||||
setImmediate(() => {
|
||||
throw error;
|
||||
});
|
||||
};
|
||||
|
||||
if (isRunningInThread) {
|
||||
channel.send({type: 'starting'}); // AVA won't terminate the worker thread until it's seen this message.
|
||||
const {options} = workerData;
|
||||
delete workerData.options; // Don't allow user code access.
|
||||
run(options).catch(onError);
|
||||
} else if (isRunningInChildProcess) {
|
||||
channel.send({type: 'ready-for-options'});
|
||||
channel.options.then(run).catch(onError);
|
||||
}
|
||||
290
node_modules/ava/lib/worker/channel.cjs
generated
vendored
Normal file
290
node_modules/ava/lib/worker/channel.cjs
generated
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
'use strict';
|
||||
const events = require('events');
|
||||
const process = require('process');
|
||||
const {MessageChannel, threadId} = require('worker_threads');
|
||||
|
||||
const timers = require('../now-and-timers.cjs');
|
||||
|
||||
const {isRunningInChildProcess, isRunningInThread} = require('./utils.cjs');
|
||||
|
||||
let pEvent = async (emitter, event, options) => {
|
||||
// We need to import p-event, but import() is asynchronous. Buffer any events
|
||||
// emitted in the meantime. Don't handle errors.
|
||||
const buffer = [];
|
||||
const addToBuffer = (...args) => buffer.push(args);
|
||||
emitter.on(event, addToBuffer);
|
||||
|
||||
try {
|
||||
({pEvent} = await import('p-event')); // eslint-disable-line node/no-unsupported-features/es-syntax
|
||||
} finally {
|
||||
emitter.off(event, addToBuffer);
|
||||
}
|
||||
|
||||
if (buffer.length === 0) {
|
||||
return pEvent(emitter, event, options);
|
||||
}
|
||||
|
||||
// Now replay buffered events.
|
||||
const replayEmitter = new events.EventEmitter();
|
||||
const promise = pEvent(replayEmitter, event, options);
|
||||
for (const args of buffer) {
|
||||
replayEmitter.emit(event, ...args);
|
||||
}
|
||||
|
||||
const replay = (...args) => replayEmitter.emit(event, ...args);
|
||||
emitter.on(event, replay);
|
||||
|
||||
try {
|
||||
return await promise;
|
||||
} finally {
|
||||
emitter.off(event, replay);
|
||||
}
|
||||
};
|
||||
|
||||
const selectAvaMessage = type => message => message.ava && message.ava.type === type;
|
||||
|
||||
class RefCounter {
|
||||
constructor() {
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
refAndTest() {
|
||||
return ++this.count === 1;
|
||||
}
|
||||
|
||||
testAndUnref() {
|
||||
return this.count > 0 && --this.count === 0;
|
||||
}
|
||||
}
|
||||
|
||||
class MessagePortHandle {
|
||||
constructor(port) {
|
||||
this.counter = new RefCounter();
|
||||
this.unreferenceable = false;
|
||||
this.channel = port;
|
||||
// Referencing the port does not immediately prevent the thread from
|
||||
// exiting. Use a timer to keep a reference for at least a second.
|
||||
this.workaroundTimer = timers.setTimeout(() => {}, 1000).unref();
|
||||
}
|
||||
|
||||
forceUnref() {
|
||||
if (this.unreferenceable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.unreferenceable = true;
|
||||
this.workaroundTimer.unref();
|
||||
this.channel.unref();
|
||||
}
|
||||
|
||||
ref() {
|
||||
if (!this.unreferenceable && this.counter.refAndTest()) {
|
||||
this.workaroundTimer.refresh().ref();
|
||||
this.channel.ref();
|
||||
}
|
||||
}
|
||||
|
||||
unref() {
|
||||
if (!this.unreferenceable && this.counter.testAndUnref()) {
|
||||
this.workaroundTimer.unref();
|
||||
this.channel.unref();
|
||||
}
|
||||
}
|
||||
|
||||
send(evt, transferList) {
|
||||
this.channel.postMessage({ava: evt}, transferList);
|
||||
}
|
||||
}
|
||||
|
||||
class IpcHandle {
|
||||
constructor(bufferedSend) {
|
||||
this.counter = new RefCounter();
|
||||
this.channel = process;
|
||||
this.sendRaw = bufferedSend;
|
||||
}
|
||||
|
||||
ref() {
|
||||
if (this.counter.refAndTest()) {
|
||||
process.channel.ref();
|
||||
}
|
||||
}
|
||||
|
||||
unref() {
|
||||
if (this.counter.testAndUnref()) {
|
||||
process.channel.unref();
|
||||
}
|
||||
}
|
||||
|
||||
send(evt) {
|
||||
this.sendRaw({ava: evt});
|
||||
}
|
||||
}
|
||||
|
||||
let handle;
|
||||
if (isRunningInChildProcess) {
|
||||
const {controlFlow} = require('../ipc-flow-control.cjs');
|
||||
handle = new IpcHandle(controlFlow(process));
|
||||
} else if (isRunningInThread) {
|
||||
const {parentPort} = require('worker_threads');
|
||||
handle = new MessagePortHandle(parentPort);
|
||||
}
|
||||
|
||||
// The attaching of message listeners will cause the port to be referenced by
|
||||
// Node.js. In order to keep track, explicitly reference before attaching.
|
||||
handle.ref();
|
||||
|
||||
exports.options = pEvent(handle.channel, 'message', selectAvaMessage('options')).then(message => message.ava.options);
|
||||
exports.peerFailed = pEvent(handle.channel, 'message', selectAvaMessage('peer-failed'));
|
||||
exports.send = handle.send.bind(handle);
|
||||
exports.unref = handle.unref.bind(handle);
|
||||
|
||||
let pendingPings = Promise.resolve();
|
||||
async function flush() {
|
||||
handle.ref();
|
||||
const promise = pendingPings.then(async () => {
|
||||
handle.send({type: 'ping'});
|
||||
await pEvent(handle.channel, 'message', selectAvaMessage('pong'));
|
||||
if (promise === pendingPings) {
|
||||
handle.unref();
|
||||
}
|
||||
});
|
||||
pendingPings = promise;
|
||||
await promise;
|
||||
}
|
||||
|
||||
exports.flush = flush;
|
||||
|
||||
let channelCounter = 0;
|
||||
let messageCounter = 0;
|
||||
|
||||
const channelEmitters = new Map();
|
||||
function createChannelEmitter(channelId) {
|
||||
if (channelEmitters.size === 0) {
|
||||
handle.channel.on('message', message => {
|
||||
if (!message.ava) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {channelId, type, ...payload} = message.ava;
|
||||
if (type === 'shared-worker-error') {
|
||||
const emitter = channelEmitters.get(channelId);
|
||||
if (emitter !== undefined) {
|
||||
emitter.emit(type, payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const emitter = new events.EventEmitter();
|
||||
channelEmitters.set(channelId, emitter);
|
||||
return [emitter, () => channelEmitters.delete(channelId)];
|
||||
}
|
||||
|
||||
function registerSharedWorker(filename, initialData) {
|
||||
const channelId = `${threadId}/channel/${++channelCounter}`;
|
||||
|
||||
const {port1: ourPort, port2: theirPort} = new MessageChannel();
|
||||
const sharedWorkerHandle = new MessagePortHandle(ourPort);
|
||||
|
||||
const [channelEmitter, unsubscribe] = createChannelEmitter(channelId);
|
||||
|
||||
handle.send({
|
||||
type: 'shared-worker-connect',
|
||||
channelId,
|
||||
filename,
|
||||
initialData,
|
||||
port: theirPort,
|
||||
}, [theirPort]);
|
||||
|
||||
let currentlyAvailable = false;
|
||||
let error = null;
|
||||
|
||||
// The attaching of message listeners will cause the port to be referenced by
|
||||
// Node.js. In order to keep track, explicitly reference before attaching.
|
||||
sharedWorkerHandle.ref();
|
||||
const ready = pEvent(ourPort, 'message', ({type}) => type === 'ready').then(() => {
|
||||
currentlyAvailable = error === null;
|
||||
}).finally(() => {
|
||||
// Once ready, it's up to user code to subscribe to messages, which (see
|
||||
// below) causes us to reference the port.
|
||||
sharedWorkerHandle.unref();
|
||||
});
|
||||
|
||||
const messageEmitters = new Set();
|
||||
|
||||
// Errors are received over the test worker channel, not the message port
|
||||
// dedicated to the shared worker.
|
||||
pEvent(channelEmitter, 'shared-worker-error').then(() => {
|
||||
unsubscribe();
|
||||
sharedWorkerHandle.forceUnref();
|
||||
error = new Error('The shared worker is no longer available');
|
||||
currentlyAvailable = false;
|
||||
for (const emitter of messageEmitters) {
|
||||
emitter.emit('error', error);
|
||||
}
|
||||
});
|
||||
|
||||
ourPort.on('message', message => {
|
||||
if (message.type === 'message') {
|
||||
// Wait for a turn of the event loop, to allow new subscriptions to be set
|
||||
// up in response to the previous message.
|
||||
setImmediate(() => {
|
||||
for (const emitter of messageEmitters) {
|
||||
emitter.emit('message', message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
forceUnref: () => sharedWorkerHandle.forceUnref(),
|
||||
ready,
|
||||
channel: {
|
||||
available: ready,
|
||||
|
||||
get currentlyAvailable() {
|
||||
return currentlyAvailable;
|
||||
},
|
||||
|
||||
async * receive() {
|
||||
if (error !== null) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const emitter = new events.EventEmitter();
|
||||
messageEmitters.add(emitter);
|
||||
try {
|
||||
sharedWorkerHandle.ref();
|
||||
for await (const [message] of events.on(emitter, 'message')) {
|
||||
yield message;
|
||||
}
|
||||
} finally {
|
||||
sharedWorkerHandle.unref();
|
||||
messageEmitters.delete(emitter);
|
||||
}
|
||||
},
|
||||
|
||||
post(data, replyTo) {
|
||||
if (error !== null) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!currentlyAvailable) {
|
||||
throw new Error('Shared worker is not yet available');
|
||||
}
|
||||
|
||||
const messageId = `${channelId}/message/${++messageCounter}`;
|
||||
ourPort.postMessage({
|
||||
type: 'message',
|
||||
messageId,
|
||||
replyTo,
|
||||
data,
|
||||
});
|
||||
|
||||
return messageId;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
exports.registerSharedWorker = registerSharedWorker;
|
||||
41
node_modules/ava/lib/worker/dependency-tracker.js
generated
vendored
41
node_modules/ava/lib/worker/dependency-tracker.js
generated
vendored
|
|
@ -1,20 +1,19 @@
|
|||
/* eslint-disable node/no-deprecated-api */
|
||||
'use strict';
|
||||
const ipc = require('./ipc');
|
||||
import process from 'node:process';
|
||||
|
||||
import channel from './channel.cjs';
|
||||
|
||||
const seenDependencies = new Set();
|
||||
let newDependencies = [];
|
||||
|
||||
function flush() {
|
||||
if (newDependencies.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ipc.send({type: 'dependencies', dependencies: newDependencies});
|
||||
channel.send({type: 'dependencies', dependencies: newDependencies});
|
||||
newDependencies = [];
|
||||
}
|
||||
|
||||
exports.flush = flush;
|
||||
|
||||
function track(filename) {
|
||||
if (seenDependencies.has(filename)) {
|
||||
return;
|
||||
|
|
@ -28,20 +27,22 @@ function track(filename) {
|
|||
newDependencies.push(filename);
|
||||
}
|
||||
|
||||
exports.track = track;
|
||||
const tracker = {
|
||||
flush,
|
||||
track,
|
||||
install(extensions, testPath) {
|
||||
for (const ext of Object.keys(extensions)) {
|
||||
const wrappedHandler = extensions[ext];
|
||||
|
||||
function install(testPath) {
|
||||
for (const ext of Object.keys(require.extensions)) {
|
||||
const wrappedHandler = require.extensions[ext];
|
||||
extensions[ext] = (module, filename) => {
|
||||
if (filename !== testPath) {
|
||||
track(filename);
|
||||
}
|
||||
|
||||
require.extensions[ext] = (module, filename) => {
|
||||
if (filename !== testPath) {
|
||||
track(filename);
|
||||
}
|
||||
wrappedHandler(module, filename);
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
wrappedHandler(module, filename);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
exports.install = install;
|
||||
export default tracker;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
'use strict';
|
||||
const path = require('path');
|
||||
const chalk = require('chalk'); // Use default Chalk instance.
|
||||
const process = require('process');
|
||||
|
||||
const {isRunningInThread, isRunningInChildProcess} = require('./utils.cjs');
|
||||
|
||||
// Check if the test is being run without AVA cli
|
||||
const isForked = typeof process.send === 'function';
|
||||
if (!isForked) {
|
||||
if (!isRunningInChildProcess && !isRunningInThread) {
|
||||
if (process.argv[1]) {
|
||||
const fp = path.relative('.', process.argv[1]);
|
||||
|
||||
console.log();
|
||||
console.error(`Test files must be run with the AVA CLI:\n\n ${chalk.grey.dim('$')} ${chalk.cyan('ava ' + fp)}\n`);
|
||||
console.error(`Test files must be run with the AVA CLI:\n\n $ ava ${fp}\n`);
|
||||
|
||||
process.exit(1); // eslint-disable-line unicorn/no-process-exit
|
||||
} else {
|
||||
201
node_modules/ava/lib/worker/ipc.js
generated
vendored
201
node_modules/ava/lib/worker/ipc.js
generated
vendored
|
|
@ -1,201 +0,0 @@
|
|||
'use strict';
|
||||
const events = require('events');
|
||||
const pEvent = require('p-event');
|
||||
const {controlFlow} = require('../ipc-flow-control');
|
||||
const {get: getOptions} = require('./options');
|
||||
|
||||
const selectAvaMessage = type => message => message.ava && message.ava.type === type;
|
||||
|
||||
exports.options = pEvent(process, 'message', selectAvaMessage('options')).then(message => message.ava.options);
|
||||
exports.peerFailed = pEvent(process, 'message', selectAvaMessage('peer-failed'));
|
||||
|
||||
const bufferedSend = controlFlow(process);
|
||||
function send(evt) {
|
||||
bufferedSend({ava: evt});
|
||||
}
|
||||
|
||||
exports.send = send;
|
||||
|
||||
let refs = 1;
|
||||
function ref() {
|
||||
if (++refs === 1) {
|
||||
process.channel.ref();
|
||||
}
|
||||
}
|
||||
|
||||
function unref() {
|
||||
if (refs > 0 && --refs === 0) {
|
||||
process.channel.unref();
|
||||
}
|
||||
}
|
||||
|
||||
exports.unref = unref;
|
||||
|
||||
let pendingPings = Promise.resolve();
|
||||
async function flush() {
|
||||
ref();
|
||||
const promise = pendingPings.then(async () => { // eslint-disable-line promise/prefer-await-to-then
|
||||
send({type: 'ping'});
|
||||
await pEvent(process, 'message', selectAvaMessage('pong'));
|
||||
if (promise === pendingPings) {
|
||||
unref();
|
||||
}
|
||||
});
|
||||
pendingPings = promise;
|
||||
await promise;
|
||||
}
|
||||
|
||||
exports.flush = flush;
|
||||
|
||||
let channelCounter = 0;
|
||||
let messageCounter = 0;
|
||||
|
||||
const channelEmitters = new Map();
|
||||
function createChannelEmitter(channelId) {
|
||||
if (channelEmitters.size === 0) {
|
||||
process.on('message', message => {
|
||||
if (!message.ava) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {channelId, type, ...payload} = message.ava;
|
||||
if (
|
||||
type === 'shared-worker-error' ||
|
||||
type === 'shared-worker-message' ||
|
||||
type === 'shared-worker-ready'
|
||||
) {
|
||||
const emitter = channelEmitters.get(channelId);
|
||||
if (emitter !== undefined) {
|
||||
emitter.emit(type, payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const emitter = new events.EventEmitter();
|
||||
channelEmitters.set(channelId, emitter);
|
||||
return [emitter, () => channelEmitters.delete(channelId)];
|
||||
}
|
||||
|
||||
function registerSharedWorker(filename, initialData) {
|
||||
const channelId = `${getOptions().forkId}/channel/${++channelCounter}`;
|
||||
const [channelEmitter, unsubscribe] = createChannelEmitter(channelId);
|
||||
|
||||
let forcedUnref = false;
|
||||
let refs = 0;
|
||||
const forceUnref = () => {
|
||||
if (forcedUnref) {
|
||||
return;
|
||||
}
|
||||
|
||||
forcedUnref = true;
|
||||
if (refs > 0) {
|
||||
unref();
|
||||
}
|
||||
};
|
||||
|
||||
const refChannel = () => {
|
||||
if (!forcedUnref && ++refs === 1) {
|
||||
ref();
|
||||
}
|
||||
};
|
||||
|
||||
const unrefChannel = () => {
|
||||
if (!forcedUnref && refs > 0 && --refs === 0) {
|
||||
unref();
|
||||
}
|
||||
};
|
||||
|
||||
send({
|
||||
type: 'shared-worker-connect',
|
||||
channelId,
|
||||
filename,
|
||||
initialData
|
||||
});
|
||||
|
||||
let currentlyAvailable = false;
|
||||
let error = null;
|
||||
|
||||
refChannel();
|
||||
const ready = pEvent(channelEmitter, 'shared-worker-ready').then(() => { // eslint-disable-line promise/prefer-await-to-then
|
||||
currentlyAvailable = error === null;
|
||||
}).finally(unrefChannel);
|
||||
|
||||
const messageEmitters = new Set();
|
||||
const handleMessage = message => {
|
||||
// Wait for a turn of the event loop, to allow new subscriptions to be set
|
||||
// up in response to the previous message.
|
||||
setImmediate(() => {
|
||||
for (const emitter of messageEmitters) {
|
||||
emitter.emit('message', message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
channelEmitter.on('shared-worker-message', handleMessage);
|
||||
|
||||
pEvent(channelEmitter, 'shared-worker-error').then(() => { // eslint-disable-line promise/prefer-await-to-then
|
||||
unsubscribe();
|
||||
forceUnref();
|
||||
|
||||
error = new Error('The shared worker is no longer available');
|
||||
currentlyAvailable = false;
|
||||
for (const emitter of messageEmitters) {
|
||||
emitter.emit('error', error);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
forceUnref,
|
||||
ready,
|
||||
channel: {
|
||||
available: ready,
|
||||
|
||||
get currentlyAvailable() {
|
||||
return currentlyAvailable;
|
||||
},
|
||||
|
||||
async * receive() {
|
||||
if (error !== null) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const emitter = new events.EventEmitter();
|
||||
messageEmitters.add(emitter);
|
||||
try {
|
||||
refChannel();
|
||||
for await (const [message] of events.on(emitter, 'message')) {
|
||||
yield message;
|
||||
}
|
||||
} finally {
|
||||
unrefChannel();
|
||||
messageEmitters.delete(emitter);
|
||||
}
|
||||
},
|
||||
|
||||
post(serializedData, replyTo) {
|
||||
if (error !== null) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!currentlyAvailable) {
|
||||
throw new Error('Shared worker is not yet available');
|
||||
}
|
||||
|
||||
const messageId = `${channelId}/message/${++messageCounter}`;
|
||||
send({
|
||||
type: 'shared-worker-message',
|
||||
channelId,
|
||||
messageId,
|
||||
replyTo,
|
||||
serializedData
|
||||
});
|
||||
|
||||
return messageId;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.registerSharedWorker = registerSharedWorker;
|
||||
|
||||
78
node_modules/ava/lib/worker/line-numbers.js
generated
vendored
78
node_modules/ava/lib/worker/line-numbers.js
generated
vendored
|
|
@ -1,18 +1,27 @@
|
|||
import * as fs from 'node:fs';
|
||||
import {createRequire, findSourceMap} from 'node:module';
|
||||
import {pathToFileURL} from 'node:url';
|
||||
|
||||
import callsites from 'callsites';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
function parse(file) {
|
||||
const fs = require('fs');
|
||||
// Avoid loading these until we actually need to select tests by line number.
|
||||
const acorn = require('acorn');
|
||||
const walk = require('acorn-walk');
|
||||
|
||||
const ast = acorn.parse(fs.readFileSync(file, 'utf8'), {
|
||||
ecmaVersion: 11,
|
||||
locations: true
|
||||
locations: true,
|
||||
sourceType: 'module',
|
||||
});
|
||||
|
||||
const locations = [];
|
||||
walk.simple(ast, {
|
||||
CallExpression(node) {
|
||||
locations.push(node.loc);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Walking is depth-first, but we want to sort these breadth-first.
|
||||
|
|
@ -49,36 +58,65 @@ function findTest(locations, declaration) {
|
|||
return spans.pop();
|
||||
}
|
||||
|
||||
const range = (start, end) => new Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const range = (start, end) => Array.from({length: end - start + 1}).fill(start).map((element, index) => element + index);
|
||||
|
||||
module.exports = ({file, lineNumbers = []}) => {
|
||||
const translate = (sourceMap, pos) => {
|
||||
if (sourceMap === undefined) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
const entry = sourceMap.findEntry(pos.line - 1, pos.column); // Source maps are 0-based
|
||||
return {
|
||||
line: entry.originalLine + 1, // Readjust for Acorn.
|
||||
column: entry.originalColumn,
|
||||
};
|
||||
};
|
||||
|
||||
export default function lineNumberSelection({file, lineNumbers = []}) {
|
||||
if (lineNumbers.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Avoid loading these until we actually need to select tests by line number.
|
||||
const callsites = require('callsites');
|
||||
const sourceMapSupport = require('source-map-support');
|
||||
|
||||
const locations = parse(file);
|
||||
const selected = new Set(lineNumbers);
|
||||
|
||||
let locations = parse(file);
|
||||
let lookedForSourceMap = false;
|
||||
let sourceMap;
|
||||
|
||||
return () => {
|
||||
if (!lookedForSourceMap) {
|
||||
lookedForSourceMap = true;
|
||||
|
||||
// The returned function is called *after* the file has been loaded.
|
||||
// Source maps are not available before then.
|
||||
sourceMap = findSourceMap(file);
|
||||
|
||||
if (sourceMap !== undefined) {
|
||||
locations = locations.map(({start, end}) => ({
|
||||
start: translate(sourceMap, start),
|
||||
end: translate(sourceMap, end),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Assume this is called from a test declaration, which is located in the file.
|
||||
// If not… don't select the test!
|
||||
const callSite = callsites().find(callSite => callSite.getFileName() === file);
|
||||
const callSite = callsites().find(callSite => {
|
||||
const current = callSite.getFileName();
|
||||
if (file.startsWith('file://')) {
|
||||
return current.startsWith('file://') ? file === current : file === pathToFileURL(current).toString();
|
||||
}
|
||||
|
||||
return current.startsWith('file://') ? pathToFileURL(file).toString() === current : file === current;
|
||||
});
|
||||
if (!callSite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: This assumes the callSite hasn't already been adjusted. It's likely
|
||||
// that if `source-map-support/register` has been loaded, this would result
|
||||
// in the wrong location.
|
||||
const sourceCallSite = sourceMapSupport.wrapCallSite(callSite);
|
||||
const start = {
|
||||
line: sourceCallSite.getLineNumber(),
|
||||
column: sourceCallSite.getColumnNumber() - 1 // Use 0-indexed columns.
|
||||
};
|
||||
const start = translate(sourceMap, {
|
||||
line: callSite.getLineNumber(), // 1-based
|
||||
column: callSite.getColumnNumber() - 1, // Comes out as 1-based, Acorn wants 0-based
|
||||
});
|
||||
|
||||
const test = findTest(locations, start);
|
||||
if (!test) {
|
||||
|
|
@ -87,4 +125,4 @@ module.exports = ({file, lineNumbers = []}) => {
|
|||
|
||||
return range(test.start.line, test.end.line).some(line => selected.has(line));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
12
node_modules/ava/lib/worker/main.cjs
generated
vendored
Normal file
12
node_modules/ava/lib/worker/main.cjs
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
'use strict';
|
||||
require('./guard-environment.cjs'); // eslint-disable-line import/no-unassigned-import
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const {flags, refs} = require('./state.cjs');
|
||||
|
||||
assert(refs.runnerChain);
|
||||
|
||||
flags.loadedMain = true;
|
||||
|
||||
module.exports = refs.runnerChain;
|
||||
21
node_modules/ava/lib/worker/main.js
generated
vendored
21
node_modules/ava/lib/worker/main.js
generated
vendored
|
|
@ -1,21 +0,0 @@
|
|||
'use strict';
|
||||
const runner = require('./subprocess').getRunner();
|
||||
|
||||
const makeCjsExport = () => {
|
||||
function test(...args) {
|
||||
return runner.chain(...args);
|
||||
}
|
||||
|
||||
return Object.assign(test, runner.chain);
|
||||
};
|
||||
|
||||
// Support CommonJS modules by exporting a test function that can be fully
|
||||
// chained. Also support ES module loaders by exporting __esModule and a
|
||||
// default. Support `import * as ava from 'ava'` use cases by exporting a
|
||||
// `test` member. Do all this whilst preventing `test.test.test() or
|
||||
// `test.default.test()` chains, though in CommonJS `test.test()` is
|
||||
// unavoidable.
|
||||
module.exports = Object.assign(makeCjsExport(), {
|
||||
__esModule: true,
|
||||
default: runner.chain
|
||||
});
|
||||
0
node_modules/ava/lib/worker/options.js → node_modules/ava/lib/worker/options.cjs
generated
vendored
0
node_modules/ava/lib/worker/options.js → node_modules/ava/lib/worker/options.cjs
generated
vendored
51
node_modules/ava/lib/worker/plugin.js → node_modules/ava/lib/worker/plugin.cjs
generated
vendored
51
node_modules/ava/lib/worker/plugin.js → node_modules/ava/lib/worker/plugin.cjs
generated
vendored
|
|
@ -1,18 +1,29 @@
|
|||
const v8 = require('v8');
|
||||
const pkg = require('../../package.json');
|
||||
const subprocess = require('./subprocess');
|
||||
const options = require('./options');
|
||||
|
||||
const {registerSharedWorker: register} = require('./channel.cjs');
|
||||
const options = require('./options.cjs');
|
||||
const {sharedWorkerTeardowns, waitForReady} = require('./state.cjs');
|
||||
|
||||
require('./guard-environment.cjs'); // eslint-disable-line import/no-unassigned-import
|
||||
|
||||
const workers = new Map();
|
||||
const workerTeardownFns = new WeakMap();
|
||||
|
||||
function createSharedWorker(filename, initialData, teardown) {
|
||||
const channel = subprocess.registerSharedWorker(filename, initialData, teardown);
|
||||
const {channel, forceUnref, ready} = register(filename, initialData, teardown);
|
||||
waitForReady.push(ready);
|
||||
sharedWorkerTeardowns.push(async () => {
|
||||
try {
|
||||
await teardown();
|
||||
} finally {
|
||||
forceUnref();
|
||||
}
|
||||
});
|
||||
|
||||
class ReceivedMessage {
|
||||
constructor(id, serializedData) {
|
||||
constructor(id, data) {
|
||||
this.id = id;
|
||||
this.data = v8.deserialize(new Uint8Array(serializedData));
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
reply(data) {
|
||||
|
|
@ -35,7 +46,7 @@ function createSharedWorker(filename, initialData, teardown) {
|
|||
|
||||
let message = messageCache.get(evt);
|
||||
if (message === undefined) {
|
||||
message = new ReceivedMessage(evt.messageId, evt.serializedData);
|
||||
message = new ReceivedMessage(evt.messageId, evt.data);
|
||||
messageCache.set(evt, message);
|
||||
}
|
||||
|
||||
|
|
@ -44,19 +55,19 @@ function createSharedWorker(filename, initialData, teardown) {
|
|||
}
|
||||
|
||||
function publishMessage(data, replyTo) {
|
||||
const id = channel.post([...v8.serialize(data)], replyTo);
|
||||
const id = channel.post(data, replyTo);
|
||||
|
||||
return {
|
||||
id,
|
||||
async * replies() {
|
||||
yield * receiveMessages(id);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
available: channel.available,
|
||||
protocol: 'experimental',
|
||||
protocol: 'ava-4',
|
||||
|
||||
get currentlyAvailable() {
|
||||
return channel.currentlyAvailable;
|
||||
|
|
@ -68,29 +79,27 @@ function createSharedWorker(filename, initialData, teardown) {
|
|||
|
||||
async * subscribe() {
|
||||
yield * receiveMessages();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const supportsSharedWorkers = process.versions.node >= '12.17.0';
|
||||
|
||||
function registerSharedWorker({
|
||||
filename,
|
||||
initialData,
|
||||
supportedProtocols,
|
||||
teardown
|
||||
teardown,
|
||||
}) {
|
||||
if (!options.get().experiments.sharedWorkers) {
|
||||
throw new Error('Shared workers are experimental. Opt in to them in your AVA configuration');
|
||||
const options_ = options.get();
|
||||
|
||||
if (!options_.workerThreads) {
|
||||
throw new Error('Shared workers can be used only when worker threads are enabled');
|
||||
}
|
||||
|
||||
if (!supportsSharedWorkers) {
|
||||
throw new Error('Shared workers require Node.js 12.17 or newer');
|
||||
if (!supportedProtocols.includes('ava-4')) {
|
||||
throw new Error(`This version of AVA (${pkg.version}) does not support any of the desired shared worker protocols: ${supportedProtocols.join(',')}`);
|
||||
}
|
||||
|
||||
if (!supportedProtocols.includes('experimental')) {
|
||||
throw new Error(`This version of AVA (${pkg.version}) does not support any of the desired shared worker protocols: ${supportedProtocols.join()}`);
|
||||
}
|
||||
filename = String(filename); // Allow URL instances.
|
||||
|
||||
let worker = workers.get(filename);
|
||||
if (worker === undefined) {
|
||||
5
node_modules/ava/lib/worker/state.cjs
generated
vendored
Normal file
5
node_modules/ava/lib/worker/state.cjs
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
'use strict';
|
||||
exports.flags = {loadedMain: false};
|
||||
exports.refs = {runnerChain: null};
|
||||
exports.sharedWorkerTeardowns = [];
|
||||
exports.waitForReady = [];
|
||||
266
node_modules/ava/lib/worker/subprocess.js
generated
vendored
266
node_modules/ava/lib/worker/subprocess.js
generated
vendored
|
|
@ -1,266 +0,0 @@
|
|||
'use strict';
|
||||
const {pathToFileURL} = require('url');
|
||||
const currentlyUnhandled = require('currently-unhandled')();
|
||||
|
||||
require('./ensure-forked'); // eslint-disable-line import/no-unassigned-import
|
||||
|
||||
const ipc = require('./ipc');
|
||||
|
||||
const supportsESM = async () => {
|
||||
try {
|
||||
await import('data:text/javascript,'); // eslint-disable-line node/no-unsupported-features/es-syntax
|
||||
return true;
|
||||
} catch {}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
ipc.send({type: 'ready-for-options'});
|
||||
ipc.options.then(async options => {
|
||||
require('./options').set(options);
|
||||
require('../chalk').set(options.chalkOptions);
|
||||
|
||||
if (options.chalkOptions.level > 0) {
|
||||
const {stdout, stderr} = process;
|
||||
global.console = Object.assign(global.console, new console.Console({stdout, stderr, colorMode: true}));
|
||||
}
|
||||
|
||||
const nowAndTimers = require('../now-and-timers');
|
||||
const providerManager = require('../provider-manager');
|
||||
const Runner = require('../runner');
|
||||
const serializeError = require('../serialize-error');
|
||||
const dependencyTracking = require('./dependency-tracker');
|
||||
const lineNumberSelection = require('./line-numbers');
|
||||
|
||||
const sharedWorkerTeardowns = [];
|
||||
|
||||
async function exit(code) {
|
||||
if (!process.exitCode) {
|
||||
process.exitCode = code;
|
||||
}
|
||||
|
||||
dependencyTracking.flush();
|
||||
await ipc.flush();
|
||||
process.exit(); // eslint-disable-line unicorn/no-process-exit
|
||||
}
|
||||
|
||||
// TODO: Initialize providers here, then pass to lineNumberSelection() so they
|
||||
// can be used to parse the test file.
|
||||
let checkSelectedByLineNumbers;
|
||||
try {
|
||||
checkSelectedByLineNumbers = lineNumberSelection({
|
||||
file: options.file,
|
||||
lineNumbers: options.lineNumbers
|
||||
});
|
||||
} catch (error) {
|
||||
ipc.send({type: 'line-number-selection-error', err: serializeError('Line number selection error', false, error, options.file)});
|
||||
checkSelectedByLineNumbers = () => false;
|
||||
}
|
||||
|
||||
const runner = new Runner({
|
||||
checkSelectedByLineNumbers,
|
||||
experiments: options.experiments,
|
||||
failFast: options.failFast,
|
||||
failWithoutAssertions: options.failWithoutAssertions,
|
||||
file: options.file,
|
||||
match: options.match,
|
||||
projectDir: options.projectDir,
|
||||
recordNewSnapshots: options.recordNewSnapshots,
|
||||
runOnlyExclusive: options.runOnlyExclusive,
|
||||
serial: options.serial,
|
||||
snapshotDir: options.snapshotDir,
|
||||
updateSnapshots: options.updateSnapshots
|
||||
});
|
||||
|
||||
ipc.peerFailed.then(() => { // eslint-disable-line promise/prefer-await-to-then
|
||||
runner.interrupt();
|
||||
});
|
||||
|
||||
const attributedRejections = new Set();
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
if (runner.attributeLeakedError(reason)) {
|
||||
attributedRejections.add(promise);
|
||||
}
|
||||
});
|
||||
|
||||
runner.on('dependency', dependencyTracking.track);
|
||||
runner.on('stateChange', state => ipc.send(state));
|
||||
|
||||
runner.on('error', error => {
|
||||
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, error, runner.file)});
|
||||
exit(1);
|
||||
});
|
||||
|
||||
runner.on('finish', async () => {
|
||||
try {
|
||||
const {cannotSave, touchedFiles} = runner.saveSnapshotState();
|
||||
if (cannotSave) {
|
||||
ipc.send({type: 'snapshot-error'});
|
||||
} else if (touchedFiles) {
|
||||
ipc.send({type: 'touched-files', files: touchedFiles});
|
||||
}
|
||||
} catch (error) {
|
||||
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, error, runner.file)});
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(sharedWorkerTeardowns.map(fn => fn()));
|
||||
} catch (error) {
|
||||
ipc.send({type: 'uncaught-exception', err: serializeError('Shared worker teardown error', false, error, runner.file)});
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
nowAndTimers.setImmediate(() => {
|
||||
currentlyUnhandled()
|
||||
.filter(rejection => !attributedRejections.has(rejection.promise))
|
||||
.forEach(rejection => {
|
||||
ipc.send({type: 'unhandled-rejection', err: serializeError('Unhandled rejection', true, rejection.reason, runner.file)});
|
||||
});
|
||||
|
||||
exit(0);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('uncaughtException', error => {
|
||||
if (runner.attributeLeakedError(error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ipc.send({type: 'uncaught-exception', err: serializeError('Uncaught exception', true, error, runner.file)});
|
||||
exit(1);
|
||||
});
|
||||
|
||||
let accessedRunner = false;
|
||||
exports.getRunner = () => {
|
||||
accessedRunner = true;
|
||||
return runner;
|
||||
};
|
||||
|
||||
exports.registerSharedWorker = (filename, initialData, teardown) => {
|
||||
const {channel, forceUnref, ready} = ipc.registerSharedWorker(filename, initialData);
|
||||
runner.waitForReady.push(ready);
|
||||
sharedWorkerTeardowns.push(async () => {
|
||||
try {
|
||||
await teardown();
|
||||
} finally {
|
||||
forceUnref();
|
||||
}
|
||||
});
|
||||
return channel;
|
||||
};
|
||||
|
||||
// Store value to prevent required modules from modifying it.
|
||||
const testPath = options.file;
|
||||
|
||||
// Install basic source map support.
|
||||
const sourceMapSupport = require('source-map-support');
|
||||
sourceMapSupport.install({
|
||||
environment: 'node',
|
||||
handleUncaughtExceptions: false
|
||||
});
|
||||
|
||||
const extensionsToLoadAsModules = Object.entries(options.moduleTypes)
|
||||
.filter(([, type]) => type === 'module')
|
||||
.map(([extension]) => extension);
|
||||
|
||||
// Install before processing options.require, so if helpers are added to the
|
||||
// require configuration the *compiled* helper will be loaded.
|
||||
const {projectDir, providerStates = []} = options;
|
||||
const providers = providerStates.map(({type, state}) => {
|
||||
if (type === 'babel') {
|
||||
const provider = providerManager.babel(projectDir).worker({extensionsToLoadAsModules, state});
|
||||
runner.powerAssert = provider.powerAssert;
|
||||
return provider;
|
||||
}
|
||||
|
||||
if (type === 'typescript') {
|
||||
return providerManager.typescript(projectDir).worker({extensionsToLoadAsModules, state});
|
||||
}
|
||||
|
||||
return null;
|
||||
}).filter(provider => provider !== null);
|
||||
|
||||
let requireFn = require;
|
||||
let isESMSupported;
|
||||
const load = async ref => {
|
||||
for (const extension of extensionsToLoadAsModules) {
|
||||
if (ref.endsWith(`.${extension}`)) {
|
||||
if (typeof isESMSupported !== 'boolean') {
|
||||
// Lazily determine support since this prints an experimental warning.
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
isESMSupported = await supportsESM();
|
||||
}
|
||||
|
||||
if (isESMSupported) {
|
||||
return import(pathToFileURL(ref)); // eslint-disable-line node/no-unsupported-features/es-syntax
|
||||
}
|
||||
|
||||
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('ECMAScript Modules are not supported in this Node.js version.'))});
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (const provider of providers) {
|
||||
if (provider.canLoad(ref)) {
|
||||
return provider.load(ref, {requireFn});
|
||||
}
|
||||
}
|
||||
|
||||
return requireFn(ref);
|
||||
};
|
||||
|
||||
try {
|
||||
for await (const ref of (options.require || [])) {
|
||||
const mod = await load(ref);
|
||||
|
||||
try {
|
||||
if (Reflect.has(mod, Symbol.for('esm:package'))) {
|
||||
requireFn = mod(module);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Install dependency tracker after the require configuration has been evaluated
|
||||
// to make sure we also track dependencies with custom require hooks
|
||||
dependencyTracking.install(testPath);
|
||||
|
||||
if (options.debug && options.debug.port !== undefined && options.debug.host !== undefined) {
|
||||
// If an inspector was active when the main process started, and is
|
||||
// already active for the worker process, do not open a new one.
|
||||
const inspector = require('inspector'); // eslint-disable-line node/no-unsupported-features/node-builtins
|
||||
if (!options.debug.active || inspector.url() === undefined) {
|
||||
inspector.open(options.debug.port, options.debug.host, true);
|
||||
}
|
||||
|
||||
if (options.debug.break) {
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
}
|
||||
}
|
||||
|
||||
await load(testPath);
|
||||
|
||||
if (accessedRunner) {
|
||||
// Unreference the IPC channel if the test file required AVA. This stops it
|
||||
// from keeping the event loop busy, which means the `beforeExit` event can be
|
||||
// used to detect when tests stall.
|
||||
ipc.unref();
|
||||
} else {
|
||||
ipc.send({type: 'missing-ava-import'});
|
||||
exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
ipc.send({type: 'uncaught-exception', err: serializeError('Uncaught exception', true, error, runner.file)});
|
||||
exit(1);
|
||||
}
|
||||
}).catch(error => {
|
||||
// There shouldn't be any errors, but if there are we may not have managed
|
||||
// to bootstrap enough code to serialize them. Re-throw and let the process
|
||||
// crash.
|
||||
setImmediate(() => {
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
6
node_modules/ava/lib/worker/utils.cjs
generated
vendored
Normal file
6
node_modules/ava/lib/worker/utils.cjs
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
const process = require('process');
|
||||
const {isMainThread} = require('worker_threads');
|
||||
|
||||
exports.isRunningInThread = isMainThread === false;
|
||||
exports.isRunningInChildProcess = typeof process.send === 'function';
|
||||
Loading…
Add table
Add a link
Reference in a new issue