index.js 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881
  1. /**
  2. * Module dependencies.
  3. */
  4. const EventEmitter = require('events').EventEmitter;
  5. const spawn = require('child_process').spawn;
  6. const path = require('path');
  7. const fs = require('fs');
  8. // @ts-check
  9. class Option {
  10. /**
  11. * Initialize a new `Option` with the given `flags` and `description`.
  12. *
  13. * @param {string} flags
  14. * @param {string} description
  15. * @api public
  16. */
  17. constructor(flags, description) {
  18. this.flags = flags;
  19. this.required = flags.includes('<'); // A value must be supplied when the option is specified.
  20. this.optional = flags.includes('['); // A value is optional when the option is specified.
  21. // variadic test ignores <value,...> et al which might be used to describe custom splitting of single argument
  22. this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values.
  23. this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line.
  24. const optionFlags = _parseOptionFlags(flags);
  25. this.short = optionFlags.shortFlag;
  26. this.long = optionFlags.longFlag;
  27. this.negate = false;
  28. if (this.long) {
  29. this.negate = this.long.startsWith('--no-');
  30. }
  31. this.description = description || '';
  32. this.defaultValue = undefined;
  33. }
  34. /**
  35. * Return option name.
  36. *
  37. * @return {string}
  38. * @api private
  39. */
  40. name() {
  41. if (this.long) {
  42. return this.long.replace(/^--/, '');
  43. }
  44. return this.short.replace(/^-/, '');
  45. };
  46. /**
  47. * Return option name, in a camelcase format that can be used
  48. * as a object attribute key.
  49. *
  50. * @return {string}
  51. * @api private
  52. */
  53. attributeName() {
  54. return camelcase(this.name().replace(/^no-/, ''));
  55. };
  56. /**
  57. * Check if `arg` matches the short or long flag.
  58. *
  59. * @param {string} arg
  60. * @return {boolean}
  61. * @api private
  62. */
  63. is(arg) {
  64. return this.short === arg || this.long === arg;
  65. };
  66. }
  67. /**
  68. * CommanderError class
  69. * @class
  70. */
  71. class CommanderError extends Error {
  72. /**
  73. * Constructs the CommanderError class
  74. * @param {number} exitCode suggested exit code which could be used with process.exit
  75. * @param {string} code an id string representing the error
  76. * @param {string} message human-readable description of the error
  77. * @constructor
  78. */
  79. constructor(exitCode, code, message) {
  80. super(message);
  81. // properly capture stack trace in Node.js
  82. Error.captureStackTrace(this, this.constructor);
  83. this.name = this.constructor.name;
  84. this.code = code;
  85. this.exitCode = exitCode;
  86. this.nestedError = undefined;
  87. }
  88. }
  89. class Command extends EventEmitter {
  90. /**
  91. * Initialize a new `Command`.
  92. *
  93. * @param {string} [name]
  94. * @api public
  95. */
  96. constructor(name) {
  97. super();
  98. this.commands = [];
  99. this.options = [];
  100. this.parent = null;
  101. this._allowUnknownOption = false;
  102. this._args = [];
  103. this.rawArgs = null;
  104. this._scriptPath = null;
  105. this._name = name || '';
  106. this._optionValues = {};
  107. this._storeOptionsAsProperties = true; // backwards compatible by default
  108. this._storeOptionsAsPropertiesCalled = false;
  109. this._passCommandToAction = true; // backwards compatible by default
  110. this._actionResults = [];
  111. this._actionHandler = null;
  112. this._executableHandler = false;
  113. this._executableFile = null; // custom name for executable
  114. this._defaultCommandName = null;
  115. this._exitCallback = null;
  116. this._aliases = [];
  117. this._combineFlagAndOptionalValue = true;
  118. this._hidden = false;
  119. this._hasHelpOption = true;
  120. this._helpFlags = '-h, --help';
  121. this._helpDescription = 'display help for command';
  122. this._helpShortFlag = '-h';
  123. this._helpLongFlag = '--help';
  124. this._hasImplicitHelpCommand = undefined; // Deliberately undefined, not decided whether true or false
  125. this._helpCommandName = 'help';
  126. this._helpCommandnameAndArgs = 'help [command]';
  127. this._helpCommandDescription = 'display help for command';
  128. }
  129. /**
  130. * Define a command.
  131. *
  132. * There are two styles of command: pay attention to where to put the description.
  133. *
  134. * Examples:
  135. *
  136. * // Command implemented using action handler (description is supplied separately to `.command`)
  137. * program
  138. * .command('clone <source> [destination]')
  139. * .description('clone a repository into a newly created directory')
  140. * .action((source, destination) => {
  141. * console.log('clone command called');
  142. * });
  143. *
  144. * // Command implemented using separate executable file (description is second parameter to `.command`)
  145. * program
  146. * .command('start <service>', 'start named service')
  147. * .command('stop [service]', 'stop named service, or all if no name supplied');
  148. *
  149. * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...`
  150. * @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable)
  151. * @param {Object} [execOpts] - configuration options (for executable)
  152. * @return {Command} returns new command for action handler, or `this` for executable command
  153. * @api public
  154. */
  155. command(nameAndArgs, actionOptsOrExecDesc, execOpts) {
  156. let desc = actionOptsOrExecDesc;
  157. let opts = execOpts;
  158. if (typeof desc === 'object' && desc !== null) {
  159. opts = desc;
  160. desc = null;
  161. }
  162. opts = opts || {};
  163. const args = nameAndArgs.split(/ +/);
  164. const cmd = this.createCommand(args.shift());
  165. if (desc) {
  166. cmd.description(desc);
  167. cmd._executableHandler = true;
  168. }
  169. if (opts.isDefault) this._defaultCommandName = cmd._name;
  170. cmd._hidden = !!(opts.noHelp || opts.hidden);
  171. cmd._hasHelpOption = this._hasHelpOption;
  172. cmd._helpFlags = this._helpFlags;
  173. cmd._helpDescription = this._helpDescription;
  174. cmd._helpShortFlag = this._helpShortFlag;
  175. cmd._helpLongFlag = this._helpLongFlag;
  176. cmd._helpCommandName = this._helpCommandName;
  177. cmd._helpCommandnameAndArgs = this._helpCommandnameAndArgs;
  178. cmd._helpCommandDescription = this._helpCommandDescription;
  179. cmd._exitCallback = this._exitCallback;
  180. cmd._storeOptionsAsProperties = this._storeOptionsAsProperties;
  181. cmd._passCommandToAction = this._passCommandToAction;
  182. cmd._combineFlagAndOptionalValue = this._combineFlagAndOptionalValue;
  183. cmd._executableFile = opts.executableFile || null; // Custom name for executable file, set missing to null to match constructor
  184. this.commands.push(cmd);
  185. cmd._parseExpectedArgs(args);
  186. cmd.parent = this;
  187. if (desc) return this;
  188. return cmd;
  189. };
  190. /**
  191. * Factory routine to create a new unattached command.
  192. *
  193. * See .command() for creating an attached subcommand, which uses this routine to
  194. * create the command. You can override createCommand to customise subcommands.
  195. *
  196. * @param {string} [name]
  197. * @return {Command} new command
  198. * @api public
  199. */
  200. createCommand(name) {
  201. return new Command(name);
  202. };
  203. /**
  204. * Add a prepared subcommand.
  205. *
  206. * See .command() for creating an attached subcommand which inherits settings from its parent.
  207. *
  208. * @param {Command} cmd - new subcommand
  209. * @param {Object} [opts] - configuration options
  210. * @return {Command} `this` command for chaining
  211. * @api public
  212. */
  213. addCommand(cmd, opts) {
  214. if (!cmd._name) throw new Error('Command passed to .addCommand() must have a name');
  215. // To keep things simple, block automatic name generation for deeply nested executables.
  216. // Fail fast and detect when adding rather than later when parsing.
  217. function checkExplicitNames(commandArray) {
  218. commandArray.forEach((cmd) => {
  219. if (cmd._executableHandler && !cmd._executableFile) {
  220. throw new Error(`Must specify executableFile for deeply nested executable: ${cmd.name()}`);
  221. }
  222. checkExplicitNames(cmd.commands);
  223. });
  224. }
  225. checkExplicitNames(cmd.commands);
  226. opts = opts || {};
  227. if (opts.isDefault) this._defaultCommandName = cmd._name;
  228. if (opts.noHelp || opts.hidden) cmd._hidden = true; // modifying passed command due to existing implementation
  229. this.commands.push(cmd);
  230. cmd.parent = this;
  231. return this;
  232. };
  233. /**
  234. * Define argument syntax for the command.
  235. *
  236. * @api public
  237. */
  238. arguments(desc) {
  239. return this._parseExpectedArgs(desc.split(/ +/));
  240. };
  241. /**
  242. * Override default decision whether to add implicit help command.
  243. *
  244. * addHelpCommand() // force on
  245. * addHelpCommand(false); // force off
  246. * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details
  247. *
  248. * @return {Command} `this` command for chaining
  249. * @api public
  250. */
  251. addHelpCommand(enableOrNameAndArgs, description) {
  252. if (enableOrNameAndArgs === false) {
  253. this._hasImplicitHelpCommand = false;
  254. } else {
  255. this._hasImplicitHelpCommand = true;
  256. if (typeof enableOrNameAndArgs === 'string') {
  257. this._helpCommandName = enableOrNameAndArgs.split(' ')[0];
  258. this._helpCommandnameAndArgs = enableOrNameAndArgs;
  259. }
  260. this._helpCommandDescription = description || this._helpCommandDescription;
  261. }
  262. return this;
  263. };
  264. /**
  265. * @return {boolean}
  266. * @api private
  267. */
  268. _lazyHasImplicitHelpCommand() {
  269. if (this._hasImplicitHelpCommand === undefined) {
  270. this._hasImplicitHelpCommand = this.commands.length && !this._actionHandler && !this._findCommand('help');
  271. }
  272. return this._hasImplicitHelpCommand;
  273. };
  274. /**
  275. * Parse expected `args`.
  276. *
  277. * For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
  278. *
  279. * @param {Array} args
  280. * @return {Command} `this` command for chaining
  281. * @api private
  282. */
  283. _parseExpectedArgs(args) {
  284. if (!args.length) return;
  285. args.forEach((arg) => {
  286. const argDetails = {
  287. required: false,
  288. name: '',
  289. variadic: false
  290. };
  291. switch (arg[0]) {
  292. case '<':
  293. argDetails.required = true;
  294. argDetails.name = arg.slice(1, -1);
  295. break;
  296. case '[':
  297. argDetails.name = arg.slice(1, -1);
  298. break;
  299. }
  300. if (argDetails.name.length > 3 && argDetails.name.slice(-3) === '...') {
  301. argDetails.variadic = true;
  302. argDetails.name = argDetails.name.slice(0, -3);
  303. }
  304. if (argDetails.name) {
  305. this._args.push(argDetails);
  306. }
  307. });
  308. this._args.forEach((arg, i) => {
  309. if (arg.variadic && i < this._args.length - 1) {
  310. throw new Error(`only the last argument can be variadic '${arg.name}'`);
  311. }
  312. });
  313. return this;
  314. };
  315. /**
  316. * Register callback to use as replacement for calling process.exit.
  317. *
  318. * @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing
  319. * @return {Command} `this` command for chaining
  320. * @api public
  321. */
  322. exitOverride(fn) {
  323. if (fn) {
  324. this._exitCallback = fn;
  325. } else {
  326. this._exitCallback = (err) => {
  327. if (err.code !== 'commander.executeSubCommandAsync') {
  328. throw err;
  329. } else {
  330. // Async callback from spawn events, not useful to throw.
  331. }
  332. };
  333. }
  334. return this;
  335. };
  336. /**
  337. * Call process.exit, and _exitCallback if defined.
  338. *
  339. * @param {number} exitCode exit code for using with process.exit
  340. * @param {string} code an id string representing the error
  341. * @param {string} message human-readable description of the error
  342. * @return never
  343. * @api private
  344. */
  345. _exit(exitCode, code, message) {
  346. if (this._exitCallback) {
  347. this._exitCallback(new CommanderError(exitCode, code, message));
  348. // Expecting this line is not reached.
  349. }
  350. process.exit(exitCode);
  351. };
  352. /**
  353. * Register callback `fn` for the command.
  354. *
  355. * Examples:
  356. *
  357. * program
  358. * .command('help')
  359. * .description('display verbose help')
  360. * .action(function() {
  361. * // output help here
  362. * });
  363. *
  364. * @param {Function} fn
  365. * @return {Command} `this` command for chaining
  366. * @api public
  367. */
  368. action(fn) {
  369. const listener = (args) => {
  370. // The .action callback takes an extra parameter which is the command or options.
  371. const expectedArgsCount = this._args.length;
  372. const actionArgs = args.slice(0, expectedArgsCount);
  373. if (this._passCommandToAction) {
  374. actionArgs[expectedArgsCount] = this;
  375. } else {
  376. actionArgs[expectedArgsCount] = this.opts();
  377. }
  378. // Add the extra arguments so available too.
  379. if (args.length > expectedArgsCount) {
  380. actionArgs.push(args.slice(expectedArgsCount));
  381. }
  382. const actionResult = fn.apply(this, actionArgs);
  383. // Remember result in case it is async. Assume parseAsync getting called on root.
  384. let rootCommand = this;
  385. while (rootCommand.parent) {
  386. rootCommand = rootCommand.parent;
  387. }
  388. rootCommand._actionResults.push(actionResult);
  389. };
  390. this._actionHandler = listener;
  391. return this;
  392. };
  393. /**
  394. * Internal routine to check whether there is a clash storing option value with a Command property.
  395. *
  396. * @param {Option} option
  397. * @api private
  398. */
  399. _checkForOptionNameClash(option) {
  400. if (!this._storeOptionsAsProperties || this._storeOptionsAsPropertiesCalled) {
  401. // Storing options safely, or user has been explicit and up to them.
  402. return;
  403. }
  404. // User may override help, and hard to tell if worth warning.
  405. if (option.name() === 'help') {
  406. return;
  407. }
  408. const commandProperty = this._getOptionValue(option.attributeName());
  409. if (commandProperty === undefined) {
  410. // no clash
  411. return;
  412. }
  413. let foundClash = true;
  414. if (option.negate) {
  415. // It is ok if define foo before --no-foo.
  416. const positiveLongFlag = option.long.replace(/^--no-/, '--');
  417. foundClash = !this._findOption(positiveLongFlag);
  418. } else if (option.long) {
  419. const negativeLongFlag = option.long.replace(/^--/, '--no-');
  420. foundClash = !this._findOption(negativeLongFlag);
  421. }
  422. if (foundClash) {
  423. throw new Error(`option '${option.name()}' clashes with existing property '${option.attributeName()}' on Command
  424. - call storeOptionsAsProperties(false) to store option values safely,
  425. - or call storeOptionsAsProperties(true) to suppress this check,
  426. - or change option name
  427. Read more on https://git.io/JJc0W`);
  428. }
  429. };
  430. /**
  431. * Internal implementation shared by .option() and .requiredOption()
  432. *
  433. * @param {Object} config
  434. * @param {string} flags
  435. * @param {string} description
  436. * @param {Function|*} [fn] - custom option processing function or default value
  437. * @param {*} [defaultValue]
  438. * @return {Command} `this` command for chaining
  439. * @api private
  440. */
  441. _optionEx(config, flags, description, fn, defaultValue) {
  442. const option = new Option(flags, description);
  443. const oname = option.name();
  444. const name = option.attributeName();
  445. option.mandatory = !!config.mandatory;
  446. this._checkForOptionNameClash(option);
  447. // default as 3rd arg
  448. if (typeof fn !== 'function') {
  449. if (fn instanceof RegExp) {
  450. // This is a bit simplistic (especially no error messages), and probably better handled by caller using custom option processing.
  451. // No longer documented in README, but still present for backwards compatibility.
  452. const regex = fn;
  453. fn = (val, def) => {
  454. const m = regex.exec(val);
  455. return m ? m[0] : def;
  456. };
  457. } else {
  458. defaultValue = fn;
  459. fn = null;
  460. }
  461. }
  462. // preassign default value for --no-*, [optional], <required>, or plain flag if boolean value
  463. if (option.negate || option.optional || option.required || typeof defaultValue === 'boolean') {
  464. // when --no-foo we make sure default is true, unless a --foo option is already defined
  465. if (option.negate) {
  466. const positiveLongFlag = option.long.replace(/^--no-/, '--');
  467. defaultValue = this._findOption(positiveLongFlag) ? this._getOptionValue(name) : true;
  468. }
  469. // preassign only if we have a default
  470. if (defaultValue !== undefined) {
  471. this._setOptionValue(name, defaultValue);
  472. option.defaultValue = defaultValue;
  473. }
  474. }
  475. // register the option
  476. this.options.push(option);
  477. // when it's passed assign the value
  478. // and conditionally invoke the callback
  479. this.on('option:' + oname, (val) => {
  480. const oldValue = this._getOptionValue(name);
  481. // custom processing
  482. if (val !== null && fn) {
  483. val = fn(val, oldValue === undefined ? defaultValue : oldValue);
  484. } else if (val !== null && option.variadic) {
  485. if (oldValue === defaultValue || !Array.isArray(oldValue)) {
  486. val = [val];
  487. } else {
  488. val = oldValue.concat(val);
  489. }
  490. }
  491. // unassigned or boolean value
  492. if (typeof oldValue === 'boolean' || typeof oldValue === 'undefined') {
  493. // if no value, negate false, and we have a default, then use it!
  494. if (val == null) {
  495. this._setOptionValue(name, option.negate
  496. ? false
  497. : defaultValue || true);
  498. } else {
  499. this._setOptionValue(name, val);
  500. }
  501. } else if (val !== null) {
  502. // reassign
  503. this._setOptionValue(name, option.negate ? false : val);
  504. }
  505. });
  506. return this;
  507. };
  508. /**
  509. * Define option with `flags`, `description` and optional
  510. * coercion `fn`.
  511. *
  512. * The `flags` string should contain both the short and long flags,
  513. * separated by comma, a pipe or space. The following are all valid
  514. * all will output this way when `--help` is used.
  515. *
  516. * "-p, --pepper"
  517. * "-p|--pepper"
  518. * "-p --pepper"
  519. *
  520. * Examples:
  521. *
  522. * // simple boolean defaulting to undefined
  523. * program.option('-p, --pepper', 'add pepper');
  524. *
  525. * program.pepper
  526. * // => undefined
  527. *
  528. * --pepper
  529. * program.pepper
  530. * // => true
  531. *
  532. * // simple boolean defaulting to true (unless non-negated option is also defined)
  533. * program.option('-C, --no-cheese', 'remove cheese');
  534. *
  535. * program.cheese
  536. * // => true
  537. *
  538. * --no-cheese
  539. * program.cheese
  540. * // => false
  541. *
  542. * // required argument
  543. * program.option('-C, --chdir <path>', 'change the working directory');
  544. *
  545. * --chdir /tmp
  546. * program.chdir
  547. * // => "/tmp"
  548. *
  549. * // optional argument
  550. * program.option('-c, --cheese [type]', 'add cheese [marble]');
  551. *
  552. * @param {string} flags
  553. * @param {string} description
  554. * @param {Function|*} [fn] - custom option processing function or default value
  555. * @param {*} [defaultValue]
  556. * @return {Command} `this` command for chaining
  557. * @api public
  558. */
  559. option(flags, description, fn, defaultValue) {
  560. return this._optionEx({}, flags, description, fn, defaultValue);
  561. };
  562. /**
  563. * Add a required option which must have a value after parsing. This usually means
  564. * the option must be specified on the command line. (Otherwise the same as .option().)
  565. *
  566. * The `flags` string should contain both the short and long flags, separated by comma, a pipe or space.
  567. *
  568. * @param {string} flags
  569. * @param {string} description
  570. * @param {Function|*} [fn] - custom option processing function or default value
  571. * @param {*} [defaultValue]
  572. * @return {Command} `this` command for chaining
  573. * @api public
  574. */
  575. requiredOption(flags, description, fn, defaultValue) {
  576. return this._optionEx({ mandatory: true }, flags, description, fn, defaultValue);
  577. };
  578. /**
  579. * Alter parsing of short flags with optional values.
  580. *
  581. * Examples:
  582. *
  583. * // for `.option('-f,--flag [value]'):
  584. * .combineFlagAndOptionalValue(true) // `-f80` is treated like `--flag=80`, this is the default behaviour
  585. * .combineFlagAndOptionalValue(false) // `-fb` is treated like `-f -b`
  586. *
  587. * @param {Boolean} [arg] - if `true` or omitted, an optional value can be specified directly after the flag.
  588. * @api public
  589. */
  590. combineFlagAndOptionalValue(arg) {
  591. this._combineFlagAndOptionalValue = (arg === undefined) || arg;
  592. return this;
  593. };
  594. /**
  595. * Allow unknown options on the command line.
  596. *
  597. * @param {Boolean} [arg] - if `true` or omitted, no error will be thrown
  598. * for unknown options.
  599. * @api public
  600. */
  601. allowUnknownOption(arg) {
  602. this._allowUnknownOption = (arg === undefined) || arg;
  603. return this;
  604. };
  605. /**
  606. * Whether to store option values as properties on command object,
  607. * or store separately (specify false). In both cases the option values can be accessed using .opts().
  608. *
  609. * @param {boolean} value
  610. * @return {Command} `this` command for chaining
  611. * @api public
  612. */
  613. storeOptionsAsProperties(value) {
  614. this._storeOptionsAsPropertiesCalled = true;
  615. this._storeOptionsAsProperties = (value === undefined) || value;
  616. if (this.options.length) {
  617. throw new Error('call .storeOptionsAsProperties() before adding options');
  618. }
  619. return this;
  620. };
  621. /**
  622. * Whether to pass command to action handler,
  623. * or just the options (specify false).
  624. *
  625. * @param {boolean} value
  626. * @return {Command} `this` command for chaining
  627. * @api public
  628. */
  629. passCommandToAction(value) {
  630. this._passCommandToAction = (value === undefined) || value;
  631. return this;
  632. };
  633. /**
  634. * Store option value
  635. *
  636. * @param {string} key
  637. * @param {Object} value
  638. * @api private
  639. */
  640. _setOptionValue(key, value) {
  641. if (this._storeOptionsAsProperties) {
  642. this[key] = value;
  643. } else {
  644. this._optionValues[key] = value;
  645. }
  646. };
  647. /**
  648. * Retrieve option value
  649. *
  650. * @param {string} key
  651. * @return {Object} value
  652. * @api private
  653. */
  654. _getOptionValue(key) {
  655. if (this._storeOptionsAsProperties) {
  656. return this[key];
  657. }
  658. return this._optionValues[key];
  659. };
  660. /**
  661. * Parse `argv`, setting options and invoking commands when defined.
  662. *
  663. * The default expectation is that the arguments are from node and have the application as argv[0]
  664. * and the script being run in argv[1], with user parameters after that.
  665. *
  666. * Examples:
  667. *
  668. * program.parse(process.argv);
  669. * program.parse(); // implicitly use process.argv and auto-detect node vs electron conventions
  670. * program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
  671. *
  672. * @param {string[]} [argv] - optional, defaults to process.argv
  673. * @param {Object} [parseOptions] - optionally specify style of options with from: node/user/electron
  674. * @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
  675. * @return {Command} `this` command for chaining
  676. * @api public
  677. */
  678. parse(argv, parseOptions) {
  679. if (argv !== undefined && !Array.isArray(argv)) {
  680. throw new Error('first parameter to parse must be array or undefined');
  681. }
  682. parseOptions = parseOptions || {};
  683. // Default to using process.argv
  684. if (argv === undefined) {
  685. argv = process.argv;
  686. // @ts-ignore
  687. if (process.versions && process.versions.electron) {
  688. parseOptions.from = 'electron';
  689. }
  690. }
  691. this.rawArgs = argv.slice();
  692. // make it a little easier for callers by supporting various argv conventions
  693. let userArgs;
  694. switch (parseOptions.from) {
  695. case undefined:
  696. case 'node':
  697. this._scriptPath = argv[1];
  698. userArgs = argv.slice(2);
  699. break;
  700. case 'electron':
  701. // @ts-ignore
  702. if (process.defaultApp) {
  703. this._scriptPath = argv[1];
  704. userArgs = argv.slice(2);
  705. } else {
  706. userArgs = argv.slice(1);
  707. }
  708. break;
  709. case 'user':
  710. userArgs = argv.slice(0);
  711. break;
  712. default:
  713. throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
  714. }
  715. if (!this._scriptPath && process.mainModule) {
  716. this._scriptPath = process.mainModule.filename;
  717. }
  718. // Guess name, used in usage in help.
  719. this._name = this._name || (this._scriptPath && path.basename(this._scriptPath, path.extname(this._scriptPath)));
  720. // Let's go!
  721. this._parseCommand([], userArgs);
  722. return this;
  723. };
  724. /**
  725. * Parse `argv`, setting options and invoking commands when defined.
  726. *
  727. * Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise.
  728. *
  729. * The default expectation is that the arguments are from node and have the application as argv[0]
  730. * and the script being run in argv[1], with user parameters after that.
  731. *
  732. * Examples:
  733. *
  734. * program.parseAsync(process.argv);
  735. * program.parseAsync(); // implicitly use process.argv and auto-detect node vs electron conventions
  736. * program.parseAsync(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0]
  737. *
  738. * @param {string[]} [argv]
  739. * @param {Object} [parseOptions]
  740. * @param {string} parseOptions.from - where the args are from: 'node', 'user', 'electron'
  741. * @return {Promise}
  742. * @api public
  743. */
  744. parseAsync(argv, parseOptions) {
  745. this.parse(argv, parseOptions);
  746. return Promise.all(this._actionResults).then(() => this);
  747. };
  748. /**
  749. * Execute a sub-command executable.
  750. *
  751. * @api private
  752. */
  753. _executeSubCommand(subcommand, args) {
  754. args = args.slice();
  755. let launchWithNode = false; // Use node for source targets so do not need to get permissions correct, and on Windows.
  756. const sourceExt = ['.js', '.ts', '.tsx', '.mjs'];
  757. // Not checking for help first. Unlikely to have mandatory and executable, and can't robustly test for help flags in external command.
  758. this._checkForMissingMandatoryOptions();
  759. // Want the entry script as the reference for command name and directory for searching for other files.
  760. let scriptPath = this._scriptPath;
  761. // Fallback in case not set, due to how Command created or called.
  762. if (!scriptPath && process.mainModule) {
  763. scriptPath = process.mainModule.filename;
  764. }
  765. let baseDir;
  766. try {
  767. const resolvedLink = fs.realpathSync(scriptPath);
  768. baseDir = path.dirname(resolvedLink);
  769. } catch (e) {
  770. baseDir = '.'; // dummy, probably not going to find executable!
  771. }
  772. // name of the subcommand, like `pm-install`
  773. let bin = path.basename(scriptPath, path.extname(scriptPath)) + '-' + subcommand._name;
  774. if (subcommand._executableFile) {
  775. bin = subcommand._executableFile;
  776. }
  777. const localBin = path.join(baseDir, bin);
  778. if (fs.existsSync(localBin)) {
  779. // prefer local `./<bin>` to bin in the $PATH
  780. bin = localBin;
  781. } else {
  782. // Look for source files.
  783. sourceExt.forEach((ext) => {
  784. if (fs.existsSync(`${localBin}${ext}`)) {
  785. bin = `${localBin}${ext}`;
  786. }
  787. });
  788. }
  789. launchWithNode = sourceExt.includes(path.extname(bin));
  790. let proc;
  791. if (process.platform !== 'win32') {
  792. if (launchWithNode) {
  793. args.unshift(bin);
  794. // add executable arguments to spawn
  795. args = incrementNodeInspectorPort(process.execArgv).concat(args);
  796. proc = spawn(process.argv[0], args, { stdio: 'inherit' });
  797. } else {
  798. proc = spawn(bin, args, { stdio: 'inherit' });
  799. }
  800. } else {
  801. args.unshift(bin);
  802. // add executable arguments to spawn
  803. args = incrementNodeInspectorPort(process.execArgv).concat(args);
  804. proc = spawn(process.execPath, args, { stdio: 'inherit' });
  805. }
  806. const signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
  807. signals.forEach((signal) => {
  808. // @ts-ignore
  809. process.on(signal, () => {
  810. if (proc.killed === false && proc.exitCode === null) {
  811. proc.kill(signal);
  812. }
  813. });
  814. });
  815. // By default terminate process when spawned process terminates.
  816. // Suppressing the exit if exitCallback defined is a bit messy and of limited use, but does allow process to stay running!
  817. const exitCallback = this._exitCallback;
  818. if (!exitCallback) {
  819. proc.on('close', process.exit.bind(process));
  820. } else {
  821. proc.on('close', () => {
  822. exitCallback(new CommanderError(process.exitCode || 0, 'commander.executeSubCommandAsync', '(close)'));
  823. });
  824. }
  825. proc.on('error', (err) => {
  826. // @ts-ignore
  827. if (err.code === 'ENOENT') {
  828. const executableMissing = `'${bin}' does not exist
  829. - if '${subcommand._name}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
  830. - if the default executable name is not suitable, use the executableFile option to supply a custom name`;
  831. throw new Error(executableMissing);
  832. // @ts-ignore
  833. } else if (err.code === 'EACCES') {
  834. throw new Error(`'${bin}' not executable`);
  835. }
  836. if (!exitCallback) {
  837. process.exit(1);
  838. } else {
  839. const wrappedError = new CommanderError(1, 'commander.executeSubCommandAsync', '(error)');
  840. wrappedError.nestedError = err;
  841. exitCallback(wrappedError);
  842. }
  843. });
  844. // Store the reference to the child process
  845. this.runningCommand = proc;
  846. };
  847. /**
  848. * @api private
  849. */
  850. _dispatchSubcommand(commandName, operands, unknown) {
  851. const subCommand = this._findCommand(commandName);
  852. if (!subCommand) this._helpAndError();
  853. if (subCommand._executableHandler) {
  854. this._executeSubCommand(subCommand, operands.concat(unknown));
  855. } else {
  856. subCommand._parseCommand(operands, unknown);
  857. }
  858. };
  859. /**
  860. * Process arguments in context of this command.
  861. *
  862. * @api private
  863. */
  864. _parseCommand(operands, unknown) {
  865. const parsed = this.parseOptions(unknown);
  866. operands = operands.concat(parsed.operands);
  867. unknown = parsed.unknown;
  868. this.args = operands.concat(unknown);
  869. if (operands && this._findCommand(operands[0])) {
  870. this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
  871. } else if (this._lazyHasImplicitHelpCommand() && operands[0] === this._helpCommandName) {
  872. if (operands.length === 1) {
  873. this.help();
  874. } else {
  875. this._dispatchSubcommand(operands[1], [], [this._helpLongFlag]);
  876. }
  877. } else if (this._defaultCommandName) {
  878. outputHelpIfRequested(this, unknown); // Run the help for default command from parent rather than passing to default command
  879. this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
  880. } else {
  881. if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
  882. // probably missing subcommand and no handler, user needs help
  883. this._helpAndError();
  884. }
  885. outputHelpIfRequested(this, parsed.unknown);
  886. this._checkForMissingMandatoryOptions();
  887. if (parsed.unknown.length > 0) {
  888. this.unknownOption(parsed.unknown[0]);
  889. }
  890. if (this._actionHandler) {
  891. const args = this.args.slice();
  892. this._args.forEach((arg, i) => {
  893. if (arg.required && args[i] == null) {
  894. this.missingArgument(arg.name);
  895. } else if (arg.variadic) {
  896. args[i] = args.splice(i);
  897. }
  898. });
  899. this._actionHandler(args);
  900. this.emit('command:' + this.name(), operands, unknown);
  901. } else if (operands.length) {
  902. if (this._findCommand('*')) {
  903. this._dispatchSubcommand('*', operands, unknown);
  904. } else if (this.listenerCount('command:*')) {
  905. this.emit('command:*', operands, unknown);
  906. } else if (this.commands.length) {
  907. this.unknownCommand();
  908. }
  909. } else if (this.commands.length) {
  910. // This command has subcommands and nothing hooked up at this level, so display help.
  911. this._helpAndError();
  912. } else {
  913. // fall through for caller to handle after calling .parse()
  914. }
  915. }
  916. };
  917. /**
  918. * Find matching command.
  919. *
  920. * @api private
  921. */
  922. _findCommand(name) {
  923. if (!name) return undefined;
  924. return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name));
  925. };
  926. /**
  927. * Return an option matching `arg` if any.
  928. *
  929. * @param {string} arg
  930. * @return {Option}
  931. * @api private
  932. */
  933. _findOption(arg) {
  934. return this.options.find(option => option.is(arg));
  935. };
  936. /**
  937. * Display an error message if a mandatory option does not have a value.
  938. * Lazy calling after checking for help flags from leaf subcommand.
  939. *
  940. * @api private
  941. */
  942. _checkForMissingMandatoryOptions() {
  943. // Walk up hierarchy so can call in subcommand after checking for displaying help.
  944. for (let cmd = this; cmd; cmd = cmd.parent) {
  945. cmd.options.forEach((anOption) => {
  946. if (anOption.mandatory && (cmd._getOptionValue(anOption.attributeName()) === undefined)) {
  947. cmd.missingMandatoryOptionValue(anOption);
  948. }
  949. });
  950. }
  951. };
  952. /**
  953. * Parse options from `argv` removing known options,
  954. * and return argv split into operands and unknown arguments.
  955. *
  956. * Examples:
  957. *
  958. * argv => operands, unknown
  959. * --known kkk op => [op], []
  960. * op --known kkk => [op], []
  961. * sub --unknown uuu op => [sub], [--unknown uuu op]
  962. * sub -- --unknown uuu op => [sub --unknown uuu op], []
  963. *
  964. * @param {String[]} argv
  965. * @return {{operands: String[], unknown: String[]}}
  966. * @api public
  967. */
  968. parseOptions(argv) {
  969. const operands = []; // operands, not options or values
  970. const unknown = []; // first unknown option and remaining unknown args
  971. let dest = operands;
  972. const args = argv.slice();
  973. function maybeOption(arg) {
  974. return arg.length > 1 && arg[0] === '-';
  975. }
  976. // parse options
  977. let activeVariadicOption = null;
  978. while (args.length) {
  979. const arg = args.shift();
  980. // literal
  981. if (arg === '--') {
  982. if (dest === unknown) dest.push(arg);
  983. dest.push(...args);
  984. break;
  985. }
  986. if (activeVariadicOption && !maybeOption(arg)) {
  987. this.emit(`option:${activeVariadicOption.name()}`, arg);
  988. continue;
  989. }
  990. activeVariadicOption = null;
  991. if (maybeOption(arg)) {
  992. const option = this._findOption(arg);
  993. // recognised option, call listener to assign value with possible custom processing
  994. if (option) {
  995. if (option.required) {
  996. const value = args.shift();
  997. if (value === undefined) this.optionMissingArgument(option);
  998. this.emit(`option:${option.name()}`, value);
  999. } else if (option.optional) {
  1000. let value = null;
  1001. // historical behaviour is optional value is following arg unless an option
  1002. if (args.length > 0 && !maybeOption(args[0])) {
  1003. value = args.shift();
  1004. }
  1005. this.emit(`option:${option.name()}`, value);
  1006. } else { // boolean flag
  1007. this.emit(`option:${option.name()}`);
  1008. }
  1009. activeVariadicOption = option.variadic ? option : null;
  1010. continue;
  1011. }
  1012. }
  1013. // Look for combo options following single dash, eat first one if known.
  1014. if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') {
  1015. const option = this._findOption(`-${arg[1]}`);
  1016. if (option) {
  1017. if (option.required || (option.optional && this._combineFlagAndOptionalValue)) {
  1018. // option with value following in same argument
  1019. this.emit(`option:${option.name()}`, arg.slice(2));
  1020. } else {
  1021. // boolean option, emit and put back remainder of arg for further processing
  1022. this.emit(`option:${option.name()}`);
  1023. args.unshift(`-${arg.slice(2)}`);
  1024. }
  1025. continue;
  1026. }
  1027. }
  1028. // Look for known long flag with value, like --foo=bar
  1029. if (/^--[^=]+=/.test(arg)) {
  1030. const index = arg.indexOf('=');
  1031. const option = this._findOption(arg.slice(0, index));
  1032. if (option && (option.required || option.optional)) {
  1033. this.emit(`option:${option.name()}`, arg.slice(index + 1));
  1034. continue;
  1035. }
  1036. }
  1037. // looks like an option but unknown, unknowns from here
  1038. if (arg.length > 1 && arg[0] === '-') {
  1039. dest = unknown;
  1040. }
  1041. // add arg
  1042. dest.push(arg);
  1043. }
  1044. return { operands, unknown };
  1045. };
  1046. /**
  1047. * Return an object containing options as key-value pairs
  1048. *
  1049. * @return {Object}
  1050. * @api public
  1051. */
  1052. opts() {
  1053. if (this._storeOptionsAsProperties) {
  1054. // Preserve original behaviour so backwards compatible when still using properties
  1055. const result = {};
  1056. const len = this.options.length;
  1057. for (let i = 0; i < len; i++) {
  1058. const key = this.options[i].attributeName();
  1059. result[key] = key === this._versionOptionName ? this._version : this[key];
  1060. }
  1061. return result;
  1062. }
  1063. return this._optionValues;
  1064. };
  1065. /**
  1066. * Argument `name` is missing.
  1067. *
  1068. * @param {string} name
  1069. * @api private
  1070. */
  1071. missingArgument(name) {
  1072. const message = `error: missing required argument '${name}'`;
  1073. console.error(message);
  1074. this._exit(1, 'commander.missingArgument', message);
  1075. };
  1076. /**
  1077. * `Option` is missing an argument, but received `flag` or nothing.
  1078. *
  1079. * @param {Option} option
  1080. * @param {string} [flag]
  1081. * @api private
  1082. */
  1083. optionMissingArgument(option, flag) {
  1084. let message;
  1085. if (flag) {
  1086. message = `error: option '${option.flags}' argument missing, got '${flag}'`;
  1087. } else {
  1088. message = `error: option '${option.flags}' argument missing`;
  1089. }
  1090. console.error(message);
  1091. this._exit(1, 'commander.optionMissingArgument', message);
  1092. };
  1093. /**
  1094. * `Option` does not have a value, and is a mandatory option.
  1095. *
  1096. * @param {Option} option
  1097. * @api private
  1098. */
  1099. missingMandatoryOptionValue(option) {
  1100. const message = `error: required option '${option.flags}' not specified`;
  1101. console.error(message);
  1102. this._exit(1, 'commander.missingMandatoryOptionValue', message);
  1103. };
  1104. /**
  1105. * Unknown option `flag`.
  1106. *
  1107. * @param {string} flag
  1108. * @api private
  1109. */
  1110. unknownOption(flag) {
  1111. if (this._allowUnknownOption) return;
  1112. const message = `error: unknown option '${flag}'`;
  1113. console.error(message);
  1114. this._exit(1, 'commander.unknownOption', message);
  1115. };
  1116. /**
  1117. * Unknown command.
  1118. *
  1119. * @api private
  1120. */
  1121. unknownCommand() {
  1122. const partCommands = [this.name()];
  1123. for (let parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) {
  1124. partCommands.unshift(parentCmd.name());
  1125. }
  1126. const fullCommand = partCommands.join(' ');
  1127. const message = `error: unknown command '${this.args[0]}'.` +
  1128. (this._hasHelpOption ? ` See '${fullCommand} ${this._helpLongFlag}'.` : '');
  1129. console.error(message);
  1130. this._exit(1, 'commander.unknownCommand', message);
  1131. };
  1132. /**
  1133. * Set the program version to `str`.
  1134. *
  1135. * This method auto-registers the "-V, --version" flag
  1136. * which will print the version number when passed.
  1137. *
  1138. * You can optionally supply the flags and description to override the defaults.
  1139. *
  1140. * @param {string} str
  1141. * @param {string} [flags]
  1142. * @param {string} [description]
  1143. * @return {this | string} `this` command for chaining, or version string if no arguments
  1144. * @api public
  1145. */
  1146. version(str, flags, description) {
  1147. if (str === undefined) return this._version;
  1148. this._version = str;
  1149. flags = flags || '-V, --version';
  1150. description = description || 'output the version number';
  1151. const versionOption = new Option(flags, description);
  1152. this._versionOptionName = versionOption.attributeName();
  1153. this.options.push(versionOption);
  1154. this.on('option:' + versionOption.name(), () => {
  1155. process.stdout.write(str + '\n');
  1156. this._exit(0, 'commander.version', str);
  1157. });
  1158. return this;
  1159. };
  1160. /**
  1161. * Set the description to `str`.
  1162. *
  1163. * @param {string} str
  1164. * @param {Object} [argsDescription]
  1165. * @return {string|Command}
  1166. * @api public
  1167. */
  1168. description(str, argsDescription) {
  1169. if (str === undefined && argsDescription === undefined) return this._description;
  1170. this._description = str;
  1171. this._argsDescription = argsDescription;
  1172. return this;
  1173. };
  1174. /**
  1175. * Set an alias for the command.
  1176. *
  1177. * You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help.
  1178. *
  1179. * @param {string} [alias]
  1180. * @return {string|Command}
  1181. * @api public
  1182. */
  1183. alias(alias) {
  1184. if (alias === undefined) return this._aliases[0]; // just return first, for backwards compatibility
  1185. let command = this;
  1186. if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
  1187. // assume adding alias for last added executable subcommand, rather than this
  1188. command = this.commands[this.commands.length - 1];
  1189. }
  1190. if (alias === command._name) throw new Error('Command alias can\'t be the same as its name');
  1191. command._aliases.push(alias);
  1192. return this;
  1193. };
  1194. /**
  1195. * Set aliases for the command.
  1196. *
  1197. * Only the first alias is shown in the auto-generated help.
  1198. *
  1199. * @param {string[]} [aliases]
  1200. * @return {string[]|Command}
  1201. * @api public
  1202. */
  1203. aliases(aliases) {
  1204. // Getter for the array of aliases is the main reason for having aliases() in addition to alias().
  1205. if (aliases === undefined) return this._aliases;
  1206. aliases.forEach((alias) => this.alias(alias));
  1207. return this;
  1208. };
  1209. /**
  1210. * Set / get the command usage `str`.
  1211. *
  1212. * @param {string} [str]
  1213. * @return {String|Command}
  1214. * @api public
  1215. */
  1216. usage(str) {
  1217. if (str === undefined) {
  1218. if (this._usage) return this._usage;
  1219. const args = this._args.map((arg) => {
  1220. return humanReadableArgName(arg);
  1221. });
  1222. return [].concat(
  1223. (this.options.length || this._hasHelpOption ? '[options]' : []),
  1224. (this.commands.length ? '[command]' : []),
  1225. (this._args.length ? args : [])
  1226. ).join(' ');
  1227. }
  1228. this._usage = str;
  1229. return this;
  1230. };
  1231. /**
  1232. * Get or set the name of the command
  1233. *
  1234. * @param {string} [str]
  1235. * @return {String|Command}
  1236. * @api public
  1237. */
  1238. name(str) {
  1239. if (str === undefined) return this._name;
  1240. this._name = str;
  1241. return this;
  1242. };
  1243. /**
  1244. * Return prepared commands.
  1245. *
  1246. * @return {Array}
  1247. * @api private
  1248. */
  1249. prepareCommands() {
  1250. const commandDetails = this.commands.filter((cmd) => {
  1251. return !cmd._hidden;
  1252. }).map((cmd) => {
  1253. const args = cmd._args.map((arg) => {
  1254. return humanReadableArgName(arg);
  1255. }).join(' ');
  1256. return [
  1257. cmd._name +
  1258. (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +
  1259. (cmd.options.length ? ' [options]' : '') +
  1260. (args ? ' ' + args : ''),
  1261. cmd._description
  1262. ];
  1263. });
  1264. if (this._lazyHasImplicitHelpCommand()) {
  1265. commandDetails.push([this._helpCommandnameAndArgs, this._helpCommandDescription]);
  1266. }
  1267. return commandDetails;
  1268. };
  1269. /**
  1270. * Return the largest command length.
  1271. *
  1272. * @return {number}
  1273. * @api private
  1274. */
  1275. largestCommandLength() {
  1276. const commands = this.prepareCommands();
  1277. return commands.reduce((max, command) => {
  1278. return Math.max(max, command[0].length);
  1279. }, 0);
  1280. };
  1281. /**
  1282. * Return the largest option length.
  1283. *
  1284. * @return {number}
  1285. * @api private
  1286. */
  1287. largestOptionLength() {
  1288. const options = [].slice.call(this.options);
  1289. options.push({
  1290. flags: this._helpFlags
  1291. });
  1292. return options.reduce((max, option) => {
  1293. return Math.max(max, option.flags.length);
  1294. }, 0);
  1295. };
  1296. /**
  1297. * Return the largest arg length.
  1298. *
  1299. * @return {number}
  1300. * @api private
  1301. */
  1302. largestArgLength() {
  1303. return this._args.reduce((max, arg) => {
  1304. return Math.max(max, arg.name.length);
  1305. }, 0);
  1306. };
  1307. /**
  1308. * Return the pad width.
  1309. *
  1310. * @return {number}
  1311. * @api private
  1312. */
  1313. padWidth() {
  1314. let width = this.largestOptionLength();
  1315. if (this._argsDescription && this._args.length) {
  1316. if (this.largestArgLength() > width) {
  1317. width = this.largestArgLength();
  1318. }
  1319. }
  1320. if (this.commands && this.commands.length) {
  1321. if (this.largestCommandLength() > width) {
  1322. width = this.largestCommandLength();
  1323. }
  1324. }
  1325. return width;
  1326. };
  1327. /**
  1328. * Return help for options.
  1329. *
  1330. * @return {string}
  1331. * @api private
  1332. */
  1333. optionHelp() {
  1334. const width = this.padWidth();
  1335. const columns = process.stdout.columns || 80;
  1336. const descriptionWidth = columns - width - 4;
  1337. function padOptionDetails(flags, description) {
  1338. return pad(flags, width) + ' ' + optionalWrap(description, descriptionWidth, width + 2);
  1339. };
  1340. // Explicit options (including version)
  1341. const help = this.options.map((option) => {
  1342. const fullDesc = option.description +
  1343. ((!option.negate && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : '');
  1344. return padOptionDetails(option.flags, fullDesc);
  1345. });
  1346. // Implicit help
  1347. const showShortHelpFlag = this._hasHelpOption && this._helpShortFlag && !this._findOption(this._helpShortFlag);
  1348. const showLongHelpFlag = this._hasHelpOption && !this._findOption(this._helpLongFlag);
  1349. if (showShortHelpFlag || showLongHelpFlag) {
  1350. let helpFlags = this._helpFlags;
  1351. if (!showShortHelpFlag) {
  1352. helpFlags = this._helpLongFlag;
  1353. } else if (!showLongHelpFlag) {
  1354. helpFlags = this._helpShortFlag;
  1355. }
  1356. help.push(padOptionDetails(helpFlags, this._helpDescription));
  1357. }
  1358. return help.join('\n');
  1359. };
  1360. /**
  1361. * Return command help documentation.
  1362. *
  1363. * @return {string}
  1364. * @api private
  1365. */
  1366. commandHelp() {
  1367. if (!this.commands.length && !this._lazyHasImplicitHelpCommand()) return '';
  1368. const commands = this.prepareCommands();
  1369. const width = this.padWidth();
  1370. const columns = process.stdout.columns || 80;
  1371. const descriptionWidth = columns - width - 4;
  1372. return [
  1373. 'Commands:',
  1374. commands.map((cmd) => {
  1375. const desc = cmd[1] ? ' ' + cmd[1] : '';
  1376. return (desc ? pad(cmd[0], width) : cmd[0]) + optionalWrap(desc, descriptionWidth, width + 2);
  1377. }).join('\n').replace(/^/gm, ' '),
  1378. ''
  1379. ].join('\n');
  1380. };
  1381. /**
  1382. * Return program help documentation.
  1383. *
  1384. * @return {string}
  1385. * @api public
  1386. */
  1387. helpInformation() {
  1388. let desc = [];
  1389. if (this._description) {
  1390. desc = [
  1391. this._description,
  1392. ''
  1393. ];
  1394. const argsDescription = this._argsDescription;
  1395. if (argsDescription && this._args.length) {
  1396. const width = this.padWidth();
  1397. const columns = process.stdout.columns || 80;
  1398. const descriptionWidth = columns - width - 5;
  1399. desc.push('Arguments:');
  1400. this._args.forEach((arg) => {
  1401. desc.push(' ' + pad(arg.name, width) + ' ' + wrap(argsDescription[arg.name] || '', descriptionWidth, width + 4));
  1402. });
  1403. desc.push('');
  1404. }
  1405. }
  1406. let cmdName = this._name;
  1407. if (this._aliases[0]) {
  1408. cmdName = cmdName + '|' + this._aliases[0];
  1409. }
  1410. let parentCmdNames = '';
  1411. for (let parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) {
  1412. parentCmdNames = parentCmd.name() + ' ' + parentCmdNames;
  1413. }
  1414. const usage = [
  1415. 'Usage: ' + parentCmdNames + cmdName + ' ' + this.usage(),
  1416. ''
  1417. ];
  1418. let cmds = [];
  1419. const commandHelp = this.commandHelp();
  1420. if (commandHelp) cmds = [commandHelp];
  1421. let options = [];
  1422. if (this._hasHelpOption || this.options.length > 0) {
  1423. options = [
  1424. 'Options:',
  1425. '' + this.optionHelp().replace(/^/gm, ' '),
  1426. ''
  1427. ];
  1428. }
  1429. return usage
  1430. .concat(desc)
  1431. .concat(options)
  1432. .concat(cmds)
  1433. .join('\n');
  1434. };
  1435. /**
  1436. * Output help information for this command.
  1437. *
  1438. * When listener(s) are available for the helpLongFlag
  1439. * those callbacks are invoked.
  1440. *
  1441. * @api public
  1442. */
  1443. outputHelp(cb) {
  1444. if (!cb) {
  1445. cb = (passthru) => {
  1446. return passthru;
  1447. };
  1448. }
  1449. const cbOutput = cb(this.helpInformation());
  1450. if (typeof cbOutput !== 'string' && !Buffer.isBuffer(cbOutput)) {
  1451. throw new Error('outputHelp callback must return a string or a Buffer');
  1452. }
  1453. process.stdout.write(cbOutput);
  1454. this.emit(this._helpLongFlag);
  1455. };
  1456. /**
  1457. * You can pass in flags and a description to override the help
  1458. * flags and help description for your command. Pass in false to
  1459. * disable the built-in help option.
  1460. *
  1461. * @param {string | boolean} [flags]
  1462. * @param {string} [description]
  1463. * @return {Command} `this` command for chaining
  1464. * @api public
  1465. */
  1466. helpOption(flags, description) {
  1467. if (typeof flags === 'boolean') {
  1468. this._hasHelpOption = flags;
  1469. return this;
  1470. }
  1471. this._helpFlags = flags || this._helpFlags;
  1472. this._helpDescription = description || this._helpDescription;
  1473. const helpFlags = _parseOptionFlags(this._helpFlags);
  1474. this._helpShortFlag = helpFlags.shortFlag;
  1475. this._helpLongFlag = helpFlags.longFlag;
  1476. return this;
  1477. };
  1478. /**
  1479. * Output help information and exit.
  1480. *
  1481. * @param {Function} [cb]
  1482. * @api public
  1483. */
  1484. help(cb) {
  1485. this.outputHelp(cb);
  1486. // exitCode: preserving original behaviour which was calling process.exit()
  1487. // message: do not have all displayed text available so only passing placeholder.
  1488. this._exit(process.exitCode || 0, 'commander.help', '(outputHelp)');
  1489. };
  1490. /**
  1491. * Output help information and exit. Display for error situations.
  1492. *
  1493. * @api private
  1494. */
  1495. _helpAndError() {
  1496. this.outputHelp();
  1497. // message: do not have all displayed text available so only passing placeholder.
  1498. this._exit(1, 'commander.help', '(outputHelp)');
  1499. };
  1500. };
  1501. /**
  1502. * Expose the root command.
  1503. */
  1504. exports = module.exports = new Command();
  1505. exports.program = exports; // More explicit access to global command.
  1506. /**
  1507. * Expose classes
  1508. */
  1509. exports.Command = Command;
  1510. exports.Option = Option;
  1511. exports.CommanderError = CommanderError;
  1512. /**
  1513. * Camel-case the given `flag`
  1514. *
  1515. * @param {string} flag
  1516. * @return {string}
  1517. * @api private
  1518. */
  1519. function camelcase(flag) {
  1520. return flag.split('-').reduce((str, word) => {
  1521. return str + word[0].toUpperCase() + word.slice(1);
  1522. });
  1523. }
  1524. /**
  1525. * Pad `str` to `width`.
  1526. *
  1527. * @param {string} str
  1528. * @param {number} width
  1529. * @return {string}
  1530. * @api private
  1531. */
  1532. function pad(str, width) {
  1533. const len = Math.max(0, width - str.length);
  1534. return str + Array(len + 1).join(' ');
  1535. }
  1536. /**
  1537. * Wraps the given string with line breaks at the specified width while breaking
  1538. * words and indenting every but the first line on the left.
  1539. *
  1540. * @param {string} str
  1541. * @param {number} width
  1542. * @param {number} indent
  1543. * @return {string}
  1544. * @api private
  1545. */
  1546. function wrap(str, width, indent) {
  1547. const regex = new RegExp('.{1,' + (width - 1) + '}([\\s\u200B]|$)|[^\\s\u200B]+?([\\s\u200B]|$)', 'g');
  1548. const lines = str.match(regex) || [];
  1549. return lines.map((line, i) => {
  1550. if (line.slice(-1) === '\n') {
  1551. line = line.slice(0, line.length - 1);
  1552. }
  1553. return ((i > 0 && indent) ? Array(indent + 1).join(' ') : '') + line.trimRight();
  1554. }).join('\n');
  1555. }
  1556. /**
  1557. * Optionally wrap the given str to a max width of width characters per line
  1558. * while indenting with indent spaces. Do not wrap if insufficient width or
  1559. * string is manually formatted.
  1560. *
  1561. * @param {string} str
  1562. * @param {number} width
  1563. * @param {number} indent
  1564. * @return {string}
  1565. * @api private
  1566. */
  1567. function optionalWrap(str, width, indent) {
  1568. // Detect manually wrapped and indented strings by searching for line breaks
  1569. // followed by multiple spaces/tabs.
  1570. if (str.match(/[\n]\s+/)) return str;
  1571. // Do not wrap to narrow columns (or can end up with a word per line).
  1572. const minWidth = 40;
  1573. if (width < minWidth) return str;
  1574. return wrap(str, width, indent);
  1575. }
  1576. /**
  1577. * Output help information if help flags specified
  1578. *
  1579. * @param {Command} cmd - command to output help for
  1580. * @param {Array} args - array of options to search for help flags
  1581. * @api private
  1582. */
  1583. function outputHelpIfRequested(cmd, args) {
  1584. const helpOption = cmd._hasHelpOption && args.find(arg => arg === cmd._helpLongFlag || arg === cmd._helpShortFlag);
  1585. if (helpOption) {
  1586. cmd.outputHelp();
  1587. // (Do not have all displayed text available so only passing placeholder.)
  1588. cmd._exit(0, 'commander.helpDisplayed', '(outputHelp)');
  1589. }
  1590. }
  1591. /**
  1592. * Takes an argument and returns its human readable equivalent for help usage.
  1593. *
  1594. * @param {Object} arg
  1595. * @return {string}
  1596. * @api private
  1597. */
  1598. function humanReadableArgName(arg) {
  1599. const nameOutput = arg.name + (arg.variadic === true ? '...' : '');
  1600. return arg.required
  1601. ? '<' + nameOutput + '>'
  1602. : '[' + nameOutput + ']';
  1603. }
  1604. /**
  1605. * Parse the short and long flag out of something like '-m,--mixed <value>'
  1606. *
  1607. * @api private
  1608. */
  1609. function _parseOptionFlags(flags) {
  1610. let shortFlag;
  1611. let longFlag;
  1612. // Use original very loose parsing to maintain backwards compatibility for now,
  1613. // which allowed for example unintended `-sw, --short-word` [sic].
  1614. const flagParts = flags.split(/[ |,]+/);
  1615. if (flagParts.length > 1 && !/^[[<]/.test(flagParts[1])) shortFlag = flagParts.shift();
  1616. longFlag = flagParts.shift();
  1617. // Add support for lone short flag without significantly changing parsing!
  1618. if (!shortFlag && /^-[^-]$/.test(longFlag)) {
  1619. shortFlag = longFlag;
  1620. longFlag = undefined;
  1621. }
  1622. return { shortFlag, longFlag };
  1623. }
  1624. /**
  1625. * Scan arguments and increment port number for inspect calls (to avoid conflicts when spawning new command).
  1626. *
  1627. * @param {string[]} args - array of arguments from node.execArgv
  1628. * @returns {string[]}
  1629. * @api private
  1630. */
  1631. function incrementNodeInspectorPort(args) {
  1632. // Testing for these options:
  1633. // --inspect[=[host:]port]
  1634. // --inspect-brk[=[host:]port]
  1635. // --inspect-port=[host:]port
  1636. return args.map((arg) => {
  1637. if (!arg.startsWith('--inspect')) {
  1638. return arg;
  1639. }
  1640. let debugOption;
  1641. let debugHost = '127.0.0.1';
  1642. let debugPort = '9229';
  1643. let match;
  1644. if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
  1645. // e.g. --inspect
  1646. debugOption = match[1];
  1647. } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
  1648. debugOption = match[1];
  1649. if (/^\d+$/.test(match[3])) {
  1650. // e.g. --inspect=1234
  1651. debugPort = match[3];
  1652. } else {
  1653. // e.g. --inspect=localhost
  1654. debugHost = match[3];
  1655. }
  1656. } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
  1657. // e.g. --inspect=localhost:1234
  1658. debugOption = match[1];
  1659. debugHost = match[3];
  1660. debugPort = match[4];
  1661. }
  1662. if (debugOption && debugPort !== '0') {
  1663. return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
  1664. }
  1665. return arg;
  1666. });
  1667. }