113 lines
4.9 KiB
JavaScript
113 lines
4.9 KiB
JavaScript
import { Worker as JestWorker } from "next/dist/compiled/jest-worker";
|
|
import { getNodeOptionsWithoutInspect } from "../server/lib/utils";
|
|
const RESTARTED = Symbol("restarted");
|
|
const cleanupWorkers = (worker)=>{
|
|
var _worker__workerPool;
|
|
for (const curWorker of ((_worker__workerPool = worker._workerPool) == null ? void 0 : _worker__workerPool._workers) || []){
|
|
var _curWorker__child;
|
|
(_curWorker__child = curWorker._child) == null ? void 0 : _curWorker__child.kill("SIGINT");
|
|
}
|
|
};
|
|
export class Worker {
|
|
constructor(workerPath, options){
|
|
let { timeout, onRestart, ...farmOptions } = options;
|
|
let restartPromise;
|
|
let resolveRestartPromise;
|
|
let activeTasks = 0;
|
|
this._worker = undefined;
|
|
const createWorker = ()=>{
|
|
var _farmOptions_forkOptions;
|
|
this._worker = new JestWorker(workerPath, {
|
|
...farmOptions,
|
|
forkOptions: {
|
|
...farmOptions.forkOptions,
|
|
env: {
|
|
...((_farmOptions_forkOptions = farmOptions.forkOptions) == null ? void 0 : _farmOptions_forkOptions.env) || {},
|
|
...process.env,
|
|
// we don't pass down NODE_OPTIONS as it can
|
|
// extra memory usage
|
|
NODE_OPTIONS: getNodeOptionsWithoutInspect().replace(/--max-old-space-size=[\d]{1,}/, "").trim()
|
|
}
|
|
}
|
|
});
|
|
restartPromise = new Promise((resolve)=>resolveRestartPromise = resolve);
|
|
/**
|
|
* Jest Worker has two worker types, ChildProcessWorker (uses child_process) and NodeThreadWorker (uses worker_threads)
|
|
* Next.js uses ChildProcessWorker by default, but it can be switched to NodeThreadWorker with an experimental flag
|
|
*
|
|
* We only want to handle ChildProcessWorker's orphan process issue, so we access the private property "_child":
|
|
* https://github.com/facebook/jest/blob/b38d7d345a81d97d1dc3b68b8458b1837fbf19be/packages/jest-worker/src/workers/ChildProcessWorker.ts
|
|
*
|
|
* But this property is not available in NodeThreadWorker, so we need to check if we are using ChildProcessWorker
|
|
*/ if (!farmOptions.enableWorkerThreads) {
|
|
var _this__worker__workerPool;
|
|
for (const worker of ((_this__worker__workerPool = this._worker._workerPool) == null ? void 0 : _this__worker__workerPool._workers) || []){
|
|
var _worker__child;
|
|
(_worker__child = worker._child) == null ? void 0 : _worker__child.on("exit", (code, signal)=>{
|
|
// log unexpected exit if .end() wasn't called
|
|
if ((code || signal && signal !== "SIGINT") && this._worker) {
|
|
console.error(`Static worker unexpectedly exited with code: ${code} and signal: ${signal}`);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
this._worker.getStdout().pipe(process.stdout);
|
|
this._worker.getStderr().pipe(process.stderr);
|
|
};
|
|
createWorker();
|
|
const onHanging = ()=>{
|
|
const worker = this._worker;
|
|
if (!worker) return;
|
|
const resolve = resolveRestartPromise;
|
|
createWorker();
|
|
worker.end().then(()=>{
|
|
resolve(RESTARTED);
|
|
});
|
|
};
|
|
let hangingTimer = false;
|
|
const onActivity = ()=>{
|
|
if (hangingTimer) clearTimeout(hangingTimer);
|
|
hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout);
|
|
};
|
|
for (const method of farmOptions.exposedMethods){
|
|
if (method.startsWith("_")) continue;
|
|
this[method] = timeout ? async (...args)=>{
|
|
activeTasks++;
|
|
try {
|
|
let attempts = 0;
|
|
for(;;){
|
|
onActivity();
|
|
const result = await Promise.race([
|
|
this._worker[method](...args),
|
|
restartPromise
|
|
]);
|
|
if (result !== RESTARTED) return result;
|
|
if (onRestart) onRestart(method, args, ++attempts);
|
|
}
|
|
} finally{
|
|
activeTasks--;
|
|
onActivity();
|
|
}
|
|
} : this._worker[method].bind(this._worker);
|
|
}
|
|
}
|
|
end() {
|
|
const worker = this._worker;
|
|
if (!worker) {
|
|
throw new Error("Farm is ended, no more calls can be done to it");
|
|
}
|
|
cleanupWorkers(worker);
|
|
this._worker = undefined;
|
|
return worker.end();
|
|
}
|
|
/**
|
|
* Quietly end the worker if it exists
|
|
*/ close() {
|
|
if (this._worker) {
|
|
cleanupWorkers(this._worker);
|
|
this._worker.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
//# sourceMappingURL=worker.js.map
|