npm i ts-commands
Create a command file:
src/bin/greet-command.ts
import { Command, OptionType, ParsedArguments } from 'ts-commands';
interface Args extends ParsedArguments {
fname: string;
lname: string;
informal?: boolean;
}
export class GreetCommand extends Command {
override key = 'greet';
override description = 'say hello';
override positional = [
{
key: 'fname',
type: OptionType.string,
description: 'first name',
default: 'Tom',
},
{
key: 'lname',
type: OptionType.string,
description: 'last name',
default: 'Tester',
},
];
override options = [
{
key: 'informal',
type: OptionType.boolean,
default: false,
description: 'Informal?',
},
];
override async handle(args: Args) {
const greeting = args.informal ? 'yo' : 'hello';
console.log(`${greeting} ${args.fname} ${args.lname}`);
}
}If your app only needs one command (e.g. it only does one thing), you can use a singular style CLI:
src/bin/index.ts
import { CommandRunner } from 'ts-commands';
import { GreetCommand } from '../commands/greet';
new CommandRunner(new GreetCommand()).run();Dispatched commands are useful for when you have multiple sub-commands in your CLI, such as migration:run, migration:generate, etc.
Create an index file to register your command(s):
src/bin/index.ts
import { CommandDispatcher } from 'ts-commands';
import { FarewellCommand } from './farewell';
import { GreetCommand } from './greet';
new CommandDispatcher({
commands: [FarewellCommand, GreetCommand],
}).run();Use ts-import-ts to load all Commands in a directory. Note, you will need to change each Command to the default export of the module, e.g. export default class MyCommand.... Please read the readme of that package for proper usage.
First, install with npm i ts-import-ts
Create your dispatcher:
src/bin/index.ts
import { tsimportDirectory } from 'ts-import-ts';
import { Command, CommandDispatcher } from 'ts-commands';
new CommandDispatcher({
commands: tsimportDirectory<typeof Command>('scripts'),
}).run();You can use the this.ask() method to prompt the user for input and await their response:
export class AskCommand extends Command {
override key = 'ask';
override description = 'ask a question and get an answer';
override async handle(args: ParsedArguments) {
const answer = await this.ask('What is your name? ');
console.log(`Your name is ${answer}`);
}
}Use LoadingIndicator from ts-commands/loading-indicator to show a spinner
while an asynchronous operation runs:
import { LoadingIndicator } from 'ts-commands/loading-indicator';
const indicator = new LoadingIndicator();
await indicator.runWithIndicator(
'Loading...',
async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
}
);The message is required and must be provided as the first argument.
The spinner redraws on the current terminal line and stops automatically on success or failure. Redirected (non-TTY) output prints messages on separate lines without animation. updateMessage() changes the message for subsequent frames.
Set clearOnStop: false to keep the final frame followed by a newline, or pass an explicit boolean to stop() to override that option.
- Repeat "Creating Commands" section for each command
- Add each command in the
commandsofCommandDispatcher