From 6a33b0633c9e422b18ec33d9a1dd4202c97adf28 Mon Sep 17 00:00:00 2001 From: Jeff Dickey <216188+jdxcode@users.noreply.github.com> Date: Wed, 31 Jan 2018 20:29:07 -0800 Subject: [PATCH] fix: added options to flags/args --- src/args.ts | 2 ++ src/errors.ts | 28 +++++++++++++++++++++++----- src/flags.ts | 12 +----------- src/parse.ts | 12 ++++++++++-- test/parse.test.ts | 29 ++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/src/args.ts b/src/args.ts index 2ae5c59..40d56e8 100644 --- a/src/args.ts +++ b/src/args.ts @@ -7,6 +7,7 @@ export interface IArg { hidden?: boolean parse?: ParseFn default?: T | (() => T) + options?: string[] } export interface ArgBase { @@ -16,6 +17,7 @@ export interface ArgBase { parse: ParseFn default?: T | (() => T) input?: string + options?: string[] } export type RequiredArg = ArgBase & { diff --git a/src/errors.ts b/src/errors.ts index d10ae9f..f0a2709 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,13 +1,13 @@ import {Arg} from './args' import {deps} from './deps' -import {IFlag} from './flags' +import * as flags from './flags' import {flagUsages} from './help' import {ParserInput, ParserOutput} from './parse' export interface ICLIParseErrorOptions { parse: { - input: ParserInput - output: ParserOutput + input?: ParserInput + output?: ParserOutput } } @@ -37,9 +37,9 @@ export class RequiredArgsError extends CLIParseError { } export class RequiredFlagError extends CLIParseError { - public flags: IFlag[] + public flags: flags.IFlag[] - constructor({flags, parse}: ICLIParseErrorOptions & { flags: IFlag[] }) { + constructor({flags, parse}: ICLIParseErrorOptions & { flags: flags.IFlag[] }) { const usage = deps.renderList(flagUsages(flags, {displayRequired: false})) const message = `Missing required flag${flags.length === 1 ? '' : 's'}:\n${usage}` super({parse, message}) @@ -56,3 +56,21 @@ export class UnexpectedArgsError extends CLIParseError { this.args = args } } + +export class FlagInvalidOptionError extends CLIParseError { + public args: string[] + + constructor(flag: flags.IOptionFlag, input: string) { + const message = `Expected --${flag.name}=${input} to be one of: ${flag.options!.join(', ')}` + super({parse: {}, message}) + } +} + +export class ArgInvalidOptionError extends CLIParseError { + public args: string[] + + constructor(arg: Arg, input: string) { + const message = `Expected ${input} to be one of: ${arg.options!.join(', ')}` + super({parse: {}, message}) + } +} diff --git a/src/flags.ts b/src/flags.ts index 4777634..c54ca01 100644 --- a/src/flags.ts +++ b/src/flags.ts @@ -25,6 +25,7 @@ export type IOptionFlag = IFlagBase & { default?: T | ((context: DefaultContext) => T | undefined) multiple: boolean input: string[] + options?: string[] } export type Definition = { @@ -71,17 +72,6 @@ export const integer = build({ }, }) -const _enum = (opts: EnumFlagOptions) => build({ - parse(input) { - if (!opts.options.includes(input)) throw new Error(`Expected --${this.name}=${input} to be one of: ${opts.options.join(', ')}`) - return input - }, - helpValue: `(${opts.options.join('|')})`, - ...opts as any, - optionType: 'enum', -}) -export {_enum as enum} - export function option(options: {parse: IOptionFlag['parse']} & Partial>) { return build({optionType: 'custom', ...options})() } diff --git a/src/parse.ts b/src/parse.ts index b21571c..049eafd 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -3,6 +3,7 @@ import * as _ from 'lodash' import {Arg} from './args' +import * as Errors from './errors' import * as Flags from './flags' let debug: any @@ -41,7 +42,7 @@ export class Parser } - constructor(readonly input: T) { + constructor(private readonly input: T) { this.argv = input.argv.slice(0) this._setNames() this.booleanFlags = _.pickBy(input.flags, f => f.type === 'boolean') as any @@ -155,7 +156,11 @@ export class Parser { + describe('flag options', () => { it('accepts valid option', () => { - const foo = flags.enum<'myopt' | 'myotheropt'>({options: ['myopt', 'myotheropt']}) const out = parse(['--foo', 'myotheropt'], { - flags: {foo: foo()}, + flags: {foo: flags.string({options: ['myopt', 'myotheropt']})}, }) expect(out.flags.foo).to.equal('myotheropt') }) it('fails when invalid', () => { - const foo = flags.enum({options: ['myopt', 'myotheropt']}) expect(() => { - parse(['--foo', 'bar'], { - flags: {foo: foo()}, + parse(['--foo', 'invalidopt'], { + flags: {foo: flags.string({options: ['myopt', 'myotheropt']})}, + }) + }).to.throw('Expected --foo=invalidopt to be one of: myopt, myotheropt') + }) + }) + + describe('arg options', () => { + it('accepts valid option', () => { + const out = parse(['myotheropt'], { + args: [{name: 'foo', options: ['myopt', 'myotheropt']}], + }) + expect(out.args.foo).to.equal('myotheropt') + }) + + it('fails when invalid', () => { + expect(() => { + parse(['invalidopt'], { + args: [{name: 'foo', options: ['myopt', 'myotheropt']}], }) - }).to.throw('Expected --foo=bar to be one of: myopt, myotheropt') + }).to.throw('Expected invalidopt to be one of: myopt, myotheropt') }) }) })