Add better --help support in argparse
This commit is contained in:
parent
7182684b4d
commit
f1865a0cc4
@ -2,6 +2,15 @@ import {argparse, arg, IArgsConfig} from './argparse'
|
|||||||
|
|
||||||
describe('argparse', () => {
|
describe('argparse', () => {
|
||||||
|
|
||||||
|
const CMD = 'command'
|
||||||
|
const exit = jest.fn()
|
||||||
|
const log = jest.fn()
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
exit.mockClear()
|
||||||
|
log.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
it('parses args', () => {
|
it('parses args', () => {
|
||||||
const args = argparse({
|
const args = argparse({
|
||||||
one: arg('string', {required: true}),
|
one: arg('string', {required: true}),
|
||||||
@ -9,7 +18,7 @@ describe('argparse', () => {
|
|||||||
four: {
|
four: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
}).parse(['--one', '1', '--two', '2', '--four'])
|
}).parse([CMD, '--one', '1', '--two', '2', '--four'])
|
||||||
|
|
||||||
const one: string = args.one
|
const one: string = args.one
|
||||||
const two: number = args.two
|
const two: number = args.two
|
||||||
@ -26,7 +35,7 @@ describe('argparse', () => {
|
|||||||
bool: {
|
bool: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
},
|
},
|
||||||
}).parse([])
|
}).parse([CMD])
|
||||||
const value: boolean = result.bool
|
const value: boolean = result.bool
|
||||||
expect(value).toBe(false)
|
expect(value).toBe(false)
|
||||||
})
|
})
|
||||||
@ -36,7 +45,7 @@ describe('argparse', () => {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
}).parse([])).toThrowError(/Missing required args: bool/)
|
}).parse([CMD])).toThrowError(/Missing required args: bool/)
|
||||||
})
|
})
|
||||||
it('optionally accepts a true/false value', () => {
|
it('optionally accepts a true/false value', () => {
|
||||||
const {parse} = argparse({
|
const {parse} = argparse({
|
||||||
@ -48,10 +57,10 @@ describe('argparse', () => {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse(['--bool']).bool).toBe(true)
|
expect(parse([CMD, '--bool']).bool).toBe(true)
|
||||||
expect(parse(['--bool', 'false']).bool).toBe(false)
|
expect(parse([CMD, '--bool', 'false']).bool).toBe(false)
|
||||||
expect(parse(['--bool', 'true']).bool).toBe(true)
|
expect(parse([CMD, '--bool', 'true']).bool).toBe(true)
|
||||||
expect(parse(['--bool', '--other', 'value'])).toEqual({
|
expect(parse([CMD, '--bool', '--other', 'value'])).toEqual({
|
||||||
bool: true,
|
bool: true,
|
||||||
other: 'value',
|
other: 'value',
|
||||||
})
|
})
|
||||||
@ -71,27 +80,27 @@ describe('argparse', () => {
|
|||||||
alias: 'o',
|
alias: 'o',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse([])).toEqual({
|
expect(parse([CMD])).toEqual({
|
||||||
a1: false,
|
a1: false,
|
||||||
b: false,
|
b: false,
|
||||||
other: '',
|
other: '',
|
||||||
})
|
})
|
||||||
expect(parse(['-ab'])).toEqual({
|
expect(parse([CMD, '-ab'])).toEqual({
|
||||||
a1: true,
|
a1: true,
|
||||||
b: true,
|
b: true,
|
||||||
other: '',
|
other: '',
|
||||||
})
|
})
|
||||||
expect(parse(['-ca'])).toEqual({
|
expect(parse([CMD, '-ca'])).toEqual({
|
||||||
a1: true,
|
a1: true,
|
||||||
b: true,
|
b: true,
|
||||||
other: '',
|
other: '',
|
||||||
})
|
})
|
||||||
expect(parse(['-abo', 'test'])).toEqual({
|
expect(parse([CMD, '-abo', 'test'])).toEqual({
|
||||||
a1: true,
|
a1: true,
|
||||||
b: true,
|
b: true,
|
||||||
other: 'test',
|
other: 'test',
|
||||||
})
|
})
|
||||||
expect(() => parse(['-abo'])).toThrowError(/must be a string: -abo/)
|
expect(() => parse([CMD, '-abo'])).toThrowError(/must be a string: -abo/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -102,18 +111,18 @@ describe('argparse', () => {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse([])).toEqual({
|
expect(parse([CMD])).toEqual({
|
||||||
a: NaN,
|
a: NaN,
|
||||||
})
|
})
|
||||||
expect(() => parse(['-a'])).toThrowError(/must be a number: -a/)
|
expect(() => parse([CMD, '-a'])).toThrowError(/must be a number: -a/)
|
||||||
expect(() => parse(['-a', 'no-number']))
|
expect(() => parse([CMD, '-a', 'no-number']))
|
||||||
.toThrowError(/must be a number: -a/)
|
.toThrowError(/must be a number: -a/)
|
||||||
expect(() => parse(['--a', 'no-number']))
|
expect(() => parse([CMD, '--a', 'no-number']))
|
||||||
.toThrowError(/must be a number: --a/)
|
.toThrowError(/must be a number: --a/)
|
||||||
expect(parse(['-a', '10'])).toEqual({
|
expect(parse([CMD, '-a', '10'])).toEqual({
|
||||||
a: 10,
|
a: 10,
|
||||||
})
|
})
|
||||||
expect(parse(['--a', '11'])).toEqual({
|
expect(parse([CMD, '--a', '11'])).toEqual({
|
||||||
a: 11,
|
a: 11,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -129,13 +138,14 @@ describe('argparse', () => {
|
|||||||
choices: [1, 2],
|
choices: [1, 2],
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
expect(() => parse(['--choice', 'c'])).toThrowError(/one of: a, b$/)
|
expect(() => parse([CMD, '--choice', 'c'])).toThrowError(/one of: a, b$/)
|
||||||
expect(() => parse(['--num', '3'])).toThrowError(/must be one of: 1, 2$/)
|
expect(() => parse([CMD, '--num', '3']))
|
||||||
expect(parse(['--choice', 'a', '--num', '1'])).toEqual({
|
.toThrowError(/must be one of: 1, 2$/)
|
||||||
|
expect(parse([CMD, '--choice', 'a', '--num', '1'])).toEqual({
|
||||||
choice: 'a',
|
choice: 'a',
|
||||||
num: 1,
|
num: 1,
|
||||||
})
|
})
|
||||||
expect(parse(['--choice', 'b', '--num', '2'])).toEqual({
|
expect(parse([CMD, '--choice', 'b', '--num', '2'])).toEqual({
|
||||||
choice: 'b',
|
choice: 'b',
|
||||||
num: 2,
|
num: 2,
|
||||||
})
|
})
|
||||||
@ -144,22 +154,25 @@ describe('argparse', () => {
|
|||||||
|
|
||||||
describe('string[] and n', () => {
|
describe('string[] and n', () => {
|
||||||
it('has a value of n = 1 by default', () => {
|
it('has a value of n = 1 by default', () => {
|
||||||
const {parse, help} = argparse({
|
const {parse} = argparse({
|
||||||
value: {
|
value: {
|
||||||
type: 'string[]',
|
type: 'string[]',
|
||||||
},
|
},
|
||||||
})
|
help: arg('boolean'),
|
||||||
expect(parse([]).value).toEqual([])
|
}, exit, log)
|
||||||
expect(parse(['--value', 'one']).value).toEqual(['one'])
|
expect(parse([CMD]).value).toEqual([])
|
||||||
expect(help()).toEqual([
|
expect(parse([CMD, '--value', 'one']).value).toEqual(['one'])
|
||||||
'[OPTIONS] ',
|
parse([CMD, '--help'])
|
||||||
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
|
`${CMD} [OPTIONS] `,
|
||||||
'',
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
' --value [VALUE] ',
|
' --value [VALUE] ',
|
||||||
|
' --help boolean ',
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
})
|
})
|
||||||
it('can be used to extract finite number of values', () => {
|
it('can be used to extract finite number of values', () => {
|
||||||
const {parse, help} = argparse({
|
const {parse} = argparse({
|
||||||
value: {
|
value: {
|
||||||
type: 'string[]',
|
type: 'string[]',
|
||||||
n: 3,
|
n: 3,
|
||||||
@ -168,68 +181,84 @@ describe('argparse', () => {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
alias: 'o',
|
alias: 'o',
|
||||||
},
|
},
|
||||||
})
|
help: arg('boolean'),
|
||||||
expect(parse([]).value).toEqual([])
|
}, exit, log)
|
||||||
expect(parse(['--value', 'a', 'b', '--other', '-o', '3'])).toEqual({
|
expect(parse([CMD]).value).toEqual([])
|
||||||
|
expect(parse([CMD, '--value', 'a', 'b', '--other', '-o', '3'])).toEqual({
|
||||||
value: ['a', 'b', '--other'],
|
value: ['a', 'b', '--other'],
|
||||||
other: 3,
|
other: 3,
|
||||||
|
help: false,
|
||||||
})
|
})
|
||||||
expect(help()).toEqual([
|
parse([CMD, '--help'])
|
||||||
'[OPTIONS] ',
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
|
`${CMD} [OPTIONS] `,
|
||||||
'',
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
' --value [VALUE1 VALUE2 VALUE3] ',
|
' --value [VALUE1 VALUE2 VALUE3] ',
|
||||||
'-o, --other number ',
|
'-o, --other number ',
|
||||||
|
' --help boolean ',
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
})
|
})
|
||||||
it('can be used to collect any remaining arguments when n = "+"', () => {
|
it('can be used to collect any remaining arguments when n = "+"', () => {
|
||||||
const {parse, help} = argparse({
|
const {parse} = argparse({
|
||||||
value: arg('string[]', {n: '+', required: true}),
|
value: arg('string[]', {n: '+', required: true}),
|
||||||
other: arg('number'),
|
other: arg('number'),
|
||||||
})
|
help: arg('boolean'),
|
||||||
expect(() => parse([])).toThrowError(/Missing required args: value/)
|
}, exit, log)
|
||||||
expect(parse(['--value', 'a', '--other', '3'])).toEqual({
|
expect(() => parse([CMD])).toThrowError(/Missing required args: value/)
|
||||||
|
expect(parse([CMD, '--value', 'a', '--other', '3'])).toEqual({
|
||||||
value: ['a', '--other', '3'],
|
value: ['a', '--other', '3'],
|
||||||
other: NaN,
|
other: NaN,
|
||||||
|
help: false,
|
||||||
})
|
})
|
||||||
expect(parse(['--other', '2', '--value', 'a', '--other', '3'])).toEqual({
|
expect(parse([CMD, '--other', '2', '--value', 'a', '--other', '3']))
|
||||||
|
.toEqual({
|
||||||
value: ['a', '--other', '3'],
|
value: ['a', '--other', '3'],
|
||||||
other: 2,
|
other: 2,
|
||||||
|
help: false,
|
||||||
})
|
})
|
||||||
expect(help()).toEqual([
|
parse([CMD, '--help'])
|
||||||
'[OPTIONS] ',
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
|
`${CMD} [OPTIONS] `,
|
||||||
'',
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
' --value VALUE... (required)',
|
' --value VALUE... (required)',
|
||||||
' --other number ',
|
' --other number ',
|
||||||
|
' --help boolean ',
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
})
|
})
|
||||||
it('can collect remaining positional arguments when n = "*"', () => {
|
it('can collect remaining positional arguments when n = "*"', () => {
|
||||||
const {parse, help} = argparse({
|
const {parse} = argparse({
|
||||||
value: arg('string[]', {n: '*', required: true, positional: true}),
|
value: arg('string[]', {n: '*', required: true, positional: true}),
|
||||||
other: arg('number'),
|
other: arg('number'),
|
||||||
})
|
help: arg('boolean'),
|
||||||
expect(parse(['a', 'b']).value).toEqual(['a', 'b'])
|
}, exit, log)
|
||||||
expect(() => parse(['--other', '3']).value)
|
expect(parse([CMD, 'a', 'b']).value).toEqual(['a', 'b'])
|
||||||
|
expect(() => parse([CMD, '--other', '3']).value)
|
||||||
.toThrowError(/Missing.*: value/)
|
.toThrowError(/Missing.*: value/)
|
||||||
expect(parse(['--other', '2', '--', '--other', '3'])).toEqual({
|
expect(parse([CMD, '--other', '2', '--', '--other', '3'])).toEqual({
|
||||||
value: ['--other', '3'],
|
value: ['--other', '3'],
|
||||||
other: 2,
|
other: 2,
|
||||||
|
help: false,
|
||||||
})
|
})
|
||||||
expect(parse(['--', '--other', '3'])).toEqual({
|
expect(parse([CMD, '--', '--other', '3'])).toEqual({
|
||||||
value: ['--other', '3'],
|
value: ['--other', '3'],
|
||||||
other: NaN,
|
other: NaN,
|
||||||
|
help: false,
|
||||||
})
|
})
|
||||||
expect(parse(['--other', '3', 'a', 'b', 'c'])).toEqual({
|
expect(parse([CMD, '--other', '3', 'a', 'b', 'c'])).toEqual({
|
||||||
value: ['a', 'b', 'c'],
|
value: ['a', 'b', 'c'],
|
||||||
other: 3,
|
other: 3,
|
||||||
|
help: false,
|
||||||
})
|
})
|
||||||
expect(help()).toEqual([
|
parse([CMD, '--help'])
|
||||||
'[OPTIONS] [VALUE...]',
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
|
`${CMD} [OPTIONS] [VALUE...]`,
|
||||||
'',
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
' --value [VALUE...] (required)',
|
' --value [VALUE...] (required)',
|
||||||
' --other number ',
|
' --other number ',
|
||||||
|
' --help boolean ',
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -242,8 +271,8 @@ describe('argparse', () => {
|
|||||||
positional: true,
|
positional: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse([]).a).toBe(NaN)
|
expect(parse([CMD]).a).toBe(NaN)
|
||||||
expect(parse(['12']).a).toBe(12)
|
expect(parse([CMD, '12']).a).toBe(12)
|
||||||
})
|
})
|
||||||
it('works with booleans', () => {
|
it('works with booleans', () => {
|
||||||
const {parse} = argparse({
|
const {parse} = argparse({
|
||||||
@ -252,10 +281,10 @@ describe('argparse', () => {
|
|||||||
positional: true,
|
positional: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse([]).a).toBe(false)
|
expect(parse([CMD]).a).toBe(false)
|
||||||
expect(parse(['true']).a).toBe(true)
|
expect(parse([CMD, 'true']).a).toBe(true)
|
||||||
expect(parse(['false']).a).toBe(false)
|
expect(parse([CMD, 'false']).a).toBe(false)
|
||||||
expect(() => parse(['invalid'])).toThrowError(/true or false/)
|
expect(() => parse([CMD, 'invalid'])).toThrowError(/true or false/)
|
||||||
})
|
})
|
||||||
it('works with strings', () => {
|
it('works with strings', () => {
|
||||||
const {parse} = argparse({
|
const {parse} = argparse({
|
||||||
@ -264,8 +293,8 @@ describe('argparse', () => {
|
|||||||
positional: true,
|
positional: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse([]).a).toBe('')
|
expect(parse([CMD]).a).toBe('')
|
||||||
expect(parse(['a']).a).toBe('a')
|
expect(parse([CMD, 'a']).a).toBe('a')
|
||||||
})
|
})
|
||||||
it('works with multiple positionals', () => {
|
it('works with multiple positionals', () => {
|
||||||
const {parse} = argparse({
|
const {parse} = argparse({
|
||||||
@ -278,7 +307,7 @@ describe('argparse', () => {
|
|||||||
positional: true,
|
positional: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse(['aaa', 'bbb'])).toEqual({
|
expect(parse([CMD, 'aaa', 'bbb'])).toEqual({
|
||||||
a: 'aaa',
|
a: 'aaa',
|
||||||
b: 'bbb',
|
b: 'bbb',
|
||||||
})
|
})
|
||||||
@ -296,7 +325,7 @@ describe('argparse', () => {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
expect(parse(['--arg1', 'one', '2', '--arg3', 'three'])).toEqual({
|
expect(parse([CMD, '--arg1', 'one', '2', '--arg3', 'three'])).toEqual({
|
||||||
arg1: 'one',
|
arg1: 'one',
|
||||||
arg2: 2,
|
arg2: 2,
|
||||||
arg3: 'three',
|
arg3: 'three',
|
||||||
@ -305,23 +334,28 @@ describe('argparse', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('help', () => {
|
describe('help', () => {
|
||||||
it('returns help string', () => {
|
it('prints help string and exits', () => {
|
||||||
const {help} = argparse({
|
const {parse} = argparse({
|
||||||
one: arg('string'),
|
one: arg('string'),
|
||||||
two: arg('number'),
|
two: arg('number'),
|
||||||
three: arg('boolean'),
|
three: arg('boolean'),
|
||||||
})
|
help: arg('boolean'),
|
||||||
expect(help()).toEqual([
|
}, exit, log)
|
||||||
'[OPTIONS] ',
|
expect(exit.mock.calls.length).toBe(0)
|
||||||
|
parse([CMD, '--help'])
|
||||||
|
expect(exit.mock.calls.length).toBe(1)
|
||||||
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
|
`${CMD} [OPTIONS] `,
|
||||||
'',
|
'',
|
||||||
'Options:',
|
'Options:',
|
||||||
' --one string ',
|
' --one string ',
|
||||||
' --two number ',
|
' --two number ',
|
||||||
' --three boolean ',
|
' --three boolean ',
|
||||||
|
' --help boolean ',
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
})
|
})
|
||||||
it('returns help string with alias, description, and samples', () => {
|
it('returns help string with alias, description, and samples', () => {
|
||||||
const {help} = argparse({
|
const {parse} = argparse({
|
||||||
one: arg('string', {
|
one: arg('string', {
|
||||||
description: 'first argument',
|
description: 'first argument',
|
||||||
required: true,
|
required: true,
|
||||||
@ -336,15 +370,20 @@ describe('argparse', () => {
|
|||||||
three: arg('number', {
|
three: arg('number', {
|
||||||
positional: true,
|
positional: true,
|
||||||
}),
|
}),
|
||||||
})
|
help: arg('boolean'),
|
||||||
expect(help()).toEqual([
|
}, exit, log)
|
||||||
'[OPTIONS] TWO [THREE]',
|
expect(exit.mock.calls.length).toBe(0)
|
||||||
|
parse([CMD, '--help'])
|
||||||
|
expect(exit.mock.calls.length).toBe(1)
|
||||||
|
expect(log.mock.calls[0][0]).toEqual([
|
||||||
|
`${CMD} [OPTIONS] TWO [THREE]`,
|
||||||
'',
|
'',
|
||||||
'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)',
|
||||||
' --two number (required)',
|
' --two number (required)',
|
||||||
' --three number ',
|
' --three number ',
|
||||||
|
' --help boolean ',
|
||||||
].join('\n'))
|
].join('\n'))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -356,7 +395,7 @@ describe('argparse', () => {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
}).parse([])).toThrowError(/missing required/i)
|
}).parse([CMD])).toThrowError(/missing required/i)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('throws when arg type is unknown', () => {
|
it('throws when arg type is unknown', () => {
|
||||||
@ -364,7 +403,7 @@ describe('argparse', () => {
|
|||||||
a: {
|
a: {
|
||||||
type: 'test',
|
type: 'test',
|
||||||
} as any,
|
} as any,
|
||||||
}).parse(['-a'])).toThrowError(/Unknown type: test/)
|
}).parse([CMD, '-a'])).toThrowError(/Unknown type: test/)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -16,8 +16,6 @@ export const N_DEFAULT_VALUE = 1
|
|||||||
|
|
||||||
export type TNumberOfArgs = number | '+' | '*'
|
export type TNumberOfArgs = number | '+' | '*'
|
||||||
|
|
||||||
export let exit = () => process.exit()
|
|
||||||
|
|
||||||
export interface IArgParam<T extends TArgTypeName> {
|
export interface IArgParam<T extends TArgTypeName> {
|
||||||
alias?: string
|
alias?: string
|
||||||
description?: string
|
description?: string
|
||||||
@ -142,10 +140,6 @@ function extractArray(
|
|||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isHelp(argv: string[]) {
|
|
||||||
return argv.some(a => /^(-h|--help)$/.test(a))
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkChoice<T>(argument: string, choice: T, choices?: T[]) {
|
function checkChoice<T>(argument: string, choice: T, choices?: T[]) {
|
||||||
if (choices) {
|
if (choices) {
|
||||||
assert(
|
assert(
|
||||||
@ -161,7 +155,7 @@ export function padRight(str: string, chars: number) {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
export function help(config: IArgsConfig) {
|
export function help(command: string, config: IArgsConfig) {
|
||||||
const keys = Object.keys(config)
|
const keys = Object.keys(config)
|
||||||
|
|
||||||
function getArrayHelp(
|
function getArrayHelp(
|
||||||
@ -189,6 +183,7 @@ export function help(config: IArgsConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const positionalHelp = [
|
const positionalHelp = [
|
||||||
|
command,
|
||||||
'[OPTIONS]',
|
'[OPTIONS]',
|
||||||
keys
|
keys
|
||||||
.filter(k => config[k].positional)
|
.filter(k => config[k].positional)
|
||||||
@ -237,12 +232,16 @@ export function arg<T extends TArgTypeName>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function argparse<T extends IArgsConfig>(config: T) {
|
export function argparse<T extends IArgsConfig>(
|
||||||
|
config: T,
|
||||||
|
exit: () => void = () => process.exit(),
|
||||||
|
/* tslint:disable-next-line */
|
||||||
|
log: (message: string) => void = console.log.bind(console),
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
help(): string {
|
|
||||||
return help(config)
|
|
||||||
},
|
|
||||||
parse(args: string[]): TArgs<T> {
|
parse(args: string[]): TArgs<T> {
|
||||||
|
const command = args[0]
|
||||||
|
args = args.slice(1)
|
||||||
const result: any = {}
|
const result: any = {}
|
||||||
const it = iterate(args)
|
const it = iterate(args)
|
||||||
|
|
||||||
@ -315,6 +314,12 @@ export function argparse<T extends IArgsConfig>(config: T) {
|
|||||||
? processFlags(argument)
|
? processFlags(argument)
|
||||||
: getNextPositional()
|
: getNextPositional()
|
||||||
const argConfig = config[argName]
|
const argConfig = config[argName]
|
||||||
|
if (!isPositional && argName === 'help') {
|
||||||
|
log(help(command, config))
|
||||||
|
exit()
|
||||||
|
// should never reach this in real life
|
||||||
|
return null as any
|
||||||
|
}
|
||||||
assert(!!argConfig, 'Unknown argument: ' + argument)
|
assert(!!argConfig, 'Unknown argument: ' + argument)
|
||||||
delete requiredArgs[argName]
|
delete requiredArgs[argName]
|
||||||
switch (argConfig.type) {
|
switch (argConfig.type) {
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
export {arg} from '@rondo/argparse'
|
|
||||||
import {info} from './log'
|
|
||||||
import {
|
|
||||||
argparse as configure, IArgsConfig, isHelp, TArgs
|
|
||||||
} from '@rondo/argparse'
|
|
||||||
|
|
||||||
export let exit = () => process.exit()
|
|
||||||
|
|
||||||
export function argparse<T extends IArgsConfig>(config: T) {
|
|
||||||
const parser = configure(config)
|
|
||||||
return {
|
|
||||||
...parser,
|
|
||||||
parse(args: string[]): TArgs<T> {
|
|
||||||
const result = parser.parse(args)
|
|
||||||
if ('help' in config && isHelp(args)) {
|
|
||||||
info(parser.help())
|
|
||||||
exit()
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as log from '../log'
|
import * as log from '../log'
|
||||||
import * as p from 'path'
|
import * as p from 'path'
|
||||||
import {argparse, arg} from '../argparse'
|
import {argparse, arg} from '@rondo/argparse'
|
||||||
import {findNodeModules} from '../modules'
|
import {findNodeModules} from '../modules'
|
||||||
import {join} from 'path'
|
import {join} from 'path'
|
||||||
import {run} from '../run'
|
import {run} from '../run'
|
||||||
@ -9,7 +9,7 @@ import {run} from '../run'
|
|||||||
const tsc = 'ttsc'
|
const tsc = 'ttsc'
|
||||||
|
|
||||||
export async function build(...argv: string[]) {
|
export async function build(...argv: string[]) {
|
||||||
const {parse, help} = argparse({
|
const {parse} = argparse({
|
||||||
project: arg('string', {
|
project: arg('string', {
|
||||||
alias: 'p',
|
alias: 'p',
|
||||||
default: '.',
|
default: '.',
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as log from '../log'
|
import * as log from '../log'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {argparse, arg} from '../argparse'
|
import {argparse, arg} from '@rondo/argparse'
|
||||||
import {run} from '../run'
|
import {run} from '../run'
|
||||||
|
|
||||||
export async function newlib(...argv: string[]) {
|
export async function newlib(...argv: string[]) {
|
||||||
const {parse, help} = argparse({
|
const {parse} = argparse({
|
||||||
name: arg('string', {positional: true, required: true}),
|
name: arg('string', {positional: true, required: true}),
|
||||||
namespace: arg('string', {default: '@rondo'}),
|
namespace: arg('string', {default: '@rondo'}),
|
||||||
help: arg('boolean', {
|
help: arg('boolean', {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as ts from 'typescript'
|
import * as ts from 'typescript'
|
||||||
import {argparse, arg} from '../argparse'
|
import {argparse, arg} from '@rondo/argparse'
|
||||||
|
|
||||||
function isObjectType(type: ts.Type): type is ts.ObjectType {
|
function isObjectType(type: ts.Type): type is ts.ObjectType {
|
||||||
return !!(type.flags & ts.TypeFlags.Object)
|
return !!(type.flags & ts.TypeFlags.Object)
|
||||||
@ -210,11 +210,9 @@ export function typecheck(...argv: string[]) {
|
|||||||
if (typeDefinitions.has(type)) {
|
if (typeDefinitions.has(type)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if (type.aliasSymbol) {
|
if (type.aliasSymbol) {
|
||||||
// // TODO figure out how to prevent iterating of properties from types
|
throw new Error('Type aliases are not supported')
|
||||||
// // such as strings
|
}
|
||||||
// return
|
|
||||||
// }
|
|
||||||
const typeParameters: ts.TypeParameter[] = []
|
const typeParameters: ts.TypeParameter[] = []
|
||||||
const expandedTypeParameters: ts.Type[] = []
|
const expandedTypeParameters: ts.Type[] = []
|
||||||
const allRelevantTypes: ts.Type[] = []
|
const allRelevantTypes: ts.Type[] = []
|
||||||
|
|||||||
@ -2,22 +2,32 @@
|
|||||||
import * as commands from './commands'
|
import * as commands from './commands'
|
||||||
import * as log from './log'
|
import * as log from './log'
|
||||||
import {TCommand} from './TCommand'
|
import {TCommand} from './TCommand'
|
||||||
|
import {argparse, arg} from '@rondo/argparse'
|
||||||
|
|
||||||
async function run(...argv: string[]) {
|
const {parse} = argparse({
|
||||||
const commandName = argv[0]
|
help: arg('boolean'),
|
||||||
if (!(commandName in commands)) {
|
debug: arg('boolean'),
|
||||||
|
command: arg('string', {required: true, positional: true}),
|
||||||
|
other: arg('string[]', {n: '*', positional: true}),
|
||||||
|
})
|
||||||
|
|
||||||
|
type TArgs = ReturnType<typeof parse>
|
||||||
|
|
||||||
|
async function run(args: TArgs) {
|
||||||
|
if (!(args.command 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')}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const command = (commands as any)[commandName] as TCommand
|
const command = (commands as any)[args.command] as TCommand
|
||||||
await command(...argv.slice(1))
|
await command(args.command, ...args.other)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof require !== 'undefined' && require.main === module) {
|
if (typeof require !== 'undefined' && require.main === module) {
|
||||||
run(...process.argv.slice(2))
|
const args = parse(process.argv.slice(1))
|
||||||
|
run(args)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
log.error('> ' + err.message)
|
log.error('> ' + (args.debug ? err.stack : err.message))
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user