npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

clirio

v3.0.4

Published

Clirio is a mini framework for node.js command-line interfaces

Downloads

61

Readme

Clirio

A mini framework for node.js command-line interfaces based on TypeScript, decorators, DTOs

Clirio is a library for routing terminal command-lines. Clirio promotes using SOLID and data typing (an alternative to commander, args, argparse and etc.).

The author is inspired by angular, nestjs

You can integrate Clirio with other interactive command-line libs (like inquirer, terminal-kit, chalk and etc.)

Clirio starter kit is here

Demo

Table of Contents

Installation

npm install clirio
yarn add clirio

Quick Start

There are 3 easy steps to build Clirio App.

The example for emulating git status cli command with options

  1. Create DTO to define input options
import { Option } from 'clirio';

class GitStatusDto {
  @Option('--branch, -b')
  readonly branch?: string;

  @Option('--ignore-submodules')
  readonly ignoreSubmodules?: string;

  @Option('--short, -s')
  readonly short?: null;
}
  1. Create module to compose a set of commands
import { Module, Command, Options } from 'clirio';

@Module()
export class GitModule {
  @Command('git status')
  public status(@Options() options: GitStatusDto) {
    // use handled data here
    console.log(options);
  }
}
  1. Configure the main class
import { Clirio } from 'clirio';

const clirio = new Clirio();
clirio.addModule(GitModule);
clirio.execute();
Result
$ my-cli git status -b master --ignore-submodules  all --short

The application will route the command git status with options to the GitModule.status method.

{ branch: 'master', ignoreSubmodules: 'all', short: null }

Then use the received data for its intended purpose

The implementation of own cli prefix (like my-cli) is described in starter kit

The way with typing, validation, transformation

The GitStatusOptionsDto entity can be more typified with additional instructions

import { Clirio, Option, Validate, Transform } from 'clirio';

class GitStatusOptionsDto {
  @Option('--branch, -b')
  @Validate(Clirio.valid.STRING)
  readonly branch: string;

  @Option('--ignore-submodules, -i')
  @Validate((v) => ['none', 'untracked','dirty', 'all'].includes(v))
  readonly ignoreSubmodules: 'none' | 'untracked' | 'dirty' | 'all';

  @Option('--short, -s')
  @Transform(Clirio.form.FLAG)
  readonly short: boolean;
}
@Module()
export class GitModule {
  @Command('git status')
  public status(@Options() options: GitStatusDto) {
    // the "options" data has bean typified, validated, transformed here
    console.log(`branch: ${options.branch}`);
    console.log(`ignoreSubmodules: ${options.ignoreSubmodules}`);
    console.log(`short: ${options.short}`);
  }
}
Result
$ my-cli git status -b master --ignore-submodules  all --short
  branch: master
  ignoreSubmodules: all
  short: true

All the details are described below

Starter kit

Clirio is developed according to SOLID principles, so it is possible apply OOP, dependency injection and other programming patterns.

Clirio starter kit contains the recommended assembly. But it is possible to integrate any other libraries and custom decorators.

Definitions

Anatomy of a shell CLI is described in wiki

The author suggests using the following definitions to describe the Clirio specification.

Bash example
$  node migration-cli.js run 123556 -u user -p pass --db="db-name"
The incoming command-line

| node migration-cli.js | run 123556 -u user -p pass --db="db-name" | | :-------------------: | :---------------------------------------: | | launch path | arguments |

The parsed command-line

| node migration-cli.js | run 123556 | -u user -p pass --db="db-name" | | :-------------------: | :--------: | :----------------------------: | | launch path | command | options |

The matched command-line

| node migration-cli.js | run | 123556 | -u user | -p pass | --db="db-name" | | :-------------------: | :----: | :----: | :-----: | :-----: | :------------: | | launch path | action | param | option | option | option |

Arguments Definition

"Arguments" are all space-separated command-line parts after launch path

Command Definition

"Command" are space-separated command-line parts without leading dashes

Params Definition

"Params" are obtained values by matching the command in accordance with the given pattern

Options Definition

