@jitesoft/cli
v2.5.0
Published
CLI API for node.js.
Downloads
16
Readme
Cli
CLI Helpers and parser for node.js.
Why?
Just cause it's fun. No real competitor to the more advanced cli helpers on the market!
How?
The base API class Manager
allows for registering Commands
. Each command can have a set of
Arguments
and Options
which are defined when the command is created.
The command class can either be extended or if wanted, passed a callback which will be fired instead of the
abstract handle
method. When the command is invoked (through the Manager
) the command handle will be
called with the command a Input
, the options set and arguments.
Example
import { Command, Option, Argument, Manager } from '@jitesoft/cli';
import myPackage from './package.json';
const manager = new Manager(myPackage.name, myPackage.description);
manager.register(
new Command('init', 'Initializes something cool', {}, async (command, input, args = [], options = []) => {
const result = await input.question('Could you write something?');
return result;
}
));
manager.register(new MySpecialCommand());
manager.register(
new Command('do-it', 'Runs something', {
args: [
new Argument('arg1', 'A special argument.', ['arg', 'a1'], true, String),
new Argument('arg2', 'Another special argument', [], false, Number)
],
options: [
new Option('opt1', 'An option!'),
new Option('opt2', 'Another option!', String, ['o', '2'])
],
aliases: [
'do-eeeet'
]
}, async (command, input, args = [], options = []) => {
return Promise.resolve('Wee!');
}
));
Promise.resolve(manager).then((mng) => {
return mng.invoke();
}).then((result) => {
console.log(result);
}).catch((error) => {
console.error(error.message);
}).finally(() => {
process.exit(1);
});
Docs
The API is kind of simple, but one might expect it to be slightly documented (more than the jsdocs supplied with the code!). The following classes are exposed and intended to be used when implementing the CLI project:
Manager
The manager is the Main class which handles most of the logic. Only one class instance should be needed, but it IS possible to use more than one by just creating a new instance of it.
Constructor (name: string, description: string, input: stream, output: stream)
The manager constructor expects a name and a description, the passed data is used in the default help command and you could pass ''
without it actually
breaking, but it will not be able to output the name and description of the application without it.
Easiest is to just pass the package.json name
and description
value.
It's also possible to add a input
and a output
object, which will be used when the command prints and listens to user input.
If they are left null, the standard stdin
and stdout
will be used for this.
register (command: Command) : self
The register method simply accepts a Command
object. If there is already a command with the name the command passed is using,
a error will be thrown letting you know that a command with that name is already registered. This also applies to aliases.
invoke (args: Array) : Promise<*>
The invoke command is pretty much the entrypoint of the manager. When called, it parses all the arguments and creates arguments and options
which are then passed to the command that is intended to be ran.
The default args
array is the standard process.argv
list, it expects the following structure:
[
'node',
'your-app-name',
'CommandName',
...args,
...options
]
The resulting return value is a promise, which can be caught if it is rejected and which, if resolved will contain the value that the command returns at the end of its handle.
registerHelp(cmd = null) : self
Further on, there is a registerHelp
method. This will be explained further down under Help command.
Read only getters:
count: int
Number of commands in manager.
Command
The command class is a data object which contains all data connected to a given command.
Simply put, the command is the initial argument used when calling the application. A command can have 0-N arguments and 0-N options.
All of those can be required or optional, but depending on the order they are passed, they will be expected to be ran in a specific order.
Any optional arguments or options will be expected after the required, this is automatically handled by the manager.
The command class have the following methods:
Constructor (name: string, description: string, options: object, callback: function|null)
The Command constructor requires two values, a name and a description. The name will be used later on when the command is called through the CLI and the
description is used in the help.
Optionally, one can add a Options object, which expects either (or all) of the following values: aliases: Array<string>
options: Array<Option>
, args: Array<Argument>
. If your command have none of the above, a empty object can be passed instead.
The final argument, callback
can be used to add a handle
callback to be used, the proffered method is to create a new command class which implements the handle
method, but it is okay to use a callback too.
The callback will be passed the same parameters as the handle
method below.
handle (command: Command, input: InputHandler, args: Array, options: Array): Promise<*>
The handle method of the command class is the method that is called when the command is invoked. If the command is called with wrong args and/or options, it will not be invoked. So all required values WILL be passed to the handle method. It's a good idea to make sure that the optional values you wish to use are set though.
The handle method takes a command
object, which is just the command that the handle is connected to. It's a good idea to use that instead of this
. The second object is the InputHandler
that the manager was initialized with (more info further down).
After input comes the args
(all set arguments) and the options
(all the set options).
When the handle method is done, it should return a Promise with any value that you intend to return to the callee. Errors are intended to be thrown and should be caught with a catch
in your app.
Read only getters:
aliases: Array<string>
List of aliases.args: Array<Argumnet>
List of arguments available.options: Array<Option>
List of options available.name: String
Name of command.description: string
Description of command.examples: Array<string>
Not yet implemented.
Argument
The argument class is a data class for a specific argument. Each argument have a name and a description, when it is parsed, a value will be set.
For an argument to be parsed properly, it either have to be a single word/string without spaces, or a string contained within quotation marks ("
).
Each argument that is set will be passed to the handle method of the command when it is invoked.
The argument have only a constructor and a set of read-only properties.
Constructor (name: string, description: string, required: bool, expectedType: Constructor)
When called, the name
and description
values need to be set. Both of those are used in the help command and the name is the only way that the argument is mapped.
Optional parameters are required
which indicates if it is an argument that must be used when calling the command and expectedType
which is intended to be a constructor
of the type that the argument is expected to be (as of now, no validation functionality have been added for the expectedType argument).
Read only getters:
name: string
Argument name.description: string
Argument description.required: bool
Weather or not the argument is a required argument.expectedType: Constructor
A constructor that the value is expected to be (currently only used in help, no validation).value: string
The value that the user passed. This is only set if there was a value, else null. It is currently only a string value.
Option
An option is either a flag or a named argument. They are expected to be either in long-format (--optionName
) or in short (-o
). The name is always treated as a long version and
the aliases are treated as short. If a alias is longer than one character, the manager will not be able to parse it correctly as of now.
As with Argument
, the only available method is the constructor, but in difference to the arguments, the option does not need to have a value. If a option is passed to the handler, it is supplied by the user.
It is possible to flag a option to require a value, in that case, the value will be set if passed to the handler.
Constructor (name: string, description: string, aliases: Array, requireValue: bool, expectedType: Constructor, required: bool)
The name and description arguments are (as with argument) used mainly for mapping and to output help data. Aliases is optional and accepts a list of strings (one-char long or issues might arise as of now),
the requireValue
boolean defaults to false and if true, the manager will throw an error if the option does not have a value on call. expectedValue
is intended to be a constructor of the type that the value
is expected to be of. Currently it is only used for the help command, i.e., no validation implemented yet. The required
parameter also defaults to false, but if true, the option will be forced and the manager will throw an error in case it is not set.
Read only getters:
name: string
Option name.description: string
Option description.aliases: Array<string>
Array of short-style aliases.expectedType: Constructor
A constructor that the value is expected to be (currently only used in help, no validation).value: null|string
The value that the option was set to. This will be null if no value is set and if set, a string.
Input
The input handler object is just a tiny wrapper around the readline
module.
It exposes two methods and a instance of the class will be passed to the handler when invoked.
line (): Promise
The line
method will make the application wait until the user have made input and pressed enter. The line will be returned as a promise.
question (query: string): Promise
The question
method allows the application to query for some data with a output string and then await input. As with line
, one line will be expected and returned as a promise.
option (query: string, options: Array, error: string): Promise
The option method calls the question method and evaluates the user input against a list of options. It will keep on asking the query and output the error
parameter
if the value is not one of the options. When the value is finally resolved to one of the options, the option will be removed (as defined in the options parameter).
All tests are done as strings in lower-case, so it's not a case sensitive test.
questionOr (query: string, or: string): Promise
Same as question
method, but in case the input is empty, the or
parameter will be returned as a default value.
lineOr (or: string): Promise
Same as line
method, but in case the input is empty, the or
parameter will be returned as a default value.
output (): Promise
Outputs a line to the output stream.
Help command
There is a default help command implemented. This have a keyword reserved and is always added when creating the manager.
The current implementation will display the command list and the name
+ description
the manager was handed, and if a command is
used as argument, the command details will be displayed. It is possible to replace the help command with a command of your own, but instead of just registering the command, the registerHelp
method needs to be used.
If null is passed, the default help command will be used.
In contrast to the normal commands, the handler will receive a bit different arguments:
handle(cmd: Command, input: InputHandler, args: Array, commands: Array): Promise
The cmd
argument is the command itself, the input
is the InputHandler
instance, the args
is all arguments that are passed to the application (argv.splice(2)
) and the commands
argument is a list of all commands the manager have in store.
The return value should be the full help output.