@oclif/core 4.9.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/help/command.js +3 -2
- package/lib/help/docopts.js +4 -3
- package/lib/interfaces/parser.d.ts +19 -0
- package/lib/parser/errors.d.ts +2 -1
- package/lib/parser/errors.js +4 -1
- package/lib/parser/parse.js +82 -32
- package/lib/parser/validate.js +40 -2
- package/lib/util/cache-command.js +1 -0
- package/package.json +1 -1
package/lib/help/command.js
CHANGED
|
@@ -57,8 +57,9 @@ class CommandHelp extends formatter_1.HelpFormatter {
|
|
|
57
57
|
if (args.filter((a) => a.description).length === 0)
|
|
58
58
|
return;
|
|
59
59
|
return args.map((a) => {
|
|
60
|
-
// Add ellipsis
|
|
61
|
-
|
|
60
|
+
// Add ellipsis for variadic args, or for all args when strict is false (backward compat)
|
|
61
|
+
const suffix = a.multiple ? '...' : this.command.strict === false ? '...' : '';
|
|
62
|
+
let name = `${a.name.toUpperCase()}${suffix}`;
|
|
62
63
|
name = a.required ? `${name}` : `[${name}]`;
|
|
63
64
|
let description = a.description || '';
|
|
64
65
|
if (a.default)
|
package/lib/help/docopts.js
CHANGED
|
@@ -100,11 +100,12 @@ class DocOpts {
|
|
|
100
100
|
toString() {
|
|
101
101
|
const opts = ['<%= command.id %>'];
|
|
102
102
|
if (this.cmd.args) {
|
|
103
|
-
// If strict is false, add ellipsis to indicate that the argument takes multiple values
|
|
104
|
-
const suffix = this.cmd.strict === false ? '...' : '';
|
|
105
103
|
const a = Object.values((0, ensure_arg_object_1.ensureArgObject)(this.cmd.args))
|
|
106
104
|
.filter((arg) => !arg.hidden)
|
|
107
|
-
.map((arg) =>
|
|
105
|
+
.map((arg) => {
|
|
106
|
+
const suffix = arg.multiple ? '...' : this.cmd.strict === false ? '...' : '';
|
|
107
|
+
return arg.required ? `${arg.name.toUpperCase()}${suffix}` : `[${arg.name.toUpperCase()}${suffix}]`;
|
|
108
|
+
}) || [];
|
|
108
109
|
opts.push(...a);
|
|
109
110
|
}
|
|
110
111
|
try {
|
|
@@ -192,6 +192,11 @@ export type ArgProps = {
|
|
|
192
192
|
* If true, the flag will be required.
|
|
193
193
|
*/
|
|
194
194
|
required?: boolean;
|
|
195
|
+
/**
|
|
196
|
+
* If true, the arg accepts multiple values. Only one arg per command can have multiple: true.
|
|
197
|
+
* All args after a variadic arg must be required.
|
|
198
|
+
*/
|
|
199
|
+
multiple?: boolean;
|
|
195
200
|
options?: string[];
|
|
196
201
|
ignoreStdin?: boolean;
|
|
197
202
|
/**
|
|
@@ -245,6 +250,20 @@ export type Arg<T, P = CustomOptions> = ArgProps & {
|
|
|
245
250
|
parse: ArgParser<T, P>;
|
|
246
251
|
};
|
|
247
252
|
export type ArgDefinition<T, P = CustomOptions> = {
|
|
253
|
+
(options: P & {
|
|
254
|
+
multiple: true;
|
|
255
|
+
} & ({
|
|
256
|
+
required: true;
|
|
257
|
+
} | {
|
|
258
|
+
default: ArgDefault<T[]>;
|
|
259
|
+
}) & Omit<Partial<Arg<T, P>>, 'default'> & {
|
|
260
|
+
default?: ArgDefault<T[] | undefined>;
|
|
261
|
+
}): Arg<T[], P>;
|
|
262
|
+
(options: P & {
|
|
263
|
+
multiple: true;
|
|
264
|
+
} & Omit<Partial<Arg<T, P>>, 'default'> & {
|
|
265
|
+
default?: ArgDefault<T[] | undefined>;
|
|
266
|
+
}): Arg<T[] | undefined, P>;
|
|
248
267
|
(options: P & ({
|
|
249
268
|
required: true;
|
|
250
269
|
} | {
|
package/lib/parser/errors.d.ts
CHANGED
|
@@ -16,8 +16,9 @@ export declare class CLIParseError extends CLIError {
|
|
|
16
16
|
}
|
|
17
17
|
export declare class InvalidArgsSpecError extends CLIParseError {
|
|
18
18
|
args: ArgInput;
|
|
19
|
-
constructor({ args, exit, parse }: CLIParseErrorOptions & {
|
|
19
|
+
constructor({ args, exit, parse, reason }: CLIParseErrorOptions & {
|
|
20
20
|
args: ArgInput;
|
|
21
|
+
reason?: string;
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
export declare class RequiredArgsError extends CLIParseError {
|
package/lib/parser/errors.js
CHANGED
|
@@ -23,8 +23,11 @@ class CLIParseError extends errors_1.CLIError {
|
|
|
23
23
|
exports.CLIParseError = CLIParseError;
|
|
24
24
|
class InvalidArgsSpecError extends CLIParseError {
|
|
25
25
|
args;
|
|
26
|
-
constructor({ args, exit, parse }) {
|
|
26
|
+
constructor({ args, exit, parse, reason }) {
|
|
27
27
|
let message = 'Invalid argument spec';
|
|
28
|
+
if (reason) {
|
|
29
|
+
message += `: ${reason}`;
|
|
30
|
+
}
|
|
28
31
|
const namedArgs = Object.values(args).filter((a) => a.name);
|
|
29
32
|
if (namedArgs.length > 0) {
|
|
30
33
|
const list = (0, list_1.default)(namedArgs.map((a) => [`${a.name} (${a.required ? 'required' : 'optional'})`, a.description]));
|
package/lib/parser/parse.js
CHANGED
|
@@ -201,43 +201,93 @@ class Parser {
|
|
|
201
201
|
const tokens = this._argTokens;
|
|
202
202
|
let stdinRead = false;
|
|
203
203
|
const ctx = this.context;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (token) {
|
|
208
|
-
if (arg.options && !arg.options.includes(token.input)) {
|
|
209
|
-
throw new errors_1.ArgInvalidOptionError(arg, token.input);
|
|
210
|
-
}
|
|
211
|
-
const parsed = await arg.parse(token.input, ctx, arg);
|
|
212
|
-
argv.push(parsed);
|
|
213
|
-
args[token.arg] = parsed;
|
|
204
|
+
const parseArgInput = async (name, arg, input) => {
|
|
205
|
+
if (arg.options && !arg.options.includes(input)) {
|
|
206
|
+
throw new errors_1.ArgInvalidOptionError(arg, input);
|
|
214
207
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
208
|
+
ctx.token = { arg: name, input, type: 'arg' };
|
|
209
|
+
const parsed = await arg.parse(input, ctx, arg);
|
|
210
|
+
argv.push(parsed);
|
|
211
|
+
args[name] = parsed;
|
|
212
|
+
};
|
|
213
|
+
const applyDefault = async (name, arg) => {
|
|
214
|
+
if (args[name] !== undefined)
|
|
215
|
+
return;
|
|
216
|
+
if (!arg.default && arg.default !== false)
|
|
217
|
+
return;
|
|
218
|
+
const value = typeof arg.default === 'function' ? await arg.default() : arg.default;
|
|
219
|
+
if (Array.isArray(value)) {
|
|
220
|
+
for (const v of value)
|
|
221
|
+
argv.push(v);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
argv.push(value);
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
args[name] = value;
|
|
227
|
+
};
|
|
228
|
+
const tryStdin = async (name, arg) => {
|
|
229
|
+
if (arg.ignoreStdin || stdinRead)
|
|
230
|
+
return;
|
|
231
|
+
let stdin = await (0, exports.readStdin)();
|
|
232
|
+
stdinRead = true;
|
|
233
|
+
if (!stdin)
|
|
234
|
+
return;
|
|
235
|
+
stdin = stdin.trim();
|
|
236
|
+
await parseArgInput(name, arg, stdin);
|
|
237
|
+
};
|
|
238
|
+
const argEntries = Object.entries(this.input.args);
|
|
239
|
+
const variadicIndex = argEntries.findIndex(([, arg]) => arg.multiple);
|
|
240
|
+
if (variadicIndex === -1) {
|
|
241
|
+
for (const [name, arg] of argEntries) {
|
|
242
|
+
const token = tokens.find((t) => t.arg === name);
|
|
243
|
+
ctx.token = token;
|
|
244
|
+
await (token ? parseArgInput(name, arg, token.input) : tryStdin(name, arg));
|
|
245
|
+
await applyDefault(name, arg);
|
|
246
|
+
}
|
|
247
|
+
// Append unmatched tokens to argv (for strict: false)
|
|
248
|
+
for (const token of tokens) {
|
|
249
|
+
if (args[token.arg] !== undefined)
|
|
250
|
+
continue;
|
|
251
|
+
argv.push(token.input);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const tokenInputs = tokens.map((t) => t.input);
|
|
256
|
+
// Consume pre-variadic args from the front
|
|
257
|
+
for (let i = 0; i < variadicIndex; i++) {
|
|
258
|
+
const [name, arg] = argEntries[i];
|
|
259
|
+
const input = tokenInputs.shift();
|
|
260
|
+
await (input === undefined ? tryStdin(name, arg) : parseArgInput(name, arg, input));
|
|
261
|
+
await applyDefault(name, arg);
|
|
262
|
+
}
|
|
263
|
+
// Consume post-variadic args from the back
|
|
264
|
+
const postVariadicParsing = [];
|
|
265
|
+
for (let i = argEntries.length - 1; i > variadicIndex; i--) {
|
|
266
|
+
const [name, arg] = argEntries[i];
|
|
267
|
+
const input = tokenInputs.pop();
|
|
268
|
+
if (input !== undefined) {
|
|
269
|
+
postVariadicParsing.unshift({ arg, input, name });
|
|
230
270
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
271
|
+
}
|
|
272
|
+
// Everything remaining goes to the variadic arg
|
|
273
|
+
const [variadicName, variadicArg] = argEntries[variadicIndex];
|
|
274
|
+
if (tokenInputs.length > 0) {
|
|
275
|
+
const parsedValues = [];
|
|
276
|
+
for (const input of tokenInputs) {
|
|
277
|
+
await parseArgInput(variadicName, variadicArg, input);
|
|
278
|
+
parsedValues.push(args[variadicName]);
|
|
234
279
|
}
|
|
280
|
+
args[variadicName] = parsedValues;
|
|
281
|
+
}
|
|
282
|
+
for (const { arg, input, name } of postVariadicParsing) {
|
|
283
|
+
await parseArgInput(name, arg, input);
|
|
284
|
+
}
|
|
285
|
+
// Apply defaults for any args that didn't receive values
|
|
286
|
+
await applyDefault(variadicName, variadicArg);
|
|
287
|
+
for (let i = variadicIndex + 1; i < argEntries.length; i++) {
|
|
288
|
+
const [name, arg] = argEntries[i];
|
|
289
|
+
await applyDefault(name, arg);
|
|
235
290
|
}
|
|
236
|
-
}
|
|
237
|
-
for (const token of tokens) {
|
|
238
|
-
if (args[token.arg] !== undefined)
|
|
239
|
-
continue;
|
|
240
|
-
argv.push(token.input);
|
|
241
291
|
}
|
|
242
292
|
return { args, argv };
|
|
243
293
|
}
|
package/lib/parser/validate.js
CHANGED
|
@@ -6,6 +6,41 @@ const errors_1 = require("./errors");
|
|
|
6
6
|
async function validate(parse) {
|
|
7
7
|
let cachedResolvedFlags;
|
|
8
8
|
function validateArgs() {
|
|
9
|
+
// Validate variadic arg constraints (definition-time checks)
|
|
10
|
+
const argEntries = Object.entries(parse.input.args);
|
|
11
|
+
const variadicIndex = argEntries.findIndex(([, arg]) => arg.multiple);
|
|
12
|
+
if (variadicIndex !== -1) {
|
|
13
|
+
// Only one variadic arg is allowed
|
|
14
|
+
const secondVariadic = argEntries.findIndex(([, arg], i) => i > variadicIndex && arg.multiple);
|
|
15
|
+
if (secondVariadic !== -1) {
|
|
16
|
+
throw new errors_1.InvalidArgsSpecError({
|
|
17
|
+
args: parse.input.args,
|
|
18
|
+
parse,
|
|
19
|
+
reason: 'only one variadic arg (multiple: true) is allowed',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// All args before a variadic arg must be required (otherwise assignment is ambiguous)
|
|
23
|
+
for (let i = 0; i < variadicIndex; i++) {
|
|
24
|
+
if (!argEntries[i][1].required) {
|
|
25
|
+
throw new errors_1.InvalidArgsSpecError({
|
|
26
|
+
args: parse.input.args,
|
|
27
|
+
parse,
|
|
28
|
+
reason: `args before a variadic arg must be required, but "${argEntries[i][0]}" is optional`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// All args after a variadic arg must be required (otherwise assignment is ambiguous)
|
|
33
|
+
for (let i = variadicIndex + 1; i < argEntries.length; i++) {
|
|
34
|
+
if (!argEntries[i][1].required) {
|
|
35
|
+
throw new errors_1.InvalidArgsSpecError({
|
|
36
|
+
args: parse.input.args,
|
|
37
|
+
parse,
|
|
38
|
+
reason: `args after a variadic arg must be required, but "${argEntries[i][0]}" is optional`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const variadicArgFound = variadicIndex !== -1;
|
|
9
44
|
if (parse.output.nonExistentFlags?.length > 0) {
|
|
10
45
|
throw new errors_1.NonExistentFlagsError({
|
|
11
46
|
flags: parse.output.nonExistentFlags,
|
|
@@ -13,7 +48,8 @@ async function validate(parse) {
|
|
|
13
48
|
});
|
|
14
49
|
}
|
|
15
50
|
const maxArgs = Object.keys(parse.input.args).length;
|
|
16
|
-
|
|
51
|
+
const hasVariadicArg = Object.values(parse.input.args).some((arg) => arg.multiple);
|
|
52
|
+
if (parse.input.strict && !hasVariadicArg && parse.output.argv.length > maxArgs) {
|
|
17
53
|
const extras = parse.output.argv.slice(maxArgs);
|
|
18
54
|
throw new errors_1.UnexpectedArgsError({
|
|
19
55
|
args: extras,
|
|
@@ -26,9 +62,11 @@ async function validate(parse) {
|
|
|
26
62
|
if (!arg.required) {
|
|
27
63
|
hasOptional = true;
|
|
28
64
|
}
|
|
29
|
-
else if (hasOptional) {
|
|
65
|
+
else if (hasOptional && !variadicArgFound) {
|
|
30
66
|
// (required arg) check whether an optional has occurred before
|
|
31
67
|
// optionals should follow required, not before
|
|
68
|
+
// Skip this check when a variadic arg is present, since the variadic
|
|
69
|
+
// validation above already enforces that args after a variadic are required
|
|
32
70
|
throw new errors_1.InvalidArgsSpecError({
|
|
33
71
|
args: parse.input.args,
|
|
34
72
|
parse,
|
|
@@ -60,6 +60,7 @@ async function cacheArgs(cmdArgs, respectNoCacheDefault) {
|
|
|
60
60
|
default: await (0, cache_default_value_1.cacheDefaultValue)(arg, respectNoCacheDefault),
|
|
61
61
|
description: arg.description,
|
|
62
62
|
hidden: arg.hidden,
|
|
63
|
+
multiple: arg.multiple,
|
|
63
64
|
name,
|
|
64
65
|
noCacheDefault: arg.noCacheDefault,
|
|
65
66
|
options: arg.options,
|