Modular typescript CLI builder
Why to have another cli parser when we have yargs, oclif and so on.
Reasons
- Modularity easy to reuse flags and parsing logic.
- Flag dependency logic. Flags can be validated against other flags.
- All validation can be interactive. All flags will be always with correct data. If user makes mistakes he have opportunity to fix it [select, retype]
const base = baseCommand()
.flag(['--debug', '-d'])
.parse((match)=>{
return {
debug: match('--debug', (v) => !!v, false)
}
})
const buildBase = base
.flag('--port')
.flag('--env')
.parse(async (match, prev)=> {
return {
...prev,
env: await match('--env', Prompt.select('Please select environment', ['production', 'development']), 'development')
port: await match('--port', Prompt.number('Please enter port number'), 3000)
}
})
cli({
cliName: 'pkg',
commands: [
base.command('lint').handle(()=> {
// Handle lint
}),
buildBase.command('watch').handle(({ debug, env, port })=>{
// handle watch command
}), buildBase.command('build').handle(({ debug, env, port })=>{
// handle watch command
})
]
})
- Arguments validation order Lets assume that we have 100 customers each customer have primary namespace. To get customer first of all we need valid namespace then we can have list of customer.
Case 1. cli execute --customer=1
- This command should fail because namespace is not provided. But instead of immediately throwing this library will show all available namespaces. And when we have namespace we can validate customer argument.
command('execute')
.flag('--namespace', 'required')
.flag('')
-
Arguments dependencies
Let's assume simple cli
yarn xxx --namespace abc --customer sample
- Customer value depends on namespace
- Let's assume that
abc
namespace is invalid and correct namespace isabc_123
Cli should fail