193 lines
8.2 KiB
JavaScript
Executable File
193 lines
8.2 KiB
JavaScript
Executable File
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
Worker: null,
|
|
getNextBuildDebuggerPortOffset: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
Worker: function() {
|
|
return Worker;
|
|
},
|
|
getNextBuildDebuggerPortOffset: function() {
|
|
return getNextBuildDebuggerPortOffset;
|
|
}
|
|
});
|
|
const _jestworker = require("next/dist/compiled/jest-worker");
|
|
const _stream = require("stream");
|
|
const _utils = require("../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');
|
|
}
|
|
};
|
|
function getNextBuildDebuggerPortOffset(_) {
|
|
// 0: export worker
|
|
return 0;
|
|
}
|
|
class Worker {
|
|
constructor(workerPath, options){
|
|
let { enableSourceMaps, timeout, onRestart, logger = console, debuggerPortOffset, isolatedMemory, ...farmOptions } = options;
|
|
let restartPromise;
|
|
let resolveRestartPromise;
|
|
let activeTasks = 0;
|
|
this._worker = undefined;
|
|
// ensure we end workers if they weren't before exit
|
|
process.on('exit', ()=>{
|
|
this.close();
|
|
});
|
|
const nodeOptions = (0, _utils.getParsedNodeOptionsWithoutInspect)();
|
|
if (debuggerPortOffset !== -1) {
|
|
const nodeDebugType = (0, _utils.getNodeDebugType)();
|
|
if (nodeDebugType) {
|
|
const address = (0, _utils.getParsedDebugAddress)();
|
|
address.port = address.port + // current process runs on `address.port`
|
|
1 + debuggerPortOffset;
|
|
nodeOptions[nodeDebugType] = (0, _utils.formatDebugAddress)(address);
|
|
}
|
|
}
|
|
if (enableSourceMaps) {
|
|
nodeOptions['enable-source-maps'] = true;
|
|
}
|
|
if (isolatedMemory) {
|
|
delete nodeOptions['max-old-space-size'];
|
|
delete nodeOptions['max_old_space_size'];
|
|
}
|
|
const createWorker = ()=>{
|
|
var _farmOptions_forkOptions;
|
|
this._worker = new _jestworker.Worker(workerPath, {
|
|
...farmOptions,
|
|
forkOptions: {
|
|
...farmOptions.forkOptions,
|
|
env: {
|
|
...process.env,
|
|
...((_farmOptions_forkOptions = farmOptions.forkOptions) == null ? void 0 : _farmOptions_forkOptions.env) || {},
|
|
IS_NEXT_WORKER: 'true',
|
|
NODE_OPTIONS: (0, _utils.formatNodeOptions)(nodeOptions)
|
|
}
|
|
},
|
|
maxRetries: 0
|
|
});
|
|
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, // if a child process emits a particular message, we track that as activity
|
|
// so the parent process can keep track of progress
|
|
_worker__child1;
|
|
(_worker__child = worker._child) == null ? void 0 : _worker__child.on('exit', (code, signal)=>{
|
|
if ((code || signal && signal !== 'SIGINT') && this._worker) {
|
|
logger.error(`Next.js build worker exited with code: ${code} and signal: ${signal}`);
|
|
// if a child process doesn't exit gracefully, we want to bubble up the exit code to the parent process
|
|
process.exit(code ?? 1);
|
|
}
|
|
});
|
|
(_worker__child1 = worker._child) == null ? void 0 : _worker__child1.on('message', ([, data])=>{
|
|
if (data && typeof data === 'object' && 'type' in data && data.type === 'activity') {
|
|
onActivity();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
let aborted = false;
|
|
const onActivityAbort = ()=>{
|
|
if (!aborted) {
|
|
options.onActivityAbort == null ? void 0 : options.onActivityAbort.call(options);
|
|
aborted = true;
|
|
}
|
|
};
|
|
// Listen to the worker's stdout and stderr, if there's any thing logged, abort the activity first
|
|
const abortActivityStreamOnLog = new _stream.Transform({
|
|
transform (_chunk, _encoding, callback) {
|
|
onActivityAbort();
|
|
callback();
|
|
}
|
|
});
|
|
// Stop the activity if there's any output from the worker
|
|
this._worker.getStdout().pipe(abortActivityStreamOnLog);
|
|
this._worker.getStderr().pipe(abortActivityStreamOnLog);
|
|
// Pipe the worker's stdout and stderr to the parent process
|
|
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();
|
|
logger.warn(`Sending SIGTERM signal to static worker due to timeout${timeout ? ` of ${timeout / 1000} seconds` : ''}. Subsequent errors may be a result of the worker exiting.`);
|
|
worker.end().then(()=>{
|
|
resolve(RESTARTED);
|
|
});
|
|
};
|
|
let hangingTimer = false;
|
|
const onActivity = ()=>{
|
|
if (hangingTimer) clearTimeout(hangingTimer);
|
|
if (options.onActivity) options.onActivity();
|
|
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 Object.defineProperty(new Error('Farm is ended, no more calls can be done to it'), "__NEXT_ERROR_CODE", {
|
|
value: "E265",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
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
|