You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
2.8 KiB
JavaScript

"use strict";
const Promise = require("bluebird");
const execa = require("execa");
const chalk = require("chalk");
const isExecaError = require("./is-execa-error");
// TODO: Eventually turn this into a generic restartableProcess abstraction?
module.exports = function createProcessManager(processPath, processArguments, { onOutput, onError, onStart }) {
let running = false;
let queuedRestart = false;
let currentProcess;
function reportEnded() {
currentProcess = null;
running = false;
if (queuedRestart === true) {
console.log(chalk.blue.bold(`Restarting process...`));
return startProcess();
}
}
function killProcess() {
if (currentProcess != null) {
currentProcess.cancel();
}
}
function startProcess() {
return Promise.try(() => {
if (!running) {
running = true;
onStart();
let process = execa("node", [ processPath, ... processArguments ]);
currentProcess = process;
return process;
} else {
throw new Error(`Tried to start already-running process; this is a bug and should never happen`);
}
}).then(({ stdout, stderr }) => {
console.error(stderr); // TODO: Think about whether to keep this
console.log(chalk.blue.bold(`Process exited normally; it will be restarted upon changes`));
try {
onOutput(JSON.parse(stdout));
} catch (error) {
if (error instanceof SyntaxError) {
// Show the original unparseable output, for debugging purposes
console.error(chalk.bold("Process output:\n"), stdout);
}
throw error;
}
return reportEnded();
}).catch(isExecaError, (error) => {
let { stdout, stderr, exitCode, isCanceled } = error;
if (exitCode != null && exitCode !== 0) {
onError({
reason: `Process exited with exit code ${exitCode}`,
output: (error.stderr + error.stdout).trim()
});
console.error(stdout);
console.error(stderr);
console.log(chalk.red.bold(`Process exited with exit code ${exitCode}; it will be restarted upon changes`));
} else if (isCanceled === true) {
// Ignore
} else {
throw error;
}
return reportEnded();
}).catch(SyntaxError, (error) => {
onError({
reason: "Failed to parse process output",
output: error.stack
});
console.error(error.stack);
console.log(chalk.red.bold(`Failed to parse output of the process; it will be restarted upon changes`));
return reportEnded();
});
}
return {
kill: killProcess,
restart: function (reason) {
console.log(chalk.green.bold(`Change detected in ${reason}; running ${processPath}...`));
if (running === true) {
queuedRestart = true;
return this.kill();
} else {
return this.start();
}
},
start: function () {
if (!running) {
return startProcess();
}
// TODO: Make this a possible error condition, rather than silently ignored?
}
};
};