Comparing version 0.5.7 to 0.6.0
398
nodemon.js
#!/usr/bin/env node | ||
var fs = require('fs'), | ||
sys = require('util'), | ||
util = require('util'), | ||
childProcess = require('child_process'), | ||
dirs = [], | ||
path = require('path'), | ||
@@ -10,5 +11,4 @@ spawn = childProcess.spawn, | ||
flag = './.monitor', | ||
nodeArgs = process.argv.splice(2), // removes 'node' and this script | ||
app = nodeArgs[0], | ||
node = null, | ||
program = getNodemonArgs(), | ||
child = null, | ||
monitor = null, | ||
@@ -22,2 +22,3 @@ ignoreFilePath = './.nodemonignore', | ||
restartTimer = null, | ||
lastStarted = +new Date, | ||
// create once, reuse as needed | ||
@@ -32,26 +33,18 @@ reEscComments = /\\#/g, | ||
function startNode() { | ||
sys.log('\x1B[32m[nodemon] starting node\x1B[0m'); | ||
util.log('\x1B[32m[nodemon] starting ' + program.options.exec + '\x1B[0m'); | ||
var ext = path.extname(app); | ||
// console.log('running: ' + program.options.exec + ' ' + program.args.join(' ')) | ||
child = spawn(program.options.exec, program.args); | ||
lastStarted = +new Date; | ||
if (ext === '.coffee') { | ||
//coffeescript requires --nodejs --debug | ||
var debugIndex = nodeArgs.indexOf('--debug'); | ||
if (debugIndex >= 0) { | ||
nodeArgs.splice(debugIndex, 0, '--nodejs'); | ||
} | ||
node = spawn('coffee', nodeArgs); | ||
} else { | ||
node = spawn('node', nodeArgs); | ||
} | ||
node.stdout.on('data', function (data) { | ||
sys.print(data); | ||
child.stdout.on('data', function (data) { | ||
util.print(data); | ||
}); | ||
node.stderr.on('data', function (data) { | ||
sys.error(data); | ||
child.stderr.on('data', function (data) { | ||
util.error(data); | ||
}); | ||
node.on('exit', function (code, signal) { | ||
child.on('exit', function (code, signal) { | ||
// exit the monitor, but do it gracefully | ||
@@ -61,16 +54,65 @@ if (signal == 'SIGUSR2') { | ||
startNode(); | ||
} else if (code === 0) { // clean exit - wait until file change to restart | ||
util.log('\x1B[32m[nodemon] clean exit - waiting for changes before restart\x1B[0m'); | ||
child = null; | ||
} else { | ||
sys.log('\x1B[1;31m[nodemon] app crashed - waiting for file change before starting...\x1B[0m'); | ||
node = null; | ||
util.log('\x1B[1;31m[nodemon] app crashed - waiting for file changes before starting...\x1B[0m'); | ||
child = null; | ||
} | ||
}); | ||
// pinched from https://github.com/DTrejo/run.js - pipes stdin to the child process - cheers DTrejo ;-) | ||
process.stdin.resume(); | ||
process.stdin.setEncoding('utf8'); | ||
process.stdin.pipe(child.stdin); | ||
setTimeout(startMonitor, timeout); | ||
} | ||
function startMonitor() { | ||
var cmd = 'find -L . -type f -newer ' + flag + ' -print'; | ||
function changedSince(time, dir, callback) { | ||
callback || (callback = dir); | ||
var changed = [], | ||
i = 0, | ||
j = 0, | ||
dir = dir && typeof dir !== 'function' ? [dir] : dirs, | ||
dlen = dir.length, | ||
todo = 0, // how fucking lame is this? promises anyone? | ||
flen = 0, | ||
done = function () { | ||
todo--; | ||
if (todo === 0) callback(changed); | ||
}; | ||
dir.forEach(function (dir) { | ||
todo++; | ||
fs.readdir(dir, function (err, files) { | ||
if (err) return; | ||
exec(cmd, function (error, stdout, stderr) { | ||
var files = stdout.split(/\n/); | ||
files.forEach(function (file) { | ||
if (program.includeHidden == true || !program.includeHidden && file.indexOf('.') !== 0) { | ||
todo++; | ||
file = path.resolve(dir + '/' + file); | ||
var stat = fs.stat(file, function (err, stat) { | ||
if (stat) { | ||
if (stat.isDirectory()) { | ||
todo++; | ||
changedSince(time, file, function (subChanged) { | ||
if (subChanged.length) changed = changed.concat(subChanged); | ||
done(); | ||
}); | ||
} else if (stat.mtime > time) { | ||
changed.push(file); | ||
} | ||
} | ||
done(); | ||
}); | ||
} | ||
}); | ||
done(); | ||
}); | ||
}); | ||
} | ||
files.pop(); // remove blank line ending and split | ||
function startMonitor() { | ||
changedSince(lastStarted, function (files) { | ||
if (files.length) { | ||
@@ -84,23 +126,21 @@ // filter ignored files | ||
fs.writeFileSync(flag, ''); | ||
if (files.length) { | ||
if (restartTimer !== null) clearTimeout(restartTimer); | ||
restartTimer = setTimeout(function () { | ||
sys.log('[nodemon] restarting due to changes...'); | ||
if (program.options.verbose) util.log('[nodemon] restarting due to changes...'); | ||
files.forEach(function (file) { | ||
sys.log('[nodemon] ' + file); | ||
if (program.options.verbose) util.log('[nodemon] ' + file); | ||
}); | ||
sys.print('\n\n'); | ||
if (program.options.verbose) util.print('\n\n'); | ||
if (node !== null) { | ||
node.kill('SIGUSR2'); | ||
if (child !== null) { | ||
child.kill('SIGUSR2'); | ||
} else { | ||
startNode(); | ||
} | ||
} | ||
}, restartDelay); | ||
return; | ||
} | ||
} | ||
setTimeout(startMonitor, timeout); | ||
@@ -131,15 +171,6 @@ }); | ||
path.exists(ignoreFilePath, function(exists) { | ||
// if (!exists) { | ||
// we'll touch the ignore file to make sure it gets created and | ||
// if Vim is writing the file, it'll just overwrite it - but also | ||
// prevent from constant file io if the file doesn't exist | ||
// fs.writeFileSync(ignoreFilePath, "\n"); | ||
// setTimeout(readIgnoreFile, 500); | ||
// return; | ||
// } | ||
if (program.options.verbose) util.log('[nodemon] reading ignore list'); | ||
sys.log('[nodemon] reading ignore list'); | ||
// ignoreFiles = ignoreFiles.concat([flag, ignoreFilePath]); | ||
addIgnoreRule(flag); | ||
// addIgnoreRule(flag); | ||
addIgnoreRule(ignoreFilePath); | ||
@@ -153,107 +184,185 @@ fs.readFileSync(ignoreFilePath).toString().split(/\n/).forEach(function (rule, i) { | ||
function usage() { | ||
sys.print([ | ||
'usage: nodemon [options] [script.js] [args]', | ||
'e.g.: nodemon script.js localhost 8080', | ||
'', | ||
'Options:', | ||
' --js monitor only JavaScript file changes', | ||
' (default if ignore file not found)', | ||
' -d n, --delay n throttle restart for "n" seconds', | ||
' --debug enable node\'s native debug port', | ||
' -v, --version current nodemon version', | ||
' -h, --help this usage', | ||
'', | ||
'Note: if the script is omitted, nodemon will try "main" from package.json', | ||
'', | ||
'For more details see http://github.com/remy/nodemon/\n' | ||
].join('\n')); | ||
} | ||
function controlArg(nodeArgs, label, fn) { | ||
var i; | ||
if ((i = nodeArgs.indexOf(label)) !== -1) { | ||
fn(nodeArgs[i], i); | ||
} else if ((i = nodeArgs.indexOf('-' + label.substr(0, 1))) !== -1) { | ||
fn(nodeArgs[i], i); | ||
} else if ((i = nodeArgs.indexOf('--' + label)) !== -1) { | ||
fn(nodeArgs[i], i); | ||
} | ||
} | ||
// attempt to shutdown the wrapped node instance and remove | ||
// the monitor file as nodemon exists | ||
function cleanup() { | ||
node && node.kill(); | ||
fs.unlink(flag); | ||
child && child.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(); | ||
}); | ||
function getNodemonArgs() { | ||
var args = process.argv, | ||
len = args.length, | ||
i = 2, | ||
dir = process.cwd(), | ||
indexOfApp = -1; | ||
controlArg(nodeArgs, 'version', function () { | ||
sys.print('v' + meta.version + '\n'); | ||
process.exit(); | ||
}); | ||
for (; i < len; i++) { | ||
if (path.existsSync(dir + '/' + args[i])) { | ||
// double check we didn't use the --watch or -w opt before this arg | ||
if (args[i-1] && (args[i-1] == '-w' || args[i-1] == '--watch')) { | ||
// ignore | ||
} else { | ||
indexOfApp = i; | ||
break; | ||
} | ||
} | ||
} | ||
// 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 | ||
app = nodeArgs[0]; | ||
if (delay) { | ||
sys.log('[nodemon] Adding delay of ' + delay + ' seconds'); | ||
restartDelay = delay * 1000; // in seconds | ||
if (indexOfApp == -1) { | ||
// not found, so assume we're reading the package.json and thus swallow up all the args | ||
indexOfApp = len; | ||
} | ||
}); | ||
controlArg(nodeArgs, 'js', function (arg, i) { | ||
nodeArgs.splice(i, 1); // remove this flag from the arguments | ||
// sys.log('[nodemon] monitoring all filetype changes'); | ||
addIgnoreRule('^((?!\.js$).)*$', true); // ignores everything except JS | ||
app = nodeArgs[0]; | ||
}); | ||
var appargs = process.argv.slice(indexOfApp), | ||
app = appargs[0], | ||
nodemonargs = process.argv.slice(2, indexOfApp), | ||
arg, | ||
options = { | ||
delay: 1, | ||
watch: [], | ||
exec: 'node', | ||
verbose: true, | ||
js: false, // becomes the default anyway... | ||
includeHidden: false | ||
// args: [] | ||
}; | ||
// process nodemon args | ||
while (arg = nodemonargs.shift()) { | ||
if (arg === '--help' || arg === '-h' || arg === '-?') { | ||
return help(); // exits program | ||
} else if (arg === '--version' || arg == '-v') { | ||
return version(); // also exits | ||
} else if (arg == '--js') { | ||
options.js = true; | ||
} else if (arg == '--quiet' || arg == '-q') { | ||
options.verbose = false; | ||
} else if (arg == '--hidden') { | ||
options.includeHidden = true; | ||
} else if (arg === '--watch' || arg === '-w') { | ||
options.watch.push(nodemonargs.shift()); | ||
} else if (arg === '--delay' || arg === '-d') { | ||
options.delay = parseInt(nodemonargs.shift()); | ||
} else if (arg === '--exec' || arg === '-x') { | ||
options.exec = nodemonargs.shift(); | ||
} else { //if (arg === "--") { | ||
// Remaining args are node arguments | ||
appargs.unshift(arg); | ||
} | ||
} | ||
controlArg(nodeArgs, '--debug', function (arg, i) { | ||
nodeArgs.splice(i, 1); | ||
app = nodeArgs[0]; | ||
nodeArgs.unshift('--debug'); // put it at the front | ||
}); | ||
var program = { nodemon: nodemonargs, options: options, args: appargs, app: app }; | ||
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; | ||
if (nodeArgs[0] == '--debug') { | ||
nodeArgs.splice(1, 0, app); | ||
} else { | ||
nodeArgs.unshift(app); | ||
getAppScript(program); | ||
return program; | ||
} | ||
function getAppScript(program) { | ||
if (!program.args.length) { | ||
// 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 { | ||
// note: this isn't nodemon's package, it's the user's cwd package | ||
program.app = JSON.parse(fs.readFileSync('./package.json').toString()).main; | ||
} catch (e) { | ||
// no app found to run - so give them a tip and get the feck out | ||
help(); | ||
} | ||
} else { | ||
program.app = program.args.slice(0, 1); | ||
} | ||
program.app = path.basename(program.app); | ||
program.ext = path.extname(program.app); | ||
if (program.ext === '.coffee') { | ||
//coffeescript requires --nodejs --debug | ||
var debugIndex = program.args.indexOf('--debug'); | ||
if (debugIndex >= 0 && program.args.indexOf('--nodejs') === -1) { | ||
program.args.splice(debugIndex, 0, '--nodejs'); | ||
} | ||
} catch (e) { | ||
// no app found to run - so give them a tip and get the feck out | ||
usage(); | ||
process.exit(); | ||
// monitor both types - TODO possibly make this an option? | ||
program.ext = '.coffee|.js'; | ||
program.exec = 'coffee'; | ||
} | ||
} | ||
sys.log('[nodemon] v' + meta.version); | ||
function version() { | ||
console.log(meta.version); | ||
process.exit(0); | ||
} | ||
function help() { | ||
util.print([ | ||
'', | ||
' Usage: nodemon [options] [script.js] [args]', | ||
'', | ||
' Options:', | ||
' -d, --delay n throttle restart for "n" seconds', | ||
' -w, --watch dir watch directory "dir". use once for each', | ||
' directory to watch', | ||
' -x, --exec app execute script with "app", ie. -x python', | ||
' -q, --quiet minimise nodemon messages to start/stop only', | ||
' -v, --version current nodemon version', | ||
' -h, --help you\'re looking at it', | ||
'', | ||
' Note: if the script is omitted, nodemon will try to ', | ||
' read "main" from package.json and without a .nodemonignore,', | ||
' nodemon will monitor .js and .coffee by default.', | ||
'', | ||
' Examples:', | ||
'', | ||
' $ nodemon server.js', | ||
' $ nodemon -w ../foo server.js apparg1 apparg2', | ||
' $ PORT=8000 nodemon --debug-brk server.js', | ||
' $ nodemon --exec python app.py', | ||
'', | ||
' For more details see http://github.com/remy/nodemon/', | ||
'' | ||
].join('\n') + '\n'); | ||
process.exit(0); | ||
} | ||
if (program.options.delay) { | ||
restartDelay = program.options.delay * 1000; | ||
} | ||
// this is the default - why am I making it a cmd line opt? | ||
if (program.options.js) { | ||
addIgnoreRule('^((?!\.js|\.coffee$).)*$', true); // ignores everything except JS | ||
} | ||
if (program.options.watch && program.options.watch.length > 0) { | ||
program.options.watch.forEach(function (dir) { | ||
dirs.push(path.resolve(dir)); | ||
}); | ||
} else { | ||
dirs.unshift(process.cwd()); | ||
} | ||
// anything left over in program.args should be prepended to our application args | ||
// like --debug-brk, etc | ||
if (program.nodemon.length) { | ||
program.args = program.nodemon.concat(program.args); | ||
} | ||
if (!program.app) { | ||
help(); | ||
} | ||
if (program.options.verbose) util.log('[nodemon] v' + meta.version); | ||
// this was causing problems for a lot of people, so now not moving to the subdirectory | ||
// process.chdir(path.dirname(app)); | ||
app = path.basename(app); | ||
sys.log('\x1B[32m[nodemon] watching: ' + process.cwd() + '\x1B[0m'); | ||
sys.log('[nodemon] running ' + app); | ||
dirs.forEach(function(dir) { | ||
if (program.options.verbose) util.log('\x1B[32m[nodemon] watching: ' + dir + '\x1B[0m'); | ||
}); | ||
if (program.options.verbose) util.log('[nodemon] running ' + program.app); | ||
startNode(); | ||
setTimeout(startMonitor, timeout); | ||
path.exists(ignoreFilePath, function (exists) { | ||
@@ -264,8 +373,9 @@ if (!exists) { | ||
if (exists) { | ||
sys.log('[nodemon] detected old style .nodemonignore'); | ||
if (program.options.verbose) util.log('[nodemon] detected old style .nodemonignore'); | ||
ignoreFilePath = oldIgnoreFilePath; | ||
} else { | ||
// don't create the ignorefile, just ignore the flag & JS | ||
addIgnoreRule(flag); | ||
addIgnoreRule('^((?!\.js$).)*$', true); | ||
// addIgnoreRule(flag); | ||
var ext = program.ext.replace(/\./g, '\\.'); | ||
addIgnoreRule('^((?!' + ext + '$).)*$', true); | ||
} | ||
@@ -283,10 +393,10 @@ }); | ||
// by accident and then try again later). | ||
if (path.existsSync(flag)) fs.unlinkSync(flag); | ||
fs.writeFileSync(flag, ''); | ||
fs.chmodSync(flag, '666'); | ||
// if (path.existsSync(flag)) fs.unlinkSync(flag); | ||
// fs.writeFileSync(flag, '.'); // requires some content https://github.com/remy/nodemon/issues/36 | ||
// fs.chmodSync(flag, '666'); | ||
// remove the flag file on exit | ||
process.on('exit', function (code) { | ||
if (program.options.verbose) util.log('[nodemon] exiting'); | ||
cleanup(); | ||
sys.log('[nodemon] exiting'); | ||
}); | ||
@@ -305,7 +415,9 @@ | ||
// TODO on a clean exit, we could continue to monitor the directory and reboot the service | ||
// 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); | ||
util.log('[nodemon] exception in nodemon killing node'); | ||
util.error(err.stack); | ||
cleanup(); | ||
}); |
@@ -12,5 +12,8 @@ { | ||
"keywords": ["monitor", "development", "restart", "autoload", "reload", "terminal"], | ||
"version": "0.5.7", | ||
"version": "0.6.0", | ||
"preferGlobal" : "true", | ||
"dependencies": { | ||
"commander": "0.5.1" | ||
}, | ||
"main": "./nodemon" | ||
} |
@@ -39,4 +39,24 @@ # nodemon | ||
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. | ||
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. | ||
# Automatic re-running | ||
`nodemon` was original written to restart hanging processes such as web servers, but now supports apps that cleanly exit. If your script exits cleanly, `nodemon` will continue to monitor the directory (or directories) and restart the script if there are any changes. | ||
# Running non-node scripts | ||
`nodemon` can also be used to execute and monitor other programs. `nodemon` will read the file extension of the script being run and monitor that extension instead of .js if there's no .nodemonignore: | ||
nodemon -exec python ./app.py | ||
Now nodemon will run `app.py` with python, and look for new or modified files with the `.py` extension. | ||
# Monitoring multiple directories | ||
By default `nodemon` monitors the current working directory. If you want to take control of that option, use the `--watch` option to add specific paths: | ||
nodemon --watch app --watch libs app/server.js | ||
Now `nodemon` will only restart if there are changes in the `./app` or `./libs` directory. By default `nodemon` will traverse sub-directories, so there's no need in explicitly including sub-directories. | ||
# Delaying restarting | ||
@@ -71,5 +91,1 @@ | ||
* File patterns (this is converted to a regex, so you have full control of the pattern) | ||
# Prerequisites | ||
`nodemon` currently depends on the [unix find](http://unixhelp.ed.ac.uk/CGI/man-cgi?find) command (which also is installed on Macs) |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
18558
7
379
90
1
+ Addedcommander@0.5.1
+ Addedcommander@0.5.1(transitive)