"Options" are command-line parts using a leading dashes

Each option is either a key-value or a key. If in the beginning 2 dashes is a long key if one dash is a short key which must be 1 character long:

--name=Alex, --name Alex, -n Alex, --version, -v

Parsing args

Parsing the command-line (Clirio implementation)

Clirio.parse('test --foo 15 -b -a -r 22');
Clirio.describe(['test', '--foo=15', '-b', '-a', '-r', '22']);

Result

[
  { "type": "action", "key": 0, "value": "test" },
  { "type": "option", "key": "foo", "value": "15" },
  { "type": "option", "key": "b", "value": null },
  { "type": "option", "key": "a", "value": null },
  { "type": "option", "key": "r", "value": "22" }
]

The another example

$ my-cli set-time 10:56 --format=AM -ei 15

| type | key | value | | ------ | -------- | ---------- | | action | 0 | "set-time" | | action | 1 | "10:56" | | option | "format" | "AM" | | option | "e" | null | | option | "i" | "15" |

Summary
  • all parts of the command-line without a leading dash will be described as actions
  • any action has keys as a numerical index in ascending order
  • any option with a missing value will be null
  • any option starting with a single dash will be separated by letters
  • all options will be parsed into key-value
  • the raw value of any options can be a string or null

App configuration

The application structure should consist of the following parts:

  1. the main class - Clirio
  2. modules (custom classes or their instances)
  3. actions (methods in class-modules with decorators )

Clirio - is the main class which configures the application and links modules

const cli = new Clirio();
cli.setModules([HelloModule, CommonModule, GitModule, MigrationModule]);
cli.execute();

Modules can be instantiated to apply dependency injection

cli.setModules([
  HelloModule,
  new CommonModule(),
  diContainer.resolve(CommonModule),
]);

The example is here

Modules

Clirio modules are custom classes with the @Module() decorator (they can be considered as controllers). An application can have either one or many modules. Each module contains actions (patterns for commands)

Using a common module

@Module()
export class CommonModule {
  @Command('hello there')
  public helloThere() {
    // ...
  }

  @Command('migration run')
  public migrationRun() {
    // ...
  }
}

As a result 2 commands will be available:

$ my-cli hello there
$ my-cli migration run

Using multiple modules to separate unrelated commands and structure the code

@Module('hello')
export class HelloModule {
  @Command('there')
  public helloThere() {
    // ...
  }
}

@Module('migration')
export class MigrationModule {
  @Command('run')
  public run() {
    // ...
  }
}

Actions

Clirio actions are methods in class-modules with decorators

@Module()
export class HelloModule {
  @Command('hello')
  public helloThere() {
    console.log('Hello! It works!');
  }

  @Command('bye')
  public help() {
    console.log('Bye! See you later');
  }

  @Empty()
  public empty() {
    console.log(chalk.yellow('You have not entered anything'));
  }

  @Failure()
  public failure() {
    console.log(chalk.red('You have entered a non-existent command'));
  }
}

Command patterns

The @Command() decorator is designed to specify the command pattern

@Module()
export class MigrationModule {
  @Command('init')
  public initMigration() {}

  @Command('migration run|up')
  public runMigration() {}

  @Command('migration to <name>')
  public migrateTo() {}

  @Command('migration merge <name1> <name2>')
  public mergeMigrations() {}

  @Command('migration delete <...names>')
  public deleteMigrations() {}
}

The total pattern based on @Module(...) and @Commands(...) will be matched with the command-line

Pattern can consist of one or more space-separated arguments

Case 1. Exact match

The exact command will be matched

  @Command('hello')
  @Command('hello my friends')

| Command pattern | Matching command-line | | ---------------- | --------------------- | | hello | hello | | hello there | hello there | | hello my friends | hello my friends |

Case 2. Match variants

Using the | operator to set match variants for params. Multiple command-lines will be matched to one route. The number of space-separated parts of the command-line should be the same.

  @Command('migration run|up')
  @Command('hello|hey|hi')

| Command pattern | Matching command-line | | ----------------- | ------------------------------ | | migration run|up | migration run migration up | | hello|hey|hi | hello hey hi |

