takin
Version:
Front end engineering base toolchain and scaffold
370 lines • 12.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cli = exports.Command = void 0;
const events_1 = require("events");
const mri_1 = __importDefault(require("mri"));
const command_1 = __importStar(require("./command"));
exports.Command = command_1.default;
const node_1 = require("./node");
const utils_1 = require("./utils");
class Cli extends events_1.EventEmitter {
/**
* @param name The program name to display in help and version message
*/
constructor(name = '', runner) {
super();
this.pluginName = '';
this.name = name;
this.runner = runner;
this.commands = new Map();
this.rawArgs = [];
this.args = [];
this.options = {};
this.globalCommand = new command_1.GlobalCommand(this);
this.globalCommand.usage('<command> [options]');
}
/**
* Add a global usage text.
*
* This is not used by sub-commands.
*/
usage(text) {
this.globalCommand.usage(text);
return this;
}
/**
* 添加子命令
*/
command(rawName, description, config) {
var _a;
const name = (0, utils_1.removeBrackets)(rawName);
if (this.commands.has(name)) {
return (_a = this.commands.get(name)) === null || _a === void 0 ? void 0 : _a.command;
}
const command = new command_1.default(rawName, description || '', config || {}, this);
const pluginName = this.pluginName;
command.globalCommand = this.globalCommand;
this.commands.set(command.name, { pluginName, command });
return command;
}
/**
* Add a global CLI option.
*
* Which is also applied to sub-commands.
*/
option(rawName, description, config) {
this.globalCommand.option(rawName, description, config);
return this;
}
/**
* Show help message when `-h, --help` flags appear.
*
*/
help(callback) {
this.globalCommand.option('-h, --help', '显示帮助信息');
this.globalCommand.helpCallback = callback;
this.showHelpOnExit = true;
return this;
}
/**
* Show version number when `-v, --version` flags appear.
*
*/
version(version, customFlags = '-v, --version') {
this.globalCommand.version(version, customFlags);
this.showVersionOnExit = true;
return this;
}
/**
* Add a global example.
*
* This example added here will not be used by sub-commands.
*/
example(example) {
this.globalCommand.example(example);
return this;
}
/**
* Output the corresponding help message
* When a sub-command is matched, output the help message for the command
* Otherwise output the global one.
*
*/
outputHelp() {
if (this.matchedCommand) {
this.matchedCommand.outputHelp();
}
else {
this.globalCommand.outputHelp();
}
}
/**
* Output the version number.
*
*/
outputVersion() {
this.globalCommand.outputVersion();
}
setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
this.args = args;
this.options = options;
if (matchedCommand) {
this.matchedCommand = matchedCommand;
}
if (matchedCommandName) {
this.matchedCommandName = matchedCommandName;
}
return this;
}
/**
* 自定义 setParsedInfo
* @param param0 - ParsedArgv 已解析的 argv
* @param matchedCommand - 匹配的 命令实例
* @param matchedCommandName - 匹配的命令名称
*/
setParsedInfo2({ args, options }, matchedCommand, matchedCommandName) {
this.args = args;
this.options = options;
if (matchedCommand) {
this.matchedCommand = matchedCommand;
}
if (matchedCommandName) {
this.matchedCommandName = matchedCommandName;
}
}
unsetMatchedCommand() {
this.matchedCommand = undefined;
this.matchedCommandName = undefined;
}
/**
* Parse argv
*/
parse(argv = node_1.processArgs, {
/** Whether to run the action for matched command */
run = true } = {}) {
this.rawArgs = argv;
if (!this.name) {
this.name = argv[1] ? (0, utils_1.getFileName)(argv[1]) : 'cli';
}
let shouldParse = true;
// Search sub-commands
for (const [, { command }] of this.commands) {
const parsed = this.mri(argv.slice(2), command);
const commandName = parsed.args[0];
if (command.isMatched(commandName)) {
shouldParse = false;
const parsedInfo = {
...parsed,
args: parsed.args.slice(1)
};
this.setParsedInfo(parsedInfo, command, commandName);
this.emit(`command:${commandName}`, command);
}
}
if (shouldParse) {
// Search the default command
for (const [, { command }] of this.commands) {
if (command.name === '') {
shouldParse = false;
const parsed = this.mri(argv.slice(2), command);
this.setParsedInfo(parsed, command);
this.emit(`command:!`, command);
}
}
}
if (shouldParse) {
const parsed = this.mri(argv.slice(2));
this.setParsedInfo(parsed);
}
if (this.options.help && this.showHelpOnExit) {
this.outputHelp();
run = false;
this.unsetMatchedCommand();
}
if (this.options.version &&
this.showVersionOnExit &&
this.matchedCommandName == null) {
this.outputVersion();
run = false;
this.unsetMatchedCommand();
}
const parsedArgv = { args: this.args, options: this.options };
if (run) {
this.runMatchedCommand();
}
if (!this.matchedCommand && this.args[0]) {
this.emit('command:*');
}
return parsedArgv;
}
// 直接解析指定的命令
parseByCommand(command) {
let shouldParse = true;
const { name: commandName = '', args = [], options = {} } = command;
const parsedInfo = { args, options };
for (const [, { command }] of this.commands) {
if (command.isMatched(commandName)) {
shouldParse = false;
this.setParsedInfo2(parsedInfo, command, commandName);
this.emit(`command:${commandName}`, command);
}
}
if (shouldParse) {
for (const [, { command }] of this.commands) {
if (command.name === '') {
shouldParse = false;
this.setParsedInfo2(parsedInfo, command);
this.emit(`command:!`, command);
}
}
}
if (shouldParse) {
this.setParsedInfo2(parsedInfo);
}
if (this.options.help && this.showHelpOnExit) {
this.outputHelp();
this.unsetMatchedCommand();
}
if (this.options.version && this.showVersionOnExit) {
this.outputVersion();
this.unsetMatchedCommand();
}
const parsedArgv = { args: this.args, options: this.options };
if (!this.matchedCommand && this.args[0]) {
this.emit('command:*');
}
return parsedArgv;
}
/**
* 准备匹配的命令选项
* @returns 匹配的命令选项
*/
prepareMatchedCommandAndArgs() {
const { args, options = {}, matchedCommand: command } = this;
// 如果没有命中的 command
// 则仅返回 args 和 options
if (!command || !command.commandAction) {
return { name: undefined, args, options };
}
command.checkUnknownOptions();
command.checkOptionValue();
command.checkRequiredArgs();
const actionArgs = [];
command.args.forEach((arg, index) => {
if (arg.variadic) {
actionArgs.push(args.slice(index));
}
else {
actionArgs.push(args[index]);
}
});
return { name: command === null || command === void 0 ? void 0 : command.name, args: actionArgs, options };
}
mri(argv,
/** Matched command */ command) {
// All added options
const cliOptions = [
...this.globalCommand.options,
...(command ? command.options : [])
];
const mriOptions = (0, utils_1.getMriOptions)(cliOptions);
// Extract everything after `--` since mri doesn't support it
let argsAfterDoubleDashes = [];
const doubleDashesIndex = argv.indexOf('--');
if (doubleDashesIndex > -1) {
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
argv = argv.slice(0, doubleDashesIndex);
}
let parsed = (0, mri_1.default)(argv, mriOptions);
parsed = Object.keys(parsed).reduce((res, name) => {
return {
...res,
[(0, utils_1.camelcaseOptionName)(name)]: parsed[name]
};
}, { _: [] });
const args = parsed._;
const options = {
'--': argsAfterDoubleDashes
};
// Set option default value
const ignoreDefault = command && command.config.ignoreOptionDefaultValue
? command.config.ignoreOptionDefaultValue
: this.globalCommand.config.ignoreOptionDefaultValue;
const transforms = Object.create(null);
for (const cliOption of cliOptions) {
if (!ignoreDefault && cliOption.config.default !== undefined) {
for (const name of cliOption.names) {
options[name] = cliOption.config.default;
}
}
// If options type is defined
if (Array.isArray(cliOption.config.type)) {
if (transforms[cliOption.name] === undefined) {
transforms[cliOption.name] = Object.create(null);
transforms[cliOption.name]['shouldTransform'] = true;
transforms[cliOption.name]['transformFunction'] =
cliOption.config.type[0];
}
}
}
// Set option values (support dot-nested property name)
for (const key of Object.keys(parsed)) {
if (key !== '_') {
const keys = key.split('.');
(0, utils_1.setDotProp)(options, keys, parsed[key]);
(0, utils_1.setByType)(options, transforms);
}
}
return {
args,
options
};
}
runMatchedCommand() {
const { args, options, matchedCommand: command } = this;
if (!command || !command.commandAction)
return;
command.checkUnknownOptions();
command.checkOptionValue();
command.checkRequiredArgs();
const actionArgs = [];
command.args.forEach((arg, index) => {
if (arg.variadic) {
actionArgs.push(args.slice(index));
}
else {
actionArgs.push(args[index]);
}
});
actionArgs.push(options);
return command.commandAction.apply(this, actionArgs);
}
}
exports.Cli = Cli;
//# sourceMappingURL=index.js.map