From 9fbf083450be2f2e39b9fa4872f1a05860340b6b Mon Sep 17 00:00:00 2001 From: XLor Date: Mon, 27 Mar 2023 19:54:34 +0800 Subject: [PATCH] feat: handle unknown options --- packages/breadc/src/breadc.ts | 6 ++++-- packages/breadc/src/command.ts | 2 ++ packages/breadc/src/parser/parser.ts | 13 +++++++++++- packages/breadc/src/parser/types.ts | 7 +++++++ packages/breadc/src/types/breadc.ts | 9 ++++++++- packages/breadc/test/command.test.ts | 30 ++++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/packages/breadc/src/breadc.ts b/packages/breadc/src/breadc.ts index a9a8fd5f..8416c0e7 100644 --- a/packages/breadc/src/breadc.ts +++ b/packages/breadc/src/breadc.ts @@ -36,9 +36,11 @@ export function breadc(name: string, config: AppOption = {}) { globalOptions.push(option); return breadc; }, - command(text, _config = {}) { + command(text, _config = {}, _config2: any = {}) { const config = - typeof _config === 'string' ? { description: _config } : _config; + typeof _config === 'string' + ? { description: _config, ..._config2 } + : _config; const command = makeCommand(text, config, root, container); diff --git a/packages/breadc/src/command.ts b/packages/breadc/src/command.ts index bd793ab5..e82c29a1 100644 --- a/packages/breadc/src/command.ts +++ b/packages/breadc/src/command.ts @@ -72,6 +72,8 @@ export function makeCommand( return makeTreeNode({ command, init(context) { + context.config.allowUnknownOption = + config.allowUnknownOption ?? 'error'; initContextOptions(options, context); }, finish(context) { diff --git a/packages/breadc/src/parser/parser.ts b/packages/breadc/src/parser/parser.ts index 924ab252..4eceea00 100644 --- a/packages/breadc/src/parser/parser.ts +++ b/packages/breadc/src/parser/parser.ts @@ -70,7 +70,15 @@ export function parseOption( context.result.options[name] = option.cast(context.result.options[name]); } } else { - throw new ParseError(`Unknown option ${token.raw()}`); + switch (context.config.allowUnknownOption) { + case 'rest': + context.result['--'].push(token.raw()); + case 'skip': + break; + case 'error': + default: + throw new ParseError(`Unknown option ${token.raw()}`); + } } return cursor; /* c8 ignore next 1 */ @@ -87,6 +95,9 @@ export function parse(root: TreeNode, args: string[]): BreadcParseResult { '--': [] }, meta: {}, + config: { + allowUnknownOption: 'error' + }, parseOption }; diff --git a/packages/breadc/src/parser/types.ts b/packages/breadc/src/parser/types.ts index 9f533744..5b66433b 100644 --- a/packages/breadc/src/parser/types.ts +++ b/packages/breadc/src/parser/types.ts @@ -53,6 +53,13 @@ export interface Context { */ meta: Record; + /** + * Configuration + */ + config: { + allowUnknownOption: 'error' | 'skip' | 'rest'; + }; + parseOption( cursor: TreeNode, token: Token, diff --git a/packages/breadc/src/types/breadc.ts b/packages/breadc/src/types/breadc.ts index 9b4321ca..3f856868 100644 --- a/packages/breadc/src/types/breadc.ts +++ b/packages/breadc/src/types/breadc.ts @@ -48,7 +48,8 @@ export interface Breadc { command( format: F, - description?: string + description?: string, + option?: CommandOption ): Command, {}, GlobalOption>; command( format: F, @@ -103,10 +104,16 @@ export interface Command< export interface CommandOption { description?: string; + + /** + * Config how to handle unknown options + */ + allowUnknownOption?: 'error' | 'skip' | 'rest'; } export interface Argument { type: 'const' | 'require' | 'optional' | 'rest'; + name: string; } diff --git a/packages/breadc/test/command.test.ts b/packages/breadc/test/command.test.ts index 2a356657..05896972 100644 --- a/packages/breadc/test/command.test.ts +++ b/packages/breadc/test/command.test.ts @@ -6,6 +6,36 @@ import { breadc } from '../src'; options.enabled = false; +describe('Skip unknown options', () => { + it('should skip', async () => { + const cli = breadc('cli'); + cli + .command('echo', { allowUnknownOption: 'skip' }) + .action((options) => options['--']); + expect(await cli.run(['echo', '--test', '--check'])).toMatchInlineSnapshot( + '[]' + ); + }); + + it('should add to rest args', async () => { + const cli = breadc('cli'); + cli + .command('echo', 'Echo command', { allowUnknownOption: 'rest' }) + .action((options) => options['--']); + expect( + await cli.run(['echo', '--test', 'abc', '--check']) + ).toMatchInlineSnapshot( + ` + [ + "--test", + "abc", + "--check", + ] + ` + ); + }); +}); + describe('Alias command', () => { it('should share alias', async () => { const cli = breadc('cli');