Commander Nest Commander

导读

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

info

nest-commander 是第三方软件包,并非由 NestJS 核心团队整体管理。请在 适当的存储库 中报告发现的与库相关的任何问题

安装

与任何其他软件包一样,您必须先安装它,然后才能使用它。

bash
$ npm i nest-commander

命令文件

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

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

运行命令

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

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

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

bootstrap()

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

ts
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 命令,或者您可以将一些 .catch() 方法链接到 bootstrap() 调用。

测试

那么,如果您不能非常轻松地测试它,那么编写一个超级棒的命令行脚本有什么用呢?幸运的是,nest-commander 有一些您可以使用的实用程序,它们与 NestJS 生态系统完美契合,它会让任何 Nestlings 感到宾至如归。您可以使用CommandTestFactory并传入元数据,而不是使用CommandFactory在测试模式下构建命令,这与@nestjs/testing中的Test.createTestingModule的工作方式非常相似。事实上,它在后台使用了这个包。在调用compile()之前,您仍然可以链接overrideProvider方法,这样您就可以在测试中直接交换 DI 部分。

将它们放在一起

以下类相当于拥有一个 CLI 命令,它可以接受子命令basic或直接调用,其中-n-s-b(及其长标志)均受支持,并且每个选项都有自定义解析器。还支持--help标志,这是指挥官的惯例。

ts
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 })
  }
}

确保命令类已添加到模块

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

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

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

bootstrap()

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

More Information

访问 nest-commander 文档站点 了解更多信息、示例和 API 文档。