NestJS Logo

Nest Commander

独立应用程序文档的基础上,还有 nest-commander 包,用于以类似于典型 Nest 应用程序的结构编写命令行应用程序。

信息nest-commander 是一个第三方软件包,不是由 NestJS 核心团队完全管理的。如果在使用该库时遇到任何问题,请在适当的存储库中报告。

安装#

就像任何其他软件包一样,在使用之前必须先安装它。


$ npm i nest-commander

命令文件#

nest-commander 通过 @Command() 类装饰器和方法的 @Option() 装饰器,使得编写新的命令行应用程序变得容易。每个命令文件都应该实现 CommandRunner 抽象类,并应该用 @Command() 装饰器进行装饰。

每个命令在 Nest 中都被视为一个 @Injectable(),因此您的正常依赖注入仍然按预期工作。需要注意的唯一一件事是抽象类 CommandRunner,每个命令都应该实现这个抽象类。CommandRunner 抽象类确保所有命令都有一个返回 Promise<void>run 方法,该方法接受参数 string[]Record<string, any>run 方法是您可以从中启动所有逻辑的地方,它会将与选项标志不匹配的任何参数都作为数组传入,以防您真的希望使用多个参数进行操作。至于选项,即 Record<string, any>,这些属性的名称与给 @Option() 装饰器指定的 name 属性匹配,而它们的值与选项处理程序的返回值匹配。如果您希望获得更好的类型安全性,可以为您的选项创建一个接口。

运行命令#

类似于在 NestJS 应用程序中我们可以使用 NestFactory 来为我们创建服务器,并使用 listen 运行它,nest-commander 包提供了一个简单易用的 API 来运行您的服务器。导入 CommandFactory 并使用静态方法 run,传入您应用程序的根模块。这可能看起来像下面这样:


import { CommandFactory } from 'nest-commander';
import { AppModule } from './app.module';

async function bootstrap() {
  await CommandFactory.run(AppModule);
}

bootstrap();

默认情况下,当使用 CommandFactory 时,Nest 的日志记录器是禁用的。但是可以通过将其作为 run 函数的第二个参数来提供它。您可以提供自定义的 NestJS 日志记录器,也可以提供您想要保留的日志级别数组 - 如果您只想打印出 Nest 的错误日志,那么至少在这里提供 ['error'] 可能会很有用。


import { CommandFactory } from 'nest-commander';
import { AppModule } from './app.module';
import { LogService } './log.service';

async function bootstrap() {
  await CommandFactory.run(AppModule, new LogService());

  // or, if you only want to print Nest's warnings and errors
  await CommandFactory.run(AppModule, ['warn', 'error']);
}

bootstrap();

就是这样了。在幕后,CommandFactory 将会为您调用 NestFactory,并在必要时调用 app.close(),因此您不需要担心内存泄漏。如果您需要添加一些错误处理,总是可以使用 try/catch 来包装 run 命令,或者您可以在 bootstrap() 调用后添加一些 .catch() 方法。

测试#

那么,如果您不能非常容易地对其进行测试,编写一个超级棒的命令行脚本有什么用呢,对吧?幸运的是,nest-commander 提供了一些工具,您可以充分利用这些工具,这些工具完美地融入了 NestJS 生态系统中,对任何 Nestlings 来说都感觉如此自然。在测试模式下,您可以使用 CommandTestFactory 来构建命令,非常类似于 @nestjs/testing 中的 Test.createTestingModule 的工作方式。实际上,在幕后它就是使用了这个软件包。您仍然可以在调用 compile() 之前使用 overrideProvider 方法进行链接,以便在测试中直接交换 DI 组件。

将所有内容整合#

下面的类将相当于具有子命令 basic 的 CLI 命令,或者可以直接调用,支持 -n-s-b(以及它们的长标志),并且每个选项都有自定义的解析器。--help 标志也是支持的,这是与 commander 习惯一致的做法。


import { Command, CommandRunner, Option } from 'nest-commander';
import { LogService } from './log.service';

interface BasicCommandOptions {
  string?: string;
  boolean?: boolean;
  number?: number;
}

@Command({ name: 'basic', description: 'A parameter parse' })
export class BasicCommand extends CommandRunner {
  constructor(private readonly logService: LogService) {
    super()
  }

  async run(
    passedParam: string[],
    options?: BasicCommandOptions,
  ): Promise<void> {
    if (options?.boolean !== undefined && options?.boolean !== null) {
      this.runWithBoolean(passedParam, options.boolean);
    } else if (options?.number) {
      this.runWithNumber(passedParam, options.number);
    } else if (options?.string) {
      this.runWithString(passedParam, options.string);
    } else {
      this.runWithNone(passedParam);
    }
  }

  @Option({
    flags: '-n, --number [number]',
    description: 'A basic number parser',
  })
  parseNumber(val: string): number {
    return Number(val);
  }

  @Option({
    flags: '-s, --string [string]',
    description: 'A string return',
  })
  parseString(val: string): string {
    return val;
  }

  @Option({
    flags: '-b, --boolean [boolean]',
    description: 'A boolean parser',
  })
  parseBoolean(val: string): boolean {
    return JSON.parse(val);
  }

  runWithString(param: string[], option: string): void {
    this.logService.log({ param, string: option });
  }

  runWithNumber(param: string[], option: number): void {
    this.logService.log({ param, number: option });
  }

  runWithBoolean(param: string[], option: boolean): void {
    this.logService.log({ param, boolean: option });
  }

  runWithNone(param: string[]): void {
    this.logService.log({ param });
  }
}

确保将该命令类添加到一个模块中:


@Module({
  providers: [LogService, BasicCommand],
})
export class AppModule {}

现在,要能够在 main.ts 中运行 CLI,您可以执行以下操作:


async function bootstrap() {
  await CommandFactory.run(AppModule);
}

bootstrap();

就这样,您就拥有了一个命令行应用程序。

更多信息#

请访问 nest-commander 文档站点 获取更多信息、示例和 API 文档。

支持一下