Command
@tsed/cli-core is the npm module that provides API to create CLI. It can be used to create your own CLI or to run your Ts.ED application code. Ts.ED cli-core uses commander to parse cli arguments, Inquirer to display prompt and Listr to run tasks.
The cli-core works as a standalone process, like the classic entry point, and will initialize a container for running your code (Service/Provider/etc...).
- Bootstrap (entry point e.g:
bin/index.ts) is invoked by cli. - Create a headless Ts.ED Application.
- Create command with decorator and inject service from your existing code.
Installation
npm install @tsed/cli-coreyarn add @tsed/cli-corepnpm add @tsed/cli-corebun add @tsed/cli-coreTIP
If you start your project from scratch, you can use Ts.ED cli v3+ to bootstrap your project with the Command feature.
Optional
You can install @tsed/cli globally to run your custom commands directly from the Ts.ED CLI:
npm install -g @tsed/cliCreate the CLI entry point
Create index.ts file in src/bin. This file will be dedicated to bootstrap the CLI with your own configuration.
#!/usr/bin/env node
import {CliCore} from "@tsed/cli-core";
import {config} from "../config"; // Import your application configuration
import {HelloCommand} from "./HelloCommand";
CliCore.bootstrap({
...config,
// add your custom commands here
commands: [HelloCommand]
}).catch(console.error);Create command
Use tsed g command to create a new Command file. Here is a basic Command example:
import {Command, CommandProvider, QuestionOptions} from "@tsed/cli-core";
export interface HelloCommandContext {}
@Command({
name: "hello-command",
description: "Command description",
args: {},
options: {},
allowUnknownOption: false
})
export class HelloCommand implements CommandProvider {
/**
* Ask questions using Inquirer. Return an empty array or don't implement the method to skip this step
*/
async $prompt(initialOptions: Partial<HelloCommandContext>): Promise<QuestionOptions> {
return [];
}
/**
* This method is called after the $prompt to create / map inputs to a proper context for the next step
*/
$mapContext(ctx: Partial<HelloCommandContext>): HelloCommandContext {
return {
...ctx
// map something, based on ctx
};
}
/**
* This step run your tasks with Listr module
*/
async $exec(ctx: HelloCommandContext): Promise<any> {
return [
{
title: "Doing something",
task: () => {
console.log("HELLO");
}
}
];
}
}The @Command decorator allow you to bind a class to Commander. Here the previous example can be run by executing the following command:
tsed run hello-commandBy default, you need to provide the name and description.
Command Arguments
Arguments are the values given to your command without a flag option:
tsed run hello-command create userTo bind these arguments with your custom command, you have to declare the arguments as follows:
import {Command, CommandProvider, QuestionOptions} from "@tsed/cli-core";
export interface HelloCommandContext {
action: "create";
subAction: "user";
}
@Command({
name: "hello-command",
description: "Command description",
args: {
action: {
type: String,
defaultValue: "create",
description: "Action description"
},
subAction: {
type: String,
defaultValue: "user",
description: "My sub-action"
}
},
options: {},
allowUnknownOption: false
})
export class HelloCommand implements CommandProvider {
$exec(ctx: HelloCommandContext) {
console.log(ctx);
}
}Command Options
Options are the values given to your command with a specific flag option. Example:
tsed run hello-command -o testTo bind this option with your custom command, you have to declare the option as following:
import {Command, CommandProvider, QuestionOptions} from "@tsed/cli-core";
export interface HelloCommandContext {
option1: string;
}
@Command({
name: "hello-command",
description: "Command description",
args: {},
options: {
"-o, --opt-1 <option1>": {
type: String,
defaultValue: "dev",
description: "My option"
}
},
allowUnknownOption: false
})
export class HelloCommand implements CommandProvider {
$exec(ctx: HelloCommandContext) {
console.log(ctx);
}
}Allow extra options
By default, commander doesn't accept unknown options. You can change this behaviour, by change the allowUnknownOption to true.
Then you'll be able to get all args and options in the rawArgs property. Here is an example:
import {Command, CommandProvider, QuestionOptions} from "@tsed/cli-core";
export interface HelloCommandContext {
rawArgs: string[];
}
@Command({
name: "hello-command",
description: "Command description",
args: {},
options: {},
allowUnknownOption: true
})
export class HelloCommand implements CommandProvider {
$exec(ctx: HelloCommandContext) {
console.log(ctx);
}
}Injecting service
Use the Inject decorator to inject your service in a command:
import {Command, CommandProvider, QuestionOptions} from "@tsed/cli-core";
import {MyService} from "../services/MyService";
export interface HelloCommandContext {
rawArgs: string[];
}
@Command({
name: "hello-command",
description: "Command description",
args: {},
options: {},
allowUnknownOption: false
})
export class HelloCommand implements CommandProvider {
@Inject()
myService: MyService;
async $exec(ctx: HelloCommandContext): Promise<any> {
return [
{
title: "Update something",
task: () => this.myService.update(ctx)
}
];
}
}Prompt
You can implement the $prompt method to provide a CLI prompt to your consumer. Prompt is based on Inquirer.
import {Command, CommandProvider, QuestionOptions} from "@tsed/cli-core";
import {MyService} from "../services/MyService";
export interface HelloCommandContext {
projectName: string;
}
@Command({
name: "hello-command",
description: "Command description",
args: {},
options: {}
})
export class HelloCommand implements CommandProvider {
@Inject()
myService: MyService;
async $prompt(initialOptions: Partial<HelloCommandContext>): Promise<QuestionOptions> {
return [
{
type: "input",
name: "projectName",
message: "What is your project name",
transformer(input) {
return paramCase(input);
}
}
];
}
async $exec(ctx: HelloCommandContext): Promise<any> {
console.log(ctx);
}
}See Inquirer for more details on creating prompts.