Add packages/argparse
This commit is contained in:
parent
15d54639ab
commit
47859ffa34
@ -9,7 +9,8 @@
|
||||
"@rondo/image-upload": "file:packages/image-upload",
|
||||
"@rondo/tasq": "file:packages/tasq",
|
||||
"@rondo/jsonrpc": "file:packages/jsonrpc",
|
||||
"@rondo/scripts": "file:packages/scripts"
|
||||
"@rondo/scripts": "file:packages/scripts",
|
||||
"@rondo/argparse": "file:packages/argparse"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^3.0.0",
|
||||
|
||||
1
packages/argparse/README.md
Normal file
1
packages/argparse/README.md
Normal file
@ -0,0 +1 @@
|
||||
# argparse
|
||||
18
packages/argparse/jest.config.js
Normal file
18
packages/argparse/jest.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
roots: [
|
||||
'<rootDir>/src'
|
||||
],
|
||||
transform: {
|
||||
'^.+\\.tsx?$': 'ts-jest'
|
||||
},
|
||||
testRegex: '(/__tests__/.*|\\.(test|spec))\\.tsx?$',
|
||||
moduleFileExtensions: [
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
'jsx'
|
||||
],
|
||||
setupFiles: ['<rootDir>/jest.setup.js'],
|
||||
maxConcurrency: 1,
|
||||
verbose: false
|
||||
}
|
||||
0
packages/argparse/jest.setup.js
Normal file
0
packages/argparse/jest.setup.js
Normal file
14
packages/argparse/package.json
Normal file
14
packages/argparse/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@rondo/argparse",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"lint": "tslint --project .",
|
||||
"compile": "tsc",
|
||||
"clean": "rm -rf lib/"
|
||||
},
|
||||
"dependencies": {},
|
||||
"types": "lib/index.d.ts",
|
||||
"devDependencies": {},
|
||||
"module": "lib/index.js"
|
||||
}
|
||||
37
packages/argparse/src/argparse.test.ts
Normal file
37
packages/argparse/src/argparse.test.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import {argparse} from './argparse'
|
||||
|
||||
describe('argparse', () => {
|
||||
|
||||
it('parses args', () => {
|
||||
const args = argparse(['--one', '1', '--two', '2', '--four'], {
|
||||
one: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
two: {
|
||||
type: 'number',
|
||||
default: 1,
|
||||
},
|
||||
four: {
|
||||
type: 'boolean',
|
||||
}
|
||||
})
|
||||
|
||||
const one: string = args.one
|
||||
const two: number = args.two
|
||||
const four: boolean = args.four
|
||||
|
||||
expect(one).toBe('1')
|
||||
expect(two).toBe(2)
|
||||
expect(four).toBe(true)
|
||||
})
|
||||
|
||||
it('throws when required args missing', () => {
|
||||
expect(() => argparse([], {
|
||||
one: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
})).toThrowError(/missing required/i)
|
||||
})
|
||||
})
|
||||
103
packages/argparse/src/argparse.ts
Normal file
103
packages/argparse/src/argparse.ts
Normal file
@ -0,0 +1,103 @@
|
||||
export type TArgTypeName = 'string' | 'number' | 'boolean'
|
||||
export type TArgType<T extends TArgTypeName> =
|
||||
T extends 'string'
|
||||
? string
|
||||
: T extends 'number'
|
||||
? number
|
||||
: T extends 'boolean'
|
||||
? boolean
|
||||
: never
|
||||
|
||||
export interface IArgConfig<T extends TArgTypeName> {
|
||||
type: T
|
||||
default?: TArgType<T>
|
||||
required?: boolean
|
||||
}
|
||||
|
||||
export interface IArgsConfig {
|
||||
[arg: string]: IArgConfig<TArgTypeName>
|
||||
}
|
||||
|
||||
export type TArgs<T> = {
|
||||
[k in keyof T]: T[k] extends IArgConfig<infer A> ?
|
||||
TArgType<A> : never
|
||||
}
|
||||
|
||||
const iterate = <T>(arr: T[]) => {
|
||||
let i = -1
|
||||
return {
|
||||
hasNext() {
|
||||
return i < arr.length - 1
|
||||
},
|
||||
next(): T {
|
||||
return arr[++i]
|
||||
},
|
||||
peek(): T {
|
||||
return arr[i + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assert(cond: boolean, message: string) {
|
||||
if (!cond) {
|
||||
throw new Error('Error parsing arguments: ' + message)
|
||||
}
|
||||
}
|
||||
|
||||
export function argparse<T extends object>(
|
||||
args: string[],
|
||||
config: T extends IArgsConfig ? T : never,
|
||||
): TArgs<T> {
|
||||
const result = {} as TArgs<T>
|
||||
const it = iterate(args)
|
||||
|
||||
const usedArgs: Record<string, true> = {}
|
||||
const requiredArgs = Object.keys(config).reduce((obj, arg) => {
|
||||
const argConfig = config[arg]
|
||||
if (argConfig.default !== undefined) {
|
||||
result[arg] = argConfig.default
|
||||
}
|
||||
obj[arg] = !!argConfig.required
|
||||
return obj
|
||||
}, {} as Record<string, boolean>)
|
||||
|
||||
while(it.hasNext()) {
|
||||
const arg = it.next()
|
||||
assert(arg.substring(0, 2) === '--', 'Arguments must start with --')
|
||||
const argName = arg.substring(2)
|
||||
const argConfig = config[argName]
|
||||
assert(!!argConfig, 'Unknown argument: ' + arg)
|
||||
delete requiredArgs[argName]
|
||||
usedArgs[argName] = true
|
||||
const peek = it.peek()
|
||||
switch(argConfig.type) {
|
||||
case 'string':
|
||||
assert(it.hasNext(), 'Value of argument must be a string: ' + arg)
|
||||
result[argName] = it.next()
|
||||
continue
|
||||
case 'number':
|
||||
const num = parseInt(it.next(), 10)
|
||||
assert(!isNaN(num), 'Value of argument must be a number: ' + arg)
|
||||
result[argName] = num
|
||||
continue
|
||||
case 'boolean':
|
||||
if (peek === 'true') {
|
||||
it.next()
|
||||
result[argName] = true
|
||||
} else if (peek === 'false') {
|
||||
it.next()
|
||||
result[argName] = false
|
||||
} else {
|
||||
result[argName] = true
|
||||
}
|
||||
continue
|
||||
default:
|
||||
throw new Error('Unknown type:' + argConfig.type)
|
||||
}
|
||||
}
|
||||
|
||||
assert(!Object.keys(requiredArgs).length, 'Missing required args: ' +
|
||||
Object.keys(requiredArgs).map(r => '--' + r))
|
||||
|
||||
return result
|
||||
}
|
||||
1
packages/argparse/src/index.ts
Normal file
1
packages/argparse/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './argparse'
|
||||
7
packages/argparse/tsconfig.esm.json
Normal file
7
packages/argparse/tsconfig.esm.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "esm"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
9
packages/argparse/tsconfig.json
Normal file
9
packages/argparse/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../tsconfig.common.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"references": [
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user