/* nodemon is a utility for node, and replaces the use of the executable node. So the user calls `nodemon foo.js` instead. nodemon can be run in a number of ways: `nodemon` - tries to use package.json#main property to run `nodemon` - if no package, looks for index.js `nodemon app.js` - runs app.js `nodemon --arg app.js --apparg` - eats arg1, and runs app.js with apparg `nodemon --apparg` - as above, but passes apparg to package.json#main (or index.js) `nodemon --debug app.js */ var fs = require('fs'); var path = require('path'); var existsSync = fs.existsSync || path.existsSync; module.exports = parse; /** * Parses the command line arguments `process.argv` and returns the * nodemon options, the user script and the executable script. * * @param {Array} full process arguments, including `node` leading arg * @return {Object} { options, script, args } */ function parse(argv) { if (typeof argv === 'string') { argv = argv.split(' '); } var eat = function (i, args) { if (i <= args.length) { return args.splice(i + 1, 1).pop(); } }; var args = argv.slice(2); var script = null; var nodemonOptions = { scriptPosition: null }; var nodemonOpt = nodemonOption.bind(null, nodemonOptions); var lookForArgs = true; // move forward through the arguments for (var i = 0; i < args.length; i++) { // if the argument looks like a file, then stop eating if (!script) { if (args[i] === '.' || existsSync(args[i])) { script = args.splice(i, 1).pop(); // we capture the position of the script because we'll reinsert it in // the right place in run.js:command (though I'm not sure we should even // take it out of the array in the first place, but this solves passing // arguments to the exec process for now). nodemonOptions.scriptPosition = i; i--; continue; } } if (lookForArgs) { // respect the standard way of saying: hereafter belongs to my script if (args[i] === '--') { args.splice(i, 1); nodemonOptions.scriptPosition = i; // cycle back one argument, as we just ate this one up i--; // ignore all further nodemon arguments lookForArgs = false; // move to the next iteration continue; } if (nodemonOpt(args[i], eat.bind(null, i, args)) !== false) { args.splice(i, 1); // cycle back one argument, as we just ate this one up i--; } } } nodemonOptions.script = script; nodemonOptions.args = args; return nodemonOptions; } /** * Given an argument (ie. from process.argv), sets nodemon * options and can eat up the argument value * * @param {Object} options object that will be updated * @param {Sting} current argument from argv * @param {Function} the callback to eat up the next argument in argv * @return {Boolean} false if argument was not a nodemon arg */ function nodemonOption(options, arg, eatNext) { // line separation on purpose to help legibility if (arg === '--help' || arg === '-h' || arg === '-?') { var help = eatNext(); options.help = help ? help : true; } else if (arg === '--version' || arg === '-v') { options.version = true; } else if (arg === '--no-update-notifier') { options.noUpdateNotifier = true; } else if (arg === '--spawn') { options.spawn = true; } else if (arg === '--dump') { options.dump = true; } else if (arg === '--verbose' || arg === '-V') { options.verbose = true; } else if (arg === '--legacy-watch' || arg === '-L') { options.legacyWatch = true; } else if (arg === '--polling-interval' || arg === '-P') { options.pollingInterval = parseInt(eatNext(), 10); } else // Depricated as this is "on" by default if (arg === '--js') { options.js = true; } else if (arg === '--quiet' || arg === '-q') { options.quiet = true; } else if (arg === '--config') { options.configFile = eatNext(); } else if (arg === '--watch' || arg === '-w') { if (!options.watch) { options.watch = []; } options.watch.push(eatNext()); } else if (arg === '--ignore' || arg === '-i') { if (!options.ignore) { options.ignore = []; } options.ignore.push(eatNext()); } else if (arg === '--exitcrash') { options.exitcrash = true; } else if (arg === '--delay' || arg === '-d') { options.delay = parseDelay(eatNext()); } else if (arg === '--exec' || arg === '-x') { options.exec = eatNext(); } else if (arg === '--no-stdin' || arg === '-I') { options.stdin = false; } else if (arg === '--on-change-only' || arg === '-C') { options.runOnChangeOnly = true; } else if (arg === '--ext' || arg === '-e') { options.ext = eatNext(); } else if (arg === '--no-colours' || arg === '--no-colors') { options.colours = false; } else if (arg === '--signal' || arg === '-s') { options.signal = eatNext(); } else if (arg === '--cwd') { options.cwd = eatNext(); // go ahead and change directory. This is primarily for nodemon tools like // grunt-nodemon - we're doing this early because it will affect where the // user script is searched for. process.chdir(path.resolve(options.cwd)); } else { // this means we didn't match return false; } } /** * Given an argument (ie. from nodemonOption()), will parse and return the * equivalent millisecond value or 0 if the argument cannot be parsed * * @param {String} argument value given to the --delay option * @return {Number} millisecond equivalent of the argument */ function parseDelay(value) { var millisPerSecond = 1000; var millis = 0; if (value.match(/^\d*ms$/)) { // Explicitly parse for milliseconds when using ms time specifier millis = parseInt(value, 10); } else { // Otherwise, parse for seconds, with or without time specifier then convert millis = parseFloat(value) * millisPerSecond; } return isNaN(millis) ? 0 : millis; }