UNPKG

@artus-cli/artus-cli

Version:

CLI framework with modern features

206 lines (205 loc) 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ParsedCommandTree = void 0; const tslib_1 = require("tslib"); const node_util_1 = require("node:util"); const node_assert_1 = tslib_1.__importDefault(require("node:assert")); const core_1 = require("@artus/core"); const parser_1 = require("./parser"); const command_1 = require("./command"); const constant_1 = require("../constant"); const utils_1 = require("../utils"); const errors_1 = require("../errors"); const parsed_command_1 = require("./parsed_command"); const bin_info_1 = require("./bin_info"); const debug = (0, node_util_1.debuglog)('artus-cli#ParsedCommands'); /** Parsed Command Tree */ let ParsedCommandTree = class ParsedCommandTree { constructor() { /** command list, the key is command string used to match argv */ this.commands = new Map(); /** cache the instance of parsedCommand */ this.parsedCommandMap = new Map(); } get descObj() { return Object.assign(Object.assign({}, this.binInfo), { $0: this.binInfo.binName, bin: this.binInfo.binName }); } resolveOptions(clz, argumentsKey) { const optionMeta = Reflect.getOwnMetadata(constant_1.MetadataEnum.OPTION, clz); const option = (optionMeta === null || optionMeta === void 0 ? void 0 : optionMeta.config) || {}; const descObj = this.descObj; const flagOptions = {}; const argumentOptions = {}; Object.keys(option).forEach(key => { const obj = argumentsKey.includes(key) ? argumentOptions : flagOptions; const config = obj[key] = Object.assign({}, option[key]); if (config.description) { config.description = (0, utils_1.formatDesc)(config.description, descObj); } }); const injections = (optionMeta === null || optionMeta === void 0 ? void 0 : optionMeta.injections) || []; injections.push( // default option in args { propName: '_', type: constant_1.OptionInjectType.KEY_OPTION }, { propName: '--', type: constant_1.OptionInjectType.KEY_OPTION }); return Object.assign(Object.assign({}, optionMeta), { injections, flagOptions, argumentOptions }); } formatCommandConfig(config, parent) { const binName = this.binInfo.binName; const descObj = this.descObj; const prefix = parent === null || parent === void 0 ? void 0 : parent.cmds.join(' '); const command = (0, utils_1.formatCmd)(config.command || '', descObj, prefix); const examples = (0, utils_1.formatToArray)(config.examples).map(info => { const items = typeof info === 'string' ? [info] : info; return { command: (0, utils_1.formatCmd)(items[0], descObj, prefix), description: items[1] ? (0, utils_1.formatDesc)(items[1], descObj) : undefined, }; }); const parsedCommandInfo = (0, parser_1.parseCommand)(command, binName); return { command, enable: typeof config.enable === 'boolean' ? config.enable : true, examples, alias: (0, utils_1.formatToArray)(config.alias), description: (0, utils_1.formatDesc)(config.description || '', descObj), originalCommandConfig: config, parsedCommandInfo, }; } /** convert Command class to ParsedCommand instance */ initParsedCommand(clz) { var _a; const metadata = Reflect.getOwnMetadata(constant_1.MetadataEnum.COMMAND, clz); if (!metadata) return; // avoid creating parsedCommand again. if (this.parsedCommandMap.has(clz)) { return this.parsedCommandMap.get(clz); } const commandMeta = metadata; const inheritClass = Object.getPrototypeOf(clz); const inheritCommand = this.initParsedCommand(inheritClass); const shouldInheritMetadata = typeof commandMeta.inheritMetadata === 'boolean' ? commandMeta.inheritMetadata : this.binInfo.inheritMetadata; let commandConfig = Object.assign({}, commandMeta.config); // mege command config with inherit command if (inheritCommand && shouldInheritMetadata) { const inheritCommandConfig = inheritCommand.commandConfig; commandConfig = Object.assign({}, { alias: inheritCommandConfig.alias, command: inheritCommandConfig.command, description: inheritCommandConfig.description, examples: inheritCommandConfig.examples, parent: inheritCommandConfig.parent, }, commandConfig); } // init parent let parentCommand; if (commandConfig.parent) { parentCommand = this.initParsedCommand(commandConfig.parent); (0, node_assert_1.default)(parentCommand, `parent ${(_a = commandConfig.parent) === null || _a === void 0 ? void 0 : _a.name} is not a valid Command`); } // format command config const formattedCommandConfig = this.formatCommandConfig(commandConfig, parentCommand); const parsedCommandInfo = formattedCommandConfig.parsedCommandInfo; // split options with argument key and merge option info with inherit command const argumentsKey = parsedCommandInfo.demanded.concat(parsedCommandInfo.optional).map(pos => pos.cmd); const optionConfig = this.resolveOptions(clz, argumentsKey); if (inheritCommand && shouldInheritMetadata) { optionConfig.injections = inheritCommand.injections.concat(optionConfig.injections || []); optionConfig.flagOptions = Object.assign({}, inheritCommand.flagOptions, optionConfig.flagOptions); optionConfig.argumentOptions = Object.assign({}, inheritCommand.argumentOptions, optionConfig.argumentOptions); } const parsedCommand = new parsed_command_1.ParsedCommand(clz, { location: commandMeta.location, commandConfig: formattedCommandConfig, optionConfig, }); if (inheritCommand) parsedCommand.inherit = inheritCommand; if (this.commands.has(parsedCommandInfo.uid)) { const existsParsedCommand = this.commands.get(parsedCommandInfo.uid); // override only allow in class inheritance or options.override=true const err = errors_1.errors.command_is_conflict(existsParsedCommand.command, existsParsedCommand.clz.name, existsParsedCommand.location, parsedCommand.clz.name, parsedCommand.location); if (!commandMeta.overrideCommand && !(0, utils_1.isInheritFrom)(parsedCommand.clz, existsParsedCommand.clz)) { throw err; } debug(err.message); } // handle middlewares // Default orders: // // In class inheritance: // command1 <-extend- command2 // trigger --> middleware1 --> middleware2 --> middleware3 --> run // // ------------ // // In run method: // command2 command1 // trigger --> middleware2 --> middleware3 --> run --> middleware1 --> super.run // merge command middlewares with inherit command const middlewareMeta = Reflect.getOwnMetadata(constant_1.MetadataEnum.MIDDLEWARE, clz); const commandMiddlewareConfigList = (middlewareMeta === null || middlewareMeta === void 0 ? void 0 : middlewareMeta.configList) || []; if (inheritCommand && shouldInheritMetadata) { parsedCommand.addMiddlewares('command', { middleware: inheritCommand.commandMiddlewares }); } commandMiddlewareConfigList.forEach(config => parsedCommand.addMiddlewares('command', config)); // add run middlewares, no need to merge with inherit command const executionMiddlewareConfig = Reflect.getOwnMetadata(constant_1.MetadataEnum.RUN_MIDDLEWARE, clz); const executionMiddlewareConfigList = (executionMiddlewareConfig === null || executionMiddlewareConfig === void 0 ? void 0 : executionMiddlewareConfig.configList) || []; executionMiddlewareConfigList.forEach(config => parsedCommand.addMiddlewares('execution', config)); // cache the instance this.commands.set(parsedCommandInfo.uid, parsedCommand); this.parsedCommandMap.set(clz, parsedCommand); return parsedCommand; } build(commandList) { this.root = undefined; this.commands.clear(); this.parsedCommandMap.clear(); const parsedCommands = commandList .map(clz => this.initParsedCommand(clz)) .filter(c => !!c); // handle parent and childs parsedCommands .sort((a, b) => a.depth - b.depth) .forEach(parsedCommand => { let parent; parsedCommand.cmds.forEach(cmd => { // fullCmd is the key of this.commands const fullCmd = parent ? parent.cmds.concat(cmd).join(' ') : cmd; let cacheParsedCommand = this.commands.get(fullCmd); if (!cacheParsedCommand) { // create empty node debug('Create empty command for \'%s\'', fullCmd); cacheParsedCommand = new parsed_command_1.ParsedCommand(command_1.EmptyCommand, { commandConfig: this.formatCommandConfig({ command: fullCmd }), }); this.commands.set(fullCmd, cacheParsedCommand); } if (!parent) { this.root = parent = cacheParsedCommand; return; } cacheParsedCommand.parent = parent; parent.childs.push(cacheParsedCommand); parent = cacheParsedCommand; }); }); } get(clz) { return this.parsedCommandMap.get(clz); } }; tslib_1.__decorate([ (0, core_1.Inject)(), tslib_1.__metadata("design:type", bin_info_1.BinInfo) ], ParsedCommandTree.prototype, "binInfo", void 0); ParsedCommandTree = tslib_1.__decorate([ (0, core_1.Injectable)({ scope: core_1.ScopeEnum.SINGLETON }) ], ParsedCommandTree); exports.ParsedCommandTree = ParsedCommandTree;