parse.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. nodemon is a utility for node, and replaces the use of the executable
  3. node. So the user calls `nodemon foo.js` instead.
  4. nodemon can be run in a number of ways:
  5. `nodemon` - tries to use package.json#main property to run
  6. `nodemon` - if no package, looks for index.js
  7. `nodemon app.js` - runs app.js
  8. `nodemon --arg app.js --apparg` - eats arg1, and runs app.js with apparg
  9. `nodemon --apparg` - as above, but passes apparg to package.json#main (or
  10. index.js)
  11. `nodemon --debug app.js
  12. */
  13. var fs = require('fs');
  14. var path = require('path');
  15. var existsSync = fs.existsSync || path.existsSync;
  16. module.exports = parse;
  17. /**
  18. * Parses the command line arguments `process.argv` and returns the
  19. * nodemon options, the user script and the executable script.
  20. *
  21. * @param {Array} full process arguments, including `node` leading arg
  22. * @return {Object} { options, script, args }
  23. */
  24. function parse(argv) {
  25. if (typeof argv === 'string') {
  26. argv = argv.split(' ');
  27. }
  28. var eat = function (i, args) {
  29. if (i <= args.length) {
  30. return args.splice(i + 1, 1).pop();
  31. }
  32. };
  33. var args = argv.slice(2);
  34. var script = null;
  35. var nodemonOptions = { scriptPosition: null };
  36. var nodemonOpt = nodemonOption.bind(null, nodemonOptions);
  37. var lookForArgs = true;
  38. // move forward through the arguments
  39. for (var i = 0; i < args.length; i++) {
  40. // if the argument looks like a file, then stop eating
  41. if (!script) {
  42. if (args[i] === '.' || existsSync(args[i])) {
  43. script = args.splice(i, 1).pop();
  44. // we capture the position of the script because we'll reinsert it in
  45. // the right place in run.js:command (though I'm not sure we should even
  46. // take it out of the array in the first place, but this solves passing
  47. // arguments to the exec process for now).
  48. nodemonOptions.scriptPosition = i;
  49. i--;
  50. continue;
  51. }
  52. }
  53. if (lookForArgs) {
  54. // respect the standard way of saying: hereafter belongs to my script
  55. if (args[i] === '--') {
  56. args.splice(i, 1);
  57. nodemonOptions.scriptPosition = i;
  58. // cycle back one argument, as we just ate this one up
  59. i--;
  60. // ignore all further nodemon arguments
  61. lookForArgs = false;
  62. // move to the next iteration
  63. continue;
  64. }
  65. if (nodemonOpt(args[i], eat.bind(null, i, args)) !== false) {
  66. args.splice(i, 1);
  67. // cycle back one argument, as we just ate this one up
  68. i--;
  69. }
  70. }
  71. }
  72. nodemonOptions.script = script;
  73. nodemonOptions.args = args;
  74. return nodemonOptions;
  75. }
  76. /**
  77. * Given an argument (ie. from process.argv), sets nodemon
  78. * options and can eat up the argument value
  79. *
  80. * @param {Object} options object that will be updated
  81. * @param {Sting} current argument from argv
  82. * @param {Function} the callback to eat up the next argument in argv
  83. * @return {Boolean} false if argument was not a nodemon arg
  84. */
  85. function nodemonOption(options, arg, eatNext) {
  86. // line separation on purpose to help legibility
  87. if (arg === '--help' || arg === '-h' || arg === '-?') {
  88. var help = eatNext();
  89. options.help = help ? help : true;
  90. } else
  91. if (arg === '--version' || arg === '-v') {
  92. options.version = true;
  93. } else
  94. if (arg === '--no-update-notifier') {
  95. options.noUpdateNotifier = true;
  96. } else
  97. if (arg === '--spawn') {
  98. options.spawn = true;
  99. } else
  100. if (arg === '--dump') {
  101. options.dump = true;
  102. } else
  103. if (arg === '--verbose' || arg === '-V') {
  104. options.verbose = true;
  105. } else
  106. if (arg === '--legacy-watch' || arg === '-L') {
  107. options.legacyWatch = true;
  108. } else
  109. if (arg === '--polling-interval' || arg === '-P') {
  110. options.pollingInterval = parseInt(eatNext(), 10);
  111. } else
  112. // Depricated as this is "on" by default
  113. if (arg === '--js') {
  114. options.js = true;
  115. } else
  116. if (arg === '--quiet' || arg === '-q') {
  117. options.quiet = true;
  118. } else
  119. if (arg === '--config') {
  120. options.configFile = eatNext();
  121. } else
  122. if (arg === '--watch' || arg === '-w') {
  123. if (!options.watch) { options.watch = []; }
  124. options.watch.push(eatNext());
  125. } else
  126. if (arg === '--ignore' || arg === '-i') {
  127. if (!options.ignore) { options.ignore = []; }
  128. options.ignore.push(eatNext());
  129. } else
  130. if (arg === '--exitcrash') {
  131. options.exitcrash = true;
  132. } else
  133. if (arg === '--delay' || arg === '-d') {
  134. options.delay = parseDelay(eatNext());
  135. } else
  136. if (arg === '--exec' || arg === '-x') {
  137. options.exec = eatNext();
  138. } else
  139. if (arg === '--no-stdin' || arg === '-I') {
  140. options.stdin = false;
  141. } else
  142. if (arg === '--on-change-only' || arg === '-C') {
  143. options.runOnChangeOnly = true;
  144. } else
  145. if (arg === '--ext' || arg === '-e') {
  146. options.ext = eatNext();
  147. } else
  148. if (arg === '--no-colours' || arg === '--no-colors') {
  149. options.colours = false;
  150. } else
  151. if (arg === '--signal' || arg === '-s') {
  152. options.signal = eatNext();
  153. } else
  154. if (arg === '--cwd') {
  155. options.cwd = eatNext();
  156. // go ahead and change directory. This is primarily for nodemon tools like
  157. // grunt-nodemon - we're doing this early because it will affect where the
  158. // user script is searched for.
  159. process.chdir(path.resolve(options.cwd));
  160. } else {
  161. // this means we didn't match
  162. return false;
  163. }
  164. }
  165. /**
  166. * Given an argument (ie. from nodemonOption()), will parse and return the
  167. * equivalent millisecond value or 0 if the argument cannot be parsed
  168. *
  169. * @param {String} argument value given to the --delay option
  170. * @return {Number} millisecond equivalent of the argument
  171. */
  172. function parseDelay(value) {
  173. var millisPerSecond = 1000;
  174. var millis = 0;
  175. if (value.match(/^\d*ms$/)) {
  176. // Explicitly parse for milliseconds when using ms time specifier
  177. millis = parseInt(value, 10);
  178. } else {
  179. // Otherwise, parse for seconds, with or without time specifier then convert
  180. millis = parseFloat(value) * millisPerSecond;
  181. }
  182. return isNaN(millis) ? 0 : millis;
  183. }