npm package: @jitesoft/cli
Description: CLI API for node.js.
Author: Johannes Tegnér
Latest version: 2.5.0

# 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

```js
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](#manager)
* [Command](#command)
* [Argument](#argument)
* [Option](#option)
* [Input](#input)

### 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`](#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:

```json
[
    '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](#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<string>, 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<string>

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<string>

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<string>, error: string): Promise<string>

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<string>

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<string>

Same as `line` method, but in case the input is empty, the `or` parameter will be returned as a default value.

#### output (): Promise<void>

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<string>, commands: Array<Command>): Promise<string>

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.