Case 3. Pattern with value masks

Using the < > operator to specify a place for any value. The number of space-separated parts of the command-line should be the same

  @Command('hello <first-name> <last-name>')
  @Command('set-time <time>')

| Command pattern | Matching command-line | | ---------------------------------- | -------------------------------------------------------------------- | | hello <first-name> <last-name> | hello Alex Smithhello John Anderson ... etc. | | set-time <time> | set-time 11:50 set-time now set-time 1232343545 ... etc. |

Use Params data control to get the entered values

Case 4. Pattern with rest values mask

Using the <... > operator to specify a place for array of values This kind of mask can be only one per command pattern and should be at the end

  @Command('hello <...all-names>')
  @Command('get cities <...cities>')

| Command pattern | Matching command-line | | ------------------------ | ------------------------------------------------------------------- | | hello <...all-names> | hello Alex John Sarah Arthur hello Max ... etc. | | get cities <...cities> | get cities Prague New-York Moscowget cities Berlin ... etc. |

Use Params data control to get the entered values

Case 5. Option match

This pattern is designed for special cases like "help" and "version". This is an exact match of the option key and value. Match variants can be separated by comma

  @Command('--help, -h')
  @Command('--mode=check')

| Command pattern | Matching command-line | | --------------- | --------------------- | | --help, -h | --help -h | | --version, -v | --version -v | | --mode=check | --mode=check |

Use Options data control to add other options To avoid problems, do not mix this pattern with the rest ones

Empty command

The @Empty() action decorator is a way to catch the case when nothing is entered Each module can have its own @Empty() decorator in an action

@Module()
export class CommonModule {
  @Command('hello')
  public hello() {}

  @Empty()
  public empty() {
    console.log("You haven't entered anything");
  }
}
$ my-cli
You haven't entered anything

When a module has a command prefix, it will be matched and ranked

@Module('migration')
export class MigrationModule {
  @Command('init')
  public initMigration() {}

  @Empty()
  public empty() {
    console.log(
      'The migration module requires additional instruction. Type --help',
    );
  }
}
$ my-cli migration
The migration module requires additional instruction. Type --help

Failure command

The @Failure() action decorator is a way to catch the case when the specified command patterns don't match

Each module can have its own @Failure() decorator in an action

if this decorator is not specified, then a default error will be displayed

@Module()
export class CommonModule {
  @Command('hello')
  public hello() {}

  @Failure()
  public failure() {
    console.log('There is no such a command!');
  }
}
$ my-cli goodbye
There is no such a command!

When a module has a command prefix, it will be matched and ranked

@Module('migration')
export class MigrationModule {
  @Command('init')
  public initMigration() {}

  @Failure()
  public failure() {
    console.log('The migration module got the wrong instruction');
  }
}
$ my-cli migration stop
The migration module got the wrong instruction

Data control

Using Parameter Decorators to control input data The @Params() and @Options() decorators provided

@Module()
export class LocatorModule {
  @Command('get-location <city>')
  public getLocation(@Params() params: unknown, @Options() options: unknown) {
    console.log(params);
    console.log(options);
  }
}
$ my-cli get-location Prague --format=DMS --verbose
{ city: "Prague" }
{ format: "DMS", verbose: true }

Params data control

To handle incoming data, use DTO (instead of unknown type)

Params DTO

The @Param() decorator for dto properties provided. It can take a key in an param mask to map DTO properties

export class HelloParamsDto {
  @Param('first-name')
  readonly firstName?: string;

  @Param('last-name')
  readonly lastName?: string;
}
@Module()
export class HelloModule {
  @Command('hello <first-name> <last-name>')
  public hello(@Params() params: HelloParamsDto) {
    console.log(params);
  }
}

Here the second and third parts of the command-line are the masks for any values that the user enters The hello method will be called if the user enters a three-part command. The last 2 parts are passed to the params argument as keys and values

$ my-cli hello Alex Smith
{ firstName: "Alex", lastName: "Smith" }

