function nodemon(settings) { nodemon.reset(); // allow the cli string as the argument to nodemon, and allow for // `node nodemon -V app.js` or just `-V app.js` if (typeof settings === 'string') { settings = settings.trim(); if (settings.indexOf('node') !== 0) { if (settings.indexOf('nodemon') !== 0) { settings = 'nodemon ' + settings; } settings = 'node ' + settings; } settings = cli.parse(settings); } // set the debug flag as early as possible to get all the detailed logging if (settings.verbose) { utils.debug = true; } if (settings.help) { console.log(help(settings.help)); if (!config.required) { process.exit(0); } } if (settings.version) { version().then(function (v) { console.log(v); if (!config.required) { process.exit(0); } }); return; } // nodemon tools like grunt-nodemon. This affects where // the script is being run from, and will affect where // nodemon looks for the nodemon.json files if (settings.cwd) { // this is protection to make sure we haven't dont the chdir already... // say like in cli/parse.js (which is where we do this once already!) if (process.cwd() !== path.resolve(config.system.cwd, settings.cwd)) { process.chdir(settings.cwd); } } config.load(settings, function (config) { if (!config.options.dump && !config.options.execOptions.script && config.options.execOptions.exec === 'node') { if (!config.required) { console.log(help('usage')); process.exit(); } return; } // before we print anything, update the colour setting on logging utils.colours = config.options.colours; // always echo out the current version utils.log.info(version.pinned); if (config.options.cwd) { utils.log.detail('process root: ' + process.cwd()); } config.loaded.forEach(function (filename) { utils.log.detail('reading config ' + filename); }); // echo out notices about running state if (config.options.stdin && config.options.restartable) { // allow nodemon to restart when the use types 'rs\n' process.stdin.resume(); process.stdin.setEncoding('utf8'); process.stdin.on('data', function (data) { data = (data + '').trim().toLowerCase(); // if the keys entered match the restartable value, then restart! if (data === config.options.restartable) { bus.emit('restart'); } }); } else if (config.options.stdin) { // if 'restartable' is disabled (via a nodemon.json) // then it's possible we're being used with a REPL // so let's make sure we don't eat the key presses // but also, since we're wrapping, watch out for // special keys, like ctrl+c x 2 or '.exit' or ctrl+d var ctrlC = false; var buffer = ''; process.stdin.on('data', function (data) { buffer += data; data = data.toString(); var chr = data.charCodeAt(0); if (chr === 3) { if (ctrlC) { process.exit(); } ctrlC = true; } else if (buffer === '.exit' || chr === 4) { process.exit(); } else if (ctrlC || chr === 10) { ctrlC = false; buffer = ''; } }); process.stdin.setRawMode(true); } if (config.options.restartable) { utils.log.info('to restart at any time, enter `' + config.options.restartable + '`'); } var none = function (v) { return v; }; utils.log.detail('ignoring: ' + config.options.monitor.map(function (rule) { return rule.slice(0, 1) === '!' ? rule.slice(1) : false; }).filter(none).join(' ')); utils.log.info('watching: ' + config.options.monitor.map(function (rule) { return rule.slice(0, 1) !== '!' ? rule : false; }).filter(none).join(' ')); utils.log.detail('watching extensions: ' + config.options.execOptions.ext); if (config.options.dump) { utils.log._log('log', '--------- ...
n/a
addListener = function (event, handler) { if (!eventHandlers[event]) { eventHandlers[event] = []; } eventHandlers[event].push(handler); bus.on(event, handler); return nodemon; }
n/a
function colour(c, str) { return (colour[c] || colour.black) + str + colour.black; }
n/a
emit = function () { bus.emit.apply(bus, [].slice.call(arguments)); return nodemon; }
...
} else {
child = spawn(sh, args);
}
if (config.required) {
var emit = {
stdout: function (data) {
bus.emit('stdout', data);
},
stderr: function (data) {
bus.emit('stderr', data);
},
};
// now work out what to bind to...
...
function match(files, monitor, ext) { // sort the rules by highest specificity (based on number of slashes) // ignore rules (!) get sorted highest as they take precedent // TODO actually check separator rules work on windows var rules = monitor.sort(function (a, b) { var r = b.split(path.sep).length - a.split(path.sep).length; var aIsIgnore = a.slice(0, 1) === '!'; var bIsIgnore = b.slice(0, 1) === '!'; if (aIsIgnore || bIsIgnore) { if (aIsIgnore) { return -1; } else { return 1; } } if (r === 0) { return b.length - a.length; } return r; }).map(function (s) { var prefix = s.slice(0, 1); if (prefix === '!') { return '!**' + (prefix !== path.sep ? path.sep : '') + s.slice(1); } else if (s.slice(0, 2) === '..') { return path.resolve(process.cwd(), s); } return '**' + (prefix !== path.sep ? path.sep : '') + s; }); var good = []; var whitelist = []; // files that we won't check against the extension var ignored = 0; var watched = 0; var usedRules = []; var minimatchOpts = {}; // enable case-insensitivity on Windows if (utils.isWindows) { minimatchOpts.nocase = true; } files.forEach(function (file) { var matched = false; for (var i = 0; i < rules.length; i++) { if (rules[i].slice(0, 1) === '!') { if (!minimatch(file, rules[i], minimatchOpts)) { ignored++; matched = true; break; } } else { if (minimatch(file, rules[i], minimatchOpts)) { watched++; // don't repeat the output if a rule is matched if (usedRules.indexOf(rules[i]) === -1) { usedRules.push(rules[i]); utils.log.detail('matched rule: ' + rules[i]); } // if the rule doesn't match the WATCH EVERYTHING // but *does* match a rule that ends with *.*, then // white list it - in that we don't run it through // the extension check too. if (rules[i] !== '**' + path.sep + '*.*' && rules[i].slice(-3) === '*.*') { whitelist.push(file); } else if (path.basename(file) === path.basename(rules[i])) { // if the file matches the actual rule, then it's put on whitelist whitelist.push(file); } else { good.push(file); } matched = true; break; } } } if (!matched) { ignored++; } }); // finally check the good files against the extensions that we're monitoring if (ext) { if (ext.indexOf(',') === -1) { ext = '**/*.' + ext; } else { ext = '**/*.{' + ext + '}'; } good = good.filter(function (file) { // only compare the filename to the extension test return minimatch(path.basename(file), ext, minimatchOpts); }); } // else assume *.* var result = good.concat(whitelist); if (utils.isWindows) { // fix for windows testing - I *think* this is okay to do result = result.map(function (file) { return file.slice(0, 1).toLowerCase() + file.slice(1); }); } return { result: result, ignored: ignored, watched: watched, total: files.length, }; }
...
* @param {String} argument value given to the --delay option
* @return {Number} millisecond equivalent of the argument
*/
function parseDelay(value) {
var millisPerSecond = 1000;
var millis = 0;
if (value.match(/^\d*ms$/)) {
// Explicitly parse for milliseconds when using ms time specifier
millis = parseInt(value, 10);
} else {
// Otherwise, parse for seconds, with or without time specifier then convert
millis = parseFloat(value) * millisPerSecond;
}
...
on = function (event, handler) { if (!eventHandlers[event]) { eventHandlers[event] = []; } eventHandlers[event].push(handler); bus.on(event, handler); return nodemon; }
...
## Pipe output to somewhere else
```js
nodemon({
script: ...,
stdout: false // important: this tells nodemon not to output to console
}).on('readable', function() { // the `readable` event indicates that data
is ready to pick up
this.stdout.pipe(fs.createWriteStream('output.txt'));
this.stderr.pipe(fs.createWriteStream('err.txt'));
});
```
## Using io.js for nodemon
...
once = function (event, handler) { if (!eventHandlers[event]) { eventHandlers[event] = []; } eventHandlers[event].push(handler); bus.once(event, function () { debug('bus.once(%s)', event); eventHandlers[event].splice(eventHandlers[event].indexOf(handler), 1); handler.apply(this, arguments); }); return nodemon; }
...
## Controlling shutdown of your script
nodemon sends a kill signal to your application when it sees a file update. If you need to clean up on shutdown inside your script
you can capture the kill signal and handle it yourself.
The following example will listen once for the `SIGUSR2` signal (used by nodemon to restart), run the clean up process and then
kill itself for nodemon to continue control:
process.once('SIGUSR2', function () {
gracefulShutdown(function () {
process.kill(process.pid, 'SIGUSR2');
});
});
Note that the `process.kill` is *only* called once your shutdown jobs are complete. Hat tip to [Benjie Gillam](http://www.benjiegillam
.com/2011/08/node-js-clean-restart-and-faster-development-with-nodemon/) for writing this technique up.
...
removeAllListeners = function (event) { // unbind only the `nodemon.on` event handlers Object.keys(eventHandlers).filter(function (e) { return event ? e === event : true; }).forEach(function (event) { eventHandlers[event].forEach(function (handler) { bus.removeListener(event, handler); eventHandlers[event].splice(eventHandlers[event].indexOf(handler), 1); }); }); return nodemon; }
...
// immediately try to stop any polling
config.run = false;
if (child) {
// give up waiting for the kids after 10 seconds
exitTimer = setTimeout(exit, 10 * 1000);
child.removeAllListeners('exit');
child.once('exit', exit);
kill(child, 'SIGINT');
} else {
exit();
}
});
...
reset = function (done) { bus.emit('reset', done); }
n/a
restart = function () { utils.log.status('restarting child process'); bus.emit('restart'); return nodemon; }
n/a
function run(options) {
var cmd = config.command.raw;
var runCmd = !options.runOnChangeOnly || config.lastStarted !== 0;
if (runCmd) {
utils.log.status('starting `' + config.command.string + '`');
}
/*jshint validthis:true*/
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,];
}
var sh = 'sh';
var shFlag = '-c';
if (utils.isWindows) {
sh = 'cmd';
shFlag = '/c';
}
var executable = cmd.executable;
// special logic for windows, as spaces in the paths need the path fragment
// quoted, so it reads: c:\"Program Files"\nodejs\node.exe
if (utils.isWindows && executable.indexOf(' ') !== -1) {
var re = /\\((\w+\s+)+\w+)(?=([\\\.]))(?=([^"]*"[^"]*")*[^"]*$)/g;
executable = executable.replace(re, '\\"$1"');
}
var args = runCmd ? utils.stringify(executable, cmd.args) : ':';
var spawnArgs = [sh, [shFlag, args]];
debug('spawning', args);
if (utils.version.major === 0 && utils.version.minor < 8) {
// use the old spawn args :-\
} else {
spawnArgs.push({
env: utils.merge(options.execOptions.env, process.env),
stdio: stdio,
});
}
child = spawn.apply(null, spawnArgs);
if (config.required) {
var emit = {
stdout: function (data) {
bus.emit('stdout', data);
},
stderr: function (data) {
bus.emit('stderr', data);
},
};
// now work out what to bind to...
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;
}
}
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 (code === 127) {
utils.log.error('failed to start process, "' + cmd.executable +
'" exec not found');
bus.emit('error', code);
process.exit();
}
if (code === 2) {
// something wrong with parsed command
utils.log.error('failed to start process, possible issue with exec ' +
'arguments');
bus.emit('error', code);
process.exit();
}
// In case we killed the app ourselves, set the signal thusly
if (killedAfterChange) {
killedAfterChange = false;
signal = config.signal;
}
// this is nasty, but it gives it windows support
if (utils.isWindows && signal === 'SIGTERM') {
signal = config.signal;
}
if (signal === config.signal || code === 0) {
// this was a clean exit, so emit exit, rather than crash
debug('bus.emit(exit) via ' + config.signal);
bus.emit('exit');
// exit the monitor, but do it gracefully
if (signal === config.signal) {
return restart();
} else if (code === 0) { // clean exit - wait until file change to restart
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(0);
}
} else {
utils.log.fail('app crashed - waiting for file changes before' +
' starting...');
child = null;
}
}
if (config.options.restartable) {
// stdin needs to kick in again to be able to listen to the
// restart command
process.stdin.resume();
}
});
run.kill = function (noRestart, callback) ...
n/a
function version(callback) { // first find the package.json as this will be our root var promise = findPackage(path.dirname(module.parent.filename)) .then(function (dir) { // now try to load the package var v = require(path.resolve(dir, 'package.json')).version; if (v && v !== '0.0.0') { return v; } root = dir; // else we're in development, give the commit out // get the last commit and whether the working dir is dirty var promises = [ branch().catch(function () { return 'master'; }), commit().catch(function () { return '<none>'; }), dirty().catch(function () { return 0; }), ]; // use the cached result as the export return Promise.all(promises).then(function (res) { var branch = res[0]; var commit = res[1]; var dirtyCount = parseInt(res[2], 10); var curr = branch + ': ' + commit; if (dirtyCount !== 0) { curr += ' (' + dirtyCount + ' dirty files)'; } return curr; }); }).catch(function (error) { console.log(error.stack); throw error; }); if (callback) { promise.then(function (res) { callback(null, res); }, callback); } return promise; }
n/a
function colour(c, str) { return (colour[c] || colour.black) + str + colour.black; }
n/a
function strip(str) { re.lastIndex = 0; // reset position return str.replace(re, ''); }
n/a
load = function (settings, ready) { reset(); var config = this; load(settings, config.options, config, function (options) { config.options = options; if (options.watch.length === 0) { // this is to catch when the watch is left blank options.watch.push('*.*'); } if (options['watch_interval']) { // jshint ignore:line options.watchInterval = options['watch_interval']; // jshint ignore:line } config.watchInterval = options.watchInterval || null; if (options['signal']) { // jshint ignore:line config.signal = options.signal; } var cmd = command(config.options); config.command = { raw: cmd, string: utils.stringify(cmd.executable, cmd.args), }; // now run automatic checks on system adding to the config object options.monitor = rulesToMonitor(options.watch, options.ignore, config); var cwd = process.cwd(); debug('config: dirs', config.dirs); if (config.dirs.length === 0) { config.dirs.unshift(cwd); } bus.emit('config:update', config); pinVersion().then(function () { ready(config); }); }); }
n/a
function reset() { rules.reset(); config.dirs = []; config.options = { ignore: [], watch: [] }; config.lastStarted = 0; config.loaded = []; }
n/a
function match(files, monitor, ext) { // sort the rules by highest specificity (based on number of slashes) // ignore rules (!) get sorted highest as they take precedent // TODO actually check separator rules work on windows var rules = monitor.sort(function (a, b) { var r = b.split(path.sep).length - a.split(path.sep).length; var aIsIgnore = a.slice(0, 1) === '!'; var bIsIgnore = b.slice(0, 1) === '!'; if (aIsIgnore || bIsIgnore) { if (aIsIgnore) { return -1; } else { return 1; } } if (r === 0) { return b.length - a.length; } return r; }).map(function (s) { var prefix = s.slice(0, 1); if (prefix === '!') { return '!**' + (prefix !== path.sep ? path.sep : '') + s.slice(1); } else if (s.slice(0, 2) === '..') { return path.resolve(process.cwd(), s); } return '**' + (prefix !== path.sep ? path.sep : '') + s; }); var good = []; var whitelist = []; // files that we won't check against the extension var ignored = 0; var watched = 0; var usedRules = []; var minimatchOpts = {}; // enable case-insensitivity on Windows if (utils.isWindows) { minimatchOpts.nocase = true; } files.forEach(function (file) { var matched = false; for (var i = 0; i < rules.length; i++) { if (rules[i].slice(0, 1) === '!') { if (!minimatch(file, rules[i], minimatchOpts)) { ignored++; matched = true; break; } } else { if (minimatch(file, rules[i], minimatchOpts)) { watched++; // don't repeat the output if a rule is matched if (usedRules.indexOf(rules[i]) === -1) { usedRules.push(rules[i]); utils.log.detail('matched rule: ' + rules[i]); } // if the rule doesn't match the WATCH EVERYTHING // but *does* match a rule that ends with *.*, then // white list it - in that we don't run it through // the extension check too. if (rules[i] !== '**' + path.sep + '*.*' && rules[i].slice(-3) === '*.*') { whitelist.push(file); } else if (path.basename(file) === path.basename(rules[i])) { // if the file matches the actual rule, then it's put on whitelist whitelist.push(file); } else { good.push(file); } matched = true; break; } } } if (!matched) { ignored++; } }); // finally check the good files against the extensions that we're monitoring if (ext) { if (ext.indexOf(',') === -1) { ext = '**/*.' + ext; } else { ext = '**/*.{' + ext + '}'; } good = good.filter(function (file) { // only compare the filename to the extension test return minimatch(path.basename(file), ext, minimatchOpts); }); } // else assume *.* var result = good.concat(whitelist); if (utils.isWindows) { // fix for windows testing - I *think* this is okay to do result = result.map(function (file) { return file.slice(0, 1).toLowerCase() + file.slice(1); }); } return { result: result, ignored: ignored, watched: watched, total: files.length, }; }
...
* @param {String} argument value given to the --delay option
* @return {Number} millisecond equivalent of the argument
*/
function parseDelay(value) {
var millisPerSecond = 1000;
var millis = 0;
if (value.match(/^\d*ms$/)) {
// Explicitly parse for milliseconds when using ms time specifier
millis = parseInt(value, 10);
} else {
// Otherwise, parse for seconds, with or without time specifier then convert
millis = parseFloat(value) * millisPerSecond;
}
...
function rulesToMonitor(watch, ignore, config) { var monitor = []; if (!Array.isArray(ignore)) { if (ignore) { ignore = [ignore]; } else { ignore = []; } } if (!Array.isArray(watch)) { if (watch) { watch = [watch]; } else { watch = []; } } if (watch && watch.length) { monitor = utils.clone(watch); } if (ignore) { [].push.apply(monitor, (ignore || []).map(function (rule) { return '!' + rule; })); } var cwd = process.cwd(); // next check if the monitored paths are actual directories // or just patterns - and expand the rule to include *.* monitor = monitor.map(function (rule) { var not = rule.slice(0, 1) === '!'; if (not) { rule = rule.slice(1); } if (rule === '.' || rule === '.*') { rule = '*.*'; } var dir = path.resolve(cwd, rule); try { var stat = fs.statSync(dir); if (stat.isDirectory()) { rule = dir; if (rule.slice(-1) !== '/') { rule += '/'; } rule += '**/*'; // `!not` ... sorry. if (!not) { config.dirs.push(dir); } } else { // ensures we end up in the check that tries to get a base directory // and then adds it to the watch list throw new Error(); } } catch (e) { var base = tryBaseDir(dir); if (!not && base) { if (config.dirs.indexOf(base) === -1) { config.dirs.push(base); } } } if (rule.slice(-1) === '/') { // just slap on a * anyway rule += '*'; } // if the url ends with * but not **/* and not *.* // then convert to **/* - somehow it was missed :-\ if (rule.slice(-4) !== '**/*' && rule.slice(-1) === '*' && rule.indexOf('*.') === -1) { rule += '*/*'; } return (not ? '!' : '') + rule; }); return monitor; }
n/a
function run(options) {
var cmd = config.command.raw;
var runCmd = !options.runOnChangeOnly || config.lastStarted !== 0;
if (runCmd) {
utils.log.status('starting `' + config.command.string + '`');
}
/*jshint validthis:true*/
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,];
}
var sh = 'sh';
var shFlag = '-c';
if (utils.isWindows) {
sh = 'cmd';
shFlag = '/c';
}
var executable = cmd.executable;
// special logic for windows, as spaces in the paths need the path fragment
// quoted, so it reads: c:\"Program Files"\nodejs\node.exe
if (utils.isWindows && executable.indexOf(' ') !== -1) {
var re = /\\((\w+\s+)+\w+)(?=([\\\.]))(?=([^"]*"[^"]*")*[^"]*$)/g;
executable = executable.replace(re, '\\"$1"');
}
var args = runCmd ? utils.stringify(executable, cmd.args) : ':';
var spawnArgs = [sh, [shFlag, args]];
debug('spawning', args);
if (utils.version.major === 0 && utils.version.minor < 8) {
// use the old spawn args :-\
} else {
spawnArgs.push({
env: utils.merge(options.execOptions.env, process.env),
stdio: stdio,
});
}
child = spawn.apply(null, spawnArgs);
if (config.required) {
var emit = {
stdout: function (data) {
bus.emit('stdout', data);
},
stderr: function (data) {
bus.emit('stderr', data);
},
};
// now work out what to bind to...
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;
}
}
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 (code === 127) {
utils.log.error('failed to start process, "' + cmd.executable +
'" exec not found');
bus.emit('error', code);
process.exit();
}
if (code === 2) {
// something wrong with parsed command
utils.log.error('failed to start process, possible issue with exec ' +
'arguments');
bus.emit('error', code);
process.exit();
}
// In case we killed the app ourselves, set the signal thusly
if (killedAfterChange) {
killedAfterChange = false;
signal = config.signal;
}
// this is nasty, but it gives it windows support
if (utils.isWindows && signal === 'SIGTERM') {
signal = config.signal;
}
if (signal === config.signal || code === 0) {
// this was a clean exit, so emit exit, rather than crash
debug('bus.emit(exit) via ' + config.signal);
bus.emit('exit');
// exit the monitor, but do it gracefully
if (signal === config.signal) {
return restart();
} else if (code === 0) { // clean exit - wait until file change to restart
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(0);
}
} else {
utils.log.fail('app crashed - waiting for file changes before' +
' starting...');
child = null;
}
}
if (config.options.restartable) {
// stdin needs to kick in again to be able to listen to the
// restart command
process.stdin.resume();
}
});
run.kill = function (noRestart, callback) ...
n/a
kill = function (flag, callback) { if (callback) { callback(); } }
...
nodemon sends a kill signal to your application when it sees a file update. If you need to clean up on shutdown inside your script
you can capture the kill signal and handle it yourself.
The following example will listen once for the `SIGUSR2` signal (used by nodemon to restart), run the clean up process and then
kill itself for nodemon to continue control:
process.once('SIGUSR2', function () {
gracefulShutdown(function () {
process.kill(process.pid, 'SIGUSR2');
});
});
Note that the `process.kill` is *only* called once your shutdown jobs are complete. Hat tip to [Benjie Gillam](http://www.benjiegillam
.com/2011/08/node-js-clean-restart-and-faster-development-with-nodemon/) for writing this technique up.
## Triggering events when nodemon state changes
...
restart = function () {}
n/a
function version(callback) { // first find the package.json as this will be our root var promise = findPackage(path.dirname(module.parent.filename)) .then(function (dir) { // now try to load the package var v = require(path.resolve(dir, 'package.json')).version; if (v && v !== '0.0.0') { return v; } root = dir; // else we're in development, give the commit out // get the last commit and whether the working dir is dirty var promises = [ branch().catch(function () { return 'master'; }), commit().catch(function () { return '<none>'; }), dirty().catch(function () { return 0; }), ]; // use the cached result as the export return Promise.all(promises).then(function (res) { var branch = res[0]; var commit = res[1]; var dirtyCount = parseInt(res[2], 10); var curr = branch + ': ' + commit; if (dirtyCount !== 0) { curr += ' (' + dirtyCount + ' dirty files)'; } return curr; }); }).catch(function (error) { console.log(error.stack); throw error; }); if (callback) { promise.then(function (res) { callback(null, res); }, callback); } return promise; }
n/a
function pin() { return version().then(function (v) { version.pinned = v; }); }
n/a
function watch() { if (watchers.length) { debug('early exit on watch, still watching (%s)', watchers.length); return; } var dirs = [].slice.call(config.dirs); debugRoot('start watch on: %s', dirs.join(', ')); debugRoot('ignore dirs regex(%s)', config.options.ignore.re); var promises = []; var watchedFiles = []; dirs.forEach(function (dir) { var promise = new Promise(function (resolve) { var ignored = config.options.ignore.re; var dotFilePattern = /[\/\\]\./; // don't ignore dotfiles if explicitly watched. if (!dir.match(dotFilePattern)) { ignored = [ignored, dotFilePattern]; } var watcher = chokidar.watch(dir, { ignored: ignored, persistent: true, usePolling: config.options.legacyWatch || false, interval: config.options.pollingInterval, }); watcher.ready = false; var total = 0; watcher.on('change', filterAndRestart); watcher.on('add', function (file) { if (watcher.ready) { return filterAndRestart(file); } watchedFiles.push(file); total++; debug('watching dir: %s', file); }); watcher.on('ready', function () { watcher.ready = true; resolve(total); debugRoot('watch is complete'); }); watcher.on('error', function (error) { if (error.code === 'EINVAL') { utils.log.error('Internal watch failed. Likely cause: too many ' + 'files being watched (perhaps from the root of a drive?\n' + 'See https://github.com/paulmillr/chokidar/issues/229 for details'); } else { utils.log.error('Internal watch failed: ' + error.message); process.exit(1); } }); watchers.push(watcher); }); promises.push(promise); }); return Promise.all(promises).then(function (res) { var total = res.reduce(function (acc, curr) { acc += curr; return acc; }, 0); var count = total.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); utils.log.detail('watching ' + count + ' files'); return watchedFiles; }); }
...
var dotFilePattern = /[\/\\]\./;
// don't ignore dotfiles if explicitly watched.
if (!dir.match(dotFilePattern)) {
ignored = [ignored, dotFilePattern];
}
var watcher = chokidar.watch(dir, {
ignored: ignored,
persistent: true,
usePolling: config.options.legacyWatch || false,
interval: config.options.pollingInterval,
});
watcher.ready = false;
...
function resetWatchers() { debug('resetting watchers'); watchers.forEach(function (watcher) { watcher.close(); }); watchers = []; }
n/a