Giter VIP home page Giter VIP logo

nestjs-console's Introduction

nestjs-console Actions Status codecov NPM Downloads npm (tag) npm peer dependency version (scoped) npm dependency version (scoped)

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, cron 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 accessible inside the container.

  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
  3. nestjs-console invoke commander
  4. commander will do the rest.

Documentation

nestjs-console's People

Contributors

0xflotus avatar dador avatar dependabot[bot] avatar idready avatar immanuel192 avatar jcrben avatar kaufmo avatar marojeee avatar martin-juul avatar minishlink avatar n0mer avatar renovate-bot avatar rmannn avatar tzellman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

nestjs-console's Issues

Redundant usage instructions printed on every command

When following readme and creating a @command class,
then running any command within a NestJS context, including default commands and the @command,
results in redundant spam about usage every time, even when starting the application:

Usage: main [options] [command]

Options:
  -h, --help             display help for command

Commands:
  example-command  		 Example
  help [command]         display help for command

image

required options is not supported

The service is using cli.option() to register an option on the command.
With latest version of commander, we must use requiredOption to specify a required option.

We must add a flag to specify that an option is required in the CommandOption interface of the nestjs-console module.

Documentation must be added to explain how to use and parse options and arguments.
We must also test options and arguments.

Task Not Exiting

I was having an issue with my tasks not exiting after completion. They would run correctly but then hang and never exit so I'd have to Ctrl+C to exit them. It seems to be caused by the fact that process.exit(0) isn't being called in my boostrap file:

import { BootstrapConsole } from 'nestjs-console';
import { AppModule } from './app.module';

const bootstrap = new BootstrapConsole({
  module: AppModule,
  useDecorators: true,
});

bootstrap.init()
  .then(async (app) => {
    try {
      await app.init();
      await bootstrap.boot();
      await app.close();
      // Adding this line fixed my issue
      process.exit(0);
    } catch (e) {
      console.error(e);
      await app.close();
      process.exit(1);
    }
  })
;

I seem to recall doing that in earlier versions of this project but it's not in the docs anymore. Am I missing something here? Or do the docs need to be updated?

3.0.4 not working with default nestjs tsconfig.json

Hello. Thanks for your work!

Unfortunately, tsconfig.json needs to be adjusted to make app work with 3.x version of this library.
Following param should be added:
allowSyntheticDefaultImports:true

As long as default nestjs setup does not have such parameter set, some note should be added (IMO preferably TLDR) that this parameter should be set to true.

CLI command doesn't work with a request scoped dependency

Hello,

I think, I found a bug with your package and request scoped providers.

We have a CLI command, which is using a request scoped provider as a dependency. Although it doesn't makes much sense to have request scoped providers in CLI contexts (as there are no requests), our motivation is to reuse this service (from a GraphQL- and REST-context, where the request scoping does make sense).

Here is a fully working example to reproduce the problem:

import { BootstrapConsole } from 'nestjs-console';
import { Injectable, Module, Scope } from '@nestjs/common';
import { Command, Console, ConsoleModule } from 'nestjs-console';

@Injectable({ scope: Scope.REQUEST })
export class MyRequestScopedService {
  public doStuff(): boolean {
    return true;
  }
}

@Console()
export class MyCliService {
  constructor(private readonly myRequestScopedService: MyRequestScopedService) {}

  @Command({
    command: 'foo',
    description: 'A CLI command with a request scoped dependency.',
  })
  public execute(): void {
    console.log(this.myRequestScopedService.doStuff());
    console.log('foo!');
  }
}

@Module({
  imports: [ConsoleModule],
  providers: [MyCliService, MyRequestScopedService],
  exports: [MyCliService],
})
export class MyCliModule {}

const nestConsole = new BootstrapConsole({ module: MyCliModule, useDecorators: true });

nestConsole
  .init()
  .then(async (app) => {
    try {
      await app.init();
      await nestConsole.boot();
      process.exit(0);
    } catch (e) {
      process.exit(1);
    }
  })
  .catch((e) => console.error(e));

Steps to repeat:

  • Run ts-node console.ts foo

Expected result:
It should print true and foo!

Actual result:
I'm getting the error Cannot read property 'doStuff' of undefined.
When you replace @Injectable({ scope: Scope.REQUEST }) with @Injectable(), everything works as it should.

Can you please fix that or are we doing something wrong?

