You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1026 lines
22 KiB

  1. /*!
  2. * commander
  3. * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var EventEmitter = require('events').EventEmitter
  10. , path = require('path')
  11. , tty = require('tty')
  12. , basename = path.basename;
  13. /**
  14. * Expose the root command.
  15. */
  16. exports = module.exports = new Command;
  17. /**
  18. * Expose `Command`.
  19. */
  20. exports.Command = Command;
  21. /**
  22. * Expose `Option`.
  23. */
  24. exports.Option = Option;
  25. /**
  26. * Initialize a new `Option` with the given `flags` and `description`.
  27. *
  28. * @param {String} flags
  29. * @param {String} description
  30. * @api public
  31. */
  32. function Option(flags, description) {
  33. this.flags = flags;
  34. this.required = ~flags.indexOf('<');
  35. this.optional = ~flags.indexOf('[');
  36. this.bool = !~flags.indexOf('-no-');
  37. flags = flags.split(/[ ,|]+/);
  38. if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
  39. this.long = flags.shift();
  40. this.description = description;
  41. }
  42. /**
  43. * Return option name.
  44. *
  45. * @return {String}
  46. * @api private
  47. */
  48. Option.prototype.name = function(){
  49. return this.long
  50. .replace('--', '')
  51. .replace('no-', '');
  52. };
  53. /**
  54. * Check if `arg` matches the short or long flag.
  55. *
  56. * @param {String} arg
  57. * @return {Boolean}
  58. * @api private
  59. */
  60. Option.prototype.is = function(arg){
  61. return arg == this.short
  62. || arg == this.long;
  63. };
  64. /**
  65. * Initialize a new `Command`.
  66. *
  67. * @param {String} name
  68. * @api public
  69. */
  70. function Command(name) {
  71. this.commands = [];
  72. this.options = [];
  73. this.args = [];
  74. this.name = name;
  75. }
  76. /**
  77. * Inherit from `EventEmitter.prototype`.
  78. */
  79. Command.prototype.__proto__ = EventEmitter.prototype;
  80. /**
  81. * Add command `name`.
  82. *
  83. * The `.action()` callback is invoked when the
  84. * command `name` is specified via __ARGV__,
  85. * and the remaining arguments are applied to the
  86. * function for access.
  87. *
  88. * When the `name` is "*" an un-matched command
  89. * will be passed as the first arg, followed by
  90. * the rest of __ARGV__ remaining.
  91. *
  92. * Examples:
  93. *
  94. * program
  95. * .version('0.0.1')
  96. * .option('-C, --chdir <path>', 'change the working directory')
  97. * .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
  98. * .option('-T, --no-tests', 'ignore test hook')
  99. *
  100. * program
  101. * .command('setup')
  102. * .description('run remote setup commands')
  103. * .action(function(){
  104. * console.log('setup');
  105. * });
  106. *
  107. * program
  108. * .command('exec <cmd>')
  109. * .description('run the given remote command')
  110. * .action(function(cmd){
  111. * console.log('exec "%s"', cmd);
  112. * });
  113. *
  114. * program
  115. * .command('*')
  116. * .description('deploy the given env')
  117. * .action(function(env){
  118. * console.log('deploying "%s"', env);
  119. * });
  120. *
  121. * program.parse(process.argv);
  122. *
  123. * @param {String} name
  124. * @return {Command} the new command
  125. * @api public
  126. */
  127. Command.prototype.command = function(name){
  128. var args = name.split(/ +/);
  129. var cmd = new Command(args.shift());
  130. this.commands.push(cmd);
  131. cmd.parseExpectedArgs(args);
  132. cmd.parent = this;
  133. return cmd;
  134. };
  135. /**
  136. * Parse expected `args`.
  137. *
  138. * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
  139. *
  140. * @param {Array} args
  141. * @return {Command} for chaining
  142. * @api public
  143. */
  144. Command.prototype.parseExpectedArgs = function(args){
  145. if (!args.length) return;
  146. var self = this;
  147. args.forEach(function(arg){
  148. switch (arg[0]) {
  149. case '<':
  150. self.args.push({ required: true, name: arg.slice(1, -1) });
  151. break;
  152. case '[':
  153. self.args.push({ required: false, name: arg.slice(1, -1) });
  154. break;
  155. }
  156. });
  157. return this;
  158. };
  159. /**
  160. * Register callback `fn` for the command.
  161. *
  162. * Examples:
  163. *
  164. * program
  165. * .command('help')
  166. * .description('display verbose help')
  167. * .action(function(){
  168. * // output help here
  169. * });
  170. *
  171. * @param {Function} fn
  172. * @return {Command} for chaining
  173. * @api public
  174. */
  175. Command.prototype.action = function(fn){
  176. var self = this;
  177. this.parent.on(this.name, function(args, unknown){
  178. // Parse any so-far unknown options
  179. unknown = unknown || [];
  180. var parsed = self.parseOptions(unknown);
  181. // Output help if necessary
  182. outputHelpIfNecessary(self, parsed.unknown);
  183. // If there are still any unknown options, then we simply
  184. // die, unless someone asked for help, in which case we give it
  185. // to them, and then we die.
  186. if (parsed.unknown.length > 0) {
  187. self.unknownOption(parsed.unknown[0]);
  188. }
  189. self.args.forEach(function(arg, i){
  190. if (arg.required && null == args[i]) {
  191. self.missingArgument(arg.name);
  192. }
  193. });
  194. // Always append ourselves to the end of the arguments,
  195. // to make sure we match the number of arguments the user
  196. // expects
  197. if (self.args.length) {
  198. args[self.args.length] = self;
  199. } else {
  200. args.push(self);
  201. }
  202. fn.apply(this, args);
  203. });
  204. return this;
  205. };
  206. /**
  207. * Define option with `flags`, `description` and optional
  208. * coercion `fn`.
  209. *
  210. * The `flags` string should contain both the short and long flags,
  211. * separated by comma, a pipe or space. The following are all valid
  212. * all will output this way when `--help` is used.
  213. *
  214. * "-p, --pepper"
  215. * "-p|--pepper"
  216. * "-p --pepper"
  217. *
  218. * Examples:
  219. *
  220. * // simple boolean defaulting to false
  221. * program.option('-p, --pepper', 'add pepper');
  222. *
  223. * --pepper
  224. * program.pepper
  225. * // => Boolean
  226. *
  227. * // simple boolean defaulting to false
  228. * program.option('-C, --no-cheese', 'remove cheese');
  229. *
  230. * program.cheese
  231. * // => true
  232. *
  233. * --no-cheese
  234. * program.cheese
  235. * // => true
  236. *
  237. * // required argument
  238. * program.option('-C, --chdir <path>', 'change the working directory');
  239. *
  240. * --chdir /tmp
  241. * program.chdir
  242. * // => "/tmp"
  243. *
  244. * // optional argument
  245. * program.option('-c, --cheese [type]', 'add cheese [marble]');
  246. *
  247. * @param {String} flags
  248. * @param {String} description
  249. * @param {Function|Mixed} fn or default
  250. * @param {Mixed} defaultValue
  251. * @return {Command} for chaining
  252. * @api public
  253. */
  254. Command.prototype.option = function(flags, description, fn, defaultValue){
  255. var self = this
  256. , option = new Option(flags, description)
  257. , oname = option.name()
  258. , name = camelcase(oname);
  259. // default as 3rd arg
  260. if ('function' != typeof fn) defaultValue = fn, fn = null;
  261. // preassign default value only for --no-*, [optional], or <required>
  262. if (false == option.bool || option.optional || option.required) {
  263. // when --no-* we make sure default is true
  264. if (false == option.bool) defaultValue = true;
  265. // preassign only if we have a default
  266. if (undefined !== defaultValue) self[name] = defaultValue;
  267. }
  268. // register the option
  269. this.options.push(option);
  270. // when it's passed assign the value
  271. // and conditionally invoke the callback
  272. this.on(oname, function(val){
  273. // coercion
  274. if (null != val && fn) val = fn(val);
  275. // unassigned or bool
  276. if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
  277. // if no value, bool true, and we have a default, then use it!
  278. if (null == val) {
  279. self[name] = option.bool
  280. ? defaultValue || true
  281. : false;
  282. } else {
  283. self[name] = val;
  284. }
  285. } else if (null !== val) {
  286. // reassign
  287. self[name] = val;
  288. }
  289. });
  290. return this;
  291. };
  292. /**
  293. * Parse `argv`, settings options and invoking commands when defined.
  294. *
  295. * @param {Array} argv
  296. * @return {Command} for chaining
  297. * @api public
  298. */
  299. Command.prototype.parse = function(argv){
  300. // store raw args
  301. this.rawArgs = argv;
  302. // guess name
  303. if (!this.name) this.name = basename(argv[1]);
  304. // process argv
  305. var parsed = this.parseOptions(this.normalize(argv.slice(2)));
  306. this.args = parsed.args;
  307. return this.parseArgs(this.args, parsed.unknown);
  308. };
  309. /**
  310. * Normalize `args`, splitting joined short flags. For example
  311. * the arg "-abc" is equivalent to "-a -b -c".
  312. *
  313. * @param {Array} args
  314. * @return {Array}
  315. * @api private
  316. */
  317. Command.prototype.normalize = function(args){
  318. var ret = []
  319. , arg;
  320. for (var i = 0, len = args.length; i < len; ++i) {
  321. arg = args[i];
  322. if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
  323. arg.slice(1).split('').forEach(function(c){
  324. ret.push('-' + c);
  325. });
  326. } else {
  327. ret.push(arg);
  328. }
  329. }
  330. return ret;
  331. };
  332. /**
  333. * Parse command `args`.
  334. *
  335. * When listener(s) are available those
  336. * callbacks are invoked, otherwise the "*"
  337. * event is emitted and those actions are invoked.
  338. *
  339. * @param {Array} args
  340. * @return {Command} for chaining
  341. * @api private
  342. */
  343. Command.prototype.parseArgs = function(args, unknown){
  344. var cmds = this.commands
  345. , len = cmds.length
  346. , name;
  347. if (args.length) {
  348. name = args[0];
  349. if (this.listeners(name).length) {
  350. this.emit(args.shift(), args, unknown);
  351. } else {
  352. this.emit('*', args);
  353. }
  354. } else {
  355. outputHelpIfNecessary(this, unknown);
  356. // If there were no args and we have unknown options,
  357. // then they are extraneous and we need to error.
  358. if (unknown.length > 0) {
  359. this.unknownOption(unknown[0]);
  360. }
  361. }
  362. return this;
  363. };
  364. /**
  365. * Return an option matching `arg` if any.
  366. *
  367. * @param {String} arg
  368. * @return {Option}
  369. * @api private
  370. */
  371. Command.prototype.optionFor = function(arg){
  372. for (var i = 0, len = this.options.length; i < len; ++i) {
  373. if (this.options[i].is(arg)) {
  374. return this.options[i];
  375. }
  376. }
  377. };
  378. /**
  379. * Parse options from `argv` returning `argv`
  380. * void of these options.
  381. *
  382. * @param {Array} argv
  383. * @return {Array}
  384. * @api public
  385. */
  386. Command.prototype.parseOptions = function(argv){
  387. var args = []
  388. , len = argv.length
  389. , literal
  390. , option
  391. , arg;
  392. var unknownOptions = [];
  393. // parse options
  394. for (var i = 0; i < len; ++i) {
  395. arg = argv[i];
  396. // literal args after --
  397. if ('--' == arg) {
  398. literal = true;
  399. continue;
  400. }
  401. if (literal) {
  402. args.push(arg);
  403. continue;
  404. }
  405. // find matching Option
  406. option = this.optionFor(arg);
  407. // option is defined
  408. if (option) {
  409. // requires arg
  410. if (option.required) {
  411. arg = argv[++i];
  412. if (null == arg) return this.optionMissingArgument(option);
  413. if ('-' == arg[0]) return this.optionMissingArgument(option, arg);
  414. this.emit(option.name(), arg);
  415. // optional arg
  416. } else if (option.optional) {
  417. arg = argv[i+1];
  418. if (null == arg || '-' == arg[0]) {
  419. arg = null;
  420. } else {
  421. ++i;
  422. }
  423. this.emit(option.name(), arg);
  424. // bool
  425. } else {
  426. this.emit(option.name());
  427. }
  428. continue;
  429. }
  430. // looks like an option
  431. if (arg.length > 1 && '-' == arg[0]) {
  432. unknownOptions.push(arg);
  433. // If the next argument looks like it might be
  434. // an argument for this option, we pass it on.
  435. // If it isn't, then it'll simply be ignored
  436. if (argv[i+1] && '-' != argv[i+1][0]) {
  437. unknownOptions.push(argv[++i]);
  438. }
  439. continue;
  440. }
  441. // arg
  442. args.push(arg);
  443. }
  444. return { args: args, unknown: unknownOptions };
  445. };
  446. /**
  447. * Argument `name` is missing.
  448. *
  449. * @param {String} name
  450. * @api private
  451. */
  452. Command.prototype.missingArgument = function(name){
  453. console.error();
  454. console.error(" error: missing required argument `%s'", name);
  455. console.error();
  456. process.exit(1);
  457. };
  458. /**
  459. * `Option` is missing an argument, but received `flag` or nothing.
  460. *
  461. * @param {String} option
  462. * @param {String} flag
  463. * @api private
  464. */
  465. Command.prototype.optionMissingArgument = function(option, flag){
  466. console.error();
  467. if (flag) {
  468. console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
  469. } else {
  470. console.error(" error: option `%s' argument missing", option.flags);
  471. }
  472. console.error();
  473. process.exit(1);
  474. };
  475. /**
  476. * Unknown option `flag`.
  477. *
  478. * @param {String} flag
  479. * @api private
  480. */
  481. Command.prototype.unknownOption = function(flag){
  482. console.error();
  483. console.error(" error: unknown option `%s'", flag);
  484. console.error();
  485. process.exit(1);
  486. };
  487. /**
  488. * Set the program version to `str`.
  489. *
  490. * This method auto-registers the "-V, --version" flag
  491. * which will print the version number when passed.
  492. *
  493. * @param {String} str
  494. * @param {String} flags
  495. * @return {Command} for chaining
  496. * @api public
  497. */
  498. Command.prototype.version = function(str, flags){
  499. if (0 == arguments.length) return this._version;
  500. this._version = str;
  501. flags = flags || '-V, --version';
  502. this.option(flags, 'output the version number');
  503. this.on('version', function(){
  504. console.log(str);
  505. process.exit(0);
  506. });
  507. return this;
  508. };
  509. /**
  510. * Set the description `str`.
  511. *
  512. * @param {String} str
  513. * @return {String|Command}
  514. * @api public
  515. */
  516. Command.prototype.description = function(str){
  517. if (0 == arguments.length) return this._description;
  518. this._description = str;
  519. return this;
  520. };
  521. /**
  522. * Set / get the command usage `str`.
  523. *
  524. * @param {String} str
  525. * @return {String|Command}
  526. * @api public
  527. */
  528. Command.prototype.usage = function(str){
  529. var args = this.args.map(function(arg){
  530. return arg.required
  531. ? '<' + arg.name + '>'
  532. : '[' + arg.name + ']';
  533. });
  534. var usage = '[options'
  535. + (this.commands.length ? '] [command' : '')
  536. + ']'
  537. + (this.args.length ? ' ' + args : '');
  538. if (0 == arguments.length) return this._usage || usage;
  539. this._usage = str;
  540. return this;
  541. };
  542. /**
  543. * Return the largest option length.
  544. *
  545. * @return {Number}
  546. * @api private
  547. */
  548. Command.prototype.largestOptionLength = function(){
  549. return this.options.reduce(function(max, option){
  550. return Math.max(max, option.flags.length);
  551. }, 0);
  552. };
  553. /**
  554. * Return help for options.
  555. *
  556. * @return {String}
  557. * @api private
  558. */
  559. Command.prototype.optionHelp = function(){
  560. var width = this.largestOptionLength();
  561. // Prepend the help information
  562. return [pad('-h, --help', width) + ' ' + 'output usage information']
  563. .concat(this.options.map(function(option){
  564. return pad(option.flags, width)
  565. + ' ' + option.description;
  566. }))
  567. .join('\n');
  568. };
  569. /**
  570. * Return command help documentation.
  571. *
  572. * @return {String}
  573. * @api private
  574. */
  575. Command.prototype.commandHelp = function(){
  576. if (!this.commands.length) return '';
  577. return [
  578. ''
  579. , ' Commands:'
  580. , ''
  581. , this.commands.map(function(cmd){
  582. var args = cmd.args.map(function(arg){
  583. return arg.required
  584. ? '<' + arg.name + '>'
  585. : '[' + arg.name + ']';
  586. }).join(' ');
  587. return cmd.name
  588. + (cmd.options.length
  589. ? ' [options]'
  590. : '') + ' ' + args
  591. + (cmd.description()
  592. ? '\n' + cmd.description()
  593. : '');
  594. }).join('\n\n').replace(/^/gm, ' ')
  595. , ''
  596. ].join('\n');
  597. };
  598. /**
  599. * Return program help documentation.
  600. *
  601. * @return {String}
  602. * @api private
  603. */
  604. Command.prototype.helpInformation = function(){
  605. return [
  606. ''
  607. , ' Usage: ' + this.name + ' ' + this.usage()
  608. , '' + this.commandHelp()
  609. , ' Options:'
  610. , ''
  611. , '' + this.optionHelp().replace(/^/gm, ' ')
  612. , ''
  613. , ''
  614. ].join('\n');
  615. };
  616. /**
  617. * Prompt for a `Number`.
  618. *
  619. * @param {String} str
  620. * @param {Function} fn
  621. * @api private
  622. */
  623. Command.prototype.promptForNumber = function(str, fn){
  624. var self = this;
  625. this.promptSingleLine(str, function parseNumber(val){
  626. val = Number(val);
  627. if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber);
  628. fn(val);
  629. });
  630. };
  631. /**
  632. * Prompt for a `Date`.
  633. *
  634. * @param {String} str
  635. * @param {Function} fn
  636. * @api private
  637. */
  638. Command.prototype.promptForDate = function(str, fn){
  639. var self = this;
  640. this.promptSingleLine(str, function parseDate(val){
  641. val = new Date(val);
  642. if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate);
  643. fn(val);
  644. });
  645. };
  646. /**
  647. * Single-line prompt.
  648. *
  649. * @param {String} str
  650. * @param {Function} fn
  651. * @api private
  652. */
  653. Command.prototype.promptSingleLine = function(str, fn){
  654. if ('function' == typeof arguments[2]) {
  655. return this['promptFor' + (fn.name || fn)](str, arguments[2]);
  656. }
  657. process.stdout.write(str);
  658. process.stdin.setEncoding('utf8');
  659. process.stdin.once('data', function(val){
  660. fn(val.trim());
  661. }).resume();
  662. };
  663. /**
  664. * Multi-line prompt.
  665. *
  666. * @param {String} str
  667. * @param {Function} fn
  668. * @api private
  669. */
  670. Command.prototype.promptMultiLine = function(str, fn){
  671. var buf = [];
  672. console.log(str);
  673. process.stdin.setEncoding('utf8');
  674. process.stdin.on('data', function(val){
  675. if ('\n' == val || '\r\n' == val) {
  676. process.stdin.removeAllListeners('data');
  677. fn(buf.join('\n'));
  678. } else {
  679. buf.push(val.trimRight());
  680. }
  681. }).resume();
  682. };
  683. /**
  684. * Prompt `str` and callback `fn(val)`
  685. *
  686. * Commander supports single-line and multi-line prompts.
  687. * To issue a single-line prompt simply add white-space
  688. * to the end of `str`, something like "name: ", whereas
  689. * for a multi-line prompt omit this "description:".
  690. *
  691. *
  692. * Examples:
  693. *
  694. * program.prompt('Username: ', function(name){
  695. * console.log('hi %s', name);
  696. * });
  697. *
  698. * program.prompt('Description:', function(desc){
  699. * console.log('description was "%s"', desc.trim());
  700. * });
  701. *
  702. * @param {String|Object} str
  703. * @param {Function} fn
  704. * @api public
  705. */
  706. Command.prototype.prompt = function(str, fn){
  707. var self = this;
  708. if ('string' == typeof str) {
  709. if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments);
  710. this.promptMultiLine(str, fn);
  711. } else {
  712. var keys = Object.keys(str)
  713. , obj = {};
  714. function next() {
  715. var key = keys.shift()
  716. , label = str[key];
  717. if (!key) return fn(obj);
  718. self.prompt(label, function(val){
  719. obj[key] = val;
  720. next();
  721. });
  722. }
  723. next();
  724. }
  725. };
  726. /**
  727. * Prompt for password with `str`, `mask` char and callback `fn(val)`.
  728. *
  729. * The mask string defaults to '', aka no output is
  730. * written while typing, you may want to use "*" etc.
  731. *
  732. * Examples:
  733. *
  734. * program.password('Password: ', function(pass){
  735. * console.log('got "%s"', pass);
  736. * process.stdin.destroy();
  737. * });
  738. *
  739. * program.password('Password: ', '*', function(pass){
  740. * console.log('got "%s"', pass);
  741. * process.stdin.destroy();
  742. * });
  743. *
  744. * @param {String} str
  745. * @param {String} mask
  746. * @param {Function} fn
  747. * @api public
  748. */
  749. Command.prototype.password = function(str, mask, fn){
  750. var self = this
  751. , buf = '';
  752. // default mask
  753. if ('function' == typeof mask) {
  754. fn = mask;
  755. mask = '';
  756. }
  757. process.stdin.resume();
  758. tty.setRawMode(true);
  759. process.stdout.write(str);
  760. // keypress
  761. process.stdin.on('keypress', function(c, key){
  762. if (key && 'enter' == key.name) {
  763. console.log();
  764. process.stdin.removeAllListeners('keypress');
  765. tty.setRawMode(false);
  766. if (!buf.trim().length) return self.password(str, mask, fn);
  767. fn(buf);
  768. return;
  769. }
  770. if (key && key.ctrl && 'c' == key.name) {
  771. console.log('%s', buf);
  772. process.exit();
  773. }
  774. process.stdout.write(mask);
  775. buf += c;
  776. }).resume();
  777. };
  778. /**
  779. * Confirmation prompt with `str` and callback `fn(bool)`
  780. *
  781. * Examples:
  782. *
  783. * program.confirm('continue? ', function(ok){
  784. * console.log(' got %j', ok);
  785. * process.stdin.destroy();
  786. * });
  787. *
  788. * @param {String} str
  789. * @param {Function} fn
  790. * @api public
  791. */
  792. Command.prototype.confirm = function(str, fn, verbose){
  793. var self = this;
  794. this.prompt(str, function(ok){
  795. if (!ok.trim()) {
  796. if (!verbose) str += '(yes or no) ';
  797. return self.confirm(str, fn, true);
  798. }
  799. fn(parseBool(ok));
  800. });
  801. };
  802. /**
  803. * Choice prompt with `list` of items and callback `fn(index, item)`
  804. *
  805. * Examples:
  806. *
  807. * var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
  808. *
  809. * console.log('Choose the coolest pet:');
  810. * program.choose(list, function(i){
  811. * console.log('you chose %d "%s"', i, list[i]);
  812. * process.stdin.destroy();
  813. * });
  814. *
  815. * @param {Array} list
  816. * @param {Number|Function} index or fn
  817. * @param {Function} fn
  818. * @api public
  819. */
  820. Command.prototype.choose = function(list, index, fn){
  821. var self = this
  822. , hasDefault = 'number' == typeof index;
  823. if (!hasDefault) {
  824. fn = index;
  825. index = null;
  826. }
  827. list.forEach(function(item, i){
  828. if (hasDefault && i == index) {
  829. console.log('* %d) %s', i + 1, item);
  830. } else {
  831. console.log(' %d) %s', i + 1, item);
  832. }
  833. });
  834. function again() {
  835. self.prompt(' : ', function(val){
  836. val = parseInt(val, 10) - 1;
  837. if (hasDefault && isNaN(val)) val = index;
  838. if (null == list[val]) {
  839. again();
  840. } else {
  841. fn(val, list[val]);
  842. }
  843. });
  844. }
  845. again();
  846. };
  847. /**
  848. * Camel-case the given `flag`
  849. *
  850. * @param {String} flag
  851. * @return {String}
  852. * @api private
  853. */
  854. function camelcase(flag) {
  855. return flag.split('-').reduce(function(str, word){
  856. return str + word[0].toUpperCase() + word.slice(1);
  857. });
  858. }
  859. /**
  860. * Parse a boolean `str`.
  861. *
  862. * @param {String} str
  863. * @return {Boolean}
  864. * @api private
  865. */
  866. function parseBool(str) {
  867. return /^y|yes|ok|true$/i.test(str);
  868. }
  869. /**
  870. * Pad `str` to `width`.
  871. *
  872. * @param {String} str
  873. * @param {Number} width
  874. * @return {String}
  875. * @api private
  876. */
  877. function pad(str, width) {
  878. var len = Math.max(0, width - str.length);
  879. return str + Array(len + 1).join(' ');
  880. }
  881. /**
  882. * Output help information if necessary
  883. *
  884. * @param {Command} command to output help for
  885. * @param {Array} array of options to search for -h or --help
  886. * @api private
  887. */
  888. function outputHelpIfNecessary(cmd, options) {
  889. options = options || [];
  890. for (var i = 0; i < options.length; i++) {
  891. if (options[i] == '--help' || options[i] == '-h') {
  892. process.stdout.write(cmd.helpInformation());
  893. cmd.emit('--help');
  894. process.exit(0);
  895. }
  896. }
  897. }