Add better help for positional args
This commit is contained in:
parent
9fc4b064da
commit
64d244fe91
@ -255,6 +255,9 @@ describe('argparse', () => {
|
|||||||
expect(log.mock.calls[0][0]).toEqual([
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
`${CMD} [OPTIONS] [VALUE...]`,
|
`${CMD} [OPTIONS] [VALUE...]`,
|
||||||
'',
|
'',
|
||||||
|
'Positional arguments:',
|
||||||
|
' VALUE string[] (required)',
|
||||||
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
' --other number',
|
' --other number',
|
||||||
' --help boolean',
|
' --help boolean',
|
||||||
@ -269,9 +272,14 @@ describe('argparse', () => {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
positional: true,
|
positional: true,
|
||||||
},
|
},
|
||||||
})
|
}, exit, log)
|
||||||
expect(parse([CMD]).a).toBe(NaN)
|
expect(parse([CMD]).a).toBe(NaN)
|
||||||
expect(parse([CMD, '12']).a).toBe(12)
|
expect(parse([CMD, '12']).a).toBe(12)
|
||||||
|
parse([CMD, '--help'])
|
||||||
|
expect(log.mock.calls[0][0]).toEqual(`${CMD} [A]
|
||||||
|
|
||||||
|
Positional arguments:
|
||||||
|
A number`)
|
||||||
})
|
})
|
||||||
it('works with booleans', () => {
|
it('works with booleans', () => {
|
||||||
const {parse} = argparse({
|
const {parse} = argparse({
|
||||||
@ -377,6 +385,10 @@ describe('argparse', () => {
|
|||||||
expect(log.mock.calls[0][0]).toEqual([
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
`${CMD} [OPTIONS] TWO [THREE]`,
|
`${CMD} [OPTIONS] TWO [THREE]`,
|
||||||
'',
|
'',
|
||||||
|
'Positional arguments:',
|
||||||
|
' TWO number (required)',
|
||||||
|
' THREE number',
|
||||||
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
'-o, --one string first argument ' +
|
'-o, --one string first argument ' +
|
||||||
'(required, default: choice-1, choices: choice-1,choice-2)',
|
'(required, default: choice-1, choices: choice-1,choice-2)',
|
||||||
|
|||||||
@ -184,23 +184,7 @@ export function help(command: string, config: IArgsConfig) {
|
|||||||
return required ? array.join(' ') : `[${array.join(' ')}]`
|
return required ? array.join(' ') : `[${array.join(' ')}]`
|
||||||
}
|
}
|
||||||
|
|
||||||
const positionalHelp = [
|
function getDescription(argConfig: IArgument<TArgTypeName>): string {
|
||||||
relative(process.cwd(), command),
|
|
||||||
'[OPTIONS]',
|
|
||||||
keys
|
|
||||||
.filter(k => config[k].positional)
|
|
||||||
.map(k => getArrayHelp(k, config[k].required, config[k].n))
|
|
||||||
.join(' '),
|
|
||||||
].join(' ')
|
|
||||||
|
|
||||||
const options = keys.filter(k => !config[k].positional)
|
|
||||||
|
|
||||||
const argsHelp = 'Options:\n' + options.map(argument => {
|
|
||||||
const argConfig = config[argument]
|
|
||||||
const {alias, type} = argConfig
|
|
||||||
const name = alias
|
|
||||||
? `-${alias}, --${argument}`
|
|
||||||
: ` --${argument}`
|
|
||||||
const samples = []
|
const samples = []
|
||||||
if (argConfig.required) {
|
if (argConfig.required) {
|
||||||
samples.push('required')
|
samples.push('required')
|
||||||
@ -214,14 +198,66 @@ export function help(command: string, config: IArgsConfig) {
|
|||||||
const description = argConfig.description
|
const description = argConfig.description
|
||||||
? ' ' + argConfig.description : ''
|
? ' ' + argConfig.description : ''
|
||||||
const sample = samples.length ? ` (${samples.join(', ')})` : ''
|
const sample = samples.length ? ` (${samples.join(', ')})` : ''
|
||||||
const argType = type === 'string[]'
|
return description + sample
|
||||||
? getArrayHelp(argument, argConfig.required, argConfig.n)
|
}
|
||||||
: type
|
|
||||||
return padRight(name + ' ' + argType, 30) + ' ' + description + sample
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
|
|
||||||
return [positionalHelp, argsHelp]
|
function getPaddedName(nameAndType: string, description: string) {
|
||||||
|
return description
|
||||||
|
? padRight(nameAndType, 30) + ' ' + description
|
||||||
|
: nameAndType
|
||||||
|
}
|
||||||
|
|
||||||
|
function getArgType(
|
||||||
|
type: TArgTypeName, argument: string, required?: boolean, n?: TNumberOfArgs,
|
||||||
|
): string {
|
||||||
|
return type === 'string[]'
|
||||||
|
? getArrayHelp(argument, required, n)
|
||||||
|
: type
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionalArgs = keys
|
||||||
|
.filter(k => config[k].positional)
|
||||||
|
.map(argument => {
|
||||||
|
const argConfig = config[argument]
|
||||||
|
const {type, required, n} = argConfig
|
||||||
|
const nameAndType = ` ${argument.toUpperCase()} ${type}`
|
||||||
|
const description = getDescription(argConfig)
|
||||||
|
return getPaddedName(nameAndType, description)
|
||||||
|
})
|
||||||
|
|
||||||
|
const options = keys
|
||||||
|
.filter(k => !config[k].positional)
|
||||||
|
.map(argument => {
|
||||||
|
const argConfig = config[argument]
|
||||||
|
const {alias, type, required, n} = argConfig
|
||||||
|
const name = alias
|
||||||
|
? `-${alias}, --${argument}`
|
||||||
|
: ` --${argument}`
|
||||||
|
const description = getDescription(argConfig)
|
||||||
|
const argType = getArgType(type, argument, required, n)
|
||||||
|
const nameAndType = `${name} ${argType}`
|
||||||
|
return getPaddedName(nameAndType, description)
|
||||||
|
})
|
||||||
|
|
||||||
|
const positionalHelp = positionalArgs.length
|
||||||
|
? 'Positional arguments:\n' + positionalArgs.join('\n')
|
||||||
|
: ''
|
||||||
|
const optionsHelp = options.length
|
||||||
|
? 'Options:\n' + options.join('\n')
|
||||||
|
: ''
|
||||||
|
|
||||||
|
const commandHelp = [
|
||||||
|
relative(process.cwd(), command),
|
||||||
|
options.length ? '[OPTIONS]' : '',
|
||||||
|
keys
|
||||||
|
.filter(k => config[k].positional)
|
||||||
|
.map(k => getArrayHelp(k, config[k].required, config[k].n))
|
||||||
|
.join(' '),
|
||||||
|
]
|
||||||
|
.filter(k => k.length)
|
||||||
|
.join(' ')
|
||||||
|
|
||||||
|
return [commandHelp, positionalHelp, optionsHelp]
|
||||||
.filter(h => h.length)
|
.filter(h => h.length)
|
||||||
.join('\n\n')
|
.join('\n\n')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,29 +5,44 @@ import {TCommand} from './TCommand'
|
|||||||
import {argparse, arg} from '@rondo/argparse'
|
import {argparse, arg} from '@rondo/argparse'
|
||||||
|
|
||||||
const {parse} = argparse({
|
const {parse} = argparse({
|
||||||
help: arg('boolean'),
|
help: arg('boolean', {alias: 'h'}),
|
||||||
debug: arg('boolean'),
|
debug: arg('boolean'),
|
||||||
command: arg('string[]', {n: '+', required: true, positional: true}),
|
command: arg('string[]', {
|
||||||
|
n: '+',
|
||||||
|
required: true,
|
||||||
|
positional: true,
|
||||||
|
description: 'Must be one of: ' + Object.keys(commands).join(', '),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
type TArgs = ReturnType<typeof parse>
|
type TArgs = ReturnType<typeof parse>
|
||||||
|
|
||||||
async function run(args: TArgs) {
|
async function run(args: TArgs, exit: (code: number) => void) {
|
||||||
const commandName = args.command[0]
|
const commandName = args.command[0]
|
||||||
if (!(commandName in commands)) {
|
if (!(commandName in commands)) {
|
||||||
const c = Object.keys(commands).filter(cmd => !cmd.startsWith('_'))
|
const c = Object.keys(commands).filter(cmd => !cmd.startsWith('_'))
|
||||||
log.info(`Available commands:\n\n${c.join('\n')}`)
|
log.info(`Available commands:\n\n${c.join('\n')}`)
|
||||||
|
exit(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const command = (commands as any)[commandName] as TCommand
|
const command = (commands as any)[commandName] as TCommand
|
||||||
await command(...args.command)
|
await command(...args.command)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof require !== 'undefined' && require.main === module) {
|
async function start(
|
||||||
const args = parse(process.argv.slice(1))
|
argv: string[] = process.argv.slice(1),
|
||||||
run(args)
|
exit = (code: number) => process.exit(code),
|
||||||
.catch(err => {
|
) {
|
||||||
log.error('> ' + (args.debug ? err.stack : err.message))
|
let args: TArgs | null = null
|
||||||
process.exit(1)
|
try {
|
||||||
})
|
args = parse(argv)
|
||||||
|
await run(args, exit)
|
||||||
|
} catch (err) {
|
||||||
|
log.error((args && args.debug ? err.stack : err.message))
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof require !== 'undefined' && require.main === module) {
|
||||||
|
start()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user