We are currently using NestJS 7.1.2, nestjs-console 3.0.5 (updating it to version 3.0.6 doesn't help) and commander 5.1.0.

TypeError: parent.addCommand is not a function

I am getting this error while running the command from the terminal.
It is coming on createCommand() function.

this.consoleService.createCommand(
      {
        command: this.updateSubscription.command,
        description: this.updateSubscription.description,
      },
      this.updateSubscription.run,
      cli
    );

am I missing something? please suggest.

Versions:
node: v12.22.6
npm: 6.14.15
"@nestjs/core": "^7.0.0"
"@nestjs/common": "^7.0.0"

TypeOrm dependency injection

Running npm run console:dev -- getAUser fails with the error:

TypeError: Cannot read property 'findOne' of undefined

Here is cli.service.ts.

import { Injectable } from '@nestjs/common';
import { ConsoleService } from 'nestjs-console';
import { User } from '@app/model/entity/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class CliService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly consoleService: ConsoleService,
  ) {
    // get the root cli
    const cli = this.consoleService.getCli();

    // just for debugging. REMOVE BEFORE PUSHING
    this.consoleService.createCommand(
      {
        command: 'getAUser',
      },
      CliService.prototype.getAUser,
      cli,
    );
  }

  async getAUser(): Promise<User> {
    let user: User;
    try {
      user = await this.userRepository.findOne({
        where: {
          loginEmail: '[email protected]',
        },
      });
    } catch (error) {
      throw new Error('ERROR ATTEMPTING TO GET USER. ' + error);
    }
    return Promise.resolve(user);
  }
}
 

Here is my cli.module


import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { LoggerModule } from 'nestjs-pino';
import { CliService } from '@app/cli/cli.service';
import { ConsoleModule } from 'nestjs-console';
import { User } from '@app/model/entity/user.entity';


@Module({
  imports: [
    TypeOrmModule.forFeature([
      User,
    ]),
    TypeOrmModule.forRoot(),
    LoggerModule.forRoot(),
    ConsoleModule,
  ],
  providers: [CliService],
  exports: [CliService],
})
export class CliModule {}

And If I create a cli.controller.ts, and run the app using npm:start, it works just fine.

import { Controller, Get } from '@nestjs/common';
import { CliService } from './cli.service';

@Controller('db')
export class CliController {
  constructor(private readonly cliService: CliService) {}

  @Get('/user')
  public async seed() {
    await this.cliService.getAUser();
  }
}


Here is the console.ts file


import { BootstrapConsole } from 'nestjs-console';
import { CliModule } from '@app/cli/cli.module';

const bootstrap = new BootstrapConsole({
  module: CliModule,
  useDecorators: false,
});
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);
  }
});

Database Connection Not Close After Command Ended

Hi, I have an issue with the database connection not close after running any command.

Every time I run the command, it will create a new database connection until I got an error Too Many Connection.

It only happens when I running the command. If not running command, the database connection will be terminated when I shut down the app.

I checked the BootstrapConsole code and I can't see the lifecycle hook init. Is that the reason?

How I use prisma is I follow the basic step here. https://docs.nestjs.com/recipes/prisma

Here I attached the gif image for your reference.

Jan-21-2021-09-27-44

Nest.js version: 7.5.2
ORM: Prisma
Database: MySQL

prisma.service.ts

import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy {
  constructor() {
    super({
      log: [{ emit: 'event', level: 'query' }],
    });
  }

  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

app.module.ts

@Module({
  imports: [ConsoleModule, TestModule],
  providers: [AppGateway],
})
export class AppModule {}

console.ts

import { BootstrapConsole } from 'nestjs-console';
import { AppModule } from './app.module';

const bootstrap = new BootstrapConsole({
  module: AppModule,
  useDecorators: true,
});

bootstrap.init().then(async (app) => {
  try {
    await app.init();

    await bootstrap.boot();

    process.exit(0);
  } catch (e) {
    process.exit(1);
  }
});

test.service.ts (Command)

import { HttpService } from '@nestjs/common';
import { Command, Console } from 'nestjs-console';
import { PrismaService } from 'src/prisma/prisma.service';

@Console()
export class TestService {
  constructor(
    private httpService: HttpService,
    private prisma: PrismaService,
  ) {}

  @Command({
    command: 'test:get',
    description: 'test command',
  })
  async testCommand() {
    const news = await this.prisma.news.findMany();

    console.log('execute test command.');
  }
}

@nestjs/common logger not printing logs

Hi, thank you very much for this great package, it helps me a lot!

I'm currently using the @nestjs/common logger in my services and wish to print the logs when I launch a command with nestjs-console.

Currently, the logs are not printed. Is their anything I can do to make them print?

CreateCommandOptions

Hello, I want to use command like
npm run console -- addTokens users=1,2,3 amount=1000

I don't understand how I can get 'users' parsed as array and 'amount' parsed as number in Service handler.
And how I can run ParserType.
Also I want that 'users' and 'amount' was required and described in command description

Cannot load repositories

I have a seed.ts file that works properly. Here is the code for that file:

async function bootstrap() {
  NestFactory.createApplicationContext(SeederModule)
    .then((appContext) => {
      const logger = appContext.get(Logger);
      const seeder = appContext.get(SeederService);
      seeder
        .seed()
        .then(() => {
          logger.debug('Seeding complete!');
        })
        .catch((error) => {
          logger.error('Seeding failed!');
          throw error;
        })
        .finally(() => appContext.close());
    })
    .catch((error) => {
      throw error;
    });
}
bootstrap();

I am trying to do this using console instead.


const bootstrap = new BootstrapConsole({
    // module: AppModule,
  module: SeederModule,
  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);
  }
});