The @Param() decorator can have no arguments. In this case DTO properties will map to input keys If the @Param() decorator is absent then there will be no mapping

export class HelloParamsDto {
  @Param()
  readonly 'first-name'?: string;

  @Param()
  readonly 'last-name'?: string;
}
$ my-cli hello Alex Smith
{ "first-name": "Alex", "last-name": "Smith" }
Example with the rest values mask
@Module()
export class GitModule {
  @Command('git add <...all-files>')
  public add(@Params() params: AddParamsDto) {
    // Type checking works for "params" variable
    console.log(params.allFiles);
  }
}
class AddParamsDto {
  @Param('all-files')
  readonly allFiles: string[];
}
$ my-cli git add test.txt logo.png
['test.txt', 'logo.png']

Options data control

The @Options() decorator provided

@Module()
export class GitModule {
  @Command('git status')
  public status(@Options() options: GitStatusOptionsDto) {
    console.log(options);
  }
}

Options DTO

The @Option() decorator for dto properties provided. It can accept comma-separated key aliases to map DTO properties

class GitStatusOptionsDto {
  @Option('--branch, -b')
  readonly branch?: string;

  @Option('--ignore-submodules, -i')
  readonly ignoreSubmodules?: string;

  @Option('--short, -s')
  @Transform(Clirio.form.FLAG)
  readonly short?: boolean;
}
$ my-cli git status --branch=master --ignore-submodules=all --short

$ my-cli git status --branch master --ignore-submodules all --short

$ my-cli git status -b master -i all -s

Each input data will lead to one result:

{ branch: 'master', ignoreSubmodules: 'all', short: true }

If the @Option() decorator is absent then there will be no mapping

$ my-cli git status --branch=master --ignore-submodules=all --short
{ branch: 'master', "ignore-submodule": 'all', short: true }

Input DTO

DTOs used to control inputs can have additional decorators, including custom ones All incoming key values can be null or string either an array of null or a string

type Value = string | null | (string | null)[];
example of data control (options and params)
@Module()
export class SomeModule {
  @Command('set-limit <limit>')
  public setLimit(
    @Params() params: SetLimitParamsDto,
    @Options() options: SetLimitOptionDto,
  ) {
    console.log(params);
    console.log(options);
  }
}

Clirio provides the @Validate() and @Transform() decorators In this example it is possible use them for DTO properties SetLimitParamsDto, SetLimitOptionDto

import { Option, Validate, Transform } from 'clirio';
import { MyCustomDecorator } from 'src/my-decorators';

class SetLimitParamsDto {
  @Param('limit')
  @Validate((v) => /^[0-9]+$/.test(v)) // checking for numbers only
  @Transform((v) => Number(v)) // string to number
  @MyCustomDecorator() // will be handled in "Pipe"
  readonly limit: number;
}

Envs data control

The @Envs() decorator provided

import { Envs } from 'clirio';

@Module('migration')
export class MigrationModule {
  @Command('test-connect')
  public testConnect(@Envs() envs: TestConnectEnvsDto) {
    // ...
  }
}

Envs DTO

The @Env() decorator for dto properties provided

import { Env } from 'clirio';

export class TestConnectEnvsDto {
  @Env('DB_HOST')
  readonly host: string;

  @Env('DB_PORT')
  @Transform((v) => Number(v))
  readonly port: number;

  @Env('DB_USER')
  readonly user: string;

  @Env('DB_PASSWORD')
  readonly password: string;
}

Validation

The @Validate() decorator provided to check input params and options. It must be used for DTO properties in conjunction with @Option() or @Param() (this depends on the type of controlled data) The @Validate() takes a function or an array of functions as an argument. Each function must return boolean or null:

  • if false is returned then an error throws
  • if null is returned then the key validation clause skips
  • if true is returned then the key validation will complete successfully
import { Module, Command, Options } from 'clirio';

@Module()
export class GitModule {
  @Command('git status')
  public status(@Options() options: GitStatusDto) {
    console.log(options);
  }
}
import { Option, Validate } from 'clirio';

