Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixing constant restart by removing and chmod'ing monitor file (Closes

…#11 - I hope!). Added throttle behaviour - Closes #21. Moved to .nodemonignore syntax as it proved a popular request! Closes #22, closes #19
  • Loading branch information...
commit 7e3a9e617890dc8592537c50c2564415b33080c7 1 parent f8479c9
@remy authored
Showing with 135 additions and 40 deletions.
  1. +23 −7 README.md
  2. +100 −32 nodemon.js
  3. +11 −0 nodemonignore.example
  4. +1 −1  package.json
View
30 README.md
@@ -2,17 +2,17 @@
For use during development of a node.js based application.
-`nodemon` will watch all the files in the directory that nodemon was started, and if they change, it will automatically restart your node application.
+`nodemon` will watch all the files in the directory that `nodemon` was started, and if they change, it will automatically restart your node application.
`nodemon` does **not** require *any* changes to your code or method of development. `nodemon` simply wraps your node application and keeps an eye on any files that have changed.
# Installation
-Either through forking or:
+Either through forking or by using [npm](http://npmjs.org) (the recommended way):
- npm install nodemon
+ npm install nodemon -g
-And `nodemon` will be installed in to your bin path.
+And `nodemon` will be installed in to your bin path. Note that as of npm v1, you must explicitly tell npm to install globally as `nodemon` is a command line utility.
# Usage
@@ -32,13 +32,29 @@ Any output from this script is prefixed with `[nodemon]`, otherwise all output f
If no script is given, `nodemon` will test for a `package.json` file and if found, will run the file associated with the *main* property ([ref](https://github.com/remy/nodemon/issues/14)).
+You can also pass the debug flag to node through the command line as you would normally:
+
+ nodemon --debug ./server.js 80
+
+Finally, if you have a `package.json` file for your app, you can omit the main script entirely and `nodemon` will read the `package.json` for the `main` property and use that value as the app.
+
+# Delaying restarting
+
+In some situations, you may want to wait until a number of files have changed. The timeout before checking for new file changes is 1 second. If you're uploading a number of files and it's taking some number of seconds, this could cause your app to restart multiple time unnecessarily.
+
+To add an extra throttle, or delay restarting, use the `--delay` command:
+
+ nodemon --delay 10 server.js
+
+The delay figure is number of seconds to delay before restarting. So `nodemon` will only restart your app the given number of seconds after the *last* file change.
+
# Ignoring files
-In some cases you will want to ignore some specific files, directories or file patterns, to prevent nodemon from prematurely restarting your application.
+In some cases you will want to ignore some specific files, directories or file patterns, to prevent `nodemon` from prematurely restarting your application.
-The `nodemon-ignore` file is automatically created in the directory that you run your application from, so that you can have application specific ignore lists.
+The `.nodemonignore` file is automatically created in the directory that you run your application from, so that you can have application specific ignore lists.
-You can use the [example ignore file](http://github.com/remy/nodemon/blob/master/nodemon-ignore.example) as a basis for your `nodemon`, but it's very simple to create your own:
+You can use the [example ignore file](http://github.com/remy/nodemon/blob/master/nodemonignore.example) as a basis for your `nodemon`, but it's very simple to create your own:
# this is my ignore file with a nice comment at the top
View
132 nodemon.js
@@ -11,10 +11,13 @@ var fs = require('fs'),
app = nodeArgs[0],
node = null,
monitor = null,
- ignoreFilePath = './nodemon-ignore',
+ ignoreFilePath = './.nodemonignore',
+ oldIgnoreFilePath = './nodemon-ignore',
ignoreFiles = [flag, ignoreFilePath], // ignore the monitor flag by default
reIgnoreFiles = null,
timeout = 1000, // check every 1 second
+ restartDelay = 0, // controlled through arg --delay 10 (for 10 seconds)
+ restartTimer = null,
// create once, reuse as needed
reEscComments = /\\#/g,
reUnescapeComments = /\^\^/g, // note that '^^' is used in place of escaped comments
@@ -71,17 +74,21 @@ function startMonitor() {
fs.writeFileSync(flag, '');
if (files.length) {
- sys.log('[nodemon] restarting due to changes...');
- files.forEach(function (file) {
- sys.log('[nodemon] ' + file);
- });
- sys.print('\n\n');
+ if (restartTimer !== null) clearTimeout(restartTimer);
- if (node !== null) {
- node.kill('SIGUSR2');
- } else {
- startNode();
- }
+ restartTimer = setTimeout(function () {
+ sys.log('[nodemon] restarting due to changes...');
+ files.forEach(function (file) {
+ sys.log('[nodemon] ' + file);
+ });
+ sys.print('\n\n');
+
+ if (node !== null) {
+ node.kill('SIGUSR2');
+ } else {
+ startNode();
+ }
+ }, restartDelay);
}
}
@@ -108,6 +115,8 @@ function readIgnoreFile() {
ignoreFiles = [flag, ignoreFilePath];
fs.readFileSync(ignoreFilePath).toString().split(/\n/).forEach(function (line) {
// remove comments and trim lines
+
+ // this mess of replace methods is escaping "\#" to allow for emacs temp files
if (line = line.replace(reEscComments, '^^').replace(reComments, '').replace(reUnescapeComments, '#').replace(reTrim, '')) {
ignoreFiles.push(line.replace(reEscapeChars, '\\$&').replace(reAsterisk, '.*'));
}
@@ -122,36 +131,72 @@ function usage() {
sys.print('usage: nodemon [--debug] [your node app]\ne.g.: nodemon ./server.js localhost 8080\nFor details see http://github.com/remy/nodemon/\n\n');
}
-function controlArg(arg, label, fn) {
- if (arg == label || arg == '--' + label || arg == '-' + label.substr(0, 1)) {
- fn();
- process.exit();
+function controlArg(nodeArgs, label, fn) {
+ var i;
+
+ if ((i = nodeArgs.indexOf(label)) !== -1) {
+ fn(nodeArgs[i], i);
+ } else if ((i = nodeArgs.indexOf('-' + label.substr(1))) !== -1) {
+ fn(nodeArgs[i], i);
+ } else if ((i = nodeArgs.indexOf('--' + label)) !== -1) {
+ fn(nodeArgs[i], i);
}
}
-if (!nodeArgs.length) {
+// attempt to shutdown the wrapped node instance and remove
+// the monitor file as nodemon exists
+function cleanup() {
+ node && node.kill();
+ fs.unlink(flag);
+}
+
+// control arguments test for "help" or "--help" or "-h", run the callback and exit
+controlArg(nodeArgs, 'help', function () {
+ usage();
+ process.exit();
+});
+
+controlArg(nodeArgs, 'version', function () {
+ sys.print('v' + meta.version + '\n');
+ process.exit();
+});
+
+// look for delay flag
+controlArg(nodeArgs, 'delay', function (arg, i) {
+ var delay = nodeArgs[i+1];
+ nodeArgs.splice(i, 2); // remove the delay from the arguments
+ if (delay) {
+ sys.log('[nodemon] Adding delay of ' + delay + ' seconds');
+ restartDelay = delay * 1000; // in seconds
+ }
+});
+
+controlArg(nodeArgs, '--debug', function (arg, i) {
+ nodeArgs.splice(i, 1);
+ app = nodeArgs[0];
+ nodeArgs.unshift('--debug'); // put it at the front
+});
+
+if (!nodeArgs.length || !path.existsSync(app)) {
// try to get the app from the package.json
// doing a try/catch because we can't use the path.exist callback pattern
// or we could, but the code would get messy, so this will do exactly
// what we're after - if the file doesn't exist, it'll throw.
try {
app = JSON.parse(fs.readFileSync('./package.json').toString()).main;
- nodeArgs.push(app);
+
+ if (nodeArgs[0] == '--debug') {
+ nodeArgs.splice(1, 0, app);
+ } else {
+ nodeArgs.unshift(app);
+ }
} catch (e) {
- nodeArgs.push('help'); // default to help
+ // no app found to run - so give them a tip and get the feck out
+ usage();
+ process.exit();
}
}
-// control arguments test for "help" or "--help" or "-h", run the callback and exit
-controlArg(nodeArgs[0], 'help', usage);
-controlArg(nodeArgs[0], 'version', function () {
- sys.print('v' + meta.version + '\n');
-});
-
-if (nodeArgs[0] == '--debug') {
- app = nodeArgs[1];
-}
-
sys.log('[nodemon] v' + meta.version);
// Change to application dir
@@ -160,25 +205,48 @@ app = path.basename(app);
sys.log('[nodemon] running ' + app + ' in ' + process.cwd());
startNode();
+
setTimeout(startMonitor, timeout);
-path.exists(ignoreFilePath, readIgnoreFile);
+path.exists(ignoreFilePath, function (exists) {
+ if (!exists) {
+ // try the old format
+ path.exists(oldIgnoreFilePath, function (exists) {
+ if (exists) {
+ sys.log('[nodemon] detected old style .nodemonignore');
+ ignoreFilePath = oldIgnoreFilePath;
+ }
+ readIgnoreFile();
+ });
+ } else {
+ readIgnoreFile();
+ }
+});
-// touch
+// this little bit of hoop jumping is because sometimes the file can't be
+// touched properly, and it send nodemon in to a loop of restarting.
+// this way, the .monitor file is removed entirely, and recreated with
+// permissions that anyone can remove it later (i.e. if you run as root
+// by accident and then try again later).
+if (path.existsSync(flag)) fs.unlinkSync(flag);
fs.writeFileSync(flag, '');
+fs.chmodSync(flag, '666');
// remove the flag file on exit
process.on('exit', function (code) {
+ cleanup();
sys.log('[nodemon] exiting');
- fs.unlink(flag);
});
+// usual suspect: ctrl+c exit
process.on('SIGINT', function () {
+ cleanup();
process.exit(0);
});
+// on exception *inside* nodemon, shutdown wrapped node app
process.on('uncaughtException', function (err) {
sys.log('[nodemon] exception in nodemon killing node');
sys.error(err.stack);
- node.kill();
+ cleanup();
});
View
11 nodemonignore.example
@@ -0,0 +1,11 @@
+# .nodemonignore file
+#
+# This is an example of a comment
+#
+# You can list individual files in the ignore list, or they can be
+# file patterns. For example:
+#
+# /vendor/*
+# /public/css/styles.css
+# ./server.js
+# ./.git/*
View
2  package.json
@@ -9,6 +9,6 @@
"repository": { "type" : "git", "url" : "http://github.com/remy/nodemon.git" },
"description": "Simple monitor script for use during development of a node.js app.",
"keywords": ["monitor", "development", "restart", "autoload", "reload", "terminal"],
- "version": "0.3.2",
+ "version": "0.4.0",
"main": "./nodemon"
}
Please sign in to comment.
Something went wrong with that request. Please try again.