My seeder file looks like this:

export class SeederService {
  creationTime: Date;
  creatorId: string;

  constructor(
    @InjectRepository(Party)
    private readonly partyRepository: Repository<Party>,
    @InjectRepository(Organization)
    private readonly organizationRepository: Repository<Organization>,
    private readonly logger: Logger,
    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: 'seedOrganization',
          description: 'description',
        },
        this.seedOrganization,
        cli, // attach the command to the cli
      );
    }

async seedOrganization(): Promise<boolean> {
    let createdParty: Party = await this.partyRepository.save(
      await this.partyRepository.create({
        creationTime: this.creationTime,
        creatorId: this.creatorId,
      }),
    );
    ....
  }

However, when I try to run it, I get the following error:

Cannot read property 'partyRepository' of undefined.

Any ideas on how to resolve this?

Add support for production mode install

At the moment, the library cannot be installed in production mode (i.e. yarn install --production), cause almost all required dependencies are in devDependencies section.

App cannot build because if incorrect exports

Hi!

I tried out your module, and it works really well. There's a problem with the imports in the module though. It results in the app only working with ts-node

node_modules/nestjs-console/lib/commander.d.ts(1,8): error TS1192: Module '"/usr/local/app/node_modules/commander/typings/index"' has no default export.
node_modules/nestjs-console/lib/service.d.ts(11,42): error TS2503: Cannot find namespace 'ora'.
error Command failed with exit code 2.

Update commander peerdependencies version

Is your feature request related to a problem? Please describe.
With npm@7 and the new peer dependencies check, and with the new version of commander package we got a npm install error

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: @...
npm ERR! Found: [email protected]
npm ERR! node_modules/commander
npm ERR!   commander@"^7.2.0" from @...
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer commander@"^5 || ^6" from [email protected]
npm ERR! node_modules/nestjs-console
npm ERR!   nestjs-console@"^4.0.0" from @...
npm ERR!   apps/api-backend

Describe the solution you'd like
A solution would be to allow commander version 7 inside their peerdependencies

Describe alternatives you've considered
For now i use npm i --force and everything is working

A command with multiple arguments is not working

I think the actual implementation:

createCommand(
options: CreateCommandOptions,
handler: CommandActionHandler,
parent: commander.Command
): commander.Command {
// const command = parent.command(options.command).exitOverride((...args) => parent._exitCallback(...args));
const args = options.command.split(' ');
const command = new commander.Command(args[0]);
command.exitOverride((...args) => parent._exitCallback(...args));
if (args.length > 1) {
command.arguments(args[1]);
}
made this command fail, you get only first argument :

    cs.createCommand(
      {
        command: 'historicoContrato <cod-contrato> <archivo>',
        description: 'Exportaciรณn de las anotaciones en el histรณrico referentes al contrato especificado',
      },
      this.exportaCSVHistoricoContrato,
      gcExportaCSV,
    );

Throws error if `@Console` service injects another service

Bug report:

If the annotated service injectes another service the console script throws an error.

Example:

import { Injectable } from '@nestjs/common'
import { Command, Console, ConsoleService } from 'nestjs-console'

import { ServiceB } from './serviceB'

@Injectable()
@Console()
export class ServiceA {

  constructor(private readonly serviceB: ServiceB) {
  }

  @Command({ command: 'test' })
  test() {
    console.log('in test');
  }
}

This throws the error:

> [email protected] console 
/folder/secret/xxx
> ts-node -r tsconfig-paths/register src/console.ts "test"

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] console: `ts-node -r tsconfig-paths/register src/console.ts "test"`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] console script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/xxx/.npm/_logs/2019-12-08T14_32_27_205Z-debug.log
โžœ  xxx git:(master) โœ— 

It works without the injected service!

method with command decorator doesnt call sub methods properly

i have a method called

fetchThisFromDB(){
   // and inside this method i can 
  const users = await this.userModel.find({is_valid : false})
   await users.forEach(this.processUser.bind(this)) // this line doesnt work properly
}

async processUser(user){
   // do some stuffs and some other stuffs
   // call other services from other modules
 }

Add examples: how to create new root commander

The decorator @InjectCommander will inject the singleton commander. With all existing commands and sub commands.
We need to be able to create new empty commands.
Allowing us to have multiple cli instances with multiple enty points.

Import { Command } from 'nestjs-console';
const cli = new Command();
  • Add example
  • Add a static service method to create a new instance

CLI will _always_ throw an error even with empty promise handler

bash-3.2$ npm run console:dev -- list-negative-layers

> <omitted>@<omitted> console:dev
> ts-node -r tsconfig-paths/register src/console.ts "<omitted>"

<omitted>
TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
    at Function.all (<anonymous>)
    at ConsoleService.<anonymous> (/Users/hbina/<omitted>/node_modules/nestjs-console/src/service.ts:106:39)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/hbina/<omitted>/node_modules/nestjs-console/dist/service.js:17:58)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
