Code coverage report for lib/cli/parse.js

Statements: 86.75% (72 / 83)      Branches: 83.52% (76 / 91)      Functions: 100% (4 / 4)      Lines: 86.42% (70 / 81)     

All files » lib/cli/ » parse.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 2221                                 1       1                 1 27       27 22 22       27       27       27   75 61 18           18 18 18       57   53 5   5     5     5     48 37   37         27 5             27 1     27 27   27                         1   48         48 3     45       45 3       42       42 2     40       40 7 7     33 2 2     31 1     30 2     28 5     23 4     19 2     17 6     11                   11       1 5               5   5 5   5                        
'use strict';
/*
 
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'),
    path = require('path'),
    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) {
  Iif (typeof argv === 'string') {
    argv = argv.split(' ');
  }
 
  var eat = function (i, args) {
    Eif (i <= args.length) {
      return args.splice(i + 1, 1).pop();
    }
  };
 
  var args = argv.slice(2),
      script = null,
      nodemonOptions = { scriptPosition: -1 };
 
  var nodemonOpt = nodemonOption.bind(null, nodemonOptions),
      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);
        // 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--;
      }
    }
  }
 
  if (script === null && !nodemonOptions.exec) {
    script = findAppScript();
  }
 
 
  // 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;
 
  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 seperation on purpose to help legibility
  Iif (arg === '--help' || arg === '-h' || arg === '-?') {
    var help = eatNext();
    options.help = help ? help : true;
  }
 
  else if (arg === '--version' || arg === '-v') {
    options.version = true;
  }
 
  else Iif (arg === '--dump') {
    options.dump = true;
  }
 
  else if (arg === '--verbose' || arg === '-V') {
    options.verbose = true;
  }
 
  // Depricated as this is "on" by default
  else Iif (arg === '--js') {
    options.js = true;
  }
 
  else if (arg === '--quiet' || arg === '-q') {
    options.quiet = true;
  }
 
  else Iif (arg === '--hidden') { // TODO document this flag?
    options.hidden = true;
  }
 
  else if (arg === '--watch' || arg === '-w') {
    Eif (!options.watch) { options.watch = []; }
    options.watch.push(eatNext());
  }
 
  else if (arg === '--ignore' || arg === '-i') {
    Eif (!options.ignore) { options.ignore = []; }
    options.ignore.push(eatNext());
  }
 
  else if (arg === '--exitcrash') {
    options.exitcrash = true;
  }
 
  else if (arg === '--delay' || arg === '-d') {
    options.delay = parseInt(eatNext(), 10) * 1000;
  }
 
  else if (arg === '--exec' || arg === '-x') {
    options.exec = eatNext();
  }
 
  else if (arg === '--legacy-watch' || arg === '-L') {
    options.legacyWatch = true;
  }
 
  else if (arg === '--no-stdin' || arg === '-I') {
    options.stdin = false;
  }
 
  else if (arg === '--ext' || arg === '-e') {
    options.ext = eatNext();
  }
 
  else Iif (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 {
    return false; // this means we didn't match
  }
}
 
function findAppScript() {
  var appScript = null;
 
  // nodemon has been run alone, so try to read the package file
  // or try to read the index.js file
 
  // 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
    appScript = JSON.parse(fs.readFileSync('./package.json').toString()).main;
    Eif (appScript !== undefined) {
      // no app found to run - so give them a tip and get the feck out
      return appScript;
    }
  } catch (e) {}
 
  // now try index.js
  if (existsSync('./index.js')) { // FIXME is ./ the right location?
    return 'index.js';
  }
 
  return null;
}