diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..5ce64e0 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,13 @@ +{ + "preset": "node-style-guide", + "requireCapitalizedComments": null, + "requireSpacesInAnonymousFunctionExpression": { + "beforeOpeningCurlyBrace": true, + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInNamedFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "excludeFiles": ["node_modules/**"], + "disallowSpacesInFunction": null +} \ No newline at end of file diff --git a/.jshintrc b/.jshintrc index 571b952..fb991ae 100644 --- a/.jshintrc +++ b/.jshintrc @@ -10,5 +10,7 @@ "node": true, "quotmark": "single", "undef": true, + "strict": false, "unused": true -} \ No newline at end of file +} + diff --git a/lib/cli/index.js b/lib/cli/index.js index 3a111dc..37ed677 100644 --- a/lib/cli/index.js +++ b/lib/cli/index.js @@ -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} diff --git a/lib/cli/parse.js b/lib/cli/parse.js index c6b699d..9dd84c5 100644 --- a/lib/cli/parse.js +++ b/lib/cli/parse.js @@ -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' }; } diff --git a/lib/config/checkWatchSupport.js b/lib/config/checkWatchSupport.js index f1ddac5..e388cf3 100644 --- a/lib/config/checkWatchSupport.js +++ b/lib/config/checkWatchSupport.js @@ -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 ` 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; diff --git a/lib/config/command.js b/lib/config/command.js index 326b5c7..2ce4634 100644 --- a/lib/config/command.js +++ b/lib/config/command.js @@ -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] + * } + * } + * @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, }; } \ No newline at end of file diff --git a/lib/config/defaults.js b/lib/config/defaults.js index 95d4e91..f1601e2 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -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, }; diff --git a/lib/config/exec.js b/lib/config/exec.js index fa9f436..e8f23a1 100644 --- a/lib/config/exec.js +++ b/lib/config/exec.js @@ -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 diff --git a/lib/config/index.js b/lib/config/index.js index d0840fa..26e891a 100644 --- a/lib/config/index.js +++ b/lib/config/index.js @@ -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 @@ -87,5 +85,4 @@ config.load = function (settings, ready) { config.reset = reset; -module.exports = config; - +module.exports = config; \ No newline at end of file diff --git a/lib/config/load.js b/lib/config/load.js index b93341d..15b1122 100644 --- a/lib/config/load.js +++ b/lib/config/load.js @@ -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) { diff --git a/lib/config/watchable.js b/lib/config/watchable.js index edcae53..bf687ae 100644 --- a/lib/config/watchable.js +++ b/lib/config/watchable.js @@ -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'); diff --git a/lib/help/index.js b/lib/help/index.js index 1f099f9..af1a1e1 100644 --- a/lib/help/index.js +++ b/lib/help/index.js @@ -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'; diff --git a/lib/monitor/changed-since.js b/lib/monitor/changed-since.js index 41e64d5..93d1cb6 100644 --- a/lib/monitor/changed-since.js +++ b/lib/monitor/changed-since.js @@ -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; diff --git a/lib/monitor/match.js b/lib/monitor/match.js index 848005b..ee097b6 100644 --- a/lib/monitor/match.js +++ b/lib/monitor/match.js @@ -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; diff --git a/lib/monitor/offset.js b/lib/monitor/offset.js index 836ad82..4e9c191 100644 --- a/lib/monitor/offset.js +++ b/lib/monitor/offset.js @@ -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 += ''; diff --git a/lib/monitor/run.js b/lib/monitor/run.js index a8c29ef..ca39895 100644 --- a/lib/monitor/run.js +++ b/lib/monitor/run.js @@ -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; diff --git a/lib/monitor/watch.js b/lib/monitor/watch.js index b0be1e3..0aebaba 100644 --- a/lib/monitor/watch.js +++ b/lib/monitor/watch.js @@ -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); + }; +} diff --git a/lib/nodemon.js b/lib/nodemon.js index 4f883e6..ddf0582 100644 --- a/lib/nodemon.js +++ b/lib/nodemon.js @@ -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; diff --git a/lib/rules/add.js b/lib/rules/add.js index 63d4fd7..d2e1827 100644 --- a/lib/rules/add.js +++ b/lib/rules/add.js @@ -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 diff --git a/lib/rules/index.js b/lib/rules/index.js index f926387..a3d47de 100644 --- a/lib/rules/index.js +++ b/lib/rules/index.js @@ -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, }; \ No newline at end of file diff --git a/lib/spawn.js b/lib/spawn.js index 287bf14..4f630aa 100644 --- a/lib/spawn.js +++ b/lib/spawn.js @@ -1,4 +1,3 @@ -'use strict'; var utils = require('./utils'), merge = utils.merge, bus = utils.bus, diff --git a/lib/utils/clone.js b/lib/utils/clone.js index aaf7c40..6ba6330 100644 --- a/lib/utils/clone.js +++ b/lib/utils/clone.js @@ -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; } diff --git a/lib/utils/colour.js b/lib/utils/colour.js index e61ccfd..a4cb6f4 100644 --- a/lib/utils/colour.js +++ b/lib/utils/colour.js @@ -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]; diff --git a/lib/utils/index.js b/lib/utils/index.js index 6a557d9..968cd57 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -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 diff --git a/package.json b/package.json index dd919b4..7307982 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/fixtures/default.json b/test/fixtures/default.json index 3d6e054..f625873 100644 --- a/test/fixtures/default.json +++ b/test/fixtures/default.json @@ -10,10 +10,10 @@ "delay": 2, "ignore": [ "/vendor/*", - "/public/*" - "./README.md" + "/public/*", + "./README.md", "*.css", "/vendor/*", - ":(\d)*\.js" + ":(\\d)*\\.js" ] } \ No newline at end of file