bash-3.2$ npm ls nestjs-console
@<omitted>@<omitted> /Users/hbina/<omitted>
โ””โ”€โ”€ [email protected]

bash-3.2$ npm --version
7.21.0

Cannot find exitOverride function

Describe the bug
A clear and concise description of what the bug is.
I catch a bug when trying to run console command via yarn run:

(node:15413) UnhandledPromiseRejectionWarning: TypeError: parent.command(...).exitOverride is not a function
    at ConsoleService.createCommand (/home/daydark-pc/GitLab/mp3studio-api/node_modules/nestjs-console/src/service.ts:167:14)
    at /home/daydark-pc/GitLab/mp3studio-api/node_modules/nestjs-console/src/module.ts:34:30
    at Set.forEach (<anonymous>)
    at ConsoleModule.scan (/home/daydark-pc/GitLab/mp3studio-api/node_modules/nestjs-console/src/module.ts:25:22)
    at BootstrapConsole.useDecorators (/home/daydark-pc/GitLab/mp3studio-api/node_modules/nestjs-console/src/bootstrap/abstract.ts:80:23)
    at BootstrapConsole.<anonymous> (/home/daydark-pc/GitLab/mp3studio-api/node_modules/nestjs-console/src/bootstrap/abstract.ts:97:18)
    at Generator.next (<anonymous>)
    at fulfilled (/home/daydark-pc/GitLab/mp3studio-api/node_modules/nestjs-console/lib/bootstrap/abstract.js:5:58)
(node:15413) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:15413) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

To Reproduce
Steps to reproduce the behavior:

  1. Use this gist to run project https://gist.github.com/daydarkln/f53f06c7bbb94344f1d00becc82471c1
  2. run yarn run console:dev update-featured-videos

Expected behavior
This command must log hi

Desktop (please complete the following information):

  • OS: Kubuntu 18.04
  • Konsole app

database connection not found

The module works correctly if the services do not use the database.
From the moment we use the repository to access to the database, we get the following error message:
Connection "default" was not found.
In my command module imported this line:
TypeOrmModule.forRoot (typeOrmConfig)
or without parameter:
TypeOrmModule.forRoot ()
=> In both cases it doesn't work
Would you know how to use the database via a command ? An example ?
Thank you.

Extracting options (flags) in the command

Hi,

I didn't find a way to retrieve a flag from. For example if I run a command with --dry-run, and I declare this like this:

@Command({
    command: 'update',
    options: [
      {
        flags: '--dry-run',
        defaultValue: false,
        description: 'Dry run the execution',
      },
    ],
    description: 'Update will create the index for actor id and type',
  })
  public async update(command) {

I tried to debug the command argument but it doesn't really help, so I think it would be wiser to give you feedback :)

Here is the command argument, dry-run is present but for me, it would be not clean to retrieve it like this: command.options[0] and I didn't find the type used in commander and your lib :(

Command {
  commands: [],
  options: [
    Option {
      flags: '--dry-run',
      required: false,
      optional: false,
      mandatory: false,
      negate: false,
      long: '--dry-run',
      description: 'Dry run the execution'
    }
  ],
  _execs: Set {},
  _allowUnknownOption: false,
  _args: [],
  _name: 'update',
  _helpFlags: '-h, --help',
  _helpDescription: 'output usage information',
  _helpShortFlag: '-h',
  _helpLongFlag: '--help',
  _noHelp: false,
  _exitCallback: undefined,
  _executableFile: undefined,
  parent: Command {
    commands: [ [Circular], [Command] ],
    options: [],
    _execs: Set {},
    _allowUnknownOption: false,
    _args: [],
    _name: '01-actor-id-actor-type-index',
    _helpFlags: '-h, --help',
    _helpDescription: 'output usage information',
    _helpShortFlag: '-h',
    _helpLongFlag: '--help',
    _noHelp: false,
    _exitCallback: undefined,
    _executableFile: undefined,
    parent: Command {
      commands: [Array],
      options: [],
      _execs: Set {},
      _allowUnknownOption: false,
      _args: [],
      _name: 'migration',
      _helpFlags: '-h, --help',
      _helpDescription: 'output usage information',
      _helpShortFlag: '-h',
      _helpLongFlag: '--help',
      _events: [Object: null prototype],
      _eventsCount: 2,
      rawArgs: [Array],
      args: []
    },
    _description: 'A command to create the index',
    _argsDescription: undefined,
    _events: [Object: null prototype] {
      'command:update': [Function: listener],
      'command:rollback': [Function: listener]
    },
    _eventsCount: 2
  },
  _description: 'Update will create the index for actor id and type',
  _argsDescription: undefined,
  _events: [Object: null prototype] { 'option:dry-run': [Function] },
  _eventsCount: 1
}

Unable to add a global option

I want to create a global option to print the project version (defined in the package.json file) with flags -v, -V and --version

$ npm run cli -- -v
0.0.1

I use @Console() decorator at the root service of my CLI, and I was expecting to be able to add global options from the decorator parameter, but turns out it's a CreateCommandOptions, so I'm forced to create a new command to create options. I guess there is another way to do so but I don't find it in Wiki documentation.

P.S. : It could be a cool default feature to have version global option.

It doesn't work with ServeStaticModule

Hello, I'm using next module:

        ServeStaticModule.forRoot({
            rootPath: join(__dirname, '..', 'static_www'),
            renderPath: '/',
            exclude: ['/api*'],
        }),

And have next error:

(node:2065) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'getInstance' of null
at ExpressLoader.register (/Users/backend/node_modules/@nestjs/serve-static/dist/loaders/express.loader.js:17:33)
at ServeStaticModule. (/Users/backend/node_modules/@nestjs/serve-static/dist/serve-static.module.js:87:25)
at Generator.next ()
at /Users/backend/node_modules/@nestjs/serve-static/dist/serve-static.module.js:20:71
at new Promise ()
at __awaiter (/Users/backend/node_modules/@nestjs/serve-static/dist/serve-static.module.js:16:12)
at ServeStaticModule.onModuleInit (/Users/backend/node_modules/@nestjs/serve-static/dist/serve-static.module.js:85:16)
at Object.callModuleInitHook (/Users/backend/node_modules/@nestjs/core/hooks/on-module-init.hook.js:50:35)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async NestApplicationContext.callInitHook (/Users/backend/node_modules/@nestjs/core/nest-application-context.js:153:13)
(node:2065) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:2065) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

