diff --git a/.nycrc b/.nycrc
index 5243a3489..d4b247808 100644
--- a/.nycrc
+++ b/.nycrc
@@ -3,6 +3,7 @@
"build/test/**",
"test/**"
],
+ "exclude-after-remap": true,
"reporter": [
"html",
"text"
@@ -10,4 +11,4 @@
"lines": 100,
"branches": "96",
"statements": "100"
-}
\ No newline at end of file
+}
diff --git a/browser.mjs b/browser.mjs
index d8a9f3de6..2d0d6e9e5 100644
--- a/browser.mjs
+++ b/browser.mjs
@@ -1,7 +1,7 @@
// Bootstrap yargs for browser:
import browserPlatformShim from './lib/platform-shims/browser.mjs';
-import {YargsWithShim} from './build/lib/yargs-factory.js';
+import {YargsFactory} from './build/lib/yargs-factory.js';
-const Yargs = YargsWithShim(browserPlatformShim);
+const Yargs = YargsFactory(browserPlatformShim);
export default Yargs;
diff --git a/deno.ts b/deno.ts
index 62613e5c9..2002f2093 100644
--- a/deno.ts
+++ b/deno.ts
@@ -1,8 +1,8 @@
// Bootstrap yargs for Deno platform:
import denoPlatformShim from './lib/platform-shims/deno.ts';
-import {YargsWithShim} from './build/lib/yargs-factory.js';
+import {YargsFactory} from './build/lib/yargs-factory.js';
-const WrappedYargs = YargsWithShim(denoPlatformShim);
+const WrappedYargs = YargsFactory(denoPlatformShim);
function Yargs(args?: string[]) {
return WrappedYargs(args);
diff --git a/docs/api.md b/docs/api.md
index 9bad6fcac..9bcf3e2cb 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1439,43 +1439,6 @@ usage information and exit.
The default behavior is to set the value of any key not followed by an
option value to `true`.
-.reset() [DEPRECATED]
---------
-
-Reset the argument object built up so far. This is useful for
-creating nested command line interfaces. Use [global](#global)
-to specify keys that should not be reset.
-
-```js
-var yargs = require('yargs/yargs')(process.argv.slice(2))
- .usage('$0 command')
- .command('hello', 'hello command')
- .command('world', 'world command')
- .demandCommand(1, 'must provide a valid command'),
- argv = yargs.argv,
- command = argv._[0];
-
-if (command === 'hello') {
- yargs.reset()
- .usage('$0 hello')
- .help('h')
- .example('$0 hello', 'print the hello message!')
- .argv
-
- console.log('hello!');
-} else if (command === 'world'){
- yargs.reset()
- .usage('$0 world')
- .help('h')
- .example('$0 world', 'print the world message!')
- .argv
-
- console.log('world!');
-} else {
- yargs.showHelp();
-}
-```
-
.scriptName($0)
------------------
diff --git a/index.cjs b/index.cjs
index 7ac4d3588..d1a169885 100644
--- a/index.cjs
+++ b/index.cjs
@@ -11,6 +11,7 @@ module.exports = Argv;
function Argv(processArgs, cwd) {
const argv = Yargs(processArgs, cwd, require);
singletonify(argv);
+ // TODO(bcoe): warn if argv.parse() or argv.argv is used directly.
return argv;
}
@@ -22,7 +23,10 @@ function Argv(processArgs, cwd) {
to get a parsed version of process.argv.
*/
function singletonify(inst) {
- Object.keys(inst).forEach(key => {
+ [
+ ...Object.keys(inst),
+ ...Object.getOwnPropertyNames(inst.constructor.prototype),
+ ].forEach(key => {
if (key === 'argv') {
Argv.__defineGetter__(key, inst.__lookupGetter__(key));
} else if (typeof inst[key] === 'function') {
diff --git a/index.mjs b/index.mjs
index 23d908013..c6440b9ed 100644
--- a/index.mjs
+++ b/index.mjs
@@ -2,7 +2,7 @@
// Bootstraps yargs for ESM:
import esmPlatformShim from './lib/platform-shims/esm.mjs';
-import {YargsWithShim} from './build/lib/yargs-factory.js';
+import {YargsFactory} from './build/lib/yargs-factory.js';
-const Yargs = YargsWithShim(esmPlatformShim);
+const Yargs = YargsFactory(esmPlatformShim);
export default Yargs;
diff --git a/lib/cjs.ts b/lib/cjs.ts
index 64b57f8ba..82dae13e6 100644
--- a/lib/cjs.ts
+++ b/lib/cjs.ts
@@ -7,7 +7,7 @@ import {isPromise} from './utils/is-promise.js';
import {objFilter} from './utils/obj-filter.js';
import {parseCommand} from './parse-command.js';
import * as processArgv from './utils/process-argv.js';
-import {YargsWithShim, rebase} from './yargs-factory.js';
+import {YargsFactory} from './yargs-factory.js';
import {YError} from './yerror.js';
import cjsPlatformShim from './platform-shims/cjs.js';
@@ -27,7 +27,7 @@ if (process && process.version) {
}
const Parser = require('yargs-parser');
-const Yargs = YargsWithShim(cjsPlatformShim);
+const Yargs = YargsFactory(cjsPlatformShim);
export default {
applyExtends,
@@ -39,6 +39,5 @@ export default {
parseCommand,
Parser,
processArgv,
- rebase,
YError,
};
diff --git a/lib/command.ts b/lib/command.ts
index 6dde6c6bc..727d32a44 100644
--- a/lib/command.ts
+++ b/lib/command.ts
@@ -207,7 +207,7 @@ export class CommandInstance {
this.handlers[command!] ||
this.handlers[this.aliasMap[command!]] ||
this.defaultCommand;
- const currentContext = yargs.getContext();
+ const currentContext = yargs.getInternalMethods().getContext();
const parentCommands = currentContext.commands.slice();
if (command) {
currentContext.commands.push(command);
@@ -264,7 +264,10 @@ export class CommandInstance {
if (isCommandBuilderCallback(builder)) {
// A function can be provided, which builds
// up a yargs chain and possibly returns it.
- const builderOutput = builder(yargs.reset(aliases), helpOrVersionSet);
+ const builderOutput = builder(
+ yargs.getInternalMethods().reset(aliases),
+ helpOrVersionSet
+ );
// Support the use-case of async builders:
if (isPromise(builderOutput)) {
return builderOutput.then(output => {
@@ -282,7 +285,7 @@ export class CommandInstance {
} else if (isCommandBuilderOptionDefinitions(builder)) {
// as a short hand, an object can instead be provided, specifying
// the options that a command takes.
- innerYargs = yargs.reset(aliases);
+ innerYargs = yargs.getInternalMethods().reset(aliases);
Object.keys(commandHandler.builder).forEach(key => {
innerYargs.option(key, builder[key]);
});
@@ -307,9 +310,10 @@ export class CommandInstance {
// A null command indicates we are running the default command,
// if this is the case, we should show the root usage instructions
// rather than the usage instructions for the nested default command:
- if (!command) innerYargs.getUsageInstance().unfreeze();
+ if (!command) innerYargs.getInternalMethods().getUsageInstance().unfreeze();
if (this.shouldUpdateUsage(innerYargs)) {
innerYargs
+ .getInternalMethods()
.getUsageInstance()
.usage(
this.usageFromParentCommandsCommandHandler(
@@ -319,13 +323,15 @@ export class CommandInstance {
commandHandler.description
);
}
- const innerArgv = innerYargs._parseArgs(
- null,
- undefined,
- true,
- commandIndex,
- helpOnly
- );
+ const innerArgv = innerYargs
+ .getInternalMethods()
+ .runYargsParserAndExecuteCommands(
+ null,
+ undefined,
+ true,
+ commandIndex,
+ helpOnly
+ );
return {
aliases: (innerYargs.parsed as DetailedArguments).aliases,
innerArgv: innerArgv as Arguments,
@@ -333,8 +339,8 @@ export class CommandInstance {
}
private shouldUpdateUsage(yargs: YargsInstance) {
return (
- !yargs.getUsageInstance().getUsageDisabled() &&
- yargs.getUsageInstance().getUsage().length === 0
+ !yargs.getInternalMethods().getUsageInstance().getUsageDisabled() &&
+ yargs.getInternalMethods().getUsageInstance().getUsage().length === 0
);
}
private usageFromParentCommandsCommandHandler(
@@ -364,7 +370,7 @@ export class CommandInstance {
// execute middleware or handlers (these may perform expensive operations
// like creating a DB connection).
if (helpOnly) return innerArgv;
- if (!yargs._hasOutput()) {
+ if (!yargs.getInternalMethods().getHasOutput()) {
positionalMap = this.populatePositionals(
commandHandler,
innerArgv as Arguments,
@@ -380,27 +386,31 @@ export class CommandInstance {
// we apply validation post-hoc, so that custom
// checks get passed populated positional arguments.
- if (!yargs._hasOutput()) {
- const validation = yargs._runValidation(
- aliases,
- positionalMap,
- (yargs.parsed as DetailedArguments).error,
- !command
- );
+ if (!yargs.getInternalMethods().getHasOutput()) {
+ const validation = yargs
+ .getInternalMethods()
+ .runValidation(
+ aliases,
+ positionalMap,
+ (yargs.parsed as DetailedArguments).error,
+ !command
+ );
innerArgv = maybeAsyncResult(innerArgv, result => {
validation(result);
return result;
});
}
- if (commandHandler.handler && !yargs._hasOutput()) {
- yargs._setHasOutput();
+ if (commandHandler.handler && !yargs.getInternalMethods().getHasOutput()) {
+ yargs.getInternalMethods().setHasOutput();
// to simplify the parsing of positionals in commands,
// we temporarily populate '--' rather than _, with arguments
const populateDoubleDash = !!yargs.getOptions().configuration[
'populate--'
];
- yargs._postProcess(innerArgv, populateDoubleDash, false, false);
+ yargs
+ .getInternalMethods()
+ .postProcess(innerArgv, populateDoubleDash, false, false);
innerArgv = applyMiddleware(innerArgv, yargs, middlewares, false);
innerArgv = maybeAsyncResult(innerArgv, result => {
@@ -412,11 +422,14 @@ export class CommandInstance {
}
});
- yargs.getUsageInstance().cacheHelpMessage();
- if (isPromise(innerArgv) && !yargs._hasParseCallback()) {
+ yargs.getInternalMethods().getUsageInstance().cacheHelpMessage();
+ if (
+ isPromise(innerArgv) &&
+ !yargs.getInternalMethods().hasParseCallback()
+ ) {
innerArgv.catch(error => {
try {
- yargs.getUsageInstance().fail(null, error);
+ yargs.getInternalMethods().getUsageInstance().fail(null, error);
} catch (_err) {
// If .fail(false) is not set, and no parse cb() has been
// registered, run usage's default fail method.
@@ -560,7 +573,10 @@ export class CommandInstance {
);
if (parsed.error) {
- yargs.getUsageInstance().fail(parsed.error.message, parsed.error);
+ yargs
+ .getInternalMethods()
+ .getUsageInstance()
+ .fail(parsed.error.message, parsed.error);
} else {
// only copy over positional keys (don't overwrite
// flag arguments that were already parsed).
@@ -587,6 +603,7 @@ export class CommandInstance {
? this.defaultCommand.original
: this.defaultCommand.original.replace(/^[^[\]<>]*/, '$0 ');
yargs
+ .getInternalMethods()
.getUsageInstance()
.usage(commandString, this.defaultCommand.description);
}
diff --git a/lib/completion.ts b/lib/completion.ts
index 1f9cbeff6..15463af1e 100644
--- a/lib/completion.ts
+++ b/lib/completion.ts
@@ -57,7 +57,7 @@ export class Completion implements CompletionInstance {
if (handlers[args[i]] && handlers[args[i]].builder) {
const builder = handlers[args[i]].builder;
if (isCommandBuilderCallback(builder)) {
- const y = this.yargs.reset();
+ const y = this.yargs.getInternalMethods().reset();
builder(y, true);
return y.argv;
}
@@ -77,7 +77,8 @@ export class Completion implements CompletionInstance {
args: string[],
current: string
) {
- const parentCommands = this.yargs.getContext().commands;
+ const parentCommands = this.yargs.getInternalMethods().getContext()
+ .commands;
if (
!current.match(/^-/) &&
parentCommands[parentCommands.length - 1] !== current
diff --git a/lib/usage.ts b/lib/usage.ts
index 5b4989674..7650eec99 100644
--- a/lib/usage.ts
+++ b/lib/usage.ts
@@ -1,11 +1,6 @@
// this file handles outputting usage instructions,
// failures, etc. keeps logging in one place.
-import {
- Dictionary,
- assertNotStrictEqual,
- PlatformShim,
- Y18N,
-} from './typings/common-types.js';
+import {Dictionary, PlatformShim} from './typings/common-types.js';
import {objFilter} from './utils/obj-filter.js';
import {YargsInstance} from './yargs-factory.js';
import {YError} from './yerror.js';
@@ -16,8 +11,8 @@ function isBoolean(fail: FailureFunction | boolean): fail is boolean {
return typeof fail === 'boolean';
}
-export function usage(yargs: YargsInstance, y18n: Y18N, shim: PlatformShim) {
- const __ = y18n.__;
+export function usage(yargs: YargsInstance, shim: PlatformShim) {
+ const __ = shim.y18n.__;
const self = {} as UsageInstance;
// methods for ouputting/building failure message.
@@ -42,7 +37,7 @@ export function usage(yargs: YargsInstance, y18n: Y18N, shim: PlatformShim) {
let failureOutput = false;
self.fail = function fail(msg, err) {
- const logger = yargs._getLoggerInstance();
+ const logger = yargs.getInternalMethods().getLoggerInstance();
if (fails.length) {
for (let i = fails.length - 1; i >= 0; --i) {
@@ -74,7 +69,7 @@ export function usage(yargs: YargsInstance, y18n: Y18N, shim: PlatformShim) {
err = err || new YError(msg);
if (yargs.getExitProcess()) {
return yargs.exit(1);
- } else if (yargs._hasParseCallback()) {
+ } else if (yargs.getInternalMethods().hasParseCallback()) {
return yargs.exit(1, err);
} else {
throw err;
@@ -239,12 +234,15 @@ export function usage(yargs: YargsInstance, y18n: Y18N, shim: PlatformShim) {
if (commands.length > 1 || (commands.length === 1 && !commands[0][2])) {
ui.div(__('Commands:'));
- const context = yargs.getContext();
+ const context = yargs.getInternalMethods().getContext();
const parentCommands = context.commands.length
? `${context.commands.join(' ')} `
: '';
- if (yargs.getParserConfiguration()['sort-commands'] === true) {
+ if (
+ yargs.getInternalMethods().getParserConfiguration()['sort-commands'] ===
+ true
+ ) {
commands = commands.sort((a, b) => a[0].localeCompare(b[0]));
}
@@ -603,7 +601,7 @@ export function usage(yargs: YargsInstance, y18n: Y18N, shim: PlatformShim) {
}
self.showHelp = (level: 'error' | 'log' | ((message: string) => void)) => {
- const logger = yargs._getLoggerInstance();
+ const logger = yargs.getInternalMethods().getLoggerInstance();
if (!level) level = 'error';
const emit = typeof level === 'function' ? level : logger[level];
emit(self.help());
@@ -675,7 +673,7 @@ export function usage(yargs: YargsInstance, y18n: Y18N, shim: PlatformShim) {
};
self.showVersion = level => {
- const logger = yargs._getLoggerInstance();
+ const logger = yargs.getInternalMethods().getLoggerInstance();
if (!level) level = 'error';
const emit = typeof level === 'function' ? level : logger[level];
emit(version);
diff --git a/lib/validation.ts b/lib/validation.ts
index 871d4784a..b550a2957 100644
--- a/lib/validation.ts
+++ b/lib/validation.ts
@@ -2,7 +2,6 @@ import {argsert} from './argsert.js';
import {
Dictionary,
assertNotStrictEqual,
- Y18N,
PlatformShim,
} from './typings/common-types.js';
import {levenshtein as distance} from './utils/levenshtein.js';
@@ -18,11 +17,10 @@ const specialKeys = ['$0', '--', '_'];
export function validation(
yargs: YargsInstance,
usage: UsageInstance,
- y18n: Y18N,
shim: PlatformShim
) {
- const __ = y18n.__;
- const __n = y18n.__n;
+ const __ = shim.y18n.__;
+ const __n = shim.y18n.__n;
const self = {} as ValidationInstance;
// validate appropriate # of non-option
@@ -32,7 +30,8 @@ export function validation(
// don't count currently executing commands
const positionalCount =
argv._.length + (argv['--'] ? argv['--'].length : 0);
- const _s = positionalCount - yargs.getContext().commands.length;
+ const _s =
+ positionalCount - yargs.getInternalMethods().getContext().commands.length;
if (
demandedCommands._ &&
@@ -146,15 +145,21 @@ export function validation(
isDefaultCommand,
checkPositionals = true
) {
- const commandKeys = yargs.getCommandInstance().getCommands();
+ const commandKeys = yargs
+ .getInternalMethods()
+ .getCommandInstance()
+ .getCommands();
const unknown: string[] = [];
- const currentContext = yargs.getContext();
+ const currentContext = yargs.getInternalMethods().getContext();
Object.keys(argv).forEach(key => {
if (
specialKeys.indexOf(key) === -1 &&
!Object.prototype.hasOwnProperty.call(positionalMap, key) &&
- !Object.prototype.hasOwnProperty.call(yargs._getParseContext(), key) &&
+ !Object.prototype.hasOwnProperty.call(
+ yargs.getInternalMethods().getParseContext(),
+ key
+ ) &&
!self.isValidAndSomeAliasIsNotNew(key, aliases)
) {
unknown.push(key);
@@ -187,9 +192,12 @@ export function validation(
};
self.unknownCommands = function unknownCommands(argv) {
- const commandKeys = yargs.getCommandInstance().getCommands();
+ const commandKeys = yargs
+ .getInternalMethods()
+ .getCommandInstance()
+ .getCommands();
const unknown: string[] = [];
- const currentContext = yargs.getContext();
+ const currentContext = yargs.getInternalMethods().getContext();
if (currentContext.commands.length > 0 || commandKeys.length > 0) {
argv._.slice(currentContext.commands.length).forEach(key => {
diff --git a/lib/yargs-factory.ts b/lib/yargs-factory.ts
index 50f310560..d83767608 100644
--- a/lib/yargs-factory.ts
+++ b/lib/yargs-factory.ts
@@ -61,431 +61,288 @@ import {isPromise} from './utils/is-promise.js';
import {maybeAsyncResult} from './utils/maybe-async-result.js';
import setBlocking from './utils/set-blocking.js';
-let shim: PlatformShim;
-export function YargsWithShim(_shim: PlatformShim) {
- shim = _shim;
- return Yargs;
-}
-
-function Yargs(
- processArgs: string | string[] = [],
- cwd = shim.process.cwd(),
- parentRequire?: RequireType
-): YargsInstance {
- const self = {} as YargsInstance;
- let command: CommandInstance;
- let completion: CompletionInstance | null = null;
- let groups: Dictionary = {};
- let output = '';
- const preservedGroups: Dictionary = {};
- const globalMiddleware = new GlobalMiddleware(self);
- let usage: UsageInstance;
- let validation: ValidationInstance;
-
- const y18n = shim.y18n;
-
- self.scriptName = function (scriptName) {
- self.customScriptName = true;
- self.$0 = scriptName;
- return self;
+export function YargsFactory(_shim: PlatformShim) {
+ return (
+ processArgs: string | string[] = [],
+ cwd = _shim.process.cwd(),
+ parentRequire?: RequireType
+ ): YargsInstance => {
+ const yargs = new YargsInstance(processArgs, cwd, parentRequire, _shim);
+ // Legacy yargs.argv interface, it's recommended that you use .parse().
+ Object.defineProperty(yargs, 'argv', {
+ get: () => {
+ return yargs.parse();
+ },
+ enumerable: true,
+ });
+ // an app should almost always have --version and --help,
+ // if you *really* want to disable this use .help(false)/.version(false).
+ yargs.help();
+ yargs.version();
+ return yargs;
};
+}
- // ignore the node bin, specify this in your
- // bin file with #!/usr/bin/env node
- let default$0: string[];
- if (/\b(node|iojs|electron)(\.exe)?$/.test(shim.process.argv()[0])) {
- default$0 = shim.process.argv().slice(1, 2);
- } else {
- default$0 = shim.process.argv().slice(0, 1);
- }
-
- self.$0 = default$0
- .map(x => {
- const b = rebase(cwd, x);
- return x.match(/^(\/|([a-zA-Z]:)?\\)/) && b.length < x.length ? b : x;
- })
- .join(' ')
- .trim();
+// Used to expose private methods to other module-level classes,
+// such as the command parser and usage printer.
+const kCopyDoubleDash = Symbol('copyDoubleDash');
+const kCreateLogger = Symbol('copyDoubleDash');
+const kDeleteFromParserHintObject = Symbol('deleteFromParserHintObject');
+const kFreeze = Symbol('freeze');
+const kGetDollarZero = Symbol('getDollarZero');
+const kGetParserConfiguration = Symbol('getParserConfiguration');
+const kGuessLocale = Symbol('guessLocale');
+const kGuessVersion = Symbol('guessVersion');
+const kParsePositionalNumbers = Symbol('parsePositionalNumbers');
+const kPkgUp = Symbol('pkgUp');
+const kPopulateParserHintArray = Symbol('populateParserHintArray');
+const kPopulateParserHintSingleValueDictionary = Symbol(
+ 'populateParserHintSingleValueDictionary'
+);
+const kPopulateParserHintArrayDictionary = Symbol(
+ 'populateParserHintArrayDictionary'
+);
+const kPopulateParserHintDictionary = Symbol('populateParserHintDictionary');
+const kSanitizeKey = Symbol('sanitizeKey');
+const kSetKey = Symbol('setKey');
+const kUnfreeze = Symbol('unfreeze');
+const kValidateAsync = Symbol('validateAsync');
+const kGetCommandInstance = Symbol('getCommandInstance');
+const kGetContext = Symbol('getContext');
+const kGetHasOutput = Symbol('getHasOutput');
+const kGetLoggerInstance = Symbol('getLoggerInstance');
+const kGetParseContext = Symbol('getParseContext');
+const kGetUsageInstance = Symbol('getUsageInstance');
+const kGetValidationInstance = Symbol('getValidationInstance');
+const kHasParseCallback = Symbol('hasParseCallback');
+const kPostProcess = Symbol('postProcess');
+const kRebase = Symbol('rebase');
+const kReset = Symbol('reset');
+const kRunYargsParserAndExecuteCommands = Symbol(
+ 'runYargsParserAndExecuteCommands'
+);
+const kRunValidation = Symbol('runValidation');
+const kSetHasOutput = Symbol('setHasOutput');
+export interface YargsInternalMethods {
+ getCommandInstance(): CommandInstance;
+ getContext(): Context;
+ getHasOutput(): boolean;
+ getLoggerInstance(): LoggerInstance;
+ getParseContext(): Object;
+ getParserConfiguration(): Configuration;
+ getUsageInstance(): UsageInstance;
+ getValidationInstance(): ValidationInstance;
+ hasParseCallback(): boolean;
+ postProcess>(
+ argv: Arguments | Promise,
+ populateDoubleDash: boolean,
+ calledFromCommand: boolean,
+ runGlobalMiddleware: boolean
+ ): any;
+ reset(aliases?: Aliases): YargsInstance;
+ runValidation(
+ aliases: Dictionary,
+ positionalMap: Dictionary,
+ parseErrors: Error | null,
+ isDefaultCommand?: boolean
+ ): (argv: Arguments) => void;
+ runYargsParserAndExecuteCommands(
+ args: string | string[] | null,
+ shortCircuit?: boolean | null,
+ calledFromCommand?: boolean,
+ commandIndex?: number,
+ helpOnly?: boolean
+ ): Arguments | Promise;
+ setHasOutput(): void;
+}
- if (shim.getEnv('_') && shim.getProcessArgvBin() === shim.getEnv('_')) {
- self.$0 = shim
- .getEnv('_')!
- .replace(`${shim.path.dirname(shim.process.execPath())}/`, '');
- }
+export class YargsInstance {
+ $0: string;
+ argv?: Arguments;
+ customScriptName = false;
+ parsed: DetailedArguments | false = false;
+ #command: CommandInstance;
+ #cwd: string;
// use context object to keep track of resets, subcommand execution, etc.,
// submodules should modify and check the state of context as necessary:
- const context = {commands: [], fullCommands: []};
- self.getContext = () => context;
-
- let hasOutput = false;
- let exitError: YError | string | undefined | null = null;
- // maybe exit, always capture
- // context about why we wanted to exit.
- self.exit = (code, err) => {
- hasOutput = true;
- exitError = err;
- if (exitProcess) shim.process.exit(code);
- };
-
- let completionCommand: string | null = null;
- self.completion = function (
- cmd?: string,
- desc?: string | false | CompletionFunction,
- fn?: CompletionFunction
+ #context: Context = {commands: [], fullCommands: []};
+ #completion: CompletionInstance | null = null;
+ #completionCommand: string | null = null;
+ #defaultShowHiddenOpt = 'show-hidden';
+ #exitError: YError | string | undefined | null = null;
+ #detectLocale = true;
+ #exitProcess = true;
+ #frozens: FrozenYargsInstance[] = [];
+ #globalMiddleware: GlobalMiddleware;
+ #groups: Dictionary = {};
+ #hasOutput = false;
+ #helpOpt: string | null = null;
+ #logger: LoggerInstance;
+ #output = '';
+ #options: Options;
+ #parentRequire?: RequireType;
+ #parserConfig: Configuration = {};
+ #parseFn: ParseCallback | null = null;
+ #parseContext: object | null = null;
+ #pkgs: Dictionary<{[key: string]: string | {[key: string]: string}}> = {};
+ #preservedGroups: Dictionary = {};
+ #processArgs: string | string[];
+ #recommendCommands = false;
+ #shim: PlatformShim;
+ #strict = false;
+ #strictCommands = false;
+ #strictOptions = false;
+ #usage: UsageInstance;
+ #versionOpt: string | null = null;
+ #validation: ValidationInstance;
+
+ constructor(
+ processArgs: string | string[] = [],
+ cwd: string,
+ parentRequire: RequireType | undefined,
+ shim: PlatformShim
) {
- argsert(
- '[string] [string|boolean|function] [function]',
- [cmd, desc, fn],
- arguments.length
- );
-
- // a function to execute when generating
- // completions can be provided as the second
- // or third argument to completion.
- if (typeof desc === 'function') {
- fn = desc;
- desc = undefined;
- }
+ this.#shim = shim;
+ this.#processArgs = processArgs;
+ this.#cwd = cwd;
+ this.#parentRequire = parentRequire;
+ this.#globalMiddleware = new GlobalMiddleware(this);
+ this.$0 = this[kGetDollarZero]();
+ // #command, #validation, and #usage are initialized on first reset:
+ this[kReset]();
+ this.#command = this!.#command;
+ this.#usage = this!.#usage;
+ this.#validation = this!.#validation;
+ this.#options = this!.#options;
+ this.#options.showHiddenOpt = this.#defaultShowHiddenOpt;
+ this.#logger = this[kCreateLogger]();
+ }
+ addHelpOpt(opt?: string | false, msg?: string): YargsInstance {
+ const defaultHelpOpt = 'help';
+ argsert('[string|boolean] [string]', [opt, msg], arguments.length);
- // register the completion command.
- completionCommand = cmd || completionCommand || 'completion';
- if (!desc && desc !== false) {
- desc = 'generate completion script';
+ // nuke the key previously configured
+ // to return help.
+ if (this.#helpOpt) {
+ this[kDeleteFromParserHintObject](this.#helpOpt);
+ this.#helpOpt = null;
}
- self.command(completionCommand, desc);
-
- // a function can be provided
- if (fn) completion!.registerFunction(fn);
-
- return self;
- };
-
- // puts yargs back into an initial state. any keys
- // that have been set to "global" will not be reset
- // by this action.
- let options: Options;
- self.resetOptions = self.reset = function resetOptions(aliases = {}) {
- options = options || {};
- // put yargs back into an initial state, this
- // logic is used to build a nested command
- // hierarchy.
- const tmpOptions = {} as Options;
- tmpOptions.local = options.local ? options.local : [];
- tmpOptions.configObjects = options.configObjects
- ? options.configObjects
- : [];
- // if a key has been explicitly set as local,
- // we should reset it before passing options to command.
- const localLookup: Dictionary = {};
- tmpOptions.local.forEach(l => {
- localLookup[l] = true;
- (aliases[l] || []).forEach(a => {
- localLookup[a] = true;
- });
- });
+ if (opt === false && msg === undefined) return this;
- // add all groups not set to local to preserved groups
- Object.assign(
- preservedGroups,
- Object.keys(groups).reduce((acc, groupName) => {
- const keys = groups[groupName].filter(key => !(key in localLookup));
- if (keys.length > 0) {
- acc[groupName] = keys;
- }
- return acc;
- }, {} as Dictionary)
+ // use arguments, fallback to defaults for opt and msg
+ this.#helpOpt = typeof opt === 'string' ? opt : defaultHelpOpt;
+ this.boolean(this.#helpOpt);
+ this.describe(
+ this.#helpOpt,
+ msg || this.#usage.deferY18nLookup('Show help')
);
- // groups can now be reset
- groups = {};
-
- const arrayOptions: KeyOf[] = [
- 'array',
- 'boolean',
- 'string',
- 'skipValidation',
- 'count',
- 'normalize',
- 'number',
- 'hiddenOptions',
- ];
-
- const objectOptions: DictionaryKeyof[] = [
- 'narg',
- 'key',
- 'alias',
- 'default',
- 'defaultDescription',
- 'config',
- 'choices',
- 'demandedOptions',
- 'demandedCommands',
- 'deprecatedOptions',
- ];
-
- arrayOptions.forEach(k => {
- tmpOptions[k] = (options[k] || []).filter((k: string) => !localLookup[k]);
- });
-
- objectOptions.forEach(>(k: K) => {
- tmpOptions[k] = objFilter(options[k], k => !localLookup[k as string]);
- });
-
- tmpOptions.envPrefix = options.envPrefix;
- options = tmpOptions;
-
- // if this is the first time being executed, create
- // instances of all our helpers -- otherwise just reset.
- usage = usage ? usage.reset(localLookup) : Usage(self, y18n, shim);
- validation = validation
- ? validation.reset(localLookup)
- : Validation(self, usage, y18n, shim);
- command = command
- ? command.reset()
- : Command(usage, validation, globalMiddleware, shim);
- if (!completion) completion = Completion(self, usage, command, shim);
- globalMiddleware.reset();
-
- completionCommand = null;
- output = '';
- exitError = null;
- hasOutput = false;
- self.parsed = false;
-
- return self;
- };
- self.resetOptions();
-
- // temporary hack: allow "freezing" of reset-able state for parse(msg, cb)
- const frozens: FrozenYargsInstance[] = [];
- function freeze() {
- frozens.push({
- options,
- configObjects: options.configObjects.slice(0),
- exitProcess,
- groups,
- strict,
- strictCommands,
- strictOptions,
- completionCommand,
- output,
- exitError,
- hasOutput,
- parsed: self.parsed,
- parseFn,
- parseContext,
- });
- usage.freeze();
- validation.freeze();
- command.freeze();
- globalMiddleware.freeze();
- }
- function unfreeze() {
- const frozen = frozens.pop();
- assertNotStrictEqual(frozen, undefined, shim);
- let configObjects: Dictionary[];
- ({
- options,
- configObjects,
- exitProcess,
- groups,
- output,
- exitError,
- hasOutput,
- parsed: self.parsed,
- strict,
- strictCommands,
- strictOptions,
- completionCommand,
- parseFn,
- parseContext,
- } = frozen);
- options.configObjects = configObjects;
- usage.unfreeze();
- validation.unfreeze();
- command.unfreeze();
- globalMiddleware.unfreeze();
+ return this;
}
-
- self.boolean = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('boolean', keys);
- return self;
- };
-
- self.array = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('array', keys);
- return self;
- };
-
- self.number = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('number', keys);
- return self;
- };
-
- self.normalize = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('normalize', keys);
- return self;
- };
-
- self.count = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('count', keys);
- return self;
- };
-
- self.string = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('string', keys);
- return self;
- };
-
- self.requiresArg = function (keys) {
- // the 2nd paramter [number] in the argsert the assertion is mandatory
- // as populateParserHintSingleValueDictionary recursively calls requiresArg
- // with Nan as a 2nd parameter, although we ignore it
- argsert(' [number]', [keys], arguments.length);
- // If someone configures nargs at the same time as requiresArg,
- // nargs should take precedent,
- // see: https://github.com/yargs/yargs/pull/1572
- // TODO: make this work with aliases, using a check similar to
- // checkAllAliases() in yargs-parser.
- if (typeof keys === 'string' && options.narg[keys]) {
- return self;
- } else {
- populateParserHintSingleValueDictionary(
- self.requiresArg,
- 'narg',
- keys,
- NaN
- );
- }
- return self;
- };
-
- self.skipValidation = function (keys) {
- argsert('', [keys], arguments.length);
- populateParserHintArray('skipValidation', keys);
- return self;
- };
-
- function populateParserHintArray>(
- type: T,
- keys: string | string[]
- ) {
- keys = ([] as string[]).concat(keys);
- keys.forEach(key => {
- key = sanitizeKey(key);
- options[type].push(key);
- });
+ help(opt?: string, msg?: string): YargsInstance {
+ return this.addHelpOpt(opt, msg);
}
- self.nargs = function (
- key: string | string[] | Dictionary,
- value?: number
- ) {
- argsert(' [number]', [key, value], arguments.length);
- populateParserHintSingleValueDictionary(self.nargs, 'narg', key, value);
- return self;
- };
+ addShowHiddenOpt(opt?: string | false, msg?: string): YargsInstance {
+ argsert('[string|boolean] [string]', [opt, msg], arguments.length);
+ if (opt === false && msg === undefined) return this;
+ const showHiddenOpt =
+ typeof opt === 'string' ? opt : this.#defaultShowHiddenOpt;
+ this.boolean(showHiddenOpt);
+ this.describe(
+ showHiddenOpt,
+ msg || this.#usage.deferY18nLookup('Show hidden options')
+ );
+ this.#options.showHiddenOpt = showHiddenOpt;
+ return this;
+ }
+ showHidden(opt?: string | false, msg?: string): YargsInstance {
+ return this.addShowHiddenOpt(opt, msg);
+ }
- self.choices = function (
+ alias(
key: string | string[] | Dictionary,
value?: string | string[]
- ) {
+ ): YargsInstance {
argsert(
'