class GitStatusDto {
  @Option('--branch, -b')
  @Validate((v) => typeof v === 'string')
  readonly branch: string;

  @Option('--ignore-submodules')
  @Validate(
    (v) => v === undefined || ['none', 'untracked', 'dirty', 'all'].includes(v),
  )
  readonly ignoreSubmodules?: 'none' | 'untracked' | 'dirty' | 'all';

  @Option('--short, -s')
  @Transform(Clirio.form.FLAG)
  readonly short?: boolean;
}
$ my-cli git status --ignore-submodules
The "branch" option is wrong

For any case of failed validation, the same error will be thrown The "%KEY_NAME%" %DATA_TYPE% is wrong

To have more flexible validations, use Pipes

It is possible to configure throwing errors on unknown keys in options

Validation of an optional key
class OptionsDto {
  @Option('--id')
  @Validate([(v) => v === undefined || null, /^[0-9]+$/.test(v)])
  readonly id?: number;
}
Using Clirio-made checks
class OptionsDto {
  @Option('--id')
  @Validate([Clirio.valid.OPTIONAL, Clirio.valid.NUMBER])
  readonly id?: number;
}
Validation of nullable key
class OptionsDto {
  @Option('--type')
  @Validate([Clirio.valid.NULLABLE, Clirio.valid.STRING])
  readonly type: string | null;
}

See Clirio-made checks

Transformation

The @Transform() decorator provided to transform input data.

It must be used for DTO properties in conjunction with @Option() or @Param() or @Env() (this depends on the type of controlled data)

The @Transform() takes a transform function as an argument

import { Option, Transform } from 'clirio';

class SetAutoParamsDto {
  @Param();
  @Transform((v) => v.toUpperCase())
  readonly model: string;

  @Param('speed-limit')
  @Transform((v) => Number(v))
  readonly speedLimit: number;
}

class SetAutoOptionsDto {
  @Option('--turbo')
  @Transform((v) => v === null || v === 'yes')
  readonly turbo: boolean;
}
import { Module, Command, Options } from 'clirio';

@Module('auto')
export class AutoModule {
  @Command('set <model> <speed-limit>')
  public set(
    @Params() params: SetAutoParamsDto,
    @Options() options: SetAutoOptionsDto,
  ) {
    console.log(params);
    console.log(options);
  }
}
$ my-cli auto set bmw 300 --turbo=yes
{ model: 'BMW', speedLimit: 300 }
{ turbo: true }
Summation and concatenation
@Module()
export class SumModule {
  @Command('sum <first> <second>')
  public sum(@Params() params: SumParamsDto) {
    console.log(params.first + params.second);
  }
}

Without transformation

class SumParamsDto {
  @Param()
  readonly first: unknown;

  @Param()
  readonly second: unknown;
}
$ my-cli sum 5 15
'515'

With transformation

class SumParamsDto {
  @Param()
  @Transform((v) => Number(v))
  readonly first: number;

  @Param()
  @Transform((v) => Number(v))
  readonly second: number;
}
$ my-cli sum 5 15
20
Using Clirio-made forms
class SumParamsDto {
  @Param()
  @Transform(Clirio.form.NUMBER)
  readonly first: number;
}
class SetAutoOptionsDto {
  @Option('--turbo')
  @Transform(Clirio.form.FLAG)
  readonly turbo: boolean;
}

See Clirio-made forms

Pipes

Pipes are designed to validate and transform controlled data (params and options). Using pipes is more functional than the @Validate() and @Transform() decorators and gives more flexibility

import { ClirioPipe, PipeContext, ClirioValidationError } from 'clirio';

export class CustomPipe implements ClirioPipe {
  transform(data: any, input: PipeContext): any | never {
    // controlled params
    if (input.dataType === 'params') {
      // validation
      if (myCustomCheckName(data.name)) {
        throw new ClirioValidationError('error message', {
          dataType: input.dataType,
          propertyName: 'name',
        });
      }

      // transformation
      return { 
        ...data, 
        name: String(data.name).toLowerCase() 
      };
    }

    // controlled options

    if (input.dataType === 'options') {
      // validation
      if (myCustomCheckTypeId(data)) {
        throw new ClirioValidationError('error message', {
          dataType: input.dataType,
          propertyName: 'typeId',
        });
      }

      // transformation
      return {
        ...data, 
        typeId: Number(data.typeId) 
      };
    }

    return data;
  }
}