After I remove 'ServeStaticModule' everything works fine.

Unable to register commands in nestjs-console

I'm following the wiki guide, trying to configure it in nestjs monorepo mode with version 7.6.15

STEP1

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

const bootstrap = new BootstrapConsole({
  module: AppModule,
  useDecorators: true,
});
bootstrap.init().then(async (app) => {
  try {
    await app.init();
    await bootstrap.boot();
    await app.close();
  } catch (e) {
    console.error(e);
    await app.close();
    process.exit(1);
  }
});

STEP2

// in app.module
@Module({
  imports: [
    ConsoleModule,
   ...
   ...
   providers: [ CLIProvider ]

STEP3 command

@Console()
export class CLIProvider {
  @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));
  }
}

After running this command npx ts-node -r tsconfig-paths/register apps/scratch/src/console.ts --help I get this wierd error which is not helpful.

(node:9784) UnhandledPromiseRejectionWarning: Error: Nest could not find given element (this provider does not exist in the current context)
    at InstanceLinksHost.get (/home/in01-nbk-741/Work/extras/nest/scratch/node_modules/@nestjs/core/injector/instance-links-host.js:18:19)
    at NestApplicationContext.resolvePerContext (/home/in01-nbk-741/Work/extras/nest/scratch/node_modules/@nestjs/core/nest-application-context.js:222:38)
    at NestApplicationContext.resolve (/home/in01-nbk-741/Work/extras/nest/scratch/node_modules/@nestjs/core/nest-application-context.js:59:21)
    at ConsoleScanner.<anonymous> (/home/in01-nbk-741/Work/extras/nest/scratch/node_modules/nestjs-console/src/scanner.ts:85:52)
    at Generator.next (<anonymous>)
    at /home/in01-nbk-741/Work/extras/nest/scratch/node_modules/nestjs-console/dist/scanner.js:8:71
    at new Promise (<anonymous>)
    at __awaiter (/home/in01-nbk-741/Work/extras/nest/scratch/node_modules/nestjs-console/dist/scanner.js:4:12)
    at /home/in01-nbk-741/Work/extras/nest/scratch/node_modules/nestjs-console/src/scanner.ts:63:106
    at Array.map (<anonymous>)
(node:9784) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:9784) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Showing "real" test usage

Hi,

I'm currently trying out your package and it's really nice job :)

I can write unit tests with ease, it does not change the way at all but I didn't find a proper way to a e2e/integration test.

I've been inspired by your Bootstrap test to run a specific command but I didn't succeed yet.
If by any chance you can provide an example that would be great for newcomer :)

Meanwhile, I'll keep digging for a proper way :)

Edit:

I succeed starting my command with the right parameters, but as its async, I can't really wait from the boot to finish and check everything is ok. Any idea about it?

Edit:2

Test run but just complain about promises not terminated/handled properly, here is a snippet of how I did handle the test, hope it will give you a bit of context:

import { INestApplicationContext } from '@nestjs/common';
import { BootstrapConsole } from 'nestjs-console';

import { Collection } from 'mongodb';
import { MigrationModule } from '../../src/migration.module';
import { getCollectionToken } from 'src/core/mongodb/mongodb.utils';
import { Snap } from 'src/snaps/model/schema/snap.schema';

