refactor: move watch out in favour of chokidar

Plus jscs clean ups
This commit is contained in:
Remy Sharp
2015-08-31 18:24:35 +01:00
parent 2e56d8404e
commit b0fd56f7de
26 changed files with 273 additions and 466 deletions

13
.jscsrc Normal file
View File

@@ -0,0 +1,13 @@
{
"preset": "node-style-guide",
"requireCapitalizedComments": null,
"requireSpacesInAnonymousFunctionExpression": {
"beforeOpeningCurlyBrace": true,
"beforeOpeningRoundBrace": true
},
"disallowSpacesInNamedFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"excludeFiles": ["node_modules/**"],
"disallowSpacesInFunction": null
}

View File

@@ -10,5 +10,7 @@
"node": true,
"quotmark": "single",
"undef": true,
"strict": false,
"unused": true
}

View File

@@ -1,9 +1,10 @@
'use strict';
var parse = require('./parse');
/**
* Converts a string to command line args, in particular
* groups together quoted values
* groups together quoted values.
* This is a utility function to allow calling nodemon as a required
* library, but with the CLI args passed in (instead of an object).
*
* @param {String} string
* @return {Array}

View File

@@ -1,4 +1,3 @@
'use strict';
/*
nodemon is a utility for node, and replaces the use of the executable
@@ -15,9 +14,9 @@ nodemon can be run in a number of ways:
*/
var fs = require('fs'),
path = require('path'),
existsSync = fs.existsSync || path.existsSync;
var fs = require('fs');
var path = require('path');
var existsSync = fs.existsSync || path.existsSync;
module.exports = parse;
@@ -96,19 +95,6 @@ function parse(argv) {
}
}
// FIXME this was commented out on 2014-12-05 due to logic being moved
// to exec.js. I know I have git to revive the code if I need to, but
// I'm going to leave it here for the short term. Next passerby: feel free
// to remove.
// -------------
// allows the user to specify a script and for nodemon to throw an exception
// *instead* of echoing out the usage and ignoring the poor user altogether,
// just because the filename (or argument) specified wasn't found.
// if (!script && args.length) {
// script = args.pop();
// }
nodemonOptions.script = script;
nodemonOptions.args = args;
@@ -216,7 +202,7 @@ function nodemonOption(options, arg, eatNext) {
function findAppScript() {
// nodemon has been run alone, so try to read the package file
// or try to read the index.js file
if (existsSync('./index.js')) { // FIXME is ./ the right location?
if (existsSync('./index.js')) {
return { exec: null, script: 'index.js' };
}

View File

@@ -1,9 +1,8 @@
'use strict';
var utils = require('../utils'),
watchable = require('./watchable'),
fs = require('fs'),
exec = require('child_process').exec,
checkComplete = false;
var utils = require('../utils');
var watchable = require('./watchable');
var fs = require('fs');
var exec = require('child_process').exec;
var checkComplete = false;
module.exports = checkWatchSupport;
@@ -21,16 +20,14 @@ function checkWatchSupport(config, callback) {
}
var ready = function () {
if (checkComplete) {
//utils.bus.emit('config:update');
} else {
if (!checkComplete) {
checkComplete = true;
callback(config);
}
};
var alternativeCheck = function () {
watchable.check(function(success) {
watchable.check(function (success) {
// whether or not fs.watch actually works on this platform, tested and set
// later before starting
config.system.useWatch = success;
@@ -42,15 +39,21 @@ function checkWatchSupport(config, callback) {
// this is because it has a default ulimit of 256 - which is WAY LOW,
// and without asking the user to `unlimit -n <BIG-ASS-NUMBER>` it'll throw
// up all over your screen like this: http://d.pr/i/R6B8+
// even with higher ulimit -n Mac has another problem: https://github.com/joyent/node/issues/5463
// even with higher ulimit -n Mac has another problem:
// https://github.com/joyent/node/issues/5463
// This will be fixed in 0.12, before then we default to find
config.system.useFind = utils.isMac || utils.isLinux || !fs.watch;
var mtime = utils.isMac ? '-mtime -1s' : '-mmin -0.01';
if (config.system.useFind) {
exec('find -L /dev/null -type f ' + mtime + ' -print', function(error) {
exec('find -L /dev/null -type f ' + mtime + ' -print', function (error) {
if (error) {
if (!fs.watch) {
utils.log.error('The version of node you are using combined with the version of find being used does not support watching files. Upgrade to a newer version of node, or install a version of find that supports search by seconds.');
var notice = 'The version of node you are using combined with the ' +
'version of find being used does not support watching files. ' +
'Upgrade to a newer version of node, or install a version of ' +
'find that supports search by seconds.';
utils.log.error(notice);
process.exit(1);
} else {
config.system.useFind = false;

View File

@@ -1,11 +1,24 @@
'use strict';
module.exports = command;
/**
* command constructs the executable command to run in a shell including the
* user script, the command arguments.
*
* @param {Object} settings Object as:
* { execOptions: {
* exec: String,
* [script: String],
* [scriptPosition: Number],
* [execArgs: Array<string>]
* }
* }
* @return {Object} an object with the node executable and the
* arguments to the command
*/
function command(settings) {
var options = settings.execOptions;
var executable = options.exec,
args = [];
var executable = options.exec;
var args = [];
// after "executable" go the exec args (like --debug, etc)
if (options.execArgs) {
@@ -19,11 +32,12 @@ function command(settings) {
// after the "executable" goes the user's script
if (options.script) {
args.splice((options.scriptPosition || 0) + options.execArgs.length, 0, options.script);
args.splice((options.scriptPosition || 0) +
options.execArgs.length, 0, options.script);
}
return {
executable: executable,
args: args
args: args,
};
}

View File

@@ -5,8 +5,8 @@ module.exports = {
py: 'python',
rb: 'ruby',
// more can be added here such as ls: lsc - but please ensure it's cross
// compatible with linux, mac and windows, or make the default.js dynamically
// append the `.cmd` for node based utilities
// compatible with linux, mac and windows, or make the default.js
// dynamically append the `.cmd` for node based utilities
},
ignore: ['.git', 'node_modules', 'bower_components', '.sass-cache'],
watch: ['*.*'],
@@ -16,6 +16,6 @@ module.exports = {
// 'stdout' refers to the default behaviour of a required nodemon's child,
// but also includes stderr. If this is false, data is still dispatched via
// nodemon.on('stdout/stderr')
stdout: true
stdout: true,
};

View File

@@ -1,6 +1,5 @@
'use strict';
var path = require('path'),
utils = require('../utils');
var path = require('path');
var utils = require('../utils');
module.exports = exec;
@@ -30,7 +29,8 @@ function execFromPackage() {
}
function replace(map, str) {
return str.replace(new RegExp('\{\{(' + Object.keys(map).join('|') + ')\}\}'), function (all, m) {
var re = new RegExp('{{(' + Object.keys(map).join('|') + ')}}');
return str.replace(re, function (all, m) {
return map[m] || all || '';
});
}
@@ -51,10 +51,10 @@ function exec(nodemonOptions, execMap) {
execMap = {};
}
// if there's no exec found yet, then try to read it from the local package.json
// this logic used to sit in the cli/parse, but actually the cli should be parsed
// first, then the user options (via nodemon.json) then finally default down to
// pot shots at the directory via package.json
// if there's no exec found yet, then try to read it from the local
// package.json this logic used to sit in the cli/parse, but actually the cli
// should be parsed first, then the user options (via nodemon.json) then
// finally default down to pot shots at the directory via package.json
if (!nodemonOptions.exec && !nodemonOptions.script) {
var found = execFromPackage();
if (found !== null) {
@@ -64,24 +64,26 @@ function exec(nodemonOptions, execMap) {
if (!nodemonOptions.script) {
nodemonOptions.script = found.script;
}
if (Array.isArray(nodemonOptions.args) && nodemonOptions.scriptPosition === null) {
if (Array.isArray(nodemonOptions.args) &&
nodemonOptions.scriptPosition === null) {
nodemonOptions.scriptPosition = nodemonOptions.args.length;
}
}
}
var options = utils.clone(nodemonOptions || {}),
script = path.basename(options.script || ''),
scriptExt = path.extname(script).slice(1),
extension = options.ext || scriptExt || 'js',
execDefined = !!options.exec;
var options = utils.clone(nodemonOptions || {});
var script = path.basename(options.script || '');
var scriptExt = path.extname(script).slice(1);
var extension = options.ext || scriptExt || 'js';
var execDefined = !!options.exec;
// strip any leading periods int he extension
if (extension.indexOf('.') === 0) {
extension = extension.slice(1);
}
// allows the user to simplify cli usage: https://github.com/remy/nodemon/issues/195
// allows the user to simplify cli usage:
// https://github.com/remy/nodemon/issues/195
// but always give preference to the user defined argument
if (!options.exec && execMap[scriptExt] !== undefined) {
options.exec = execMap[scriptExt];
@@ -99,9 +101,14 @@ function exec(nodemonOptions, execMap) {
options.exec = 'node';
} else {
// allow variable substitution for {{filename}} and {{pwd}}
var substitution = replace.bind(null, { filename: options.script, pwd: process.cwd() });
var substitution = replace.bind(null, {
filename: options.script,
pwd: process.cwd(),
});
var newExec = substitution(options.exec);
if (newExec !== options.exec && options.exec.indexOf('{{filename}}') !== -1) {
if (newExec !== options.exec &&
options.exec.indexOf('{{filename}}') !== -1) {
options.script = null;
}
options.exec = newExec;
@@ -119,7 +126,8 @@ function exec(nodemonOptions, execMap) {
}
// note: indexOf('coffee') handles both .coffee and .litcoffee
if (!execDefined && options.exec === 'node' && scriptExt.indexOf('coffee') !== -1) {
if (!execDefined && options.exec === 'node' &&
scriptExt.indexOf('coffee') !== -1) {
options.exec = 'coffee';
// we need to get execArgs set before the script

View File

@@ -1,18 +1,16 @@
'use strict';
/**
* Manages the internal config of nodemon, checking for the state of support
* with fs.watch, how nodemon can watch files (using find or fs methods).
*
* This is *not* the user's config.
*/
var load = require('./load'),
rules = require('../rules'),
utils = require('../utils'),
command = require('./command'),
path = require('path'),
rulesToMonitor = require('../monitor/match').rulesToMonitor,
bus = utils.bus,
checkWatchSupport = require('./checkWatchSupport');
var load = require('./load');
var rules = require('../rules');
var utils = require('../utils');
var command = require('./command');
var rulesToMonitor = require('../monitor/match').rulesToMonitor;
var bus = utils.bus;
var checkWatchSupport = require('./checkWatchSupport');
function reset() {
rules.reset();
@@ -34,7 +32,7 @@ var config = {
required: false,
dirs: [],
timeout: 1000,
options: {}
options: {},
};
/**
@@ -57,14 +55,14 @@ config.load = function (settings, ready) {
options.watch.push('*.*');
}
if (options.watch_interval) {
config.watch_interval = options.watch_interval;
}
config.watchInterval = options.watchInterval ||
options.watch_interval || // jshint ignore:line
null;
var cmd = command(config.options);
config.command = {
raw: cmd,
string: utils.stringify(cmd.executable, cmd.args)
string: utils.stringify(cmd.executable, cmd.args),
};
// now run automatic checks on system adding to the config object
@@ -88,4 +86,3 @@ config.load = function (settings, ready) {
config.reset = reset;
module.exports = config;

View File

@@ -1,12 +1,11 @@
'use strict';
var fs = require('fs'),
path = require('path'),
exists = fs.exists || path.exists,
utils = require('../utils'),
rules = require('../rules'),
parse = require('../rules/parse'),
exec = require('./exec'),
defaults = require('./defaults');
var fs = require('fs');
var path = require('path');
var exists = fs.exists || path.exists;
var utils = require('../utils');
var rules = require('../rules');
var parse = require('../rules/parse');
var exec = require('./exec');
var defaults = require('./defaults');
module.exports = load;
@@ -40,7 +39,7 @@ function load(settings, options, config, callback) {
scriptPosition: options.scriptPosition,
nodeArgs: options.nodeArgs,
ext: options.ext,
env: options.env
env: options.env,
}, options.execMap);
// clean up values that we don't need at the top level
@@ -49,22 +48,6 @@ function load(settings, options, config, callback) {
delete options.args;
delete options.ext;
/*
// if the script was discovered via package.main or package.start, it
// would have happened in the `exec` function above, except `options.script`
// doesn't get updated, when `options.execOptions.script` does actually
// hold the right value now, so we copy it across along with the
// scriptPosition as this is used in command.js to correctly construct
// the executable command
if (!options.script && options.execOptions.script) {
options.script = options.execOptions.script;
// options.scriptPosition = options.execOptions.scriptPosition;
}
*/
// copy the extension across to a user readable format
// options.ext = options.execOptions.ext.replace(/[\*\.\$]/g, '').split('|').join(' ');
if (options.quiet) {
utils.quiet();
}
@@ -105,7 +88,7 @@ function load(settings, options, config, callback) {
*
* @param {Object} options nodemon user options
* @param {Function} success
* @param {String} filename the ignore file (.nodemonignore or nodemon-ignore)
* @param {String} filename ignore file (.nodemonignore or nodemon-ignore)
* @param {Function} fail (optional) failure callback
*/
function loadLegacyIgnore(options, success, filename, fail) {

View File

@@ -1,10 +1,9 @@
'use strict';
var utils = require('../utils'),
path = require('path'),
crypto = require('crypto'),
fs = require('fs'),
watchFileName,
watchFile;
var utils = require('../utils');
var path = require('path');
var crypto = require('crypto');
var fs = require('fs');
var watchFileName;
var watchFile;
// Attempts to see if fs.watch will work. On some platforms, it doesn't.
// See: http://nodejs.org/api/fs.html#fs_caveats
@@ -29,14 +28,16 @@ function check(cb) {
tmpdir = '/tmp';
}
watchFileName = path.join(tmpdir, 'nodemonCheckFsWatch' + crypto.randomBytes(16).toString('hex'));
watchFileName = path.join(tmpdir, 'nodemonCheckFsWatch' +
crypto.randomBytes(16).toString('hex'));
watchFile = fs.openSync(watchFileName, 'w');
if (watchFile < 0) {
utils.log.fail('Unable to write to temp directory. If you experience problems with file reloading, ensure ' + tmpdir + ' is writable.');
utils.log.fail('Unable to write to temp directory. If you experience ' +
'problems with file reloading, ensure ' + tmpdir + ' is writable.');
cb(true);
return;
}
fs.watch(watchFileName, function() {
fs.watch(watchFileName, function () {
cb(true);
});
@@ -56,7 +57,7 @@ function check(cb) {
// Verifies that fs.watch was not triggered and sends false to the callback
// but if the callback has already been used (changeDetected), it won't call.
var finish = function() {
var finish = function () {
try {
fs.unlinkSync(watchFileName);
watchable.cb(false);
@@ -67,7 +68,7 @@ var finish = function() {
process.on('exit', finish);
var watchable = module.exports = function (config, ready) {
check(function(success) {
check(function (success) {
config.system.useWatch = success;
if (changeDetected) {
utils.bus.emit('config:update');

View File

@@ -1,5 +1,5 @@
var fs = require('fs'),
path = require('path');
var fs = require('fs');
var path = require('path');
module.exports = help;
@@ -15,7 +15,8 @@ function help(item) {
item = item.replace(/[^a-z]/gi, '');
try {
var body = fs.readFileSync(path.join(__dirname, '..', '..', 'doc', 'cli', item + '.txt'), 'utf8');
var dir = path.join(__dirname, '..', '..', 'doc', 'cli', item + '.txt');
var body = fs.readFileSync(dir, 'utf8');
return body;
} catch (e) {
return '"' + item + '" help can\'t be found';

View File

@@ -1,7 +1,6 @@
var fs = require('fs'),
utils = require('../utils'),
config = require('../config'),
path = require('path');
var fs = require('fs');
var config = require('../config');
var path = require('path');
module.exports = changedSince;

View File

@@ -1,9 +1,7 @@
'use strict';
var minimatch = require('minimatch'),
path = require('path'),
fs = require('fs'),
utils = require('../utils');
var minimatch = require('minimatch');
var path = require('path');
var fs = require('fs');
var utils = require('../utils');
module.exports = match;
module.exports.rulesToMonitor = rulesToMonitor;
@@ -105,10 +103,11 @@ function rulesToMonitor(watch, ignore, config) {
}
function tryBaseDir(dir) {
var stat;
if (/[?*\{\[]+/.test(dir)) { // if this is pattern, then try to find the base
try {
var base = path.dirname(dir.replace(/([?*\{\[]+.*$)/, 'foo'));
var stat = fs.statSync(base);
stat = fs.statSync(base);
if (stat.isDirectory()) {
return base;
}
@@ -117,7 +116,7 @@ function tryBaseDir(dir) {
}
} else {
try {
var stat = fs.statSync(dir);
stat = fs.statSync(dir);
// if this path is actually a single file that exists, then just monitor
// that, *specifically*.
if (stat.isFile() || stat.isDirectory()) {
@@ -155,7 +154,7 @@ function match(files, monitor, ext) {
if (prefix === '!') {
return '!**' + (prefix !== path.sep ? path.sep : '') + s.slice(1);
} else if (s.slice(0, 2) == '..') {
} else if (s.slice(0, 2) === '..') {
return path.resolve(process.cwd(), s);
}
return '**' + (prefix !== path.sep ? path.sep : '') + s;

View File

@@ -1,4 +1,3 @@
'use strict';
var fs = require('fs');
var config = require('../config');
var offset = null;
@@ -34,8 +33,10 @@ module.exports = function () {
module.exports.pretty = function () {
var date = new Date(offset);
return two(date.getHours()) + 'h' + two(date.getMinutes()) + 'm' + two(date.getSeconds()) + 's';
}
return two(date.getHours()) + 'h' +
two(date.getMinutes()) + 'm' +
two(date.getSeconds()) + 's';
};
function two(s) {
s += '';

View File

@@ -1,18 +1,16 @@
'use strict';
var utils = require('../utils'),
bus = utils.bus,
childProcess = require('child_process'),
spawn = childProcess.spawn,
exec = childProcess.exec,
watch = require('./watch'),
config = require('../config'),
child = null, // the actual child process we spawn
killedAfterChange = false,
// timeout = 1000, // check every 1 second
noop = function() {},
restart = null,
psTree = require('ps-tree'),
hasPS = true;
var utils = require('../utils');
var bus = utils.bus;
var childProcess = require('child_process');
var spawn = childProcess.spawn;
var exec = childProcess.exec;
var watch = require('./watch');
var config = require('../config');
var child = null; // the actual child process we spawn
var killedAfterChange = false;
var noop = function() {};
var restart = null;
var psTree = require('ps-tree');
var hasPS = true;
// discover if the OS has `ps`, and therefore can use psTree
exec('ps', function(error) {
@@ -141,7 +139,9 @@ function run(options) {
// restart
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');
if (runCmd) {
utils.log.status('clean exit - waiting for changes before restart');
}
child = null;
}
} else {
@@ -250,7 +250,7 @@ function kill(child, signal, callback) {
}))).on('close', callback);
});
} else {
exec('kill -s ' + signal + ' ' + child.pid, function (error) {
exec('kill -s ' + signal + ' ' + child.pid, function () {
// ignore if the process has been killed already
callback();
});
@@ -261,7 +261,7 @@ function kill(child, signal, callback) {
// stubbed out for now, filled in during run
run.kill = function (flag, callback) {
if (callback) {
callback()
callback();
}
};
run.restart = noop;

View File

@@ -1,251 +1,43 @@
'use strict';
var fs = require('fs'),
path = require('path'),
changedSince = require('./changed-since'),
utils = require('../utils'),
bus = utils.bus,
match = require('./match'),
config = require('../config'),
offset = require('./offset'),
childProcess = require('child_process'),
touch = require('touch'),
exec = childProcess.exec,
watched = [],
debouncedBus,
watchers = [];
module.exports = watch;
var chokidar = require('chokidar');
var config = require('../config');
var path = require('path');
var utils = require('../utils');
var bus = utils.bus;
var match = require('./match');
var watchers = [];
var debouncedBus;
var changeFunction = function () {
utils.log.error('changeFunction called when it shouldn\'t be.');
};
function reset() {
// manually remove all the file watchers.
// note that fs.unwatchFile doesn't really work, particularly in vagrant
// shared folders and in Travis CI...
var watcher;
while (watchers.length) {
watcher = watchers.pop();
watcher.removeAllListeners('change');
if (watcher.close) { // watchFile doesn't have this
watcher.close();
}
function watch() {
if (watchers.length) {
return;
}
// reset the watched directory too
watched.length = 0;
}
var dirs = [].slice.call(config.dirs);
function showWatchCount() {
var cmds = [];
config.dirs.forEach(function(dir) {
cmds.push('find -L "' + dir + '" ' + ignoredFileTypesForFind(dir) + ' \\( -type f -print \\) | wc');
});
exec(cmds.join(';'), function (error, stdout) {
if (!config.run) {
return; // we're too late
}
var total = 0;
stdout.split(/\n/).map(function (line) {
total += (line.trim().split(/\s+/)[0]||0) * 1;
dirs.forEach(function (dir) {
var watcher = chokidar.watch(dir, {
// ignore our files, but also ignore dotfiles
ignored: [config.options.ignore.re, /[\/\\]\./],
persistent: true,
});
utils.log.detail('watching ' + (total).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' files');
if (total > 25000) {
utils.log.fail('watching ' + (total).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' files - this might cause high cpu usage. To reduce use "--watch".');
}
});
}
bus.on('config:update', function () {
reset();
if (config.system.useFind) {
// let's do a sanity check for the amount of files we'll be watching for...
showWatchCount();
// if native fs.watch doesn't work the way we want, we keep polling find
// command (mac only oddly)
changeFunction = function (lastStarted, callback) {
var cmds = [];
var delta = ((((Date.now() - config.lastStarted) * -1) + offset())/1000|0);
var mtime = utils.isMac ? '-mtime ' + delta + 's' : '-mmin ' + (delta/60);
if (delta > 0) {
// we can't use -mmin or -mtime, we need to use -newer, and best bet is
// to touch a file with our reference timestamp, and search http://superuser.com/a/416191
touch.sync('.nodemon-find-ref', { time: new Date(Date.now() + (delta * 1000)) });
mtime = '-newer .nodemon-find-ref';
}
// don't run the find when delta is 0 seconds, otherwise
// nodemon goes into a barmy restart loop
if (delta === 0) {
return callback([]);
}
config.dirs.forEach(function(dir) {
cmds.push('find -L "' + dir + '" ' + ignoredFileTypesForFind(dir) + ' \\( -type f -and ' + mtime + ' -print \\)');
});
exec(cmds.join(';'), function (error, stdout) {
var files = stdout.split(/\n/);
files.pop(); // remove blank line ending and split
if (delta > 0) {
try { fs.unlinkSync('.nodemon-find-ref'); }
catch (e) {} // swallow any errors
}
callback(files);
});
};
} else if (config.system.useWatch || config.system.useWatchFile) {
bus.once('quit', reset);
var watchFile = config.system.useWatch === false && (config.system.useWatchFile || config.options.legacyWatch),
watchMethod = watchFile ? 'watchFile' : 'watch';
changeFunction = function (lastStarted, callback) {
// recursive watch - watch each directory and it's subdirectories, etc, etc
function watch(err, dir) {
try {
if (watched.indexOf(dir) === -1 && ignoredFilter(dir)) {
var watcher_options = { persistent: false };
if (config.watch_interval) {
watcher_options.interval = config.watch_interval;
}
var watcher = fs[watchMethod](dir, watcher_options, function (event, filename) {
var filepath;
if (typeof filename === 'string') {
filepath = path.join(dir, filename || '');
} else { // was called from watchFile
filepath = dir;
}
callback([filepath]);
});
watched.push(dir);
watchers.push(watcher);
}
fs.readdir(dir, function (err, files) {
if (err) { return; }
files.forEach(function (rawfile) {
var file = path.join(dir, rawfile);
if (watched.indexOf(file) === -1 && ignoredFilter(file)) {
fs.lstat(file, function (err, stat) {
if (err || !stat) { return; }
// if we're using fs.watch, then watch directories
if (!watchFile && stat.isDirectory()) {
// recursive call to watch()
fs.realpath(file, watch);
} else {
// if we're in legacy mode, i.e. Vagrant + editing over
// shared drive, then watch the individual file
if (watchFile) {
fs.realpath(file, watch);
} else if (ignoredFilter(file)) {
watched.push(file);
}
}
});
}
});
});
} catch (e) {
if ('EMFILE' === e.code) {
utils.log.error('EMFILE: Watching too many files.');
}
// ignoring this directory, likely it's "My Music"
// or some such windows fangled stuff
}
}
showWatchCount();
config.dirs.forEach(function (dir) {
fs.realpath(dir, watch);
});
};
} else {
// changedSince, the fallback for when both the find method and fs.watch
// don't work, is not compatible with the way changeFunction works. If we
// have reached this point, changeFunction should not be called from herein
// out.
utils.log.error('No clean method to watch files with - please report\nto http://github.com/remy/nodemon/issues/new with `nodemon --dump`');
}
});
// filter ignored files
function ignoredFilter(file) {
if (config.options.ignore.length && config.options.ignore.re) {
// If we are in a Windows machine
if (utils.isWindows) {
// Break up the file by slashes
var fileParts = file.split(/\\/g);
// Remove the first piece (C:)
fileParts.shift();
// Join the parts together with Unix slashes
file = '/' + fileParts.join('/');
}
return !config.options.ignore.re.test(file);
} else {
return true;
}
}
function ignoredFileTypesForFind(dir) {
// search within dir for ignored files and paths
if (!config.options.ignore.length) {
return '';
}
var paths = [];
if (dir.charAt(dir.length - 1) !== '/') {
dir += '/';
}
config.options.ignore.forEach(function (path) {
var pathIsDir = (fs.existsSync(dir + path) && fs.statSync(dir + path).isDirectory()) || (path.charAt(path.length - 1) === '/');
if (pathIsDir) {
paths.push(' -ipath "' + dir + path + '*" -prune');
}
paths.push(' -ipath "' + dir + path + '" -prune');
});
return ' \\( ' + paths.join(' -or ') + ' \\) -or ';
}
function debounce(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
function restartBus(matched) {
utils.log.status('restarting due to changes...');
matched.result.map(function (file) {
utils.log.detail(path.relative(process.cwd(), file));
watcher.on('change', filterAndRestart);
watchers.push(watcher);
});
if (config.options.verbose) {
utils.log._log('');
}
bus.emit('restart', matched.result);
bus.on('reset', function () {
watchers.forEach(function (watcher) {
watcher.close();
});
watchers = [];
});
}
function filterAndRestart(files) {
if (!Array.isArray(files)) {
files = [files];
}
if (files.length) {
if (utils.isWindows) {
// ensure the drive letter is in uppercase (c:\foo -> C:\foo)
@@ -278,30 +70,29 @@ function filterAndRestart(files) {
}
}
}
if (config.system.useFind || config.options.legacyWatch) {
if (config.run) {
setTimeout(watch, config.timeout);
}
}
}
var watch = module.exports = function () {
// if we have useFind or useWatch (i.e. not using `find`)
// then called `changeFunction` which is local to this script
if ((config.system.useFind || config.system.useWatch || config.system.useWatchFile) && !config.options.legacyWatch) {
changeFunction(config.lastStarted, function (files) {
if (config.run) {
filterAndRestart(files);
}
});
} else {
// Fallback for when both find and fs.watch don't work
// using the `changedSince` which is external
changedSince(config.lastStarted, function (files) {
if (config.run) {
filterAndRestart(files);
}
});
function restartBus(matched) {
utils.log.status('restarting due to changes...');
matched.result.map(function (file) {
utils.log.detail(path.relative(process.cwd(), file));
});
if (config.options.verbose) {
utils.log._log('');
}
};
bus.emit('restart', matched.result);
}
function debounce(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}

View File

@@ -1,16 +1,14 @@
'use strict';
var path = require('path'),
monitor = require('./monitor'),
cli = require('./cli'),
version = require('./version'),
util = require('util'),
utils = require('./utils'),
rules = require('./rules'),
bus = utils.bus,
help = require('./help'),
config = require('./config'),
spawn = require('./spawn'),
eventHandlers = {};
var path = require('path');
var monitor = require('./monitor');
var cli = require('./cli');
var version = require('./version');
var util = require('util');
var utils = require('./utils');
var bus = utils.bus;
var help = require('./help');
var config = require('./config');
var spawn = require('./spawn');
var eventHandlers = {};
// this is fairly dirty, but theoretically sound since it's part of the
// stable module API
@@ -232,6 +230,10 @@ nodemon.removeAllListeners = function (event) {
};
nodemon.reset = function (done) {
bus.emit('reset', done);
};
bus.on('reset', function (done) {
monitor.run.kill(true, function () {
nodemon.removeAllListeners();
utils.reset();
@@ -241,27 +243,27 @@ nodemon.reset = function (done) {
done();
}
});
};
});
// expose the full config
nodemon.config = config;
// on exception *inside* nodemon, shutdown wrapped node app
if (!config.required) {
process.on('uncaughtException', function (err) {
console.error('exception in nodemon killing node');
console.error(err.stack);
console.error();
console.error('----------------------------------------------------------');
console.error('If appropriate, please file an error with the output from:');
console.error('$ ' + process.argv.join(' ') + (process.argv.indexOf('--dump') === -1 ? ' --dump' : ''));
console.error('At http://github.com/remy/nodemon/issues/new');
console.error('----------------------------------------------------------\n');
if (!config.required) {
process.exit(1);
}
});
}
// if (!config.required) {
// process.on('uncaughtException', function (err) {
// console.error('exception in nodemon killing node');
// console.error(err.stack);
// console.error();
// console.error('----------------------------------------------------------');
// console.error('If appropriate, please file an error with the output from:');
// console.error('$ ' + process.argv.join(' ') + (process.argv.indexOf('--dump') === -1 ? ' --dump' : ''));
// console.error('At http://github.com/remy/nodemon/issues/new');
// console.error('----------------------------------------------------------\n');
// if (!config.required) {
// process.exit(1);
// }
// });
// }
module.exports = nodemon;

View File

@@ -3,11 +3,12 @@
var utils = require('../utils');
// internal
var reEscComments = /\\#/g,
reUnescapeComments = /\^\^/g, // note that '^^' is used in place of escaped comments
reComments = /#.*$/,
reEscapeChars = /[.|\-[\]()\\]/g,
reAsterisk = /\*/g;
var reEscComments = /\\#/g;
// note that '^^' is used in place of escaped comments
var reUnescapeComments = /\^\^/g;
var reComments = /#.*$/;
var reEscapeChars = /[.|\-[\]()\\]/g;
var reAsterisk = /\*/g;
module.exports = add;
@@ -24,13 +25,15 @@ module.exports = add;
* add(rules, 'watch', ':(\d)*\.js'); // note: string based regexp
* add(rules, 'watch', /\d*\.js/);
*
* @param {Object} rules containing `watch` and `ignore`. Also updated during execution
* @param {Object} rules containing `watch` and `ignore`. Also updated during
* execution
* @param {String} which must be either "watch" or "ignore"
* @param {String|RegExp} the actual rule.
*/
function add(rules, which, rule) {
if (!{ 'ignore' : 1, 'watch' : 1}[which]) {
throw new Error('rules/index.js#add requires "ignore" or "watch" as the first argument');
if (!{ ignore: 1, watch: 1}[which]) {
throw new Error('rules/index.js#add requires "ignore" or "watch" as the ' +
'first argument');
}
if (Array.isArray(rule)) {
@@ -71,7 +74,7 @@ function add(rules, which, rule) {
// rules[which].push(rule);
} else {
// rule = rule.replace(reEscapeChars, '\\$&')
// .replace(reAsterisk, '.*');
// .replace(reAsterisk, '.*');
rules[which].push(rule);
// compile a regexp of all the rules for this ignore or watch

View File

@@ -42,12 +42,12 @@ module.exports = {
load: load,
ignore: {
test: add.bind(null, rules, 'ignore'),
add: add.bind(null, rules, 'ignore')
add: add.bind(null, rules, 'ignore'),
},
watch: {
test: add.bind(null, rules, 'watch'),
add: add.bind(null, rules, 'watch')
add: add.bind(null, rules, 'watch'),
},
add: add.bind(null, rules),
rules: rules
rules: rules,
};

View File

@@ -1,4 +1,3 @@
'use strict';
var utils = require('./utils'),
merge = utils.merge,
bus = utils.bus,

View File

@@ -3,7 +3,7 @@ module.exports = clone;
// via http://stackoverflow.com/a/728694/22617
function clone(obj) {
// Handle the 3 simple types, and null or undefined
if (null === obj || "object" !== typeof obj) {
if (null === obj || 'object' !== typeof obj) {
return obj;
}

View File

@@ -16,7 +16,7 @@ function strip(str) {
colour.red = '\x1B[31m';
colour.yellow = '\x1B[33m';
colour.green = '\x1B[32m';
colour.black = '\x1B[39m'
colour.black = '\x1B[39m';
var reStr = Object.keys(colour).map(function (key) {
return colour[key];

View File

@@ -1,9 +1,7 @@
'use strict';
var noop = function () {},
path = require('path'),
version = process.versions.node.split('.') || [null, null, null];
var utils = module.exports = {
version: {
major: parseInt(version[0] || 0, 10),
@@ -17,11 +15,12 @@ var utils = module.exports = {
isMac: process.platform === 'darwin',
isLinux: process.platform === 'linux',
isRequired: (function () {
var p = module;
while (p = p.parent) {
var p = module.parent;
while (p) {
if (p.filename.indexOf('bin' + path.sep + 'nodemon.js') !== -1) {
return false;
}
p = p.parent;
}
return true;
@@ -48,7 +47,10 @@ var utils = module.exports = {
this.debug = false;
},
regexpToText: function (t) {
return t.replace(/\.\*\\./g, '*.').replace(/\\{2}/g, '^^').replace(/\\/g, '').replace(/\^\^/g, '\\');
return t.replace(/\.\*\\./g, '*.')
.replace(/\\{2}/g, '^^')
.replace(/\\/g, '')
.replace(/\^\^/g, '\\');
},
stringify: function (exec, args) {
// serializes an executable string and array of arguments into a string

View File

@@ -42,6 +42,8 @@
"should": "~4.0.0"
},
"dependencies": {
"anymatch": "^1.3.0",
"chokidar": "^1.0.5",
"minimatch": "~0.3.0",
"ps-tree": "~0.0.3",
"touch": "~1.0.0",

View File

@@ -10,10 +10,10 @@
"delay": 2,
"ignore": [
"/vendor/*",
"/public/*"
"./README.md"
"/public/*",
"./README.md",
"*.css",
"/vendor/*",
":(\d)*\.js"
":(\\d)*\\.js"
]
}