The input: PipeContext argument contains the value input.entity (it is a DTO). It is possible to extract reflection data and use custom decorators.

The @Pipe() decorator provided

Example
@Module()
export class MigrationModule {
  @Command('migration up <migration-id>')
  @Pipe(MigrationUpPipe)
  public up(
    @Params() params: MigrationUpParamsDto,
    @Options() options: MigrationUpOptionsDto,
  ) {
    console.log('transformed params after pipes:', params);
    console.log('transformed options after pipes:', options);
  }
}
export class MigrationUpParamsDto {
  @Param('migration-id')
  readonly migrationId: number;
}
import { ClirioPipe, PipeContext, ClirioValidationError } from 'clirio';

export class MigrationUpPipe implements ClirioPipe {
  transform(data: any, input: PipeContext): any | never {
    if (input.dataType === 'params') {
      // validation
      if (/^[0-9]+$/.test(data.migrationId)) {
        throw new ClirioValidationError('the "migration-id" param is not a number', {
          dataType: input.dataType,
          propertyName: 'migrationId',
        });
      }

      // transformation string to number
      return { migrationId: Number(data.migrationId) };
    }

    return data;
  }
}

It is possible to add pipes for each action or globally for all at once

Example of a global pipe

const cli = new Clirio();
cli.addModule(MigrationModule);
cli.setGlobalPipe(CommonPipe);
cli.execute();

Exceptions

Exceptions can be thrown in pipes or actions. Special exceptions designed to complete the script with the desired result

  • ClirioValidationError
  • ClirioCommonError
import { Module, Command, ClirioCommonError } from 'clirio';

@Module()
export class CommonModule {
  @Command('check')
  public check() {
    throw new ClirioCommonError('Not working!', { code: 'CUSTOM_ERR_CODE' });
  }
}
import { Clirio, ClirioValidationError, ClirioCommonError } from 'clirio';

const cli = new Clirio();
cli.setModules([GitModule]);
cli.execute().catch((err) => {
  if (err instanceof ClirioValidationError) {
    console.log('Validation error', err.message);
    process.exit(9);
  }

  if (err instanceof ClirioCommonError) {
    console.log('Common error', err.message);
    process.exit(5);
  }

  console.log('unknown error', err.message);
  process.exit(1);
});

Filters

Filters are designed to catch exceptions

@Module('ping')
export class PingModule {
  @Command('pong')
  @Filter(PingPongFilter)
  public pong() {
    throw new ClirioCommonError('Not working!', { code: 'CUSTOM_ERR_CODE' });
  }
}
import {
  ClirioCommonError,
  ClirioFilter,
  ClirioValidationError,
  FilterContext,
} from 'clirio';

export class PingPongFilter implements ClirioFilter {
  catch(
    error: Error | ClirioCommonError | ClirioValidationError,
    context: FilterContext,
  ): void | never {
    if (error instanceof ClirioValidationError) {
      console.log('Validation error', error.message);
      process.exit(9);
    }

    if (error instanceof ClirioCommonError) {
      console.log('Common error', error.message);
      process.exit(5);
    }

    console.log('unknown error', error.message);
    process.exit(1);
  }
}

It is possible to add filters for each action or apply them globally to all actions at once

Example of a global filter

const cli = new Clirio();
cli.addModule(MigrationModule);
cli.setGlobalFilter(CommonFilter);
cli.execute();

Displaying help

Special case for the command as an option designed

@Module()
export class CommonModule {
  @Command('--help')
  public help() {
    console.log('Description of commands is here');
  }
}
$ my-cli --help
Description of commands is here

It is possible to implement any other commands

@Command('-m, --man')

@Command('help|h')

@Command('man <command>')

Helper decorator

The @Helper() decorator provided to handle the help mode

