This example can be found in bin/example-cli.js
git clone https://github.com/maarteNNNN/cli-builder.git
npm install
npm link
cli-builder help
npm unlink # to delete the cli-builder bin
npm install cli-builder
Be sure to take a look at bin/example-cli.js
Instantiate a new CLI with new REPLClient({ ...options })
REPL
stands for Read Eval Print Loop
defining an commands
Object as shown below in below
cli.run(commands);
// DO NOT ADD HELP TO THE ROOT OBJECT. THIS IS DYNAMICALLY MOUNTED
const commands = {
test: {
execute: () => console.log('this is the test run'),
help: 'help of test',
testing: {
execute: ({ argument, options }) =>
console.log('ARGUMENT: ', argument, '\nOPTIONS: ', options),
help: 'testing help',
input: '<arg-to-pass-to-execute-function>', // argument and options are passed as an object passed to execute function
// cmd test testing IAMPASSEDTOFUNCTION -f
options: [{ option: 'f', help: 'Follow the logs' }],
},
testing2: {
execute: () => console.log('executing testing2'),
help: 'testing2 help',
},
},
test2: {
execute: () => console.log('this is the test2 run'),
help: 'help of test2',
},
deep: {
nesting: {
works: {
as: {
// Without help
command: () =>
console.log('to run this type `deep nesting works as command`'),
// Or with help
// command: {
// // this get executed as `deep nesting works as command`
// execute: () => console.log('to run this type `deep nesting works as command`'),
// // this get executed as `deep nesting works as command help`
// help: 'help of command'
// }
},
},
},
},
runSomeFunction: async () => {
// DO SOME INSANE LOGIC HERE
},
// A more comprehensive example below titled: Defining help with only one execute function
exampleWithOneExecute: {
knownCommand: {
help: 'Help for this known command',
},
// Show the user there are other commands available
'any-yet-unknown-property': {
help: 'Help for ANY UNKOWN PROPERTY',
},
async execute({ argument, options }) {
console.log(this); // Mounts the REPLClient dynamically
console.log(param); // Passes the last given argument/param dynamically
console.log(options); // Passes the last given options/flags dynamically
},
},
};
WARNING
Help is dynamically mounted to the commands
object. It generates a function which will mount all child help
properties.
NOTE
Running is going through the object and should be written as arguments the following way: deep nesting works as command
for the example above (It executes the function or the execute
child function in case help wants to be added).
deep nesting works as command --help
(executes --help or -h
child function in case it's available)
WARNING
functions are added as camelCase but are transformed to kebab-case:
run-some-function
will call result in runSomeFunction
.
ADVANCED EXAMPLE
actions
can be used to integrate imported files. You can check out the example of a full-fledged cli implementation in ldpos-commander or SocketCluster.
The CLI (REPLCient
) object is passed to the actions functions by Function.prototype.bind
The bindActionArgs
are passed via Function.prototype.bind
as well. Except if an argument is passed in the commands object function Eg.
someFunctionCmd: {
// CLI as this will be available but bindActionArgs won't be as argument replaces them
execute: (argument) => cli.actions.someFunction(argument),
}
const actions = {
getUserData = async (id, someFunction, aString, aNumber) => {
try {
const data = await axios.get(`user/${id}`)
console.log(this.argv) // logs all passed arguments with - and --
// this references to the cli object as it's bound
this.successLog(data)
someFunction()
console.log(aString, aNumber)
} catch (e) {
throw new Error(e)
}
}
}
// Binding below array to `options.bindActionArgs` in `new REPLClient({ ...options })`
const options = {
bindActionArgs = [123, () => console.log('function executed'), 'a string', 23123]
}
const cli = new REPLClient(options)
const commands = {
// No arguments are passed here, they are mounted dynamically
anActionTest: async () => await cli.actions.getUserData()
}
cli.run(commands)
using cli-builder an-action-test
will execute the getUserData
function with the bindActionArgs
parameters bound to it.
When you want to dynamically output to the console Eg. get a JSON Object from an API with undefined structure but you want to be able to log any of those properties to the console it can be done via:
const commands = {
entrypoint: {
any: {
help: 'Help for any',
},
extra: {
help: 'Help for extra',
},
helping: {
help: 'Help for helping',
},
commands: {
help: 'Help for commands',
},
'any-unknown-property': {
help: 'Help for ANY UNKOWN PROPERTY',
},
async execute(param) {
param = cli.kebabCaseToCamel(param);
// We don't want the paging info in this case
const {
data: { data },
} = axios.get('https://reqres.in/api/users?page=2');
// If null it could be accessible, if undefined we know for sure it isn't
if (!data[param] === undefined)
throw new Error('Custom property not found.');
// The `REPLCient` is object is dynamically mounted
this.successLog(data[param], `${param}:`);
},
},
};