nestjs-console
Advanced tools
Comparing version 4.0.0 to 5.0.0
@@ -7,2 +7,42 @@ # Changelog | ||
## [5.0.0] - 2021-04-13 | ||
### BREAKING CHANGE | ||
- Signature of `@Console` decorator has been updated. | ||
ConsoleOptions has been replaced by CreateCommandOptions, to update from ^4.0.0 you simply have to change property `name` by `command`. | ||
eg: `@Console({name: "myCommand"})` => `@Console({command: "myCommand"})` | ||
- Command handler signature has changed. Options are now passed to the handler. See [the command handler signature for more details](https://github.com/Pop-Code/nestjs-console/wiki/Command-handler-signature#signature) | ||
- Commander@< 7.2.0 is not any more supported. Please upgrade to latest version `npm install commander@^7.2.0` or using yarn `yarn add commander@^7.2.0` | ||
- The helper formatResponse has been removed | ||
### Changed | ||
- Update tests | ||
- Remove calls to `command.exitOverride()` to simplify code | ||
- Native errors handling with commander | ||
- update docs | ||
- update dependencies | ||
- Update to work with commander@7.2.0 | ||
### Added | ||
- All commands and sub commands are stored in the ConsoleService and can be found using the `ConsoleService.getCommand(name: string)`. Name is a command path separated by dots. eg: `parent.command.subcommand` | ||
- Sub commands can now be registered on other subCommand without any args. If a parent command define options, they can be retrieved using `command.parent.opts()` inside the executed subCommand. This allow you to create group of command with global options based on parent options | ||
### Fixed | ||
- fix #302 | ||
## [4.0.0] - 2020-10-16 | ||
### BREAKING CHANGE | ||
- Remove Pretiier format helpers and use JSON.stringify instead to reduce package size. | ||
### Changed | ||
- update dependencies | ||
- update docs | ||
## [3.1.0] - 2020-07-30 | ||
@@ -181,3 +221,5 @@ | ||
[unreleased]: https://github.com/Pop-Code/nestjs-console/compare/v3.1.0...HEAD | ||
[unreleased]: https://github.com/Pop-Code/nestjs-console/compare/v5.0.0...HEAD | ||
[5.0.0]: https://github.com/Pop-Code/nestjs-console/compare/v4.0.0...HEAD | ||
[4.0.0]: https://github.com/Pop-Code/nestjs-console/compare/v3.1.0...v4.0.0 | ||
[3.1.0]: https://github.com/Pop-Code/nestjs-console/compare/v3.0.6...v3.1.0 | ||
@@ -184,0 +226,0 @@ [3.0.6]: https://github.com/Pop-Code/nestjs-console/compare/v3.0.5...v3.0.6 |
@@ -7,3 +7,2 @@ import { INestApplicationContext } from '@nestjs/common'; | ||
module: any; | ||
withContainer?: boolean; | ||
useDecorators?: boolean; | ||
@@ -10,0 +9,0 @@ includeModules?: any[]; |
@@ -34,6 +34,3 @@ "use strict"; | ||
this.service = this.container.get(service_1.ConsoleService); | ||
if ((this.options.withContainer || this.options.useDecorators) && | ||
typeof this.service.setContainer === 'function') { | ||
this.service.setContainer(this.container); | ||
} | ||
this.service.setContainer(this.container); | ||
if (this.options.useDecorators) { | ||
@@ -40,0 +37,0 @@ this.useDecorators(); |
@@ -17,8 +17,3 @@ export declare type ParserType = (value: string, previous: any) => any; | ||
export declare const Command: (options: CreateCommandOptions) => MethodDecorator; | ||
export interface ConsoleOptions { | ||
name?: string; | ||
description?: string; | ||
alias?: string; | ||
} | ||
export declare const Console: (options?: ConsoleOptions) => ClassDecorator; | ||
export declare const Console: (options?: CreateCommandOptions | undefined) => ClassDecorator; | ||
//# sourceMappingURL=decorators.d.ts.map |
@@ -6,5 +6,8 @@ "use strict"; | ||
const constants_1 = require("./constants"); | ||
exports.InjectCli = () => common_1.Inject(constants_1.CLI_SERVICE_TOKEN); | ||
exports.Command = (options) => (target, method) => Reflect.defineMetadata(constants_1.COMMAND_METADATA_NAME, options, target, method); | ||
exports.Console = (options) => (target) => Reflect.defineMetadata(constants_1.CONSOLE_METADATA_NAME, options || {}, target); | ||
const InjectCli = () => common_1.Inject(constants_1.CLI_SERVICE_TOKEN); | ||
exports.InjectCli = InjectCli; | ||
const Command = (options) => (target, method) => Reflect.defineMetadata(constants_1.COMMAND_METADATA_NAME, options, target, method); | ||
exports.Command = Command; | ||
const Console = (options) => (target) => Reflect.defineMetadata(constants_1.CONSOLE_METADATA_NAME, options || {}, target); | ||
exports.Console = Console; | ||
//# sourceMappingURL=decorators.js.map |
import * as ora from 'ora'; | ||
export declare const createSpinner: (opts?: ora.Options) => ora.Ora; | ||
export declare const formatResponse: (data: string | Record<string, unknown> | Error, options?: { | ||
space?: number; | ||
}) => string; | ||
export declare const createSpinner: (opts?: ora.Options | undefined) => ora.Ora; | ||
//# sourceMappingURL=helpers.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.formatResponse = exports.createSpinner = void 0; | ||
exports.createSpinner = void 0; | ||
const ora = require("ora"); | ||
exports.createSpinner = (opts) => ora(opts); | ||
exports.formatResponse = (data, options) => { | ||
const opts = Object.assign({ space: 2 }, options); | ||
return data instanceof Error ? data.message : JSON.stringify(data, null, opts.space); | ||
}; | ||
const createSpinner = (opts) => ora(opts); | ||
exports.createSpinner = createSpinner; | ||
//# sourceMappingURL=helpers.js.map |
import * as commander from 'commander'; | ||
export declare type CommandActionHandler = (...args: any[]) => any | Promise<any>; | ||
export declare type CommandActionHandler = ((...args: any[]) => any | Promise<any>) | { | ||
instance: any; | ||
methodName: string; | ||
}; | ||
export declare type CommandActionWrapper = (...args: any[]) => Promise<CommandResponse>; | ||
@@ -4,0 +7,0 @@ export interface CommandResponse { |
@@ -19,3 +19,3 @@ "use strict"; | ||
provide: constants_1.CLI_SERVICE_TOKEN, | ||
useFactory: () => service_1.ConsoleService.create() | ||
useFactory: () => service_1.ConsoleService.createCli() | ||
}; | ||
@@ -29,13 +29,24 @@ let ConsoleModule = class ConsoleModule { | ||
const scanResponse = this.scanner.scan(app, includedModules); | ||
const cli = this.service.getCli(); | ||
const cli = this.service.getRootCli(); | ||
scanResponse.forEach(({ methods, instance, metadata }) => { | ||
let parent = cli; | ||
if (metadata.name) { | ||
parent = this.service.getCli(metadata.name); | ||
if (!parent) { | ||
let subCli = this.service.getCli(metadata.command); | ||
if (subCli !== undefined) { | ||
parent = subCli; | ||
} | ||
else { | ||
const commandNames = metadata.command.split('.'); | ||
if (commandNames.length > 1) { | ||
commandNames.pop(); | ||
subCli = this.service.getCli(commandNames.join('.')); | ||
} | ||
if (subCli === undefined) { | ||
parent = this.service.createGroupCommand(metadata, cli); | ||
} | ||
else { | ||
parent = this.service.createGroupCommand(metadata, subCli); | ||
} | ||
} | ||
for (const method of methods) { | ||
this.service.createCommand(method.metadata, instance[method.name].bind(instance), parent); | ||
this.service.createCommand(method.metadata, { instance, methodName: method.name }, parent); | ||
} | ||
@@ -42,0 +53,0 @@ }); |
import { INestApplicationContext } from '@nestjs/common'; | ||
import { ConsoleOptions, CreateCommandOptions } from './decorators'; | ||
import { CreateCommandOptions } from './decorators'; | ||
export interface MethodsMetadata { | ||
@@ -9,3 +9,3 @@ name: string; | ||
instance: any; | ||
metadata: ConsoleOptions; | ||
metadata: CreateCommandOptions; | ||
methods: MethodsMetadata[]; | ||
@@ -12,0 +12,0 @@ } |
import { INestApplicationContext } from '@nestjs/common'; | ||
import * as commander from 'commander'; | ||
import { ConsoleOptions, CreateCommandOptions } from './decorators'; | ||
import { CreateCommandOptions } from './decorators'; | ||
import { CommandActionHandler, CommandActionWrapper, CommandResponse } from './interfaces'; | ||
@@ -10,13 +10,14 @@ export declare class ConsoleService { | ||
constructor(cli: commander.Command); | ||
static create(): commander.Command; | ||
static createCli(name?: string): commander.Command; | ||
resetCli(): void; | ||
logError(error: Error): void; | ||
getCli(name?: string): commander.Command; | ||
getRootCli(): commander.Command; | ||
getCli(name?: string): commander.Command | undefined; | ||
setContainer(container: INestApplicationContext): ConsoleService; | ||
getContainer(): INestApplicationContext; | ||
createHandler(action: CommandActionHandler): CommandActionWrapper; | ||
init(argv: string[]): Promise<CommandResponse | undefined>; | ||
createCommand(options: CreateCommandOptions, handler: CommandActionHandler, parent: commander.Command): commander.Command; | ||
createGroupCommand(options: ConsoleOptions, parent: commander.Command): commander.Command; | ||
getContainer(): INestApplicationContext | undefined; | ||
static createHandler(action: CommandActionHandler): CommandActionWrapper; | ||
init(argv: string[]): Promise<CommandResponse>; | ||
createCommand(options: CreateCommandOptions, handler: CommandActionHandler, parent: commander.Command, commanderOptions?: commander.CommandOptions): commander.Command; | ||
createGroupCommand(options: CreateCommandOptions, parent: commander.Command): commander.Command; | ||
getCommandFullName(command: commander.Command): string; | ||
} | ||
//# sourceMappingURL=service.d.ts.map |
@@ -29,3 +29,2 @@ "use strict"; | ||
const decorators_1 = require("./decorators"); | ||
const helpers_1 = require("./helpers"); | ||
let ConsoleService = ConsoleService_1 = class ConsoleService { | ||
@@ -36,14 +35,23 @@ constructor(cli) { | ||
} | ||
static create() { | ||
const cli = new commander.Command(); | ||
static createCli(name) { | ||
const cli = new commander.Command(name); | ||
cli.storeOptionsAsProperties(false); | ||
cli.allowUnknownOption(false); | ||
return cli; | ||
} | ||
resetCli() { | ||
this.cli = ConsoleService_1.create(); | ||
this.cli = ConsoleService_1.createCli(); | ||
} | ||
logError(error) { | ||
console.error(helpers_1.formatResponse(error)); | ||
getRootCli() { | ||
return this.cli; | ||
} | ||
getCli(name) { | ||
return name ? this.commands.get(name) : this.cli; | ||
let cli; | ||
if (typeof name === 'string') { | ||
cli = this.commands.get(name); | ||
} | ||
else { | ||
cli = this.cli; | ||
} | ||
return cli; | ||
} | ||
@@ -57,6 +65,12 @@ setContainer(container) { | ||
} | ||
createHandler(action) { | ||
static createHandler(action) { | ||
return (...args) => __awaiter(this, void 0, void 0, function* () { | ||
const command = args.find((c) => c instanceof commander.Command); | ||
let data = action(...args); | ||
let data; | ||
if (typeof action === 'object') { | ||
data = action.instance[action.methodName](...args); | ||
} | ||
else { | ||
data = action(...args); | ||
} | ||
if (data instanceof Promise) { | ||
@@ -70,41 +84,18 @@ data = yield data; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const cli = this.getCli(); | ||
try { | ||
if (cli.commands.length === 0) { | ||
throw new commander.CommanderError(1, 'empty', 'The cli does not contain sub command'); | ||
} | ||
cli.exitOverride((e) => { | ||
throw e; | ||
}); | ||
const command = cli.parse(argv); | ||
const results = yield Promise.all(command._actionResults); | ||
return results[0]; | ||
} | ||
catch (e) { | ||
if (e instanceof commander.CommanderError) { | ||
if (/(helpDisplayed|commander\.help)/.test(e.code)) { | ||
return; | ||
} | ||
if (/(missingMandatoryOptionValue|optionMissingArgument|missingArgument|unknownCommand)/.test(e.code)) { | ||
throw e; | ||
} | ||
} | ||
this.logError(e); | ||
throw e; | ||
} | ||
yield this.cli.parseAsync(argv); | ||
const results = yield Promise.all(this.cli._actionResults); | ||
return results[0]; | ||
}); | ||
} | ||
createCommand(options, handler, parent) { | ||
createCommand(options, handler, parent, commanderOptions) { | ||
const args = options.command.split(' '); | ||
const command = new commander.Command(args[0]); | ||
command.exitOverride((...args) => parent._exitCallback(...args)); | ||
const commandNames = args[0].split('.'); | ||
const command = ConsoleService_1.createCli(commandNames[commandNames.length - 1]); | ||
if (args.length > 1) { | ||
command.arguments(args.slice(1).join(' ')); | ||
} | ||
command.storeOptionsAsProperties(false); | ||
command.allowUnknownOption(false); | ||
if (options.description) { | ||
if (options.description !== undefined) { | ||
command.description(options.description); | ||
} | ||
if (options.alias) { | ||
if (options.alias !== undefined) { | ||
command.alias(options.alias); | ||
@@ -115,3 +106,3 @@ } | ||
let method = 'option'; | ||
if (opt.required) { | ||
if (opt.required === true) { | ||
method = 'requiredOption'; | ||
@@ -122,4 +113,5 @@ } | ||
} | ||
command.action(this.createHandler(handler)); | ||
parent.addCommand(command); | ||
command.action(ConsoleService_1.createHandler(handler)); | ||
parent.addCommand(command, commanderOptions); | ||
this.commands.set(this.getCommandFullName(command), command); | ||
return command; | ||
@@ -131,14 +123,32 @@ } | ||
} | ||
const command = new commander.Command(options.name); | ||
command.exitOverride((...args) => parent._exitCallback(...args)); | ||
if (options.description) { | ||
const commandNames = options.command.split('.'); | ||
const command = ConsoleService_1.createCli(commandNames[commandNames.length - 1]); | ||
if (options.description !== undefined) { | ||
command.description(options.description); | ||
} | ||
if (options.alias) { | ||
if (options.alias !== undefined) { | ||
command.alias(options.alias); | ||
} | ||
this.commands.set(command.name(), command); | ||
if (Array.isArray(options.options)) { | ||
for (const opt of options.options) { | ||
let method = 'option'; | ||
if (opt.required === true) { | ||
method = 'requiredOption'; | ||
} | ||
command[method](opt.flags, opt.description, opt.fn || opt.defaultValue, opt.defaultValue); | ||
} | ||
} | ||
parent.addCommand(command); | ||
this.commands.set(this.getCommandFullName(command), command); | ||
return command; | ||
} | ||
getCommandFullName(command) { | ||
let fullName = command.name(); | ||
let commandParent = command.parent; | ||
while (commandParent instanceof commander.Command && commandParent.name() !== '') { | ||
fullName = `${commandParent.name()}.${fullName}`; | ||
commandParent = commandParent.parent; | ||
} | ||
return fullName; | ||
} | ||
}; | ||
@@ -145,0 +155,0 @@ ConsoleService = ConsoleService_1 = __decorate([ |
{ | ||
"name": "nestjs-console", | ||
"version": "4.0.0", | ||
"version": "5.0.0", | ||
"description": "A NestJS module that provide a cli", | ||
@@ -10,3 +10,4 @@ "keywords": [ | ||
"console", | ||
"commander" | ||
"commander", | ||
"terminal" | ||
], | ||
@@ -19,42 +20,42 @@ "main": "./dist/index.js", | ||
"peerDependencies": { | ||
"@nestjs/common": "^6 || ^7", | ||
"@nestjs/core": "^6 || ^7", | ||
"commander": "^5 || ^6" | ||
"@nestjs/common": "^7", | ||
"@nestjs/core": "^7", | ||
"commander": "^7.2.0" | ||
}, | ||
"dependencies": { | ||
"ora": "5.1.0" | ||
"ora": "5.4.0" | ||
}, | ||
"devDependencies": { | ||
"@nestjs/common": "7.4.4", | ||
"@nestjs/core": "7.4.4", | ||
"@nestjs/platform-express": "7.4.4", | ||
"@nestjs/testing": "7.4.4", | ||
"@types/jest": "26.0.14", | ||
"@types/node": "14.11.8", | ||
"@typescript-eslint/eslint-plugin": "4.4.1", | ||
"@typescript-eslint/parser": "4.4.1", | ||
"codecov": "3.8.0", | ||
"commander": "6.1.0", | ||
"prettier": "2.1.2", | ||
"eslint": "7.11.0", | ||
"eslint-config-prettier": "6.12.0", | ||
"eslint-plugin-import": "2.22.1", | ||
"eslint-plugin-prefer-arrow": "1.2.2", | ||
"jest": "26.5.3", | ||
"@nestjs/common": "7.6.17", | ||
"@nestjs/core": "7.6.17", | ||
"@nestjs/platform-express": "7.6.17", | ||
"@nestjs/testing": "7.6.17", | ||
"@types/jest": "26.0.23", | ||
"@types/node": "15.3.1", | ||
"@typescript-eslint/eslint-plugin": "4.24.0", | ||
"@typescript-eslint/parser": "4.24.0", | ||
"codecov": "3.8.2", | ||
"commander": "7.2.0", | ||
"eslint": "7.26.0", | ||
"eslint-config-prettier": "8.3.0", | ||
"eslint-plugin-import": "2.23.1", | ||
"eslint-plugin-prefer-arrow": "1.2.3", | ||
"jest": "26.6.3", | ||
"prettier": "2.3.0", | ||
"reflect-metadata": "0.1.13", | ||
"rxjs": "6.6.3", | ||
"ts-jest": "26.4.1", | ||
"ts-node": "9.0.0", | ||
"rxjs": "6.6.7", | ||
"ts-jest": "26.5.6", | ||
"ts-node": "9.1.1", | ||
"tsconfig-paths": "3.9.0", | ||
"typedoc": "0.19.2", | ||
"typescript": "4.0.3" | ||
"typedoc": "0.20.36", | ||
"typescript": "4.2.4" | ||
}, | ||
"scripts": { | ||
"build": "rm -Rf ./dist && tsc -b", | ||
"doc": "rm -Rf ./docs && typedoc ./src && touch ./docs/.nojekyll", | ||
"console": "node dist/test/console.js", | ||
"console:dev": "ts-node -r tsconfig-paths/register src/test/console.ts", | ||
"console:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register src/test/console.ts", | ||
"build": "rm -Rf ./dist && tsc -b tsconfig.build.json", | ||
"doc": "rm -Rf ./docs && typedoc && touch ./docs/.nojekyll", | ||
"console": "node -r tsconfig-paths/register -r ts-node/register test/console.ts", | ||
"console:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register test/console.ts", | ||
"console:decorators": "node -r tsconfig-paths/register -r ts-node/register test/console.decorators.ts", | ||
"console:decorators:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register test/console.decorators.ts", | ||
"test": "jest", | ||
"test:build": "jest --testRegex .spec.js", | ||
"test:watch": "jest --watch", | ||
@@ -61,0 +62,0 @@ "test:cov": "jest --coverage", |
365
README.md
@@ -28,365 +28,13 @@ <div align="center"> | ||
1. Bootstrap (entry point e.g console.ts) is invoked by cli. | ||
2. Create a headless nest app | ||
- Any module inside the app can create command and subcommands using nestjs-console with [commander][commander] | ||
2. Create a headless nest app, any module inside the app can create command and subcommands using nestjs-console with [commander][commander] | ||
3. nestjs-console invoke commander | ||
4. commander will do the rest. | ||
## [Install FROM NPM][npm] | ||
# Documentation | ||
```bash | ||
npm install commander nestjs-console | ||
# or unig yarn | ||
yarn add commander nestjs-console | ||
``` | ||
- [Wiki](https://github.com/Pop-Code/nestjs-console/wiki) | ||
- [API](https://pop-code.github.io/nestjs-console) | ||
- [Changelog](https://github.com/Pop-Code/nestjs-console/blob/master/CHANGELOG.md) | ||
- [Upgrade from v4 to v5](https://github.com/Pop-Code/nestjs-console/wiki/Upgrade-from-v4-to-v5) | ||
Note: | ||
For commander <5.0.0, use nestjs-console@2.1.0 | ||
For commander >=5.0.0 (latest), use nestjs-console@^3.0.2 | ||
## Create a cli endpoint | ||
Create a file at root next to your entry point named console.ts | ||
Import your app module or any module you want to be loaded. Usually this is your main nestjs module. | ||
```ts | ||
// console.ts - example of entrypoint | ||
import { BootstrapConsole } from 'nestjs-console'; | ||
import { MyModule } from './module'; | ||
const bootstrap = new BootstrapConsole({ | ||
module: MyModule, | ||
useDecorators: true | ||
}); | ||
bootstrap.init().then(async (app) => { | ||
try { | ||
// init your app | ||
await app.init(); | ||
// boot the cli | ||
await bootstrap.boot(); | ||
process.exit(0); | ||
} catch (e) { | ||
process.exit(1); | ||
} | ||
}); | ||
``` | ||
## Import the ConsoleModule in your main module | ||
```ts | ||
// module.ts - your module | ||
import { Module } from '@nestjs/common'; | ||
import { ConsoleModule } from 'nestjs-console'; | ||
import { MyService } from './service'; | ||
@Module({ | ||
imports: [ | ||
ConsoleModule // import the ConsoleModule | ||
], | ||
providers: [MyService], | ||
exports: [MyService] | ||
}) | ||
export class MyModule {} | ||
``` | ||
You can now inject the _ConsoleService_ inside any nestjs providers, controllers... | ||
There are 2 ways of registering providers methods to the console. | ||
Using @decorators or using the _ConsoleService_. | ||
Example of cli stack | ||
``` | ||
Cli -> Command_A -> [ | ||
Command_A1 -> execution, | ||
Command_A2 -> execution | ||
] | ||
-> Command_B -> [ | ||
Command_B1 -> execution, | ||
Command_B2 -> [ | ||
Command_B2_a -> execution | ||
Command_B2_b -> [... more sub commands ...] | ||
] | ||
] | ||
-> Command_C -> execution | ||
``` | ||
# Api | ||
As a simple example, we will define a cli with 2 commands (new and list), one of the command (new) will have 2 sub commands (directory and file) | ||
``` | ||
Cli -> list -> -> execution, | ||
-> new -> [ | ||
directory -> execution, | ||
file -> execution | ||
] | ||
``` | ||
## How to use Decorators | ||
Registering methods using class decorators is very easy. | ||
Nestjs providers that are decorated with @Console will be scanned and each member method that is decorated with @Command will be registered on the cli. | ||
```ts | ||
// service.ts - a nestjs provider using console decorators | ||
import { Console, Command, createSpinner } from 'nestjs-console'; | ||
@Console() | ||
export class MyService { | ||
@Command({ | ||
command: 'list <directory>', | ||
description: 'List content of a directory' | ||
}) | ||
async listContent(directory: string): Promise<void> { | ||
// See Ora npm package for details about spinner | ||
const spin = createSpinner(); | ||
spin.start(`Listing files in directory ${directory}`); | ||
// simulate a long task of 1 seconds | ||
const files = await new Promise((done) => setTimeout(() => done(['fileA', 'fileB']), 1000)); | ||
spin.succeed('Listing done'); | ||
// send the response to the cli | ||
// you could also use process.stdout.write() | ||
console.log(JSON.stringify(files)); | ||
} | ||
} | ||
``` | ||
#### Register a command with sub commands | ||
By default, the @Console will tell the module to register all decorated methods at root of the cli. | ||
`Example of Usage: [options] [command]` | ||
You can name your provider to be registered in a group command container. | ||
This is useful when you have a lot of commands and you want to group them as sub command. (git style) | ||
To achieve this, you have to group your methods into class. | ||
You have to pass options to the @Console decorator to configure the name of the parent cli. | ||
Decorated methods of the providers will be registered as a sub command instead of being registered at root. | ||
```ts | ||
// service.new.ts - a nestjs provider using console decorators (sub commands) | ||
@Console({ | ||
name: 'new', | ||
description: 'A command to create an item' | ||
}) | ||
export class MyNewService { | ||
@Command({ | ||
command: 'file <name>', | ||
description: 'Create a file' | ||
}) | ||
async createFile(name: string): void | Promise<void> { | ||
console.log(`Creating a file named ${name}`); | ||
// your code... | ||
} | ||
@Command({ | ||
command: 'directory <name>', | ||
description: 'Create a directory' | ||
}) | ||
async createDirectory(name: string): void | Promise<void> { | ||
console.log(`Creating a directory named ${name}`); | ||
// your code... | ||
} | ||
} | ||
``` | ||
If you need to register other sub commands from other Class to the same cli container, you have to decorate your class using the @Console decorator with the same name. | ||
```ts | ||
@Console({ | ||
name: 'new' // here the name is the same as the one from MyNewService, grouping all commands | ||
}) | ||
export class MyOtherService {...} | ||
``` | ||
`Example of Usage: new [options] [command]` | ||
## How to use the ConsoleService | ||
Registering methods using the ConsoleService is more flexible than decorators. | ||
When you use the ConsoleService, you simply bind your methods to the cli manually. | ||
This is useful if you need to create the cli or a part of the cli at runtime. | ||
This way you can also create multiple commands and sub commands from the same context. | ||
```ts | ||
// service.ts - a nestjs provider | ||
import { Injectable } from '@nestjs/common'; | ||
import { ConsoleService } from 'nestjs-console'; | ||
@Injectable() | ||
export class MyService { | ||
constructor(private readonly consoleService: ConsoleService) { | ||
// get the root cli | ||
const cli = this.consoleService.getCli(); | ||
// create a single command (See [npm commander arguments/options for more details]) | ||
this.consoleService.createCommand( | ||
{ | ||
command: 'list <directory>', | ||
description: 'description' | ||
}, | ||
this.listContent, | ||
cli // attach the command to the cli | ||
); | ||
// create a parent command container | ||
const groupCommand = this.consoleService.createGroupCommand( | ||
{ | ||
name: 'new', | ||
description: 'A command to create an item' | ||
}, | ||
cli // attach the command to the root cli | ||
); | ||
// create command | ||
this.consoleService.createCommand( | ||
{ | ||
command: 'file <name>', | ||
description: 'Create a file' | ||
}, | ||
this.createFile, | ||
groupCommand // attach the command to the group | ||
); | ||
// create an other sub command | ||
this.consoleService.createCommand( | ||
{ | ||
command: 'directory <name>', | ||
description: 'Create a directory' | ||
}, | ||
this.createDirectory, | ||
groupCommand // attach the command to the group | ||
); | ||
} | ||
listContent = async (directory: string): void | Promise<void> => { | ||
console.log(`Listing files in directory ${directory}`); | ||
// your code... | ||
}; | ||
createFile = async (name: string): void | Promise<void> => { | ||
console.log(`Creating a file named ${name}`); | ||
// your code... | ||
}; | ||
createDirectory = async (name: string): void | Promise<void> => { | ||
console.log(`Creating a directory named ${name}`); | ||
// your code... | ||
}; | ||
} | ||
``` | ||
## Command Handler signature | ||
```ts | ||
(...commandArguments[]; commandInstance: commander.Command) => Promise<any> | any | ||
``` | ||
Your handler will receive all command arguments, the last argument is the command instance from commander. | ||
You can read options from the command instance using `command.opts()` | ||
```ts | ||
@Command({ | ||
description: 'A complete command handler', | ||
command: 'myCommandWithArgumentsAndOptions <arg1> <arg2>', | ||
options: [ | ||
{ | ||
flags: '-o1, --option1 <o1Value>', | ||
required: false | ||
}, | ||
{ | ||
flags: '-o2, --option2 <o1Value>', | ||
required: true | ||
} | ||
] | ||
}) | ||
completeCommandHandler(arg1: string, arg2: string, command: commander.Command): void { | ||
// read command arguments | ||
console.log(arg1, arg2); | ||
// read command options | ||
const options = command.opts(); | ||
console.log(options.option1, options.option2); | ||
} | ||
``` | ||
## Command Options | ||
By default the presence of an option is not required. | ||
- If you need to force the presence of an option, set the required options to true. | ||
- If you need to force the presence of the argument of an option, you have to use `<options>` instead of `[options]` | ||
With option.required = false | ||
`-o, --option` => option is optionnal and will be true if specified | ||
`-o, --option <oValue>` option is optionnal and oValue argument is required | ||
`-o, --option [oValue]` option is optionnal and oValue argument is also optionnal | ||
With option.required = true | ||
`-o, --option` option is required and will be true | ||
`-o, --option <oValue>` option is required and oValue argument is required | ||
`-o, --option [oValue]` option is required but oValue argument is optionnal | ||
You can use variadic option argument using `[oValue...]` and `<oValue...>`, | ||
in this case oValue will be an array. | ||
Details from commander https://github.com/tj/commander.js#variadic-option | ||
## Add scripts in your package.json (if you want to use them) | ||
```js | ||
{ | ||
"scripts": { | ||
// from sources | ||
"console:dev": "ts-node -r tsconfig-paths/register src/console.ts", | ||
// from build (we suppose your app was built in the dist folder) | ||
"console": "node dist/console.js" | ||
} | ||
} | ||
``` | ||
## Usage | ||
Call the cli (production) | ||
```bash | ||
# using node | ||
node dist/console.js --help | ||
# using npm | ||
npm run console -- --help | ||
# using yarn | ||
yarn console --help | ||
``` | ||
Call the cli from sources (dev) | ||
```bash | ||
# using ts-node | ||
ts-node -r tsconfig-paths/register src/console.ts --help | ||
# using npm | ||
npm run console:dev -- --help | ||
# using yarn | ||
yarn console:dev --help | ||
``` | ||
#### Example of Response | ||
``` | ||
Usage: console [options] [command] | ||
Options: | ||
-h, --help output usage information | ||
Commands: | ||
list <directory> List content of a directory | ||
new A command to create an item | ||
``` | ||
### [API DOCUMENTATION][doclink] | ||
### [CHANGELOG][changelog] | ||
[npm]: https://www.npmjs.com/package/nestjs-console | ||
@@ -396,4 +44,3 @@ [npmchart]: https://npmcharts.com/compare/nestjs-console?minimal=true | ||
[codecov]: https://codecov.io/gh/Pop-Code/nestjs-console | ||
[doclink]: https://pop-code.github.io/nestjs-console | ||
[commander]: https://www.npmjs.com/package/commander | ||
[changelog]: https://github.com/Pop-Code/nestjs-console/blob/master/CHANGELOG.md |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
116535
63
2025
45
1
+ Addedbase64-js@1.5.1(transitive)
+ Addedbl@4.1.0(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedcommander@7.2.0(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedora@5.4.0(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
- Removedcommander@6.2.1(transitive)
- Removedmute-stream@0.0.8(transitive)
- Removedora@5.1.0(transitive)
Updatedora@5.4.0