diff --git a/lib/resolveTaskFn.js b/lib/resolveTaskFn.js index 45f16b570..9a60e8350 100644 --- a/lib/resolveTaskFn.js +++ b/lib/resolveTaskFn.js @@ -46,30 +46,41 @@ const handleOutput = (command, result, ctx, isError = false) => { } } +/** + * Kill an execa process along with all its child processes. + * @param {execa.ExecaChildProcess} execaProcess + */ +const killExecaProcess = async (execaProcess) => { + try { + const childPids = await pidTree(execaProcess.pid) + for (const childPid of childPids) { + process.kill(childPid) + } + } catch { + // Suppress "No matching pid found" error. This probably means + // the process already died before executing. + } + + // The execa process is killed separately in order to get the `KILLED` status. + execaProcess.kill() +} + /** * Interrupts the execution of the execa process that we spawned if * another task adds an error to the context. * * @param {Object} ctx * @param {execa.ExecaChildProcess} execaChildProcess - * @returns {function(): void} Function that clears the interval that + * @returns {() => void} Function that clears the interval that * checks the context. */ const interruptExecutionOnError = (ctx, execaChildProcess) => { let loopIntervalId - async function loop() { + const loop = async () => { if (ctx.errors.size > 0) { clearInterval(loopIntervalId) - - const childPids = await pidTree(execaChildProcess.pid) - for (const pid of childPids) { - process.kill(pid) - } - - // The execa process is killed separately in order - // to get the `KILLED` status. - execaChildProcess.kill() + await killExecaProcess(execaChildProcess) } } @@ -111,7 +122,7 @@ const makeErr = (command, result, ctx) => { * @param {Array} options.files — Filepaths to run the linter task against * @param {Boolean} [options.shell] — Whether to skip parsing linter task for better shell support * @param {Boolean} [options.verbose] — Always show task verbose - * @returns {function(): Promise>} + * @returns {() => Promise>} */ export const resolveTaskFn = ({ command,