3 Object.defineProperty(exports, '__esModule', {
6 exports.default = void 0;
8 function _child_process() {
9 const data = require('child_process');
11 _child_process = function () {
19 const data = require('stream');
21 _stream = function () {
28 function _mergeStream() {
29 const data = _interopRequireDefault(require('merge-stream'));
31 _mergeStream = function () {
38 function _supportsColor() {
39 const data = require('supports-color');
41 _supportsColor = function () {
48 var _types = require('../types');
50 function _interopRequireDefault(obj) {
51 return obj && obj.__esModule ? obj : {default: obj};
54 function _defineProperty(obj, key, value) {
56 Object.defineProperty(obj, key, {
68 const SIGNAL_BASE_EXIT_CODE = 128;
69 const SIGKILL_EXIT_CODE = SIGNAL_BASE_EXIT_CODE + 9;
70 const SIGTERM_EXIT_CODE = SIGNAL_BASE_EXIT_CODE + 15; // How long to wait after SIGTERM before sending SIGKILL
72 const SIGKILL_DELAY = 500;
74 * This class wraps the child process and provides a nice interface to
75 * communicate with. It takes care of:
77 * - Re-spawning the process if it dies.
78 * - Queues calls while the worker is busy.
79 * - Re-sends the requests if the worker blew up.
81 * The reason for queueing them here (since childProcess.send also has an
82 * internal queue) is because the worker could be doing asynchronous work, and
83 * this would lead to the child process to read its receiving buffer and start a
84 * second call. By queueing calls here, we don't send the next call to the
85 * children until we receive the result of the previous one.
87 * As soon as a request starts to be processed by a worker, its "processed"
88 * field is changed to "true", so that other workers which might encounter the
92 class ChildProcessWorker {
93 constructor(options) {
94 _defineProperty(this, '_child', void 0);
96 _defineProperty(this, '_options', void 0);
98 _defineProperty(this, '_request', void 0);
100 _defineProperty(this, '_retries', void 0);
102 _defineProperty(this, '_onProcessEnd', void 0);
104 _defineProperty(this, '_onCustomMessage', void 0);
106 _defineProperty(this, '_fakeStream', void 0);
108 _defineProperty(this, '_stdout', void 0);
110 _defineProperty(this, '_stderr', void 0);
112 _defineProperty(this, '_exitPromise', void 0);
114 _defineProperty(this, '_resolveExitPromise', void 0);
116 this._options = options;
117 this._request = null;
118 this._fakeStream = null;
121 this._exitPromise = new Promise(resolve => {
122 this._resolveExitPromise = resolve;
128 const forceColor = _supportsColor().stdout
133 const child = (0, _child_process().fork)(
134 require.resolve('./processChild'),
140 JEST_WORKER_ID: String(this._options.workerId + 1),
141 // 0-indexed workerId, 1-indexed JEST_WORKER_ID
144 // Suppress --debug / --inspect flags while preserving others (like --harmony).
145 execArgv: process.execArgv.filter(v => !/^--(debug|inspect)/.test(v)),
147 ...this._options.forkOptions
153 // We need to add a permanent stream to the merged stream to prevent it
154 // from ending when the subprocess stream ends
155 this._stdout = (0, _mergeStream().default)(this._getFakeStream());
158 this._stdout.add(child.stdout);
163 // We need to add a permanent stream to the merged stream to prevent it
164 // from ending when the subprocess stream ends
165 this._stderr = (0, _mergeStream().default)(this._getFakeStream());
168 this._stderr.add(child.stderr);
171 child.on('message', this._onMessage.bind(this));
172 child.on('exit', this._onExit.bind(this));
174 _types.CHILD_MESSAGE_INITIALIZE,
176 this._options.workerPath,
177 this._options.setupArgs
180 this._retries++; // If we exceeded the amount of retries, we will emulate an error reply
181 // coming from the child. This avoids code duplication related with cleaning
182 // the queue, and scheduling the next call.
184 if (this._retries > this._options.maxRetries) {
185 const error = new Error(
186 `Jest worker encountered ${this._retries} child process exceptions, exceeding retry limit`
190 _types.PARENT_MESSAGE_CLIENT_ERROR,
202 // End the temporary streams so the merged streams end too
203 if (this._fakeStream) {
204 this._fakeStream.end();
206 this._fakeStream = null;
209 this._resolveExitPromise();
212 _onMessage(response) {
213 // TODO: Add appropriate type check
216 switch (response[0]) {
217 case _types.PARENT_MESSAGE_OK:
218 this._onProcessEnd(null, response[1]);
222 case _types.PARENT_MESSAGE_CLIENT_ERROR:
225 if (error != null && typeof error === 'object') {
226 const extra = error; // @ts-expect-error: no index
228 const NativeCtor = global[response[1]];
229 const Ctor = typeof NativeCtor === 'function' ? NativeCtor : Error;
230 error = new Ctor(response[2]);
231 error.type = response[1];
232 error.stack = response[3];
234 for (const key in extra) {
235 error[key] = extra[key];
239 this._onProcessEnd(error, null);
243 case _types.PARENT_MESSAGE_SETUP_ERROR:
244 error = new Error('Error when calling setup: ' + response[2]);
245 error.type = response[1];
246 error.stack = response[3];
248 this._onProcessEnd(error, null);
252 case _types.PARENT_MESSAGE_CUSTOM:
253 this._onCustomMessage(response[1]);
258 throw new TypeError('Unexpected response from worker: ' + response[0]);
266 exitCode !== SIGTERM_EXIT_CODE &&
267 exitCode !== SIGKILL_EXIT_CODE
272 this._child.send(this._request);
279 send(request, onProcessStart, onProcessEnd, onCustomMessage) {
280 onProcessStart(this);
282 this._onProcessEnd = (...args) => {
283 // Clean the request to avoid sending past requests to workers that fail
284 // while waiting for a new request (timers, unhandled rejections...)
285 this._request = null;
286 return onProcessEnd(...args);
289 this._onCustomMessage = (...arg) => onCustomMessage(...arg);
291 this._request = request;
294 this._child.send(request, () => {});
298 return this._exitPromise;
302 this._child.kill('SIGTERM');
304 const sigkillTimeout = setTimeout(
305 () => this._child.kill('SIGKILL'),
309 this._exitPromise.then(() => clearTimeout(sigkillTimeout));
313 return this._options.workerId;
325 if (!this._fakeStream) {
326 this._fakeStream = new (_stream().PassThrough)();
329 return this._fakeStream;
333 exports.default = ChildProcessWorker;