/ live.thehmm.nl / back / node_modules / @rushstack / ts-command-line /

[ICO]NameLast modifiedSizeDescription
[PARENTDIR]Parent Directory  -  
[DIR]dist/2 years ago -  
[DIR]lib/2 years ago -  
[DIR]node_modules/2 years ago -  
[   ]LICENSE40 years ago1.1K 
[TXT]README.md40 years ago 11Kd7c1522 post receive test [كارل مبارك]
[   ]package.json2 years ago2.0K 
README.md

ts-command-line

This library helps you create professional command-line tools using TypeScript. By "professional", we mean:

Internally, the implementation is based on argparse and the Python approach to command-lines.

Compared to other libraries, ts-command-line doesn't provide zillions of custom syntaxes and bells and whistles. Instead it aims to be a simple, consistent, and professional solution for your command-line tool. Give it a try!

Some Terminology

Suppose that we want to parse a command-line like this:

widget --verbose push --force --max-count 123

In this example, we can identify the following components:

Parameter Kinds

Several different kinds of parameters are supported:

Parameter Kind Example Data Type Description
flag --verbose boolean Value is true if the flag was specified on the command line, false otherwise.
integer --max-retry 3 int The argument is an integer number
string --title "Hello, world" string The argument is a text string.
choice --color red string The argument is must be a string from a list of allowed choices (similar to an enum).
string list -o file1.txt -o file2.txt string[] The argument is a text string. The parameter can be specified multiple times to build a list.

Other parameter kinds could be implemented if requested. That said, keeping your CLI grammar simple and systematic makes it easier for users to learn.

Scaffold Model

If your tool uses the scaffold model, you will create subclasses of two abstract base classes: CommandLineParser for the overall command-line, and CommandLineAction for each action.

Continuing our example from above, suppose we want to start with a couple simple flags like this:

widget --verbose push --force

We could define our subclass for the "push" action like this:

export class PushAction extends CommandLineAction {
  private _force: CommandLineFlagParameter;
  private _protocol: CommandLineChoiceParameter;

  public constructor() {
    super({
      actionName: 'push',
      summary: 'Pushes a widget to the service',
      documentation: 'Here we provide a longer description of how our action works.'
    });
  }

  protected onExecute(): Promise<void> { // abstract
    return BusinessLogic.doTheWork(this._force.value, this._protocol.value || "(none)");
  }

  protected onDefineParameters(): void { // abstract
    this._force = this.defineFlagParameter({
      parameterLongName: '--force',
      parameterShortName: '-f',
      description: 'Push and overwrite any existing state'
    });

    this._protocol = this.defineChoiceParameter({
      parameterLongName: '--protocol',
      description: 'Specify the protocol to use',
      alternatives: ['ftp', 'webdav', 'scp'],
      environmentVariable: 'WIDGET_PROTOCOL',
      defaultValue: 'scp'
    });
  }
}

Then we might define the parser subclass like this:

export class WidgetCommandLine extends CommandLineParser {
  private _verbose: CommandLineFlagParameter;

  public constructor() {
    super({
      toolFilename: 'widget',
      toolDescription: 'The "widget" tool is a code sample for using the @rushstack/ts-command-line library.'
    });

    this.addAction(new PushAction());
  }

  protected onDefineParameters(): void { // abstract
    this._verbose = this.defineFlagParameter({
      parameterLongName: '--verbose',
      parameterShortName: '-v',
      description: 'Show extra logging detail'
    });
  }

  protected onExecute(): Promise<void> { // override
    BusinessLogic.configureLogger(this._verbose.value);
    return super.onExecute();
  }
}

To invoke the parser, the application entry point will do something like this:

const commandLine: WidgetCommandLine = new WidgetCommandLine();
commandLine.execute();

When we run widget --verbose push --force, the PushAction.onExecute() method will get invoked and then your business logic takes over.


For a more complete example, take a look at the ts-command-line-test sample project.


Testing out the docs

If you invoke the tool as "widget --help", the docs are automatically generated:

usage: widget [-h] [-v] <command> ...

The "widget" tool is a code sample for using the @rushstack/ts-command-line
library.

Positional arguments:
  <command>
    push         Pushes a widget to the service

Optional arguments:
  -h, --help     Show this help message and exit.
  -v, --verbose  Show extra logging detail

For detailed help about a specific command, use: widget <command> -h

For help about the push action, the user can type "widget push --help", which shows this output:

usage: widget push [-h] [-f] [--protocol {ftp,webdav,scp}]

Here we provide a longer description of how our action works.

Optional arguments:
  -h, --help            Show this help message and exit.
  -f, --force           Push and overwrite any existing state
  --protocol {ftp,webdav,scp}
                        Specify the protocol to use. This parameter may
                        alternatively specified via the WIDGET_PROTOCOL
                        environment variable. The default value is "scp".

Dynamic Model

The action subclasses provide a simple, recognizable pattern that you can use across all your tooling projects. It's the generally recommended approach. However, there are some cases where we need to break out of the scaffold. For example:

In this case, you can use the DynamicCommandLineAction and DynamicCommandLineParser classes which are not abstract (and not intended to be subclassed). Here's our above example rewritten for this model:

// Define the parser
const commandLineParser: DynamicCommandLineParser = new DynamicCommandLineParser({
  toolFilename: 'widget',
  toolDescription: 'The "widget" tool is a code sample for using the @rushstack/ts-command-line library.'
});

commandLineParser.defineFlagParameter({
  parameterLongName: '--verbose',
  parameterShortName: '-v',
  description: 'Show extra logging detail'
});

// Define the action
const action: DynamicCommandLineAction = new DynamicCommandLineAction({
  actionName: 'push',
  summary: 'Pushes a widget to the service',
  documentation: 'Here we provide a longer description of how our action works.'
});

commandLineParser.addAction(action);

action.defineFlagParameter({
  parameterLongName: '--force',
  parameterShortName: '-f',
  description: 'Push and overwrite any existing state'
});

action.defineChoiceParameter({
  parameterLongName: '--protocol',
  description: 'Specify the protocol to use',
  alternatives: ['ftp', 'webdav', 'scp'],
  environmentVariable: 'WIDGET_PROTOCOL',
  defaultValue: 'scp'
});

// Parse the command line
commandLineParser.execute().then(() => {
  console.log('The action is: ' + commandLineParser.selectedAction!.actionName);
  console.log('The force flag is: ' + action.getFlagParameter('--force').value);
});

You can also mix the two models. For example, we could augment the WidgetCommandLine from the original model by adding DynamicAction objects to it.

Here are some real world GitHub projects that illustrate different use cases for ts-command-line:

@rushstack/ts-command-line is part of the Rush Stack family of projects.

Apache/2.4.38 (Debian) Server at www.karls.computer Port 80