Git Product home page Git Product logo

nestjs-console's Introduction

nestjs-console Actions Status codecov NPM Downloads npm

nestjs-console is a module that provide a cli. A ready to use service class for your modules that exposes methods to register commands and sub commands using the npm package commander

Why

The nestjs framework is missing a cli to access the application context.
Common use case : Headless application, cront task, export data, etc... nestjs-console provide a way to bind cli command and subcommands to providers's methods.

How it works

The console service works as a standalone process, like the classic entry point, and will initialize a NestApplicationContext (headless) instead a NestApplication. The console service will be accesible inside the container.

  1. Bootstrap (entry point e.g console.ts) is invoked by cli.
  2. Init a headless nest app
    • Any module inside the app can create command and subcommands using nestjs-console with commander
  3. nestjs-console invoke commander
  4. commander will do the rest.
npm install commander nestjs-console
# or unig yarn
yarn add commander nestjs-console

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. You can create as many entry points as you want. You can also extend the BootstrapConsole class to suit your needs.

// console.ts - example of entrypoint
import { BootstrapConsole } from 'nestjs-console';
import { MyModule } from './module';

BootstrapConsole.init({ module: MyModule })
    .then(({ app, boot }) => {
        // do something with your app container if you need (app)
        // boot the cli
        boot(/*process.argv*/);
    })
    .catch(e => console.log('Error', e));

Import the ConsoleModule in your main module

// 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 (>=v1.1) or using the ConsoleService.

At the moment, it's not possible to have more than 2 dimensions in the commands stack using decorators.

Example of possible cli stack using decorators

Cli -> Command_A -> [
        Command_A1 -> execution,
        Command_A2 -> execution
    ]
    -> Command_B -> [
        Command_B1 -> execution,
        Command_B2 -> execution
    ]
    -> Command_C -> execution

Example of possible cli stack using the ConsoleService (More flexible, Multi dimensions)

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 an 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.

// service.ts - a nestjs provider using console decorators
import { Console, Command } from 'nestjs-console';

@Console()
export class MyService {
    @Command({
        command: 'list <directory>',
        description: 'List content of a directory'
    })
    async listContent(directory: string): void | Promise<void> {
        // See Ora npm package for details about spinner
        const spin = this.consoleService.constructor.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));

        process.exit(0);
    }
}

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 use it as a parent command container. This is usefull 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 your provider as a parent command. Decorated methods of the providers will be registered as a sub command instead of beeing registered at root.

// 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...
        process.exit(0); // it's important to exit the process.
    }

    @Command({
        command: 'directory <name>',
        description: 'Create a directory'
    })
    async createDirectory(name: string): void | Promise<void> {
        console.log(`Creating a directory named ${name}`);
        // your code...
        process.exit(0); // it's important to exit the process.
    }
}

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 usefull if you need to create the cli or a part of the cli at runtime.
This way you can also create mutliple commands ans sub commands from the same class.

// service.ts - a nestjs provider
import { Injectable } from '@nestjs/common';
import { ConsoleService } from 'nestjs-console';

@Injectable()
export class MyService {
    constructor(private readonly consoleService: ConsoleService) {
        this.listContent = this.listContent.bind(this);
        this.createFile = this.createFile.bind(this);
        this.createDirectory = this.createDirectory.bind(this);

        // get the root cli
        const cli = this.consoleService.getCli();

        // create a single command (See [npm commander for more details])
        cli.command('list <directory>')
            .description('List content of a directory')
            .action(this.listContent);

        // create a parent command container
        const parentCommand = this.consoleService.subCommands(
            cli,
            'new',
            'A command to create an item'
        );

        // create sub command
        parentCommand
            .command('file <name>')
            .description('Create a file')
            .action(this.createFile);

        // create an other sub command
        parentCommand
            .command('directory <name>')
            .description('Create a directory')
            .action(this.createDirectory);
    }

    async listContent(directory: string): void | Promise<void> {
        console.log(`Listing files in directory ${directory}`);
        // your code...
        process.exit(0); // it's important to exit the process.
    }

    async createFile(name: string): void | Promise<void> {
        console.log(`Creating a file named ${name}`);
        process.exit(0); // it's important to exit the process.
    }

    async createDirectory(name: string): void | Promise<void> {
        console.log(`Creating a directory named ${name}`);
        // your code...
        process.exit(0); // it's important to exit the process.
    }
}

Add scripts in your package.json (if you want to use them)

{
    "scripts": {
        // from sources
        "console:dev": "ts-node -r tsconfig-paths/register src/console.ts",
        // from build (we suppose your app was built in the lib forlder)
        "console": "node lib/console.js"
    }
}

Usage

Call the cli (production)

# using node
node lib/console.js --help
# using npm
npm run console -- --help
# using yarn
yarn console --help

Call the cli from sources (dev)

# 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

nestjs-console's People

Contributors

immanuel192 avatar jcrben avatar martin-juul avatar renovate-bot avatar rmannn avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.