Skip to content

Commit

Permalink
fix: readOptions should remove args from internal state (#301)
Browse files Browse the repository at this point in the history
  • Loading branch information
dangreen authored Jan 17, 2022
1 parent 9206316 commit 97cbd42
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 25 deletions.
4 changes: 2 additions & 2 deletions .size-limit
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[
{
"path": "dist/index.cjs",
"limit": "765 B"
"limit": "780 B"
},
{
"path": "dist/index.js",
"limit": "610 B"
"limit": "620 B"
}
]
14 changes: 7 additions & 7 deletions src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
ArgRef,
PrimitiveConstructor,
OptionsReaderState,
OptionsReaderNext,
OptionsReaderRead,
OptionResult
} from './types'
import { matchArgName } from './utils'
Expand All @@ -30,12 +30,12 @@ export function alias<T extends string>(name: T, alias: string, ...restAliases:
*/
export function option<T extends string, K extends PrimitiveConstructor>(argRef: ArgRef<T>, type: K) {
if (type === String) {
return (option: string, next: OptionsReaderNext) => {
return (option: string, read: OptionsReaderRead) => {
const argName = matchArgName(argRef, option)

if (argName) {
return {
[argName]: next(true)
[argName]: read()
} as OptionResult<T, K>
}

Expand All @@ -44,12 +44,12 @@ export function option<T extends string, K extends PrimitiveConstructor>(argRef:
}

if (type === Number) {
return (option: string, next: OptionsReaderNext) => {
return (option: string, read: OptionsReaderRead) => {
const argName = matchArgName(argRef, option)

if (argName) {
return {
[argName]: parseFloat(next(true))
[argName]: parseFloat(read())
} as OptionResult<T, K>
}

Expand All @@ -58,12 +58,12 @@ export function option<T extends string, K extends PrimitiveConstructor>(argRef:
}

if (type === Array) {
return (option: string, next: OptionsReaderNext, options: OptionsReaderState) => {
return (option: string, read: OptionsReaderRead, options: OptionsReaderState) => {
const argName = matchArgName(argRef, option)

if (argName) {
const prevValues = options[argName]
const values = next(true).split(',')
const values = read().split(',')

return {
[argName]: Array.isArray(prevValues)
Expand Down
5 changes: 4 additions & 1 deletion src/options.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { setArgs } from './argv'
import { setArgs, argv } from './argv'
import { option } from './args'
import { readOptions } from './options'

Expand All @@ -14,12 +14,14 @@ describe('options', () => {
setArgs('--verbose')

expect(readOptions()).toEqual({})
expect(argv).toEqual(['--verbose'])
})

it('should return empty object if no options in args', () => {
setArgs('--debug')

expect(readOptions(option('verbose', Boolean))).toEqual({})
expect(argv).toEqual(['--debug'])
})

it('should return options with values', () => {
Expand All @@ -42,6 +44,7 @@ describe('options', () => {
'c'
]
})
expect(argv).toEqual(['compile', '--unknown'])
})

it('should throw error on unexpected end', () => {
Expand Down
30 changes: 24 additions & 6 deletions src/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { argv } from './argv'
import { OptionReader } from './types'
import { OptionReader, OptionResult } from './types'
import { Merge, ReturnTypes, UnionMerge } from './utils'

function isOption(arg: string) {
Expand All @@ -17,7 +17,7 @@ function createOptionReader<T extends OptionReader[]>(optionReaders: [...T]) {
return readOption
}

return (option, next, options) => readOption(option, next, options) ?? readNextOption(option, next, options)
return (option, read, options) => readOption(option, read, options) ?? readNextOption(option, read, options)
},
null
)
Expand All @@ -31,18 +31,31 @@ function createOptionReader<T extends OptionReader[]>(optionReaders: [...T]) {
* @returns Options with values.
*/
export function readOptions<T extends OptionReader[]>(...optionReaders: [...T]): Partial<Merge<ReturnTypes<T>>> {
if (!argv.length || !optionReaders.length) {
if (!argv.length) {
return {}
}

const readOption = createOptionReader(optionReaders)

if (!readOption) {
return {}
}

const options = {}
let i = 0
let arg = argv[i]
const next = (required = false) => {
let optionResult: OptionResult | null
const next = () => {
arg = argv[++i]
}
const remove = () => {
argv.splice(i--, 1)
}
const read = () => {
next()
remove()

if (required && !arg) {
if (!arg) {
throw new Error('Unexpected end of arguments')
}

Expand All @@ -52,7 +65,12 @@ export function readOptions<T extends OptionReader[]>(...optionReaders: [...T]):
// eslint-disable-next-line no-unmodified-loop-condition
while (arg) {
if (isOption(arg)) {
Object.assign(options, readOption?.(removePrefix(arg), next, options))
optionResult = readOption(removePrefix(arg), read, options)

if (optionResult) {
remove()
Object.assign(options, optionResult)
}
}

next()
Expand Down
13 changes: 4 additions & 9 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ export type ArgRefName<T extends ArgRef<string>> =

export type OptionsReaderState = Record<string, unknown>

export type OptionsReaderNext =
<T extends boolean>(required?: T) => (
T extends true
? string
: string | undefined
)
export type OptionsReaderRead = () => string

export type PrimitiveConstructor = StringConstructor | NumberConstructor | ArrayConstructor | BooleanConstructor

Expand All @@ -32,11 +27,11 @@ export type OptionValueType<T extends PrimitiveConstructor> =
? string[]
: boolean

export type OptionResult<T extends string, K extends PrimitiveConstructor> = Record<T, OptionValueType<K>>
export type OptionResult<T extends string = string, K extends PrimitiveConstructor = PrimitiveConstructor> = Record<T, OptionValueType<K>>

export type OptionReader<T extends OptionResult<string, PrimitiveConstructor> = OptionResult<string, PrimitiveConstructor>> =
export type OptionReader<T extends OptionResult = OptionResult> =
(
option: string,
next: OptionsReaderNext,
read: OptionsReaderRead,
options: OptionsReaderState
) => T | null

0 comments on commit 97cbd42

Please sign in to comment.