@oclif/core
Advanced tools
@@ -60,4 +60,5 @@ "use strict"; | ||
| return args.map((a) => { | ||
| // Add ellipsis to indicate that the argument takes multiple values if strict is false | ||
| let name = this.command.strict === false ? `${a.name.toUpperCase()}...` : a.name.toUpperCase(); | ||
| // Add ellipsis for variadic args, or for all args when strict is false (backward compat) | ||
| const suffix = a.multiple ? '...' : this.command.strict === false ? '...' : ''; | ||
| let name = `${a.name.toUpperCase()}${suffix}`; | ||
| name = a.required ? `${name}` : `[${name}]`; | ||
@@ -64,0 +65,0 @@ let description = a.description || ''; |
@@ -103,7 +103,8 @@ "use strict"; | ||
| if (this.cmd.args) { | ||
| // If strict is false, add ellipsis to indicate that the argument takes multiple values | ||
| const suffix = this.cmd.strict === false ? '...' : ''; | ||
| const a = Object.values((0, ensure_arg_object_1.ensureArgObject)(this.cmd.args)) | ||
| .filter((arg) => !arg.hidden) | ||
| .map((arg) => arg.required ? `${arg.name.toUpperCase()}${suffix}` : `[${arg.name.toUpperCase()}${suffix}]`) || []; | ||
| .map((arg) => { | ||
| const suffix = arg.multiple ? '...' : this.cmd.strict === false ? '...' : ''; | ||
| return arg.required ? `${arg.name.toUpperCase()}${suffix}` : `[${arg.name.toUpperCase()}${suffix}]`; | ||
| }) || []; | ||
| opts.push(...a); | ||
@@ -110,0 +111,0 @@ } |
@@ -195,2 +195,7 @@ import { Command } from '../command'; | ||
| required?: boolean; | ||
| /** | ||
| * If true, the arg accepts multiple values. Only one arg per command can have multiple: true. | ||
| * All args after a variadic arg must be required. | ||
| */ | ||
| multiple?: boolean; | ||
| options?: string[]; | ||
@@ -249,2 +254,16 @@ ignoreStdin?: boolean; | ||
| export type ArgDefinition<T, P = CustomOptions> = { | ||
| (options: P & { | ||
| multiple: true; | ||
| } & ({ | ||
| required: true; | ||
| } | { | ||
| default: ArgDefault<T[]>; | ||
| }) & Omit<Partial<Arg<T, P>>, 'default'> & { | ||
| default?: ArgDefault<T[] | undefined>; | ||
| }): Arg<T[], P>; | ||
| (options: P & { | ||
| multiple: true; | ||
| } & Omit<Partial<Arg<T, P>>, 'default'> & { | ||
| default?: ArgDefault<T[] | undefined>; | ||
| }): Arg<T[] | undefined, P>; | ||
| (options: P & ({ | ||
@@ -251,0 +270,0 @@ required: true; |
@@ -19,4 +19,5 @@ import { CLIError } from '../errors'; | ||
| args: ArgInput; | ||
| constructor({ args, exit, parse }: CLIParseErrorOptions & { | ||
| constructor({ args, exit, parse, reason }: CLIParseErrorOptions & { | ||
| args: ArgInput; | ||
| reason?: string; | ||
| }); | ||
@@ -23,0 +24,0 @@ } |
@@ -26,4 +26,7 @@ "use strict"; | ||
| args; | ||
| constructor({ args, exit, parse }) { | ||
| constructor({ args, exit, parse, reason }) { | ||
| let message = 'Invalid argument spec'; | ||
| if (reason) { | ||
| message += `: ${reason}`; | ||
| } | ||
| const namedArgs = Object.values(args).filter((a) => a.name); | ||
@@ -30,0 +33,0 @@ if (namedArgs.length > 0) { |
+82
-32
@@ -204,40 +204,90 @@ "use strict"; | ||
| const ctx = this.context; | ||
| for (const [name, arg] of Object.entries(this.input.args)) { | ||
| const token = tokens.find((t) => t.arg === name); | ||
| ctx.token = token; | ||
| if (token) { | ||
| if (arg.options && !arg.options.includes(token.input)) { | ||
| throw new errors_1.ArgInvalidOptionError(arg, token.input); | ||
| } | ||
| const parsed = await arg.parse(token.input, ctx, arg); | ||
| argv.push(parsed); | ||
| args[token.arg] = parsed; | ||
| const parseArgInput = async (name, arg, input) => { | ||
| if (arg.options && !arg.options.includes(input)) { | ||
| throw new errors_1.ArgInvalidOptionError(arg, input); | ||
| } | ||
| else if (!arg.ignoreStdin && !stdinRead) { | ||
| let stdin = await (0, exports.readStdin)(); | ||
| if (stdin) { | ||
| stdin = stdin.trim(); | ||
| const parsed = await arg.parse(stdin, ctx, arg); | ||
| argv.push(parsed); | ||
| args[name] = parsed; | ||
| ctx.token = { arg: name, input, type: 'arg' }; | ||
| const parsed = await arg.parse(input, ctx, arg); | ||
| argv.push(parsed); | ||
| args[name] = parsed; | ||
| }; | ||
| const applyDefault = async (name, arg) => { | ||
| if (args[name] !== undefined) | ||
| return; | ||
| if (!arg.default && arg.default !== false) | ||
| return; | ||
| const value = typeof arg.default === 'function' ? await arg.default() : arg.default; | ||
| if (Array.isArray(value)) { | ||
| for (const v of value) | ||
| argv.push(v); | ||
| } | ||
| else { | ||
| argv.push(value); | ||
| } | ||
| args[name] = value; | ||
| }; | ||
| const tryStdin = async (name, arg) => { | ||
| if (arg.ignoreStdin || stdinRead) | ||
| return; | ||
| let stdin = await (0, exports.readStdin)(); | ||
| stdinRead = true; | ||
| if (!stdin) | ||
| return; | ||
| stdin = stdin.trim(); | ||
| await parseArgInput(name, arg, stdin); | ||
| }; | ||
| const argEntries = Object.entries(this.input.args); | ||
| const variadicIndex = argEntries.findIndex(([, arg]) => arg.multiple); | ||
| if (variadicIndex === -1) { | ||
| for (const [name, arg] of argEntries) { | ||
| const token = tokens.find((t) => t.arg === name); | ||
| ctx.token = token; | ||
| await (token ? parseArgInput(name, arg, token.input) : tryStdin(name, arg)); | ||
| await applyDefault(name, arg); | ||
| } | ||
| // Append unmatched tokens to argv (for strict: false) | ||
| for (const token of tokens) { | ||
| if (args[token.arg] !== undefined) | ||
| continue; | ||
| argv.push(token.input); | ||
| } | ||
| } | ||
| else { | ||
| const tokenInputs = tokens.map((t) => t.input); | ||
| // Consume pre-variadic args from the front | ||
| for (let i = 0; i < variadicIndex; i++) { | ||
| const [name, arg] = argEntries[i]; | ||
| const input = tokenInputs.shift(); | ||
| await (input === undefined ? tryStdin(name, arg) : parseArgInput(name, arg, input)); | ||
| await applyDefault(name, arg); | ||
| } | ||
| // Consume post-variadic args from the back | ||
| const postVariadicParsing = []; | ||
| for (let i = argEntries.length - 1; i > variadicIndex; i--) { | ||
| const [name, arg] = argEntries[i]; | ||
| const input = tokenInputs.pop(); | ||
| if (input !== undefined) { | ||
| postVariadicParsing.unshift({ arg, input, name }); | ||
| } | ||
| stdinRead = true; | ||
| } | ||
| if (!args[name] && (arg.default || arg.default === false)) { | ||
| if (typeof arg.default === 'function') { | ||
| const f = await arg.default(); | ||
| argv.push(f); | ||
| args[name] = f; | ||
| // Everything remaining goes to the variadic arg | ||
| const [variadicName, variadicArg] = argEntries[variadicIndex]; | ||
| if (tokenInputs.length > 0) { | ||
| const parsedValues = []; | ||
| for (const input of tokenInputs) { | ||
| await parseArgInput(variadicName, variadicArg, input); | ||
| parsedValues.push(args[variadicName]); | ||
| } | ||
| else { | ||
| argv.push(arg.default); | ||
| args[name] = arg.default; | ||
| } | ||
| args[variadicName] = parsedValues; | ||
| } | ||
| for (const { arg, input, name } of postVariadicParsing) { | ||
| await parseArgInput(name, arg, input); | ||
| } | ||
| // Apply defaults for any args that didn't receive values | ||
| await applyDefault(variadicName, variadicArg); | ||
| for (let i = variadicIndex + 1; i < argEntries.length; i++) { | ||
| const [name, arg] = argEntries[i]; | ||
| await applyDefault(name, arg); | ||
| } | ||
| } | ||
| for (const token of tokens) { | ||
| if (args[token.arg] !== undefined) | ||
| continue; | ||
| argv.push(token.input); | ||
| } | ||
| return { args, argv }; | ||
@@ -244,0 +294,0 @@ } |
@@ -9,2 +9,37 @@ "use strict"; | ||
| function validateArgs() { | ||
| // Validate variadic arg constraints (definition-time checks) | ||
| const argEntries = Object.entries(parse.input.args); | ||
| const variadicIndex = argEntries.findIndex(([, arg]) => arg.multiple); | ||
| if (variadicIndex !== -1) { | ||
| // Only one variadic arg is allowed | ||
| const secondVariadic = argEntries.findIndex(([, arg], i) => i > variadicIndex && arg.multiple); | ||
| if (secondVariadic !== -1) { | ||
| throw new errors_1.InvalidArgsSpecError({ | ||
| args: parse.input.args, | ||
| parse, | ||
| reason: 'only one variadic arg (multiple: true) is allowed', | ||
| }); | ||
| } | ||
| // All args before a variadic arg must be required (otherwise assignment is ambiguous) | ||
| for (let i = 0; i < variadicIndex; i++) { | ||
| if (!argEntries[i][1].required) { | ||
| throw new errors_1.InvalidArgsSpecError({ | ||
| args: parse.input.args, | ||
| parse, | ||
| reason: `args before a variadic arg must be required, but "${argEntries[i][0]}" is optional`, | ||
| }); | ||
| } | ||
| } | ||
| // All args after a variadic arg must be required (otherwise assignment is ambiguous) | ||
| for (let i = variadicIndex + 1; i < argEntries.length; i++) { | ||
| if (!argEntries[i][1].required) { | ||
| throw new errors_1.InvalidArgsSpecError({ | ||
| args: parse.input.args, | ||
| parse, | ||
| reason: `args after a variadic arg must be required, but "${argEntries[i][0]}" is optional`, | ||
| }); | ||
| } | ||
| } | ||
| } | ||
| const variadicArgFound = variadicIndex !== -1; | ||
| if (parse.output.nonExistentFlags?.length > 0) { | ||
@@ -17,3 +52,4 @@ throw new errors_1.NonExistentFlagsError({ | ||
| const maxArgs = Object.keys(parse.input.args).length; | ||
| if (parse.input.strict && parse.output.argv.length > maxArgs) { | ||
| const hasVariadicArg = Object.values(parse.input.args).some((arg) => arg.multiple); | ||
| if (parse.input.strict && !hasVariadicArg && parse.output.argv.length > maxArgs) { | ||
| const extras = parse.output.argv.slice(maxArgs); | ||
@@ -31,5 +67,7 @@ throw new errors_1.UnexpectedArgsError({ | ||
| } | ||
| else if (hasOptional) { | ||
| else if (hasOptional && !variadicArgFound) { | ||
| // (required arg) check whether an optional has occurred before | ||
| // optionals should follow required, not before | ||
| // Skip this check when a variadic arg is present, since the variadic | ||
| // validation above already enforces that args after a variadic are required | ||
| throw new errors_1.InvalidArgsSpecError({ | ||
@@ -36,0 +74,0 @@ args: parse.input.args, |
@@ -63,2 +63,3 @@ "use strict"; | ||
| hidden: arg.hidden, | ||
| multiple: arg.multiple, | ||
| name, | ||
@@ -65,0 +66,0 @@ noCacheDefault: arg.noCacheDefault, |
+1
-1
| { | ||
| "name": "@oclif/core", | ||
| "description": "base library for oclif CLIs", | ||
| "version": "4.9.0", | ||
| "version": "4.10.0", | ||
| "author": "Salesforce", | ||
@@ -6,0 +6,0 @@ "bugs": "https://github.com/oclif/core/issues", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
416556
1.27%10425
1.11%