describe('Migrations (e2e)', () => {
  let app: INestApplicationContext;
  let snapsCollection: Collection<Snap>;
  let boot: Function;

  beforeAll(async () => {
    const result = await BootstrapConsole.init({
      module: MigrationModule,
    });

    app = result.app;
    boot = result.boot;

    snapsCollection = app.get<Collection<Snap>>(getCollectionToken('snaps'));

    jest.spyOn(process, 'exit').mockImplementation();
  });

  beforeEach(async () => {
    await snapsCollection.dropIndexes({});
  });

  afterAll(async () => {
    await app.close();
  });

  describe('ActorIdActorTypeMigrationService (CLI)', () => {
    it('should succeed and create the index', async () => {
      boot([process.argv0, 'node', '01-actor-id-actor-type-index', 'update']);

      await new Promise(resolve => setTimeout(resolve, 100));

      expect(await snapsCollection.listIndexes().toArray()).toEqual([
        {
          v: 2,
          key: {
            _id: 1,
          },
          name: '_id_',
          ns: 'snapping.snaps',
        },
        {
          v: 2,
          key: {
            actor_id: 1,
            actor_type: 1,
          },
          name: 'actor_id_1_actor_type_1',
          ns: 'snapping.snaps',
          background: true,
        },
      ]);
    });

    it('should succeed and remove the index', async () => {
      boot([process.argv0, 'node', '01-actor-id-actor-type-index', 'update']);
      await new Promise(resolve => setTimeout(resolve, 100));

      boot([process.argv0, 'node', '01-actor-id-actor-type-index', 'rollback']);
      await new Promise(resolve => setTimeout(resolve, 100));

      expect(await snapsCollection.listIndexes().toArray()).toEqual([
        {
          v: 2,
          key: {
            _id: 1,
          },
          name: '_id_',
          ns: 'snapping.snaps',
        },
      ]);
    });
  });
});

And the service:

import { Console, Command } from 'nestjs-console';
import { Inject } from '@nestjs/common';

import { Migrate } from '../interfaces/migrate.interface';
import { Collection } from 'mongodb';
import { Snap } from 'src/snaps/model/schema/snap.schema';
import { InjectCollection } from 'src/core/mongodb/decorators/inject-collection.decorator';
import { LoggerFactory } from 'src/core/logger/logger.factory';
import { LoggerService } from 'src/core/logger/logger.service';

@Console({
  name: '01-actor-id-actor-type-index',
  description: 'A command to create the index',
})
export class ActorIdActorTypeMigrationService implements Migrate {
  private indexName = 'actor_id_1_actor_type_1';
  private readonly logger: LoggerService;

  public constructor(
    @InjectCollection('snaps') private readonly collection: Collection<Snap>,
    @Inject(LoggerFactory) loggerFactory: LoggerFactory,
  ) {
    this.logger = loggerFactory.makeLogger(
      ActorIdActorTypeMigrationService.name,
    );
  }

  @Command({
    command: 'update',
    description: 'Update will create the index for actor id and type',
  })
  public async update() {
    this.logger.info('Creating index...', { name: this.indexName });

    await this.collection.createIndex(
      {
        actor_id: 1,
        actor_type: 1,
      },
      {
        name: this.indexName,
        background: true,
      },
    );

    this.logger.info('Index created', { name: this.indexName });
  }

  @Command({
    command: 'rollback',
    description: 'Update will remove the index for actor id and type',
  })
  public async rollback() {
    this.logger.info('Removing index...', { name: this.indexName });

    await this.collection.dropIndex(this.indexName);

    this.logger.info('Index removed', { name: this.indexName });
  }
}

Swallowing errors in v1

My project is not open-source and would have to do some work to create a reproduction, but I may get around to that later. But I'm noticing that this sometimes swallows errors. Node version 12.7.0

I'm seeing this:
image

I've run into this before and found the error with a debugger, but in this case vscode isn't picking up an exception - it's just restarting constantly. It seems to be related to this ConfigService
image. I eventually narrowed down the line where it stops and
after adding a try / catch around https://github.com/nestjs/nest/blob/2d5cd89135a77066870abaf067986cb1c08213ca/packages/core/injector/injector.ts#L213 I saw this:
image

I'm wondering if there's a way to adjust the code for the console without messing with the underlying nestjs code so that these errors are not swallowed.

Last version not work with example

Last version throw (node:11492) UnhandledPromiseRejectionWarning: TypeError: parent.addCommand is not a function

When i downgrade to version 2.1.0 everything works welll

Create Spinner

In the examples you create a spinner like this:

const spin = this.consoleService.createSpinner()

I've injected the service. However, it errors out:

Property 'createSpinner' is a static member of type 'ConsoleService'

Is there anything I'm missing here? Thank you ๐Ÿ˜

Documentation Request

Have a function "createUser" that takes 2 required parameters and 2 optional parameters.
Want to be able to specify the command, like so:
npm run console -- create-user -n adi -e [email protected] -w 180
Tried the following but it didn't work. Please provide some idea about how to create such commands