import { Module, Command, Helper, ClirioHelper } from 'clirio';

@Module()
export class CommonModule {
  @Command('hello there', {
    description: 'Say hello there',
  })
  public helloThere() {
    // ...
  }

  @Command('-h, --help')
  public help(@Helper() helper: ClirioHelper) {
    const dump = helper.dumpAll();
    console.log(ClirioHelper.formatDump(dump));
  }
}
$ my-cli --help

The ClirioHelper class provides api for getting descriptions of actions and methods for formatting it

The dumpAll method returns description for all commands. It is possible to do custom formatting

Displaying help in a particular module

The dumpThisModule method returns description for the current module.

@Module('ping')
export class PingModule {
  @Command('test')
  public test() {
    console.log('ping test');
  }

  @Command('-h, --help')
  public help(@Helper() helper: ClirioHelper) {
    const dump = helper.dumpThisModule();
    console.log(ClirioHelper.formatDump(dump));
  }
}
$ my-cli ping --help

Hidden commands

The hidden option in the Command() decorator lets hide description of the command in displaying help

import { Module, Command, Hidden } from 'clirio';

@Module()
export class Module {
  @Command('debug', { hidden: true })
  public debug() {
    // ...
  }

  @Command('hello there', {
    description: 'Say hello there',
  })
  public helloThere() {
    // ...
  }
}

The ClirioHelper.formatDump will ignore description of the debug command in this case

Version

import { Module, Command } from 'clirio';

@Module()
export class CommonModule {
  @Command('-v, --version')
  public version() {
    console.log('1.3.1');
  }
}
$ my-cli --version
1.3.1

Clirio API

setConfig

Setting the global configuration

Parameters:

  • config: Object

Returns:

  • Clirio
cli.setConfig({
  allowUncontrolledOptions: false,
});

| Param | Description | Default | | ------------------------ | :------------------------------------------------------------------------------------------------: | ------: | | allowUncontrolledOptions | Clirio can throw Error if DTO are not specified for options but it will be got in the command-line | true |

setGlobalPipe

sets global pipe

Parameters:

  • pipe: ClirioPipe

Returns:

  • Clirio
cli.setGlobalPipe(CommonPipe);

setGlobalFilter

sets global filter

Parameters:

  • pipe: ClirioFilter

Returns:

  • Clirio
cli.setGlobalFilter(CommonFilter);

addModule

adds one module

Parameters:

  • module: Constructor | Constructor['prototype']

Returns:

  • Clirio
cli.addModule(PingModule);
cli.addModule(new MigrationModule());

setModules

sets several modules

Parameters:

  • modules (Constructor | Constructor['prototype'])[]

Returns:

  • Clirio
cli.setModules([HelloModule, new MigrationModule()]);

setArgs

sets arguments of the command-line

Arguments will be determined automatically but it is possible to set them manually. This option is useful for testing and debugging the application

Parameters:

  • args: string[]

Returns:

  • Clirio
cli.setArgs(['git', 'add', 'test.txt', 'logo.png']);

execute

launches Clirio App

Parameters:

no parameters

Returns:

  • Promise
await cli.execute();

Clirio utils

Clirio class has static methods and values

Clirio.valid

an object of check functions for validation

Clirio.valid.BOOLEAN;
Clirio.valid.NUMBER;

| Key | Checks if the value is | | --------- | ---------------------------------------------------------------------------- | | OPTIONAL | undefined then it returns true otherwise it returns null | | REQUIRED | undefined then it returns false otherwise it returns null | | NULLABLE | null then it returns true otherwise it returns null | | NULL | null | | NUMBER | null | | INTEGER | number (integer) or a string that resembles an integer | | STRING | string | | BOOLEAN | boolean or string that resembles boolean ("true"or"false") | | FLAG | null or string that resembles boolean ("true" or "false") | | KEY_VALUE | string or array of string in the key=value format ("DB_USER=user") |

example
export class MigrationRunOptionsDto {
  @Option('--id')
  @Validate(Clirio.valid.NUMBER)
  readonly id: number;

