UNPKG

nest-commander

Version:

A module for making CLI applications with NestJS. Decorators for running commands and separating out config parsers included. This package works on top of commander.

222 lines 11.4 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CommandRunnerService = void 0; const common_1 = require("@nestjs/common"); const nestjs_discovery_1 = require("@golevelup/nestjs-discovery"); const commander_1 = require("commander"); const constants_1 = require("./constants"); const command_decorators_1 = require("./command.decorators"); let CommandRunnerService = class CommandRunnerService { constructor(discoveryService, commander, options, logger) { this.discoveryService = discoveryService; this.commander = commander; this.options = options; this.logger = logger; } async onModuleInit() { var _a; await this.setUpDefaultCommand(); const providers = await this.discoveryService.providersWithMetaAtKey(constants_1.CommandMeta); const commands = await this.populateCommandMapInstances(providers); await this.setUpCommander(commands); if (this.options.usePlugins) { this.commander.showHelpAfterError(` ${this.commander.helpInformation()} ${(0, constants_1.cliPluginError)((_a = this.options.cliName) !== null && _a !== void 0 ? _a : 'nest-commander', this.options.pluginsAvailable)}`); } if (this.options.helpConfiguration) { this.commander.configureHelp(this.options.helpConfiguration); } if (this.options.errorHandler) { this.commander.exitOverride(this.options.errorHandler); } if (!this.options.serviceErrorHandler) { this.options.serviceErrorHandler = (err) => { process.stderr.write(err.toString()); }; } if (this.options.outputConfiguration) { this.commander.configureOutput(this.options.outputConfiguration); } if (this.options.version) { this.commander.version(this.options.version); } } /** * override the initial `commander` instance to be the `@DefaultCommand()` * This will allow for the -h action on a default call of the command to * provide the information from the default command and not the overall * application. */ async setUpDefaultCommand() { const [defaultCommand, ...others] = await this.discoveryService.providersWithMetaAtKey(constants_1.RootCommandMeta); if (others === null || others === void 0 ? void 0 : others.length) { throw new Error('You can only have one @RootCommand() in your application'); } if (!defaultCommand) { return; } const [populatedCommand] = await this.populateCommandMapInstances([ defaultCommand, ]); const builtDefault = await this.buildCommand(populatedCommand); this.commander = builtDefault; } async populateCommandMapInstances(providers) { const commands = []; for (const provider of providers) { const optionProviders = await this.discoveryService.providerMethodsWithMetaAtKey(constants_1.OptionMeta, (found) => found.name === provider.discoveredClass.name); const helpProviders = await this.discoveryService.providerMethodsWithMetaAtKey(constants_1.HelpMeta, (found) => found.name === provider.discoveredClass.name); commands.push({ command: provider.meta, instance: provider.discoveredClass.instance, params: optionProviders, help: helpProviders, }); } return commands; } async setUpCommander(commands) { for (const command of commands) { const newCommand = await this.buildCommand(command); this.commander.addCommand(newCommand, command.command.options); } } async buildCommand(command) { var _a, _b, _c, _d, _e, _f, _g; const newCommand = new commander_1.Command(command.command.name); command.instance.setCommand(newCommand); if (this.options.outputConfiguration) { newCommand.configureOutput(this.options.outputConfiguration); } if (this.options.helpConfiguration) { newCommand.configureHelp(this.options.helpConfiguration); } if (command.command.allowUnknownOptions) { newCommand.allowUnknownOption(); } if (command.command.allowExcessArgs) { newCommand.allowExcessArguments(); } if (command.command.arguments) { this.mapArgumentDescriptions(newCommand, command.command.arguments, command.command.argsDescription); } newCommand.description((_a = command.command.description) !== null && _a !== void 0 ? _a : ''); // Needs to be applied to every command // Commands created with the constructor do not inherit from the parent if (this.options.enablePositionalOptions) { newCommand.enablePositionalOptions(true); } if (this.options.enablePassThroughOptions) { newCommand.passThroughOptions(true); } const optionNameMap = {}; for (const option of command.params) { const { flags, description, defaultValue = undefined, required = false, choices = [], name: optionName = '', env = undefined, } = option.meta; const handler = option.discoveredMethod.handler.bind(command.instance); const commandOption = new commander_1.Option(flags, description) .default(defaultValue) .preset(defaultValue) .makeOptionMandatory(required); // choices can be a true boolean or an array of string options for commander. // If a boolean, then we know that we are expected to go find the OptionChoiceFOr method. if (choices === true || (Array.isArray(choices) && choices.length)) { let optionChoices = []; if (choices === true) { const choicesMethods = await this.discoveryService.providerMethodsWithMetaAtKey(constants_1.OptionChoiceMeta, (item) => item.instance === command.instance); const cMethod = choicesMethods .filter((choiceMethod) => choiceMethod.meta.name === optionName) .map((method) => method.discoveredMethod)[0]; optionChoices = cMethod.handler.call(command.instance); } else { optionChoices = choices; } commandOption.choices(optionChoices); } if (env) { commandOption.env(env); } commandOption.argParser(handler); newCommand.addOption(commandOption); optionNameMap[commandOption.attributeName()] = optionName || commandOption.attributeName(); } for (const help of (_b = command.help) !== null && _b !== void 0 ? _b : []) { newCommand.addHelpText(help.meta, help.discoveredMethod.handler.bind(command.instance)); } (_c = command.command.aliases) === null || _c === void 0 ? void 0 : _c.forEach((alias) => newCommand.alias(alias)); newCommand.action(async () => { var _a, _b; try { command.instance.run.bind(command.instance); const passedOptions = newCommand.opts(); const trueOptions = {}; for (const opt in passedOptions) { trueOptions[optionNameMap[opt]] = passedOptions[opt]; } return await command.instance.run(newCommand.args, trueOptions); } catch (err) { if (err instanceof Error) { if (err.message.includes('Cannot read properties of undefined')) { const className = (_b = /\s+at\s(\w+)\.run/.exec((_a = err.stack) !== null && _a !== void 0 ? _a : '')) === null || _b === void 0 ? void 0 : _b[1]; this.logger.error(`A service tried to call a property of "undefined" in the ${className} class. Did you use a request scoped provider without the @RequestModule() decorator?\n\n${err.message}`, err.stack, 'CommandRunnerService'); } } throw err; } }); if ((_d = command.command.subCommands) === null || _d === void 0 ? void 0 : _d.length) { (_e = this.subCommands) !== null && _e !== void 0 ? _e : (this.subCommands = await this.discoveryService.providersWithMetaAtKey(constants_1.SubCommandMeta)); const subCommandsMetaForCommand = this.subCommands.filter((subMeta) => { var _a; return (_a = command.command.subCommands) === null || _a === void 0 ? void 0 : _a.map((subCommand) => subCommand.name).includes(subMeta.discoveredClass.name); }); const subCommands = await this.populateCommandMapInstances(subCommandsMetaForCommand); for (const subCommand of subCommands) { newCommand.addCommand(await this.buildCommand(subCommand), { isDefault: (_g = (_f = subCommand.command.options) === null || _f === void 0 ? void 0 : _f.isDefault) !== null && _g !== void 0 ? _g : false, }); } } return newCommand; } mapArgumentDescriptions(command, args = '', argDescriptions = {}) { const trueArgDefs = {}; const splitArgs = args.split(' '); for (const arg of splitArgs) { let added = false; for (const key of Object.keys(argDescriptions).filter((key) => arg.includes(key))) { added = true; trueArgDefs[arg] = argDescriptions[key]; } command.argument(arg, added ? trueArgDefs[arg] : ''); } } async run(args) { await this.commander .parseAsync(args || process.argv) .catch(this.options.serviceErrorHandler); } }; exports.CommandRunnerService = CommandRunnerService; exports.CommandRunnerService = CommandRunnerService = __decorate([ __param(1, (0, command_decorators_1.InjectCommander)()), __param(2, (0, common_1.Inject)(constants_1.CommanderOptions)), __metadata("design:paramtypes", [nestjs_discovery_1.DiscoveryService, commander_1.Command, Object, common_1.Logger]) ], CommandRunnerService); //# sourceMappingURL=command-runner.service.js.map