123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- var debug = require('debug')('nodemon');
- const statSync = require('fs').statSync;
- var utils = require('../utils');
- var bus = utils.bus;
- var childProcess = require('child_process');
- var spawn = childProcess.spawn;
- var exec = childProcess.exec;
- var fork = childProcess.fork;
- var watch = require('./watch').watch;
- var config = require('../config');
- var child = null;
- var killedAfterChange = false;
- var noop = function () { };
- var restart = null;
- var psTree = require('pstree.remy');
- var path = require('path');
- var signals = require('./signals');
- function run(options) {
- var cmd = config.command.raw;
- var runCmd = !options.runOnChangeOnly || config.lastStarted !== 0;
- if (runCmd) {
- utils.log.status('starting `' + config.command.string + '`');
- }
-
- restart = run.bind(this, options);
- run.restart = restart;
- config.lastStarted = Date.now();
- var stdio = ['pipe', 'pipe', 'pipe'];
- if (config.options.stdout) {
- stdio = ['pipe', process.stdout, process.stderr];
- }
- if (config.options.stdin === false) {
- stdio = [process.stdin, process.stdout, process.stderr];
- }
- var sh = 'sh';
- var shFlag = '-c';
- const binPath = process.cwd() + '/node_modules/.bin';
- const spawnOptions = {
- env: Object.assign({}, process.env, options.execOptions.env, {
- PATH: binPath + ':' + process.env.PATH,
- }),
- stdio: stdio,
- }
- var executable = cmd.executable;
- if (utils.isWindows) {
-
-
-
- if (executable.indexOf('/') !== -1) {
- executable = executable.split(' ').map((e, i) => {
- if (i === 0) {
- return path.normalize(e);
- }
- return e;
- }).join(' ');
- }
-
- sh = process.env.comspec || 'cmd';
- shFlag = '/d /s /c';
- spawnOptions.windowsVerbatimArguments = true;
- }
- var args = runCmd ? utils.stringify(executable, cmd.args) : ':';
- var spawnArgs = [sh, [shFlag, args], spawnOptions];
- const firstArg = cmd.args[0] || '';
- var inBinPath = false;
- try {
- inBinPath = statSync(`${binPath}/${executable}`).isFile();
- } catch (e) {}
-
-
- const hasStdio = utils.satisfies('>= 6.4.0 || < 5');
-
-
- const shouldFork =
- !config.options.spawn &&
- !inBinPath &&
- !(firstArg.indexOf('-') === 0) &&
- firstArg !== 'inspect' &&
- executable === 'node' &&
- utils.version.major > 4
- if (shouldFork) {
- var forkArgs = cmd.args.slice(1);
- var env = utils.merge(options.execOptions.env, process.env);
- stdio.push('ipc');
- child = fork(options.execOptions.script, forkArgs, {
- env: env,
- stdio: stdio,
- silent: !hasStdio,
- });
- utils.log.detail('forking');
- debug('fork', sh, shFlag, args)
- } else {
- utils.log.detail('spawning');
- child = spawn.apply(null, spawnArgs);
- debug('spawn', sh, shFlag, args)
- }
- if (config.required) {
- var emit = {
- stdout: function (data) {
- bus.emit('stdout', data);
- },
- stderr: function (data) {
- bus.emit('stderr', data);
- },
- };
-
- if (config.options.stdout) {
- child.on('stdout', emit.stdout).on('stderr', emit.stderr);
- } else {
- child.stdout.on('data', emit.stdout);
- child.stderr.on('data', emit.stderr);
- bus.stdout = child.stdout;
- bus.stderr = child.stderr;
- }
- if (shouldFork) {
- child.on('message', function (message, sendHandle) {
- bus.emit('message', message, sendHandle);
- });
- }
- }
- bus.emit('start');
- utils.log.detail('child pid: ' + child.pid);
- child.on('error', function (error) {
- bus.emit('error', error);
- if (error.code === 'ENOENT') {
- utils.log.error('unable to run executable: "' + cmd.executable + '"');
- process.exit(1);
- } else {
- utils.log.error('failed to start child process: ' + error.code);
- throw error;
- }
- });
- child.on('exit', function (code, signal) {
- if (child && child.stdin) {
- process.stdin.unpipe(child.stdin);
- }
- if (code === 127) {
- utils.log.error('failed to start process, "' + cmd.executable +
- '" exec not found');
- bus.emit('error', code);
- process.exit();
- }
-
-
-
- if (code === 2 && Date.now() < config.lastStarted + 500) {
- utils.log.error('process failed, unhandled exit code (2)');
- utils.log.error('');
- utils.log.error('Either the command has a syntax error,');
- utils.log.error('or it is exiting with reserved code 2.');
- utils.log.error('');
- utils.log.error('To keep nodemon running even after a code 2,');
- utils.log.error('add this to the end of your command: || exit 1');
- utils.log.error('');
- utils.log.error('Read more here: https://git.io/fNOAG');
- utils.log.error('');
- utils.log.error('nodemon will stop now so that you can fix the command.');
- utils.log.error('');
- bus.emit('error', code);
- process.exit();
- }
-
- if (killedAfterChange) {
- killedAfterChange = false;
- signal = config.signal;
- }
-
- if (utils.isWindows && signal === 'SIGTERM') {
- signal = config.signal;
- }
- if (signal === config.signal || code === 0) {
-
- debug('bus.emit(exit) via ' + config.signal);
- bus.emit('exit', signal);
-
- if (signal === config.signal) {
- return restart();
- }
- if (code === 0) {
- if (runCmd) {
- utils.log.status('clean exit - waiting for changes before restart');
- }
- child = null;
- }
- } else {
- bus.emit('crash');
- if (options.exitcrash) {
- utils.log.fail('app crashed');
- if (!config.required) {
- process.exit(1);
- }
- } else {
- utils.log.fail('app crashed - waiting for file changes before' +
- ' starting...');
- child = null;
- }
- }
- if (config.options.restartable) {
-
-
- process.stdin.resume();
- }
- });
- run.kill = function (noRestart, callback) {
-
- if (typeof noRestart === 'function') {
- callback = noRestart;
- noRestart = false;
- }
- if (!callback) {
- callback = noop;
- }
- if (child !== null) {
-
-
- if (options.stdin) {
- process.stdin.unpipe(child.stdin);
- }
-
-
- if (!noRestart) {
- killedAfterChange = true;
- }
-
- var oldPid = child.pid;
- if (child) {
- kill(child, config.signal, function () {
-
-
-
- if (child && options.stdin && child.stdin && oldPid === child.pid) {
- child.stdin.end();
- }
- callback();
- });
- }
- } else if (!noRestart) {
-
-
-
- bus.once('start', callback);
- restart();
- } else {
- callback();
- }
- };
-
- if (options.stdin) {
- process.stdin.resume();
-
-
-
-
- if (hasStdio) {
- child.stdin.on('error', () => { });
- process.stdin.pipe(child.stdin);
- } else {
- if (child.stdout) {
- child.stdout.pipe(process.stdout);
- } else {
- utils.log.error('running an unsupported version of node ' +
- process.version);
- utils.log.error('nodemon may not work as expected - ' +
- 'please consider upgrading to LTS');
- }
- }
- bus.once('exit', function () {
- if (child && process.stdin.unpipe) {
- process.stdin.unpipe(child.stdin);
- }
- });
- }
- debug('start watch on: %s', config.options.watch);
- if (config.options.watch !== false) {
- watch();
- }
- }
- function kill(child, signal, callback) {
- if (!callback) {
- callback = function () { };
- }
- if (utils.isWindows) {
-
-
-
-
-
-
-
- exec('taskkill /pid ' + child.pid + ' /T /F');
- callback();
- } else {
-
-
-
-
-
-
- const sig = signal.replace('SIG', '');
- psTree(child.pid, function (err, kids) {
- if (psTree.hasPS) {
- spawn('kill', ['-s', sig, child.pid].concat(kids))
- .on('close', callback);
- } else {
-
- const pids = kids.concat(child.pid).sort();
- pids.forEach(pid => {
- exec('kill -' + signals[signal] + ' ' + pid, () => { });
- });
- callback();
- }
- });
- }
- }
- run.kill = function (flag, callback) {
- if (callback) {
- callback();
- }
- };
- run.restart = noop;
- bus.on('quit', function onQuit(code) {
- if (code === undefined) {
- code = 0;
- }
-
- var exitTimer = null;
- var exit = function () {
- clearTimeout(exitTimer);
- exit = noop;
- child = null;
- if (!config.required) {
-
- bus.listeners('quit').forEach(function (listener) {
- if (listener !== onQuit) {
- listener();
- }
- });
- process.exit(code);
- } else {
- bus.emit('exit');
- }
- };
-
- if (config.run === false) {
- return exit();
- }
-
- config.run = false;
- if (child) {
-
- exitTimer = setTimeout(exit, 10 * 1000);
- child.removeAllListeners('exit');
- child.once('exit', exit);
- kill(child, 'SIGINT');
- } else {
- exit();
- }
- });
- bus.on('restart', function () {
-
-
- run.kill();
- });
- process.on('exit', function () {
- utils.log.detail('exiting');
- if (child) { child.kill(); }
- });
- if (!utils.isWindows) {
- bus.once('boot', () => {
-
- process.once('SIGINT', () => bus.emit('quit', 130));
- process.once('SIGTERM', () => {
- bus.emit('quit', 143);
- if (child) { child.kill('SIGTERM'); }
- });
- })
- }
- module.exports = run;
|