diff --git a/src/command-help.ts b/src/command-help.ts index 0ff6a2c..94ddc06 100644 --- a/src/command-help.ts +++ b/src/command-help.ts @@ -1,4 +1,5 @@ import { Command } from './command'; +import { CommandOption, OptionType } from './command-options'; export class CommandHelp { public constructor(public command: Command) {} @@ -37,17 +38,45 @@ export class CommandHelp { public showPositional() { for (const positional of this.command.positional) { - console.log(` <${positional.key}>: ${positional.description}`); + this.showCommandOption(positional, true); } } public showOptions() { for (const option of this.command.options) { - const key = `--${option.key}`; - const alias = option.alias ? ` -${option.alias}` : ''; - console.log(` ${key}${alias}: ${option.description}`); + this.showCommandOption(option); } } + public showCommandOption(option: CommandOption, isPositional = false) { + let key = ''; + + if (isPositional) { + key = `<${option.key}>`; + } + else { + if (option.alias) { + key = `-${option.alias}, `; + } + + key += `--${option.key}`; + } + + const type = + option.type && option.type !== OptionType.string + ? ` {${option.type}}` + : ''; + const defaultValue = + option.default !== undefined ? ` (default: ${option.default})` : ''; + const choices = option.choices + ? ` [choices: ${option.choices.join(', ')}]` + : ''; + const description = option.description ?? ''; + const metadata = `${type}${defaultValue}${choices}`; + const fullDescription = `${description}${metadata}`; + + console.log(` ${key}: ${fullDescription}`); + } + public footer() {} } diff --git a/src/command-options.ts b/src/command-options.ts index 0a77e1d..96e8344 100644 --- a/src/command-options.ts +++ b/src/command-options.ts @@ -1,9 +1,9 @@ import { ArgumentValue } from './argument-parser'; export enum OptionType { - boolean, - number, - string, + boolean = 'bool', + number = 'number', + string = 'string', } export interface CommandOption { diff --git a/test/spec/command-help.spec.ts b/test/spec/command-help.spec.ts index 1e0c18b..b304e8c 100644 --- a/test/spec/command-help.spec.ts +++ b/test/spec/command-help.spec.ts @@ -1,17 +1,18 @@ import { MockConsole } from 'ts-jasmine-spies'; import { MockCommand } from '../mock/mock-command'; import { CommandHelp } from '../../src/command-help'; +import { CommandOption, OptionType } from '../../src/command-options'; class HelpTestCommand extends MockCommand { override key = 'test'; override description = 'Test command description'; - override positional = [ + override positional: CommandOption[] = [ { key: 'arg1', description: 'First argument' }, { key: 'arg2', description: 'Second argument' }, ]; - override options = [ + override options: CommandOption[] = [ { key: 'opt1', description: 'First option', alias: 'o' }, { key: 'opt2', description: 'Second option', alias: undefined }, ]; @@ -53,7 +54,85 @@ describe('CommandHelp', () => { it('can show optional arguments', () => { commandHelp.showOptions(); mockConsole.expectStdout( - ' --opt1 -o: First option\n --opt2: Second option\n' + ' -o, --opt1: First option\n --opt2: Second option\n' + ); + }); + + it('shows option default value', () => { + mockCommand.options = [ + { + key: 'opt1', + description: 'First option', + default: 'defaultValue', + }, + ]; + + commandHelp.showOptions(); + mockConsole.expectStdout( + ' --opt1: First option (default: defaultValue)\n' + ); + }); + + it('can show option without description', () => { + mockCommand.options = [{ key: 'opt1', default: 'defaultValue' }]; + commandHelp.showOptions(); + mockConsole.expectStdout(' --opt1: (default: defaultValue)\n'); + }); + + it('shows option choices', () => { + mockCommand.options = [ + { + key: 'opt1', + description: 'First option', + choices: ['choice1', 'choice2'], + }, + ]; + + commandHelp.showOptions(); + mockConsole.expectStdout( + ' --opt1: First option [choices: choice1, choice2]\n' + ); + }); + + it('shows option type', () => { + mockCommand.options = [ + { + key: 'opt1', + description: 'First option', + type: OptionType.number, + }, + ]; + + commandHelp.showOptions(); + mockConsole.expectStdout(' --opt1: First option {number}\n'); + }); + + it('shows skips option type for strings', () => { + mockCommand.options = [ + { + key: 'opt1', + description: 'First option', + }, + ]; + + commandHelp.showOptions(); + mockConsole.expectStdout(' --opt1: First option\n'); + }); + + it('shows default value and choices', () => { + mockCommand.options = [ + { + key: 'opt1', + description: 'First option', + default: 'defaultValue', + choices: ['choice1', 'choice2'], + type: OptionType.string, + }, + ]; + commandHelp.showOptions(); + mockConsole.expectStdout( + ' --opt1: First option (default: defaultValue)' + + ' [choices: choice1, choice2]\n' ); });