/**
     * Creates a user
     * Required Params: name, email
     * Optional Params: height, weight
     */
    this.consoleService.createCommand(
      {
        command:
          'create-user <name> <email> [height] [weight]',
        description: 'create an organization',
        options: [
          {
            flags: '-n, --name',
            description: 'name of person',
          },
          {
            flags: '-e, --email',
            description:
              'email of person',
          },
          {
            flags: '-h, --height',
            description: 'height of person,
          },
          {
            flags: '-w, --weight',
            description: 'weight of person',
          },
        ],
      },
      DatabaseService.prototype.createUser.bind(this),
      cli, // attach the command to the cli
    );

Examples doesn't work?

I copy pasted the examples in the docs and they don't seem to work for me.

There is no output:

$ npm run console new
> node dist/console.js "new"

$ npm run console new file asdf
> node dist/console.js "new" "file" "asdf"

The first example with "list" command only gives error:

$ npm run console

> node dist/console.js

The cli does not contain sub command
npm ERR! code ELIFECYCLE

And the syntax async createFile(name: string): void | Promise<void> does not parse for me in TS.

Have to changed it to:
async createFile(name: string): Promise<void>

Is this package outdated or did I forget something?

NestJS 7.6.15

_actionResults is undefined

When using this library with the latest version I got this error:

TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
    at Function.all (<anonymous>)
    at ConsoleService.<anonymous> (/data/Projects/stack/node_modules/nestjs-console/src/service.ts:107:27)
    at Generator.next (<anonymous>)
    at fulfilled (/data/Projects/stack/node_modules/nestjs-console/dist/service.js:17:58)

This error is telling that this.cli._actionResults is undefined. My handler is correctly executed and this error pop after the execution.

const results = await Promise.all((this.cli as any)._actionResults as Promise<CommandResponse>[]);

v2 init fails silently and kills the process

Describe the bug
Console silently fails with:
error Command failed with exit code 1.

and kills the process.

To Reproduce
entry point looks like:

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

const bootstrap = new BootstrapConsole({
  module: ApplicationModule,
  useDecorators: true
});

bootstrap.init().then(async app => {
  try {
    // init your app
    await app.init();
    // boot the cli
    await bootstrap.boot();
    console.log('**********should boot?*********');
    process.exit(0);
  } catch (e) {
    console.log('*******************');
    console.log(e);
  }
});

Expected behavior
A failure with some kind of error output

Desktop (please complete the following information):

  • OS: [e.g. iOS] Darwin Kevs-MacBook-Pro.local 18.5.0 Darwin Kernel Version 18.5.0: Mon Mar 11 20:40:32 PDT 2019; root:xnu-4903.251.3~3/RELEASE_X86_64 x86_64
  • Version [e.g. 22] [email protected]
kevs-mbp:svc-social kevzettler$ node -v
v12.13.1

Error during compile new app with nestjs-console

I created a new NestJS app using @nestjs/cli and added nestjs-console with simple test console and test command, but I have some issue while compiling the project via the default script npm run build

npm run build

> [email protected] prebuild /home/hex/Sandbox/nestjs-console-issue
> rimraf dist


> [email protected] build /home/hex/Sandbox/nestjs-console-issue
> nest build

node_modules/nestjs-console/dist/helpers.d.ts:1:8 - error TS1259: Module '"/home/hex/Sandbox/nestjs-console-issue/node_modules/nestjs-console/node_modules/ora/index"' can only be default-imported using the 'esModuleInterop' flag

1 import ora from 'ora';
         ~~~

  node_modules/nestjs-console/node_modules/ora/index.d.ts:269:1
    269 export = ora;
        ~~~~~~~~~~~~~
    This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.
node_modules/nestjs-console/dist/interfaces.d.ts:1:8 - error TS1259: Module '"/home/hex/Sandbox/nestjs-console-issue/node_modules/commander/typings/index"' can only be default-imported using the 'esModuleInterop' flag

1 import commander from 'commander';
         ~~~~~~~~~

  node_modules/commander/typings/index.d.ts:386:1
    386 export = commander;
        ~~~~~~~~~~~~~~~~~~~
    This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.

Found 2 error(s).

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] build: `nest build`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/hex/.npm/_logs/2020-04-29T16_50_18_279Z-debug.log

Full repo here: https://github.com/techvlad/nestjs-console-issue

Add tests and documentation about command handler

Actually it's really difficult to understand how to get options values, argument values, and command instance.

The signature of an handler is arg1, arg2 arg3..., command.
To get the options we must use command.opts().

This should be documented

Commands defined in yargs(such as TypeORM) fail after adding ConsoleModule

Following the instructions in readme,
when adding ConsoleModule to the main application module,
then all TypeORM commands start failing with "command not found".
Removing the ConsoleModule from the main application module restores TypeORM functionality.

Latest as of today versions of everything.

Incorrect peer dependecy (commander)

Since nestjs uses commander ^4.0.1, installing nestjs-console leads to:

