takin
Version:
Front end engineering base toolchain and scaffold
289 lines • 10.1 kB
JavaScript
"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