  @Option('--start-date, -b')
  @Validate([Clirio.valid.NULLABLE, Clirio.valid.STRING])
  readonly startDate: string;
}

Clirio.form

Clirio.form.BOOLEAN;
Clirio.form.NUMBER;

an object of functions for transformation

| Key | transforms into | | --------- | -------------------------------------------------------------------------------------------- | | NUMBER | number | | STRING | string | | BOOLEAN | boolean | | FLAG | boolean (from null or "true" or "false") | | KEY_VALUE | object from the key=value format or array of ones ("DB_USER=user") | | ARRAY | array (if the value is originally an array, that array will be returned) | | PLAIN | string or null (if the value is originally an array, the first element will be returned) |

example
export class MigrationRunOptionsDto {
  @Option('--env, -e')
  @Transform(Clirio.form.KEY_VALUE)
  readonly envs: Record<string, string>;

  // always an array
  @Option('--id, -i')
  @Transform(Clirio.form.ARRAY)
  readonly ids: string[];

  // always a primitive type
  @Option('-f, --format')
  @Transform(Clirio.form.PLAIN)
  readonly format: string;
}

Clirio.parse

parses and describes the command-line

Arguments will be determined automatically but it is possible to set them manually. This option is useful for testing and debugging the application

Parameters:

  • commandLine: string

Returns:

Array<
  | {
      type: 'option';
      key: string;
      value: string | null;
    }
  | {
      type: 'action';
      key: number;
      value: string;
    }
>;
Clirio.parse('foo -a --bbb');

Clirio.describe

describes arguments of the command-line

Parameters:

  • args: string[]

Returns:

Array<{
  type: ArgType;
  key: string;
  value: string | null;
}>;
Clirio.describe(['foo', '-a', '--bbb']);

Decorators

Clirio works with decorators. More about decorators

"Command" decorator

The @Command() decorator specifies the command pattern

Parameters:

  • command: string [optional] - command pattern
  • options: object [optional] - extra options
    • options.description: string [optional] - description for the help mode
    • options.hidden: boolean [optional]- hiding the action in the help mode

"Empty" decorator

The @Empty() decorator catches the case when nothing is entered

"Env" decorator

The @Env() decorator maps DTO properties in Envs DTO

"Envs" decorator

The @Envs() decorator controls environments

Parameters: no parameters

"Filter" decorator

The @Filter() decorator catches exceptions in actions or pipe

Parameters: no parameters

"Failure" decorator

The @Failure() decorator catches the case when the specified command patterns don't match

Parameters: no parameters

"Helper" decorator

The @Helper() decorator handles the help mode

Parameters: no parameters

"Module" decorator

The @Module() decorator makes classes as controllers in to configure Clirio app

Parameters:

  • command: string [optional] - command prefix
  • options: object [optional] - extra options
    • options.description: string [optional] - description for the help mode
    • options.hidden: boolean [optional] - hiding the module in the help mode

"Option" decorator

The @Option() decorator maps DTO properties in options DTO

Parameters:

  • key: string [optional] - comma separated key aliases
  • options: object [optional] - extra options
    • options.description: string [optional] - description for the help mode
    • options.hidden: boolean [optional] - hiding the option in the help mode

"Options" decorator

The @Options() decorator controls input options

Parameters: no parameters

"Param" decorator

The @Param() decorator maps DTO properties in params DTO

Parameters:

  • key: string [optional] - comma separated key aliases
  • options: object [optional] - extra options
    • options.description: string [optional] - description for the help mode
    • options.hidden: boolean [optional] - hiding the param in the help mode

"Params" decorator

The @Params() decorator controls input params

Parameters: no parameters

"Pipe" decorator

The @Pipe() decorator validates and transforms controlled data (params and options).

Parameters:

  • pipe: ClirioPipe

"Transform" decorator

The @Transform() decorator transforms input params and options

Parameters:

  • value: a function or an array of functions
    • (value: any) => boolean | null
    • (value: any) => (boolean | null)[]

Validate decorator

The @Validate() decorator validates input params and options

Parameters:

  • value: a transform function
    • (value: any) => any