yarn add commander nestjs-console                                                                                                                                    โœน โœญ
yarn add v1.19.2
[1/4] ๐Ÿ”  Resolving packages...
[2/4] ๐Ÿšš  Fetching packages...
warning Pattern ["commander@^4.0.1"] is trying to unpack in the same destination "/Users/panki/Library/Caches/Yarn/v6/npm-commander-4.0.1-b67622721785993182e807f4883633e6401ba53c-integrity/node_modules/commander" as pattern ["[email protected]"]. This could result in non-deterministic behavior, skipping.
[3/4] ๐Ÿ”—  Linking dependencies...
warning " > [email protected]" has incorrect peer dependency "commander@^3.0.0".
[4/4] ๐Ÿ”จ  Building fresh packages...
success Saved lockfile.
success Saved 3 new dependencies.
info Direct dependencies
โ”œโ”€ [email protected]
โ””โ”€ [email protected]
info All dependencies
โ”œโ”€ [email protected]
โ”œโ”€ [email protected]
โ””โ”€ [email protected]
โœจ  Done in 3.26s.

Documentation update for using opts

Is your feature request related to a problem? Please describe.
Documentation suggests to use command.opts() to retrieve command options, but actual behavior is that commands are available directly on the command object, i.e. command.opt1

You can still access the commander object, but that comes after the options object. See the last example

Describe the solution you'd like
Update documentation to reflect real usage.

Describe alternatives you've considered
N/A

Additional context
This doesn't work

@Command({
        command: 'create <email>',
        description: 'Create a new user',
        options: [
            {
                flags: '--firstName <firstNameValue>',
                required: false
            },
        ]
    })
    async create(email: string, command): Promise<void> {
       command.opts() // opts is not a function
}

This works

@Command({
        command: 'create <email>',
        description: 'Create a new user',
        options: [
            {
                flags: '--firstName <firstNameValue>',
                required: false
            },
        ]
    })
    async create(email: string, options, commander): Promise<void> {
         options.firstName // works
         commander.opts().firstName //works

}

NestJS 7.6.13
nestjs-console 4.0.0

Importing REQUEST breaks cli

Adding @Inject(REQUEST) private request to any service leads to the cli commands in that service not being read properly.

import { REQUEST } from '@nestjs/core';

@Injectable()
export class CLIService {
  constructor(
    @Inject(REQUEST) private request,
    private readonly consoleService: ConsoleService,
  ) {
    // get the root cli
    const cli = this.consoleService.getCli();
    this.consoleService.createCommand(
      {
        command: 'test-cli',
      },
      CLIService.prototype.testCLIWrapper.bind(this),
      cli,
    );
}

test-cli command is not recognized.

If @Inject(REQUEST) private request is commented out, then the test command works

Can't read CLI options to configure the database

In my CLI program, I have a --db option that allows configuring database connection for all Commands. The code below is what I got so far. However this causes the program to freeze because module waits until useFactory() resolves the Promise but it will never resolve because commandline options haven't been parsed yet.

Is there any way to make sure that CLI commands get parsed and are available before anything else?

`import { Auth0Module } from '@app/common/auth0/auth0.module';
import { CommonModule } from '@app/common/common.module';
import { UsersModule } from '@app/common/users/users.module';
import { HttpException, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose';
import { ConsoleModule, ConsoleService } from 'nestjs-console';

@module({
imports: [
ConsoleModule,
MongooseModule.forRootAsync({
imports: [ConfigModule, ConsoleModule],
useFactory: async function (configService: ConfigService, cli: ConsoleService): Promise {
const c = cli.getCli().requiredOption("--db ", "DB string")

    let v = {
      uri: configService.get('MONGODB_URL', 'mongodb://localhost/mydb'),
      retryAttempts: 1,
    }

    return new Promise((resolve) => {
      console.log("promise start")
      c.on('option:db', (op: string) => {
        console.log("op:" + op)
        v.uri = op //override database uri with cli global option
        resolve(v)
      })
    })

  },
  inject: [ConfigService, ConsoleService],
}),
CommonModule,

],
providers: [

]
})
export class AppModule { }`

Any way to bind the cli to a running server?

I see in the docs that this requires an applicationInstance instead of a server - is it possible to modify it so that the cli binds to an existing server instance or creates it on initialization?

My use case is that I want to see currently connected clients (socketio) on demand from the CLI, something that seems impossible without attaching to the running instance.

[Question]: example use nx project

Dear @Rmannn
Hello
I am very grateful to have this library.
Currently, using Nx, Monorepo Project,
We would like to divide the development into two applications, api Application and Cli Application.

Is there an example of developing this library using nx cli instead of using ts-node?

[Question]: Build for Production

Dear @Rmannn ,
thanks for this library, i really like it! Currently, i am at the point, where i would like to deploy my application to a docker container in production.

When i run the build command, however, the cli.ts file (which is next to the main.ts) is not compiled to the dist/ folder. Therefore, i am not able to run commands on my production environment.

How can i overcome this issue?
Can you provide some instructions how to run the cli in a production environment? I am aware, that you have a short usage in the README.md file, however, this assumes that you have a dist/cli.js file - which i dont :)

All the best and thanks a lot

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.