From 003ad6fc2d28a53db3335237de02aa244cb9cbec Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Tue, 19 Mar 2024 07:27:08 -0600 Subject: [PATCH] fix: show options when required flag or arg is not given a value (#1017) --- src/parser/errors.ts | 7 ++++++- src/parser/parse.ts | 4 ++++ test/parser/parse.test.ts | 21 +++++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/parser/errors.ts b/src/parser/errors.ts index 98551bf73..3b3de0063 100644 --- a/src/parser/errors.ts +++ b/src/parser/errors.ts @@ -58,7 +58,12 @@ export class RequiredArgsError extends CLIParseError { let message = `Missing ${args.length} required arg${args.length === 1 ? '' : 's'}` const namedArgs = args.filter((a) => a.name) if (namedArgs.length > 0) { - const list = renderList(namedArgs.map((a) => [a.name, a.description] as [string, string])) + const list = renderList( + namedArgs.map((a) => { + const description = a.options ? `(${a.options.join('|')}) ${a.description}` : a.description + return [a.name, description] + }), + ) message += `:\n${list}` } diff --git a/src/parser/parse.ts b/src/parser/parse.ts index eefd9ea1c..237447142 100644 --- a/src/parser/parse.ts +++ b/src/parser/parse.ts @@ -188,6 +188,10 @@ export class Parser< // if the value ends up being one of the command's flags, the user didn't provide an input if (typeof input !== 'string' || this.findFlag(input).name) { + if (flag.options) { + throw new CLIError(`Flag --${name} expects one of these values: ${flag.options.join(', ')}`) + } + throw new CLIError(`Flag --${name} expects a value`) } diff --git a/test/parser/parse.test.ts b/test/parser/parse.test.ts index 6f136dffb..abcb3f178 100644 --- a/test/parser/parse.test.ts +++ b/test/parser/parse.test.ts @@ -146,6 +146,23 @@ describe('parse', () => { } }) + it('throws error when no value provided to required flag with options', async () => { + try { + await parse(['--myflag', '--second', 'value'], { + flags: { + myflag: Flags.string({ + required: true, + options: ['a', 'b'], + }), + second: Flags.string(), + }, + }) + assert.fail('should have thrown') + } catch (error) { + expect((error as CLIError).message).to.include('Flag --myflag expects one of these values: a, b') + } + }) + it('throws error when no value provided to required flag before a short char flag', async () => { try { await parse(['--myflag', '-s', 'value'], { @@ -270,7 +287,7 @@ describe('parse', () => { await parse(['arg1'], { args: { arg1: Args.string({required: true}), - arg2: Args.string({required: true, description: 'arg2 desc'}), + arg2: Args.string({required: true, description: 'arg2 desc', options: ['a', 'b']}), arg3: Args.string({required: true, description: 'arg3 desc'}), }, }) @@ -279,7 +296,7 @@ describe('parse', () => { } expect(message).to.include(`Missing 2 required args: -arg2 arg2 desc +arg2 (a|b) arg2 desc arg3 arg3 desc See more help with --help`) })