UNPKG

takin

Version:

Front end engineering base toolchain and scaffold

289 lines 10.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GlobalCommand = void 0; const errors_1 = require("../errors"); const logger_1 = require("../logger"); const node_1 = require("./node"); const option_1 = __importDefault(require("./option")); const utils_1 = require("./utils"); class Command { constructor(rawName, description, config = {}, cli) { this.rawName = rawName; this.description = description; this.config = config; this.cli = cli; this.options = []; this.aliasNames = []; this.name = (0, utils_1.removeBrackets)(rawName); this.args = (0, utils_1.findAllBrackets)(rawName); this.examples = []; } usage(text) { this.usageText = text; return this; } allowUnknownOptions() { this.config.allowUnknownOptions = true; return this; } ignoreOptionDefaultValue() { this.config.ignoreOptionDefaultValue = true; return this; } version(version, customFlags = '-v, --version', description) { this.versionNumber = version; this.option(customFlags, description || '显示版本信息'); return this; } example(example) { this.examples.push(example); return this; } /** * Add a option for this command * @param rawName Raw option name(s) * @param description Option description * @param config Option config */ option(rawName, description, config) { const option = new option_1.default(rawName, description, config); this.options.push(option); return this; } alias(name) { const tokenCommand = this.cli.commands.get(name); if (tokenCommand) { throw new errors_1.CliError(`命令 '${this.name}' 的别名 '${name}' 已被插件 '${tokenCommand.pluginName}' ` + `注册的 '${tokenCommand.command.name}' 占用, 请更换.`); } else { this.cli.commands.set(name, { pluginName: this.cli.pluginName, command: this }); this.aliasNames.push(name); } return this; } action(callback) { const cli = this.cli; const commandName = this.name; if (!this.commandAction) { // 将 runner 中注册的 run hook 作为实际需要执行的 callback // 这里并不会实际执行, 以防万一, 会将 runner 实际执行的方法作为 cli 的 action this.commandAction = async function (...args) { const options = args.pop(); await cli.runner.invokeCommandAction({ name: commandName, args, options }); }; } // 注册 hook cli.runner.addCommandAction({ name: commandName, pluginName: cli.pluginName, callback }); return this; } /** * Check if a command name is matched by this command * @param name Command name */ isMatched(name) { return this.name === name || this.aliasNames.includes(name); } get isDefaultCommand() { return this.name === '' || this.aliasNames.includes('!'); } get isGlobalCommand() { return this instanceof GlobalCommand; } /** * Check if an option is registered in this command * @param name Option name */ hasOption(name) { return !!this.getOption(name); } /** * 获取已设置的命令行选项 * @param name - 选项名称(需要为驼峰形式, 如 outputPath) * @returns 选项实例 */ getOption(name) { name = name.split('.')[0]; return this.options.find((option) => { return option.names.includes(name); }); } /** * 为 option 设置别名 * @param name - option 名称 * @param alias - option 别名 */ aliasOption(name, alias) { const names = (0, utils_1.removeBrackets)(name) .split(',') .map((v) => { let name = v.trim().replace(/^-{1,2}/, ''); if (name.startsWith('no-')) { name = name.replace(/^no-/, ''); } return (0, utils_1.camelcaseOptionName)(name); }); let option; for (const name of names) { option = this.getOption(name); if (option) break; } if (option) { option.alias(alias); } else { logger_1.logger.debug(`设置命令选项 ${name} 的别名 ${alias} 失败, 原因: 未找到选项`); } return this; } outputHelp() { const { name } = this.cli; const commands = []; // 过滤 alias 名称 this.cli.commands.forEach(function (c, k) { if (!c.command.aliasNames.includes(k)) { commands.push(c.command); } }); const { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand; let sections = [ { body: `${name}${versionNumber ? `/${versionNumber}` : ''}` } ]; sections.push({ title: '用法', body: ` $ ${name} ${this.usageText || this.rawName}` }); const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0; if (showCommands) { const longestCommandName = (0, utils_1.findLongest)(commands.map((command) => command.rawName)); sections.push({ title: '命令', body: commands .map((command) => { return ` ${(0, utils_1.padRight)(command.rawName, longestCommandName.length)} ${command.description}`; }) .join('\n') }); sections.push({ title: '更多信息可通过 `--help` 选项,运行下方命令获取', body: commands .map((command) => ` $ ${name}${command.name === '' ? '' : ` ${command.name}`} --help`) .join('\n') }); } let options = this.isGlobalCommand ? globalOptions : [...this.options, ...(globalOptions || [])]; if (!this.isGlobalCommand && !this.isDefaultCommand) { options = options.filter((option) => option.name !== 'version'); } if (options.length > 0) { const longestOptionName = (0, utils_1.findLongest)(options.map((option) => option.rawName)); sections.push({ title: '选项', body: options .map((option) => { return ` ${(0, utils_1.padRight)(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === undefined ? '' : `(默认: ${option.config.default})`}`; }) .join('\n') }); } if (this.examples.length > 0) { sections.push({ title: '举例', body: this.examples .map((example) => { if (typeof example === 'function') { return example(name); } return example; }) .join('\n') }); } if (helpCallback) { sections = helpCallback(sections) || sections; } console.log(sections .map((section) => { return section.title ? `${section.title}:\n${section.body}` : section.body; }) .join('\n\n')); } outputVersion() { const { name } = this.cli; const { versionNumber } = this.cli.globalCommand; if (versionNumber) { console.log(`${name}/${versionNumber} ${node_1.platformInfo}`); } } checkRequiredArgs() { const minimalArgsCount = this.args.filter((arg) => arg.required).length; if (this.cli.args.length < minimalArgsCount) { throw new errors_1.CliError(`缺少必填的命令参数 \`${this.rawName}\``); } } /** * Check if the parsed options contain any unknown options * * Exit and output error when true */ checkUnknownOptions() { const { options, globalCommand } = this.cli; if (!this.config.allowUnknownOptions) { for (const name of Object.keys(options)) { if (name !== '--' && !this.hasOption(name) && !globalCommand.hasOption(name)) { throw new errors_1.CliError(`未知的命令行选项 \`${name.length > 1 ? `--${name}` : `-${name}`}\``); } } } } /** * Check if the required string-type options exist */ checkOptionValue() { const { options: parsedOptions, globalCommand } = this.cli; const options = [...globalCommand.options, ...this.options]; for (const option of options) { const value = parsedOptions[option.name.split('.')[0]]; // Check required option value if (option.required) { const hasNegated = options.some((o) => o.negated && o.names.includes(option.name)); if (value === true || (value === false && !hasNegated)) { throw new errors_1.CliError(`缺少选项 \`${option.rawName}\` 的值`); } } } } } class GlobalCommand extends Command { constructor(cli) { super('@@global@@', '', {}, cli); } } exports.GlobalCommand = GlobalCommand; exports.default = Command; //# sourceMappingURL=command.js.map