@jitesoft/cli

2.5.0 • Public • Published

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.

Readme

Keywords

Package Sidebar

Install

npm i @jitesoft/cli

Weekly Downloads

0

Version

2.5.0

License

MIT

Unpacked Size

79.7 kB

Total Files

13

Last publish

Collaborators

  • jitesoft~
  • johannestegner