clirio
v3.1.0
Published
Clirio is a mini framework for node.js command-line interfaces
Maintainers
Readme
Clirio
A mini framework for Node.js command-line interfaces built around TypeScript, decorators, and DTOs
Clirio is a routing and data-control library for terminal commands. It helps you describe CLI commands as modules and actions, map raw input into typed DTOs, and keep validation and transformation close to the place where a command is handled. In that sense, it can be used as an alternative to tools like commander, args, argparse and others.
The author is inspired by angular and nestjs
You can also combine Clirio with interactive command-line libraries such as inquirer, terminal-kit, chalk, and others.
The Clirio starter kit is here

Table of Contents
- Clirio
Installation
npm install clirio
yarn add clirio
Quick Start
There are 3 easy steps to build a Clirio app.
The example below emulates the git status CLI command with options.
- Create a DTO to describe 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;
}- Create a module to group related commands
import { Module, Command, Options } from 'clirio';
@Module()
export class GitModule {
@Command('git status')
public status(@Options() options: GitStatusDto) {
// handled data is available here
console.log(options);
}
}- 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 --shortThe command git status will be routed to the GitModule.status method.
{ branch: 'master', ignoreSubmodules: 'all', short: null }At this point you can use the received data in your own application logic.
The implementation of your own CLI entrypoint (for example my-cli) is described in the starter kit
The way with typing, validation, transformation
The GitStatusOptionsDto entity can be made stricter with validation and transformation rules.
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: GitStatusOptionsDto) {
// "options" has already been typed, validated, and 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: trueThe details are described below.
Starter kit
Clirio is designed in a way that works well with SOLID-style architecture, OOP, dependency injection, and other common application patterns.
The Clirio starter kit contains a recommended project setup, but you can also integrate Clirio with your own libraries, containers, and custom decorators.
Definitions
The anatomy of a shell CLI is described in wiki
The following definitions are used throughout the Clirio documentation.
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" is the space-separated part of the input without leading dashes
Params Definition
"Params" are values obtained by matching a command against a command pattern
Options Definition
"Options" are command-line parts that start with one or more leading dashes
Each option is either a key-value pair or a standalone key. If an option starts with two dashes it is treated as a long key; if it starts with one dash it is treated as a short key, which must be one character long:
--name=Alex, --name Alex, -n Alex, --version, -v
Parsing args
This is how Clirio parses a command-line:
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" }
]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
stringornull
App configuration
The application usually consists of the following parts:
- the main class:
Clirio - modules: custom classes or their instances
- actions: methods in those modules decorated with Clirio decorators
Clirio is the main class. It configures the application and links modules together.
const cli = new Clirio();
cli.setModules([HelloModule, CommonModule, GitModule, MigrationModule]);
cli.execute();Modules can be instantiated manually if you want to plug in dependency injection.
cli.setModules([
HelloModule,
new CommonModule(),
diContainer.resolve(CommonModule),
]);An example of that setup is available in the starter kit
Modules
Clirio modules are custom classes marked with the @Module() decorator. You can think of them as controllers for your CLI.
An application can have one module or many. Each module contains actions that describe command patterns.
Using a single 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 runUsing multiple modules helps separate unrelated commands and keep the code organized:
@Module('hello')
export class HelloModule {
@Command('there')
public helloThere() {
// ...
}
}
@Module('migration')
export class MigrationModule {
@Command('run')
public run() {
// ...
}
}Actions
Clirio actions are methods inside modules decorated with Clirio action 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 used to define a 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 final pattern is built from @Module(...) and @Command(...), then matched against the command-line.
A 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
Use the | operator to set match variants. Multiple command-lines can be routed to the same action. The number of space-separated parts must stay 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
Use the < > operator to mark a position that can contain any value. The number of space-separated parts must still match.
@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 receive those values in a DTO.
Case 4. Pattern with rest values mask
Use the <... > operator to capture an array of values.
Only one rest mask can appear in a command pattern, and it must 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 such as "help" and "version". It performs an exact match against an option-style command. Match variants can be separated by commas.
@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 if you want that command to accept additional options. To avoid ambiguous matching, do not mix this pattern with other kinds of command patterns.
Empty command
The @Empty() action decorator lets a module handle the case when nothing is entered.
Each module can declare its own @Empty() action.
@Module()
export class CommonModule {
@Command('hello')
public hello() {}
@Empty()
public empty() {
console.log("You haven't entered anything");
}
}$ my-cliYou haven't entered anythingWhen a module has a command prefix, empty handlers are matched and ranked by that prefix.
@Module('migration')
export class MigrationModule {
@Command('init')
public initMigration() {}
@Empty()
public empty() {
console.log(
'The migration module requires additional instruction. Type --help',
);
}
}$ my-cli migrationThe migration module requires additional instruction. Type --helpFailure command
The @Failure() action decorator lets a module handle the case when command patterns do not match.
Each module can declare its own @Failure() action.
If this decorator is not specified, Clirio throws a default error.
@Module()
export class CommonModule {
@Command('hello')
public hello() {}
@Failure()
public failure() {
console.log('There is no such a command!');
}
}$ my-cli goodbyeThere is no such a command!When a module has a command prefix, failure handlers are matched and ranked by that prefix.
@Module('migration')
export class MigrationModule {
@Command('init')
public initMigration() {}
@Failure()
public failure() {
console.log('The migration module got the wrong instruction');
}
}$ my-cli migration stopThe migration module got the wrong instructionData control
Clirio uses parameter decorators to control input data.
The @Params() and @Options() decorators are provided for this purpose.
@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: null }Params data control
To handle incoming data in a typed way, use DTOs instead of unknown.
Params DTO
The @Param() decorator is provided for DTO properties. It can take the name of a param mask and map it to a DTO property.
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 value masks.
The hello method will be called when the user enters a three-part command. The last two parts are passed into the params DTO.
$ my-cli hello Alex Smith{ firstName: "Alex", lastName: "Smith" }The @Param() decorator can be used without arguments. In that case, DTO properties are mapped to input keys with the same name.
If the @Param() decorator is absent, no mapping is performed for that property.
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{ allFiles: ['test.txt', 'logo.png'] }Options data control
The @Options() decorator is provided for command options.
@Module()
export class GitModule {
@Command('git status')
public status(@Options() options: GitStatusOptionsDto) {
console.log(options);
}
}Options DTO
The @Option() decorator is provided for DTO properties. It can accept comma-separated key aliases and map them to a DTO property.
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 of these inputs leads to the same result:
{ branch: 'master', ignoreSubmodules: 'all', short: true }If the @Option() decorator is absent, no mapping is performed for that property.
$ my-cli git status --branch=master --ignore-submodules=all --short{ branch: 'master', 'ignore-submodules': 'all', short: null }Input DTO
DTOs used to control input can have additional decorators, including custom ones. Before validation and transformation, every incoming value is one of the following raw forms:
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 they are used on DTO properties such as SetLimitParamsDto and SetLimitOptionDto.
import { Option, Validate, Transform } from 'clirio';
import { MyCustomDecorator } from 'src/my-decorators';
class SetLimitParamsDto {
@Param('limit')
@Validate((v) => /^[0-9]+$/.test(String(v))) // accept digits only
@Transform((v) => Number(v)) // transform string to number
@MyCustomDecorator() // can be handled later in a pipe
readonly limit: number;
}Envs data control
The @Envs() decorator is provided for environment variables.
import { Envs } from 'clirio';
@Module('migration')
export class MigrationModule {
@Command('test-connect')
public testConnect(@Envs() envs: TestConnectEnvsDto) {
// ...
}
}Envs DTO
The @Env() decorator is provided for DTO properties.
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 is provided to check input params, options, and envs.
It should be used on DTO properties together with @Option(), @Param(), or @Env(), depending on the kind of controlled data.
@Validate() accepts either a single function or an array of functions. The functions are executed from left to right, and each one must return boolean or null:
- if a function returns
false, Clirio throws a validation error immediately - if a function returns
null, Clirio moves on to the next validator in the chain - if a function returns
true, the validation chain for that property stops successfully
This makes validator order important. Guard validators such as OPTIONAL and NULLABLE should usually come before stricter validators such as STRING or NUMBER.
import { Module, Command, Options } from 'clirio';
@Module()
export class GitModule {
@Command('git status')
public status(@Options() options: GitStatusDto) {
console.log(options);
}
}import { Clirio, Option, Transform, 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-submodulesThe "branch" option is wrongFor every failed validation, Clirio throws the same error shape: The "%KEY_NAME%" %DATA_TYPE% is wrong
The error is thrown as a ClirioValidationError with dataType and propertyName, so filters and callers can distinguish whether the problem came from params, options, or envs.
To have more flexible validations, use Pipes.
You can configure unknown option handling. If allowUncontrolledOptions is set to false, any option key that is not declared in the DTO will cause an INVALID_OPTIONS error.
Validation of an optional key
class OptionsDto {
@Option('--id')
@Validate([
(v) => (v === undefined ? true : null),
(v) => /^[0-9]+$/.test(String(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;
}Validation of a required key
class OptionsDto {
@Option('--name')
@Validate([Clirio.valid.REQUIRED, Clirio.valid.STRING])
readonly name!: string;
}Transformation
The @Transform() decorator is provided to transform input data.
It should be used on DTO properties together with @Option(), @Param(), or @Env(), depending on the kind of controlled data.
@Transform() takes a transform function as an argument.
import { Option, Param, 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, Params } 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 1520Using Clirio-made forms
class SumParamsDto {
@Param()
@Transform(Clirio.form.NUMBER)
readonly first: number;
}class SetAutoOptionsDto {
@Option('--turbo')
@Transform(Clirio.form.FLAG)
readonly turbo: boolean;
}Pipes
Pipes are designed to validate and transform controlled data such as params, options, and envs.
Compared to @Validate() and @Transform(), pipes work at the whole-object level and give you more flexibility when validation depends on several fields at once.
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 input.entity (the DTO class). This makes it possible to inspect reflection data and build your own advanced behavior around custom decorators.
The @Pipe() decorator is provided for attaching pipes to an action.
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;
}
}You can add pipes to each action or apply them globally to all commands.
If several pipes are applied, they run in sequence. The next pipe receives the result returned by the previous one.
Global pipes run before action-level pipes. If needed, @Pipe(SomePipe, { overwriteGlobal: true }) can be used to disable the global pipe for a specific action.
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. Clirio also provides special exception classes that make it easier to communicate CLI-friendly errors.
- 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 raised by actions, pipes, validation, or explicit application logic.
@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);
}
}You can add filters to each action or apply them globally to all actions at once.
If both global and action-level filters are used, they run in sequence. A filter can handle the current error, rethrow it, or throw a different error.
If needed, @Filter(SomeFilter, { overwriteGlobal: true }) can be used to disable the global filter for a specific action.
Example of a global filter
const cli = new Clirio();
cli.addModule(MigrationModule);
cli.setGlobalFilter(CommonFilter);
cli.execute();Displaying help
Clirio also supports special commands expressed as options, which is useful for things like help and version output.
@Module()
export class CommonModule {
@Command('--help')
public help() {
console.log('Description of commands is here');
}
}$ my-cli --helpDescription of commands is hereYou can implement other option-style commands in the same way.
@Command('-m, --man')
@Command('help|h')
@Command('man <command>')Clirio Helper
The @Helper() decorator is provided to handle 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 --helpThe ClirioHelper class provides an API for collecting command descriptions and formatting them.
The dumpAll method returns descriptions for all commands. You can format that data yourself or pass it to ClirioHelper.formatDump.
Displaying help in a particular module
The dumpThisModule method returns the 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 --helpHidden commands
The hidden option in the Command() decorator hides a command from generated help output.
import { Module, Command } from 'clirio';
@Module()
export class Module {
@Command('debug', { hidden: true })
public debug() {
// ...
}
@Command('hello there', {
description: 'Say hello there',
})
public helloThere() {
// ...
}
}In this case, ClirioHelper.formatDump will omit the debug command from help output.
Displaying Version
import { Module, Command } from 'clirio';
@Module()
export class CommonModule {
@Command('-v, --version')
public version() {
console.log('1.3.1');
}
}$ my-cli --version1.3.1Clirio API
setConfig
Sets the global configuration.
Parameters:
- config: Object
Returns:
- Clirio
cli.setConfig({
allowUncontrolledOptions: false,
});| Param | Description | Default |
| ------------------------ | :------------------------------------------------------------------------------------------------: | ------: |
| allowUncontrolledOptions | If false, any option key that is not declared in the DTO causes an INVALID_OPTIONS error | true |
setGlobalPipe
Sets a global pipe.
Parameters:
- pipe: ClirioPipe
Returns:
- Clirio
cli.setGlobalPipe(CommonPipe);setGlobalFilter
Sets a 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 at once.
Parameters:
- modules (Constructor | Constructor['prototype'])[]
Returns:
- Clirio
cli.setModules([HelloModule, new MigrationModule()]);setArgs
There is no separate setArgs() method. To pass arguments manually, call execute(args).
By default Clirio reads arguments from process.argv.slice(2), but for testing and debugging you can pass them directly.
Parameters:
- args: string[]
Returns:
- Clirio
await cli.execute(['git', 'add', 'test.txt', 'logo.png']);execute
Launches the Clirio app.
Parameters:
args?: string[]
Returns:
- Promise
await cli.execute();Clirio utils
The Clirio class also exposes static methods and helpers.
Clirio.valid
An object of built-in check functions for validation.
Clirio.valid.BOOLEAN;
Clirio.valid.NUMBER;| Key | Checks if the value is |
| --------- | ---------------------------------------------------------------------------------------------------- |
| OPTIONAL | undefined; returns true for undefined, otherwise returns null so the next validator can run |
| REQUIRED | present; returns false for undefined or null, otherwise returns null |
| NULLABLE | null; returns true for null, otherwise returns null |
| NULL | exactly null |
| NUMBER | a number or a string that can be converted to a number |
| INTEGER | an integer number |
| STRING | string |
| BOOLEAN | boolean or a string that looks like true or false |
| FLAG | null or a string that looks like true or false |
| KEY_VALUE | a string or array of strings in the key=value format ("DB_USER=user") |
OPTIONAL, REQUIRED, and NULLABLE are especially useful as guard validators at the start of a validation chain.
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 built-in functions for transformation.
| Key | transforms into |
| --------- | ------------------------------------------------------------------------------------------------------------ |
| NUMBER | number using Number(value) |
| STRING | string using String(value ?? '') |
| BOOLEAN | JavaScript boolean coercion using Boolean(value) |
| FLAG | boolean from CLI-style flag values (null, "true", "false") |
| KEY_VALUE | an object built from one or more key=value entries ("DB_USER=user") |
| ARRAY | array (if the value is already an array, that same array is returned) |
| PLAIN | string or null (if the value is an array, the first element is returned) |
For CLI flags, prefer Clirio.form.FLAG. Clirio.form.BOOLEAN uses normal JavaScript coercion, so for example "false" becomes true.
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.
Use Clirio.parse() when you have the raw command line as a string and want the parsed structure shown above.
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 already-split 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. You can read more about TypeScript decorators here.
"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 an Envs DTO.
"Envs" decorator
The @Envs() decorator controls environment variables.
Parameters: no parameters
"Filter" decorator
The @Filter() decorator catches exceptions raised by actions or pipes.
Parameters:
- filter: ClirioFilter
- options: object [optional]
- options.overwriteGlobal: boolean [optional] - disables the global filter for this action
"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 help mode.
Parameters: no parameters
"Module" decorator
The @Module() decorator turns a class into a module used to configure a 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 an 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 a params DTO.
Parameters:
- key: string [optional] - param mask key
- 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.
Parameters:
- pipe: ClirioPipe
- options: object [optional]
- options.overwriteGlobal: boolean [optional] - disables the global pipe for this action
"Transform" decorator
The @Transform() decorator transforms input data.
Parameters:
- value: a transform function
(value: any) => any
Validate decorator
The @Validate() decorator validates input data.
Parameters:
- value: a validation function or an array of validation functions
(value: any) => boolean | null
