diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2024-03-27 15:44:30 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2024-03-27 15:44:30 +0800 |
| commit | 1c0387549700d9ce64918fb5536e37350757c7dd (patch) | |
| tree | 8f27183c1f6f698afdc49017ed19821360280b51 /node_modules/commander/index.js | |
| parent | bd543050c40bb988689fc20304bba0d8b4398a80 (diff) | |
updates
Diffstat (limited to 'node_modules/commander/index.js')
| -rw-r--r-- | node_modules/commander/index.js | 847 |
1 files changed, 211 insertions, 636 deletions
diff --git a/node_modules/commander/index.js b/node_modules/commander/index.js index 37d20cc5..ec1d61d5 100644 --- a/node_modules/commander/index.js +++ b/node_modules/commander/index.js @@ -43,10 +43,9 @@ exports.Option = Option; function Option(flags, description) { this.flags = flags; - this.required = flags.indexOf('<') >= 0; // A value must be supplied when the option is specified. - this.optional = flags.indexOf('[') >= 0; // A value is optional when the option is specified. - this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line. - this.negate = flags.indexOf('-no-') !== -1; + this.required = flags.indexOf('<') >= 0; + this.optional = flags.indexOf('[') >= 0; + this.bool = flags.indexOf('-no-') === -1; flags = flags.split(/[ ,|]+/); if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift(); this.long = flags.shift(); @@ -61,7 +60,9 @@ function Option(flags, description) { */ Option.prototype.name = function() { - return this.long.replace(/^--/, ''); + return this.long + .replace('--', '') + .replace('no-', ''); }; /** @@ -73,7 +74,7 @@ Option.prototype.name = function() { */ Option.prototype.attributeName = function() { - return camelcase(this.name().replace(/^no-/, '')); + return camelcase(this.name()); }; /** @@ -89,108 +90,98 @@ Option.prototype.is = function(arg) { }; /** - * CommanderError class - * @class - */ -class CommanderError extends Error { - /** - * Constructs the CommanderError class - * @param {Number} exitCode suggested exit code which could be used with process.exit - * @param {String} code an id string representing the error - * @param {String} message human-readable description of the error - * @constructor - */ - constructor(exitCode, code, message) { - super(message); - // properly capture stack trace in Node.js - Error.captureStackTrace(this, this.constructor); - this.name = this.constructor.name; - this.code = code; - this.exitCode = exitCode; - } -} - -exports.CommanderError = CommanderError; - -/** * Initialize a new `Command`. * - * @param {String} [name] + * @param {String} name * @api public */ function Command(name) { this.commands = []; this.options = []; - this._execs = new Set(); + this._execs = {}; this._allowUnknownOption = false; this._args = []; this._name = name || ''; - this._optionValues = {}; - this._storeOptionsAsProperties = true; // backwards compatible by default - this._passCommandToAction = true; // backwards compatible by default - this._actionResults = []; - - this._helpFlags = '-h, --help'; - this._helpDescription = 'output usage information'; - this._helpShortFlag = '-h'; - this._helpLongFlag = '--help'; } /** - * Define a command. + * Add command `name`. + * + * The `.action()` callback is invoked when the + * command `name` is specified via __ARGV__, + * and the remaining arguments are applied to the + * function for access. * - * There are two styles of command: pay attention to where to put the description. + * When the `name` is "*" an un-matched command + * will be passed as the first arg, followed by + * the rest of __ARGV__ remaining. * * Examples: * - * // Command implemented using action handler (description is supplied separately to `.command`) * program - * .command('clone <source> [destination]') - * .description('clone a repository into a newly created directory') - * .action((source, destination) => { - * console.log('clone command called'); + * .version('0.0.1') + * .option('-C, --chdir <path>', 'change the working directory') + * .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf') + * .option('-T, --no-tests', 'ignore test hook') + * + * program + * .command('setup') + * .description('run remote setup commands') + * .action(function() { + * console.log('setup'); * }); * - * // Command implemented using separate executable file (description is second parameter to `.command`) * program - * .command('start <service>', 'start named service') - * .command('stop [service]', 'stop named service, or all if no name supplied'); + * .command('exec <cmd>') + * .description('run the given remote command') + * .action(function(cmd) { + * console.log('exec "%s"', cmd); + * }); + * + * program + * .command('teardown <dir> [otherDirs...]') + * .description('run teardown commands') + * .action(function(dir, otherDirs) { + * console.log('dir "%s"', dir); + * if (otherDirs) { + * otherDirs.forEach(function (oDir) { + * console.log('dir "%s"', oDir); + * }); + * } + * }); * - * @param {string} nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` - * @param {Object|string} [actionOptsOrExecDesc] - configuration options (for action), or description (for executable) - * @param {Object} [execOpts] - configuration options (for executable) - * @return {Command} returns new command for action handler, or top-level command for executable command + * program + * .command('*') + * .description('deploy the given env') + * .action(function(env) { + * console.log('deploying "%s"', env); + * }); + * + * program.parse(process.argv); + * + * @param {String} name + * @param {String} [desc] for git-style sub-commands + * @return {Command} the new command * @api public */ -Command.prototype.command = function(nameAndArgs, actionOptsOrExecDesc, execOpts) { - var desc = actionOptsOrExecDesc; - var opts = execOpts; +Command.prototype.command = function(name, desc, opts) { if (typeof desc === 'object' && desc !== null) { opts = desc; desc = null; } opts = opts || {}; - var args = nameAndArgs.split(/ +/); + var args = name.split(/ +/); var cmd = new Command(args.shift()); if (desc) { cmd.description(desc); this.executables = true; - this._execs.add(cmd._name); + this._execs[cmd._name] = true; if (opts.isDefault) this.defaultExecutable = cmd._name; } cmd._noHelp = !!opts.noHelp; - cmd._helpFlags = this._helpFlags; - cmd._helpDescription = this._helpDescription; - cmd._helpShortFlag = this._helpShortFlag; - cmd._helpLongFlag = this._helpLongFlag; - cmd._exitCallback = this._exitCallback; - cmd._storeOptionsAsProperties = this._storeOptionsAsProperties; - cmd._passCommandToAction = this._passCommandToAction; - - cmd._executableFile = opts.executableFile; // Custom name for executable file this.commands.push(cmd); cmd.parseExpectedArgs(args); cmd.parent = this; @@ -262,47 +253,6 @@ Command.prototype.parseExpectedArgs = function(args) { }; /** - * Register callback to use as replacement for calling process.exit. - * - * @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing - * @return {Command} for chaining - * @api public - */ - -Command.prototype.exitOverride = function(fn) { - if (fn) { - this._exitCallback = fn; - } else { - this._exitCallback = function(err) { - if (err.code !== 'commander.executeSubCommandAsync') { - throw err; - } else { - // Async callback from spawn events, not useful to throw. - } - }; - } - return this; -}; - -/** - * Call process.exit, and _exitCallback if defined. - * - * @param {Number} exitCode exit code for using with process.exit - * @param {String} code an id string representing the error - * @param {String} message human-readable description of the error - * @return never - * @api private - */ - -Command.prototype._exit = function(exitCode, code, message) { - if (this._exitCallback) { - this._exitCallback(new CommanderError(exitCode, code, message)); - // Expecting this line is not reached. - } - process.exit(exitCode); -}; - -/** * Register callback `fn` for the command. * * Examples: @@ -329,8 +279,7 @@ Command.prototype.action = function(fn) { var parsed = self.parseOptions(unknown); // Output help if necessary - outputHelpIfRequested(self, parsed.unknown); - self._checkForMissingMandatoryOptions(); + outputHelpIfNecessary(self, parsed.unknown); // If there are still any unknown options, then we simply // die, unless someone asked for help, in which case we give it @@ -354,26 +303,16 @@ Command.prototype.action = function(fn) { } }); - // The .action callback takes an extra parameter which is the command itself. - var expectedArgsCount = self._args.length; - var actionArgs = args.slice(0, expectedArgsCount); - if (self._passCommandToAction) { - actionArgs[expectedArgsCount] = self; + // Always append ourselves to the end of the arguments, + // to make sure we match the number of arguments the user + // expects + if (self._args.length) { + args[self._args.length] = self; } else { - actionArgs[expectedArgsCount] = self.opts(); - } - // Add the extra arguments so available too. - if (args.length > expectedArgsCount) { - actionArgs.push(args.slice(expectedArgsCount)); + args.push(self); } - const actionResult = fn.apply(self, actionArgs); - // Remember result in case it is async. Assume parseAsync getting called on root. - let rootCommand = self; - while (rootCommand.parent) { - rootCommand = rootCommand.parent; - } - rootCommand._actionResults.push(actionResult); + fn.apply(self, args); }; var parent = this.parent || this; var name = parent === this ? '*' : this._name; @@ -383,29 +322,63 @@ Command.prototype.action = function(fn) { }; /** - * Internal implementation shared by .option() and .requiredOption() + * Define option with `flags`, `description` and optional + * coercion `fn`. + * + * The `flags` string should contain both the short and long flags, + * separated by comma, a pipe or space. The following are all valid + * all will output this way when `--help` is used. + * + * "-p, --pepper" + * "-p|--pepper" + * "-p --pepper" + * + * Examples: + * + * // simple boolean defaulting to false + * program.option('-p, --pepper', 'add pepper'); + * + * --pepper + * program.pepper + * // => Boolean + * + * // simple boolean defaulting to true + * program.option('-C, --no-cheese', 'remove cheese'); + * + * program.cheese + * // => true + * + * --no-cheese + * program.cheese + * // => false + * + * // required argument + * program.option('-C, --chdir <path>', 'change the working directory'); + * + * --chdir /tmp + * program.chdir + * // => "/tmp" + * + * // optional argument + * program.option('-c, --cheese [type]', 'add cheese [marble]'); * - * @param {Object} config * @param {String} flags * @param {String} description - * @param {Function|*} [fn] - custom option processing function or default vaue + * @param {Function|*} [fn] or default * @param {*} [defaultValue] * @return {Command} for chaining - * @api private + * @api public */ -Command.prototype._optionEx = function(config, flags, description, fn, defaultValue) { +Command.prototype.option = function(flags, description, fn, defaultValue) { var self = this, option = new Option(flags, description), oname = option.name(), name = option.attributeName(); - option.mandatory = !!config.mandatory; // default as 3rd arg if (typeof fn !== 'function') { if (fn instanceof RegExp) { - // This is a bit simplistic (especially no error messages), and probably better handled by caller using custom option processing. - // No longer documented in README, but still present for backwards compatibility. var regex = fn; fn = function(val, def) { var m = regex.exec(val); @@ -417,16 +390,13 @@ Command.prototype._optionEx = function(config, flags, description, fn, defaultVa } } - // preassign default value for --no-*, [optional], <required>, or plain flag if boolean value - if (option.negate || option.optional || option.required || typeof defaultValue === 'boolean') { - // when --no-foo we make sure default is true, unless a --foo option is already defined - if (option.negate) { - const positiveLongFlag = option.long.replace(/^--no-/, '--'); - defaultValue = self.optionFor(positiveLongFlag) ? self._getOptionValue(name) : true; - } + // preassign default value only for --no-*, [optional], or <required> + if (!option.bool || option.optional || option.required) { + // when --no-* we make sure default is true + if (!option.bool) defaultValue = true; // preassign only if we have a default if (defaultValue !== undefined) { - self._setOptionValue(name, defaultValue); + self[name] = defaultValue; option.defaultValue = defaultValue; } } @@ -439,22 +409,22 @@ Command.prototype._optionEx = function(config, flags, description, fn, defaultVa this.on('option:' + oname, function(val) { // coercion if (val !== null && fn) { - val = fn(val, self._getOptionValue(name) === undefined ? defaultValue : self._getOptionValue(name)); + val = fn(val, self[name] === undefined ? defaultValue : self[name]); } - // unassigned or boolean value - if (typeof self._getOptionValue(name) === 'boolean' || typeof self._getOptionValue(name) === 'undefined') { - // if no value, negate false, and we have a default, then use it! + // unassigned or bool + if (typeof self[name] === 'boolean' || typeof self[name] === 'undefined') { + // if no value, bool true, and we have a default, then use it! if (val == null) { - self._setOptionValue(name, option.negate - ? false - : defaultValue || true); + self[name] = option.bool + ? defaultValue || true + : false; } else { - self._setOptionValue(name, val); + self[name] = val; } } else if (val !== null) { // reassign - self._setOptionValue(name, option.negate ? false : val); + self[name] = val; } }); @@ -462,80 +432,6 @@ Command.prototype._optionEx = function(config, flags, description, fn, defaultVa }; /** - * Define option with `flags`, `description` and optional - * coercion `fn`. - * - * The `flags` string should contain both the short and long flags, - * separated by comma, a pipe or space. The following are all valid - * all will output this way when `--help` is used. - * - * "-p, --pepper" - * "-p|--pepper" - * "-p --pepper" - * - * Examples: - * - * // simple boolean defaulting to undefined - * program.option('-p, --pepper', 'add pepper'); - * - * program.pepper - * // => undefined - * - * --pepper - * program.pepper - * // => true - * - * // simple boolean defaulting to true (unless non-negated option is also defined) - * program.option('-C, --no-cheese', 'remove cheese'); - * - * program.cheese - * // => true - * - * --no-cheese - * program.cheese - * // => false - * - * // required argument - * program.option('-C, --chdir <path>', 'change the working directory'); - * - * --chdir /tmp - * program.chdir - * // => "/tmp" - * - * // optional argument - * program.option('-c, --cheese [type]', 'add cheese [marble]'); - * - * @param {String} flags - * @param {String} description - * @param {Function|*} [fn] - custom option processing function or default vaue - * @param {*} [defaultValue] - * @return {Command} for chaining - * @api public - */ - -Command.prototype.option = function(flags, description, fn, defaultValue) { - return this._optionEx({}, flags, description, fn, defaultValue); -}; - -/* - * Add a required option which must have a value after parsing. This usually means - * the option must be specified on the command line. (Otherwise the same as .option().) - * - * The `flags` string should contain both the short and long flags, separated by comma, a pipe or space. - * - * @param {String} flags - * @param {String} description - * @param {Function|*} [fn] - custom option processing function or default vaue - * @param {*} [defaultValue] - * @return {Command} for chaining - * @api public - */ - -Command.prototype.requiredOption = function(flags, description, fn, defaultValue) { - return this._optionEx({ mandatory: true }, flags, description, fn, defaultValue); -}; - -/** * Allow unknown options on the command line. * * @param {Boolean} arg if `true` or omitted, no error will be thrown @@ -548,70 +444,7 @@ Command.prototype.allowUnknownOption = function(arg) { }; /** - * Whether to store option values as properties on command object, - * or store separately (specify false). In both cases the option values can be accessed using .opts(). - * - * @param {boolean} value - * @return {Command} Command for chaining - * @api public - */ - -Command.prototype.storeOptionsAsProperties = function(value) { - this._storeOptionsAsProperties = (value === undefined) || value; - if (this.options.length) { - // This is for programmer, not end user. - console.error('Commander usage error: call storeOptionsAsProperties before adding options'); - } - return this; -}; - -/** - * Whether to pass command to action handler, - * or just the options (specify false). - * - * @param {boolean} value - * @return {Command} Command for chaining - * @api public - */ - -Command.prototype.passCommandToAction = function(value) { - this._passCommandToAction = (value === undefined) || value; - return this; -}; - -/** - * Store option value - * - * @param {String} key - * @param {Object} value - * @api private - */ - -Command.prototype._setOptionValue = function(key, value) { - if (this._storeOptionsAsProperties) { - this[key] = value; - } else { - this._optionValues[key] = value; - } -}; - -/** - * Retrieve option value - * - * @param {String} key - * @return {Object} value - * @api private - */ - -Command.prototype._getOptionValue = function(key) { - if (this._storeOptionsAsProperties) { - return this[key]; - } - return this._optionValues[key]; -}; - -/** - * Parse `argv`, setting options and invoking commands when defined. + * Parse `argv`, settings options and invoking commands when defined. * * @param {Array} argv * @return {Command} for chaining @@ -631,117 +464,72 @@ Command.prototype.parse = function(argv) { // github-style sub-commands with no sub-command if (this.executables && argv.length < 3 && !this.defaultExecutable) { // this user needs help - argv.push(this._helpLongFlag); + argv.push('--help'); } // process argv - var normalized = this.normalize(argv.slice(2)); - var parsed = this.parseOptions(normalized); + var parsed = this.parseOptions(this.normalize(argv.slice(2))); var args = this.args = parsed.args; var result = this.parseArgs(this.args, parsed.unknown); - if (args[0] === 'help' && args.length === 1) this.help(); - - // Note for future: we could return early if we found an action handler in parseArgs, as none of following code needed? - - // <cmd> --help - if (args[0] === 'help') { - args[0] = args[1]; - args[1] = this._helpLongFlag; - } else { - // If calling through to executable subcommand we could check for help flags before failing, - // but a somewhat unlikely case since program options not passed to executable subcommands. - // Wait for reports to see if check needed and what usage pattern is. - this._checkForMissingMandatoryOptions(); - } - // executable sub-commands - // (Debugging note for future: args[0] is not right if an action has been called) var name = result.args[0]; - var subCommand = null; - // Look for subcommand + var aliasCommand = null; + // check alias of sub commands if (name) { - subCommand = this.commands.find(function(command) { - return command._name === name; - }); - } - - // Look for alias - if (!subCommand && name) { - subCommand = this.commands.find(function(command) { + aliasCommand = this.commands.filter(function(command) { return command.alias() === name; - }); - if (subCommand) { - name = subCommand._name; - args[0] = name; - } - } - - // Look for default subcommand - if (!subCommand && this.defaultExecutable) { - name = this.defaultExecutable; - args.unshift(name); - subCommand = this.commands.find(function(command) { - return command._name === name; - }); + })[0]; } - if (this._execs.has(name)) { - return this.executeSubCommand(argv, args, parsed.unknown, subCommand ? subCommand._executableFile : undefined); + if (this._execs[name] === true) { + return this.executeSubCommand(argv, args, parsed.unknown); + } else if (aliasCommand) { + // is alias of a subCommand + args[0] = aliasCommand._name; + return this.executeSubCommand(argv, args, parsed.unknown); + } else if (this.defaultExecutable) { + // use the default subcommand + args.unshift(this.defaultExecutable); + return this.executeSubCommand(argv, args, parsed.unknown); } return result; }; /** - * Parse `argv`, setting options and invoking commands when defined. - * - * Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise. - * - * @param {Array} argv - * @return {Promise} - * @api public - */ -Command.prototype.parseAsync = function(argv) { - this.parse(argv); - return Promise.all(this._actionResults); -}; - -/** * Execute a sub-command executable. * * @param {Array} argv * @param {Array} args * @param {Array} unknown - * @param {String} executableFile * @api private */ -Command.prototype.executeSubCommand = function(argv, args, unknown, executableFile) { +Command.prototype.executeSubCommand = function(argv, args, unknown) { args = args.concat(unknown); if (!args.length) this.help(); + if (args[0] === 'help' && args.length === 1) this.help(); - var isExplicitJS = false; // Whether to use node to launch "executable" + // <cmd> --help + if (args[0] === 'help') { + args[0] = args[1]; + args[1] = '--help'; + } // executable - var pm = argv[1]; - // name of the subcommand, like `pm-install` - var bin = basename(pm, path.extname(pm)) + '-' + args[0]; - if (executableFile != null) { - bin = executableFile; - // Check for same extensions as we scan for below so get consistent launch behaviour. - var executableExt = path.extname(executableFile); - isExplicitJS = executableExt === '.js' || executableExt === '.ts' || executableExt === '.mjs'; - } + var f = argv[1]; + // name of the subcommand, link `pm-install` + var bin = basename(f, path.extname(f)) + '-' + args[0]; // In case of globally installed, get the base dir where executable // subcommand file should be located at var baseDir; - var resolvedLink = fs.realpathSync(pm); + var resolvedLink = fs.realpathSync(f); baseDir = dirname(resolvedLink); @@ -749,15 +537,13 @@ Command.prototype.executeSubCommand = function(argv, args, unknown, executableFi var localBin = path.join(baseDir, bin); // whether bin file is a js script with explicit `.js` or `.ts` extension + var isExplicitJS = false; if (exists(localBin + '.js')) { bin = localBin + '.js'; isExplicitJS = true; } else if (exists(localBin + '.ts')) { bin = localBin + '.ts'; isExplicitJS = true; - } else if (exists(localBin + '.mjs')) { - bin = localBin + '.mjs'; - isExplicitJS = true; } else if (exists(localBin)) { bin = localBin; } @@ -769,16 +555,14 @@ Command.prototype.executeSubCommand = function(argv, args, unknown, executableFi if (isExplicitJS) { args.unshift(bin); // add executable arguments to spawn - args = incrementNodeInspectorPort(process.execArgv).concat(args); + args = (process.execArgv || []).concat(args); - proc = spawn(process.argv[0], args, { stdio: 'inherit' }); + proc = spawn(process.argv[0], args, { stdio: 'inherit', customFds: [0, 1, 2] }); } else { - proc = spawn(bin, args, { stdio: 'inherit' }); + proc = spawn(bin, args, { stdio: 'inherit', customFds: [0, 1, 2] }); } } else { args.unshift(bin); - // add executable arguments to spawn - args = incrementNodeInspectorPort(process.execArgv).concat(args); proc = spawn(process.execPath, args, { stdio: 'inherit' }); } @@ -790,30 +574,14 @@ Command.prototype.executeSubCommand = function(argv, args, unknown, executableFi } }); }); - - // By default terminate process when spawned process terminates. - // Suppressing the exit if exitCallback defined is a bit messy and of limited use, but does allow process to stay running! - const exitCallback = this._exitCallback; - if (!exitCallback) { - proc.on('close', process.exit.bind(process)); - } else { - proc.on('close', () => { - exitCallback(new CommanderError(process.exitCode || 0, 'commander.executeSubCommandAsync', '(close)')); - }); - } + proc.on('close', process.exit.bind(process)); proc.on('error', function(err) { if (err.code === 'ENOENT') { console.error('error: %s(1) does not exist, try --help', bin); } else if (err.code === 'EACCES') { console.error('error: %s(1) not executable. try chmod or run with root', bin); } - if (!exitCallback) { - process.exit(1); - } else { - const wrappedError = new CommanderError(1, 'commander.executeSubCommandAsync', '(error)'); - wrappedError.nestedError = err; - exitCallback(wrappedError); - } + process.exit(1); }); // Store the reference to the child process @@ -834,9 +602,7 @@ Command.prototype.normalize = function(args) { var ret = [], arg, lastOpt, - index, - short, - opt; + index; for (var i = 0, len = args.length; i < len; ++i) { arg = args[i]; @@ -850,17 +616,10 @@ Command.prototype.normalize = function(args) { break; } else if (lastOpt && lastOpt.required) { ret.push(arg); - } else if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') { - short = arg.slice(0, 2); - opt = this.optionFor(short); - if (opt && (opt.required || opt.optional)) { - ret.push(short); - ret.push(arg.slice(2)); - } else { - arg.slice(1).split('').forEach(function(c) { - ret.push('-' + c); - }); - } + } else if (arg.length > 1 && arg[0] === '-' && arg[1] !== '-') { + arg.slice(1).split('').forEach(function(c) { + ret.push('-' + c); + }); } else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) { ret.push(arg.slice(0, index), arg.slice(index + 1)); } else { @@ -891,14 +650,14 @@ Command.prototype.parseArgs = function(args, unknown) { if (this.listeners('command:' + name).length) { this.emit('command:' + args.shift(), args, unknown); } else { - this.emit('command:*', args, unknown); + this.emit('command:*', args); } } else { - outputHelpIfRequested(this, unknown); + outputHelpIfNecessary(this, unknown); // If there were no args and we have unknown options, // then they are extraneous and we need to error. - if (unknown.length > 0 && !this.defaultExecutable) { + if (unknown.length > 0) { this.unknownOption(unknown[0]); } if (this.commands.length === 0 && @@ -927,28 +686,11 @@ Command.prototype.optionFor = function(arg) { }; /** - * Display an error message if a mandatory option does not have a value. - * - * @api private - */ - -Command.prototype._checkForMissingMandatoryOptions = function() { - // Walk up hierarchy so can call from action handler after checking for displaying help. - for (var cmd = this; cmd; cmd = cmd.parent) { - cmd.options.forEach((anOption) => { - if (anOption.mandatory && (cmd._getOptionValue(anOption.attributeName()) === undefined)) { - cmd.missingMandatoryOptionValue(anOption); - } - }); - } -}; - -/** * Parse options from `argv` returning `argv` * void of these options. * * @param {Array} argv - * @return {{args: Array, unknown: Array}} + * @return {Array} * @api public */ @@ -995,7 +737,7 @@ Command.prototype.parseOptions = function(argv) { ++i; } this.emit('option:' + option.name(), arg); - // flag + // bool } else { this.emit('option:' + option.name()); } @@ -1009,7 +751,7 @@ Command.prototype.parseOptions = function(argv) { // If the next argument looks like it might be // an argument for this option, we pass it on. // If it isn't, then it'll simply be ignored - if ((i + 1) < argv.length && (argv[i + 1][0] !== '-' || argv[i + 1] === '-')) { + if ((i + 1) < argv.length && argv[i + 1][0] !== '-') { unknownOptions.push(argv[++i]); } continue; @@ -1029,19 +771,14 @@ Command.prototype.parseOptions = function(argv) { * @api public */ Command.prototype.opts = function() { - if (this._storeOptionsAsProperties) { - // Preserve original behaviour so backwards compatible when still using properties - var result = {}, - len = this.options.length; - - for (var i = 0; i < len; i++) { - var key = this.options[i].attributeName(); - result[key] = key === this._versionOptionName ? this._version : this[key]; - } - return result; - } + var result = {}, + len = this.options.length; - return this._optionValues; + for (var i = 0; i < len; i++) { + var key = this.options[i].attributeName(); + result[key] = key === this._versionOptionName ? this._version : this[key]; + } + return result; }; /** @@ -1052,41 +789,25 @@ Command.prototype.opts = function() { */ Command.prototype.missingArgument = function(name) { - const message = `error: missing required argument '${name}'`; - console.error(message); - this._exit(1, 'commander.missingArgument', message); + console.error("error: missing required argument `%s'", name); + process.exit(1); }; /** * `Option` is missing an argument, but received `flag` or nothing. * - * @param {Option} option - * @param {String} [flag] + * @param {String} option + * @param {String} flag * @api private */ Command.prototype.optionMissingArgument = function(option, flag) { - let message; if (flag) { - message = `error: option '${option.flags}' argument missing, got '${flag}'`; + console.error("error: option `%s' argument missing, got `%s'", option.flags, flag); } else { - message = `error: option '${option.flags}' argument missing`; + console.error("error: option `%s' argument missing", option.flags); } - console.error(message); - this._exit(1, 'commander.optionMissingArgument', message); -}; - -/** - * `Option` does not have a value, and is a mandatory option. - * - * @param {Option} option - * @api private - */ - -Command.prototype.missingMandatoryOptionValue = function(option) { - const message = `error: required option '${option.flags}' not specified`; - console.error(message); - this._exit(1, 'commander.missingMandatoryOptionValue', message); + process.exit(1); }; /** @@ -1098,9 +819,8 @@ Command.prototype.missingMandatoryOptionValue = function(option) { Command.prototype.unknownOption = function(flag) { if (this._allowUnknownOption) return; - const message = `error: unknown option '${flag}'`; - console.error(message); - this._exit(1, 'commander.unknownOption', message); + console.error("error: unknown option `%s'", flag); + process.exit(1); }; /** @@ -1111,9 +831,8 @@ Command.prototype.unknownOption = function(flag) { */ Command.prototype.variadicArgNotLast = function(name) { - const message = `error: variadic arguments must be last '${name}'`; - console.error(message); - this._exit(1, 'commander.variadicArgNotLast', message); + console.error("error: variadic arguments must be last `%s'", name); + process.exit(1); }; /** @@ -1122,27 +841,22 @@ Command.prototype.variadicArgNotLast = function(name) { * This method auto-registers the "-V, --version" flag * which will print the version number when passed. * - * You can optionally supply the flags and description to override the defaults. - * * @param {String} str * @param {String} [flags] - * @param {String} [description] * @return {Command} for chaining * @api public */ -Command.prototype.version = function(str, flags, description) { +Command.prototype.version = function(str, flags) { if (arguments.length === 0) return this._version; this._version = str; flags = flags || '-V, --version'; - description = description || 'output the version number'; - var versionOption = new Option(flags, description); + var versionOption = new Option(flags, 'output the version number'); this._versionOptionName = versionOption.long.substr(2) || 'version'; this.options.push(versionOption); - var self = this; this.on('option:' + this._versionOptionName, function() { process.stdout.write(str + '\n'); - self._exit(0, 'commander.version', str); + process.exit(0); }); return this; }; @@ -1151,7 +865,7 @@ Command.prototype.version = function(str, flags, description) { * Set the description to `str`. * * @param {String} str - * @param {Object} [argsDescription] + * @param {Object} argsDescription * @return {String|Command} * @api public */ @@ -1188,7 +902,7 @@ Command.prototype.alias = function(alias) { /** * Set / get the command usage `str`. * - * @param {String} [str] + * @param {String} str * @return {String|Command} * @api public */ @@ -1211,7 +925,7 @@ Command.prototype.usage = function(str) { /** * Get or set the name of the command * - * @param {String} [str] + * @param {String} str * @return {String|Command} * @api public */ @@ -1271,9 +985,8 @@ Command.prototype.largestCommandLength = function() { Command.prototype.largestOptionLength = function() { var options = [].slice.call(this.options); options.push({ - flags: this._helpFlags + flags: '-h, --help' }); - return options.reduce(function(max, option) { return Math.max(max, option.flags.length); }, 0); @@ -1326,15 +1039,11 @@ Command.prototype.padWidth = function() { Command.prototype.optionHelp = function() { var width = this.padWidth(); - var columns = process.stdout.columns || 80; - var descriptionWidth = columns - width - 4; - // Append the help information return this.options.map(function(option) { - const fullDesc = option.description + - ((!option.negate && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : ''); - return pad(option.flags, width) + ' ' + optionalWrap(fullDesc, descriptionWidth, width + 2); - }).concat([pad(this._helpFlags, width) + ' ' + optionalWrap(this._helpDescription, descriptionWidth, width + 2)]) + return pad(option.flags, width) + ' ' + option.description + + ((option.bool && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : ''); + }).concat([pad('-h, --help', width) + ' ' + 'output usage information']) .join('\n'); }; @@ -1351,14 +1060,11 @@ Command.prototype.commandHelp = function() { var commands = this.prepareCommands(); var width = this.padWidth(); - var columns = process.stdout.columns || 80; - var descriptionWidth = columns - width - 4; - return [ 'Commands:', commands.map(function(cmd) { var desc = cmd[1] ? ' ' + cmd[1] : ''; - return (desc ? pad(cmd[0], width) : cmd[0]) + optionalWrap(desc, descriptionWidth, width + 2); + return (desc ? pad(cmd[0], width) : cmd[0]) + desc; }).join('\n').replace(/^/gm, ' '), '' ].join('\n'); @@ -1382,12 +1088,10 @@ Command.prototype.helpInformation = function() { var argsDescription = this._argsDescription; if (argsDescription && this._args.length) { var width = this.padWidth(); - var columns = process.stdout.columns || 80; - var descriptionWidth = columns - width - 5; desc.push('Arguments:'); desc.push(''); this._args.forEach(function(arg) { - desc.push(' ' + pad(arg.name, width) + ' ' + wrap(argsDescription[arg.name], descriptionWidth, width + 4)); + desc.push(' ' + pad(arg.name, width) + ' ' + argsDescription[arg.name]); }); desc.push(''); } @@ -1397,12 +1101,8 @@ Command.prototype.helpInformation = function() { if (this._alias) { cmdName = cmdName + '|' + this._alias; } - var parentCmdNames = ''; - for (var parentCmd = this.parent; parentCmd; parentCmd = parentCmd.parent) { - parentCmdNames = parentCmd.name() + ' ' + parentCmdNames; - } var usage = [ - 'Usage: ' + parentCmdNames + cmdName + ' ' + this.usage(), + 'Usage: ' + cmdName + ' ' + this.usage(), '' ]; @@ -1424,10 +1124,7 @@ Command.prototype.helpInformation = function() { }; /** - * Output help information for this command. - * - * When listener(s) are available for the helpLongFlag - * those callbacks are invoked. + * Output help information for this command * * @api public */ @@ -1438,49 +1135,19 @@ Command.prototype.outputHelp = function(cb) { return passthru; }; } - const cbOutput = cb(this.helpInformation()); - if (typeof cbOutput !== 'string' && !Buffer.isBuffer(cbOutput)) { - throw new Error('outputHelp callback must return a string or a Buffer'); - } - process.stdout.write(cbOutput); - this.emit(this._helpLongFlag); -}; - -/** - * You can pass in flags and a description to override the help - * flags and help description for your command. - * - * @param {String} [flags] - * @param {String} [description] - * @return {Command} - * @api public - */ - -Command.prototype.helpOption = function(flags, description) { - this._helpFlags = flags || this._helpFlags; - this._helpDescription = description || this._helpDescription; - - var splitFlags = this._helpFlags.split(/[ ,|]+/); - - if (splitFlags.length > 1) this._helpShortFlag = splitFlags.shift(); - - this._helpLongFlag = splitFlags.shift(); - - return this; + process.stdout.write(cb(this.helpInformation())); + this.emit('--help'); }; /** * Output help information and exit. * - * @param {Function} [cb] * @api public */ Command.prototype.help = function(cb) { this.outputHelp(cb); - // exitCode: preserving original behaviour which was calling process.exit() - // message: do not have all displayed text available so only passing placeholder. - this._exit(process.exitCode || 0, 'commander.help', '(outputHelp)'); + process.exit(); }; /** @@ -1512,70 +1179,25 @@ function pad(str, width) { } /** - * Wraps the given string with line breaks at the specified width while breaking - * words and indenting every but the first line on the left. + * Output help information if necessary * - * @param {String} str - * @param {Number} width - * @param {Number} indent - * @return {String} + * @param {Command} command to output help for + * @param {Array} array of options to search for -h or --help * @api private */ -function wrap(str, width, indent) { - var regex = new RegExp('.{1,' + (width - 1) + '}([\\s\u200B]|$)|[^\\s\u200B]+?([\\s\u200B]|$)', 'g'); - var lines = str.match(regex) || []; - return lines.map(function(line, i) { - if (line.slice(-1) === '\n') { - line = line.slice(0, line.length - 1); - } - return ((i > 0 && indent) ? Array(indent + 1).join(' ') : '') + line.trimRight(); - }).join('\n'); -} -/** - * Optionally wrap the given str to a max width of width characters per line - * while indenting with indent spaces. Do not wrap if insufficient width or - * string is manually formatted. - * - * @param {String} str - * @param {Number} width - * @param {Number} indent - * @return {String} - * @api private - */ -function optionalWrap(str, width, indent) { - // Detect manually wrapped and indented strings by searching for line breaks - // followed by multiple spaces/tabs. - if (str.match(/[\n]\s+/)) return str; - // Do not wrap to narrow columns (or can end up with a word per line). - const minWidth = 40; - if (width < minWidth) return str; - - return wrap(str, width, indent); -} - -/** - * Output help information if help flags specified - * - * @param {Command} cmd - command to output help for - * @param {Array} options - array of options to search for -h or --help - * @api private - */ - -function outputHelpIfRequested(cmd, options) { +function outputHelpIfNecessary(cmd, options) { options = options || []; - for (var i = 0; i < options.length; i++) { - if (options[i] === cmd._helpLongFlag || options[i] === cmd._helpShortFlag) { + if (options[i] === '--help' || options[i] === '-h') { cmd.outputHelp(); - // (Do not have all displayed text available so only passing placeholder.) - cmd._exit(0, 'commander.helpDisplayed', '(outputHelp)'); + process.exit(0); } } } /** - * Takes an argument and returns its human readable equivalent for help usage. + * Takes an argument an returns its human readable equivalent for help usage. * * @param {Object} arg * @return {String} @@ -1600,50 +1222,3 @@ function exists(file) { return false; } } - -/** - * Scan arguments and increment port number for inspect calls (to avoid conflicts when spawning new command). - * - * @param {string[]} args - array of arguments from node.execArgv - * @returns {string[]} - * @api private - */ - -function incrementNodeInspectorPort(args) { - // Testing for these options: - // --inspect[=[host:]port] - // --inspect-brk[=[host:]port] - // --inspect-port=[host:]port - return args.map((arg) => { - var result = arg; - if (arg.indexOf('--inspect') === 0) { - var debugOption; - var debugHost = '127.0.0.1'; - var debugPort = '9229'; - var match; - if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) { - // e.g. --inspect - debugOption = match[1]; - } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) { - debugOption = match[1]; - if (/^\d+$/.test(match[3])) { - // e.g. --inspect=1234 - debugPort = match[3]; - } else { - // e.g. --inspect=localhost - debugHost = match[3]; - } - } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) { - // e.g. --inspect=localhost:1234 - debugOption = match[1]; - debugHost = match[3]; - debugPort = match[4]; - } - - if (debugOption && debugPort !== '0') { - result = `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`; - } - } - return result; - }); -} |
