commanding
Version:
A simple yet practical command-Line application framework, written in TypeScript.
190 lines (189 loc) • 7.41 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = __importDefault(require("lodash"));
const parser_1 = require("./helpers/parser");
const not_fancy_1 = require("./loggers/not-fancy");
const constant_1 = require("./loggers/constant");
const awesome_1 = require("./presenters/awesome");
const MissingArgument_1 = require("./errors/MissingArgument");
const MissingOption_1 = require("./errors/MissingOption");
const SanitizationError_1 = require("./errors/SanitizationError");
const CommandNotFound_1 = require("./errors/CommandNotFound");
const GLOBAL_OPTION_REQUIREMENTS = [
{
sign: '-h, --help',
shorthand: '-h',
longhand: '--help',
description: 'Display help',
required: false,
repeatable: false,
csv: false,
},
{
sign: '-V, --version',
shorthand: '-V',
longhand: '--version',
description: 'Display version',
required: false,
repeatable: false,
csv: false,
},
{
sign: '--no-color',
longhand: '--no-color',
description: 'Disable colors',
required: false,
repeatable: false,
csv: false,
},
{
sign: '--quiet',
longhand: '--quiet',
description: 'Quiet mode - only displays `warn` and `error` level messages',
required: false,
repeatable: false,
csv: false,
},
{
sign: '-v, --verbose',
longhand: '--verbose',
description: 'Verbose mode - displays `debug` level messages',
required: false,
repeatable: false,
csv: false,
},
];
class Application {
constructor(name, description, version, commands, defaultCommand) {
this.name = name;
this.description = description;
this.version = version;
this.commands = commands;
this.defaultCommand = defaultCommand;
this.commandMap = lodash_1.default.keyBy(commands, command => command.getName());
}
hasSubCommand() {
return this.commands.length !== 0;
}
hasDefaultCommand() {
return this.defaultCommand !== undefined;
}
matchCommand(name) {
return this.commandMap[name];
}
getGlobalOptionRequirements() {
return GLOBAL_OPTION_REQUIREMENTS;
}
makeDefaultPresenter(colorEnabled = true, output = process.stdout) {
return new awesome_1.AwesomePresenter(colorEnabled, output);
}
makeDefaultLogger(level = constant_1.LogLevel.INFO, colorEnabled = true, standardOutput = process.stdout, errorOutput = process.stderr) {
return new not_fancy_1.NotFancyLogger(level, colorEnabled, standardOutput, errorOutput);
}
getGlobalOptions(parsedOptions) {
return parser_1.mapOptions(parsedOptions, this.getGlobalOptionRequirements());
}
containHelpCommand(parsedArgs) {
if (parsedArgs.length !== 2) {
return false;
}
const [instruction, commandName] = parsedArgs;
if (instruction !== 'help') {
return false;
}
return this.matchCommand(commandName) !== undefined;
}
async execute(executable, parsedArgs, parsedOptions, presenter, logger) {
const globalOptions = this.getGlobalOptions(parsedOptions);
if (globalOptions['--verbose']) {
logger.setLevel(constant_1.LogLevel.DEBUG);
}
if (globalOptions['--quiet']) {
logger.setLevel(constant_1.LogLevel.WARN);
}
if (globalOptions['--no-color']) {
presenter.setColor(false);
}
// Matching sub-command first.
if (this.hasSubCommand() && parsedArgs.length > 0) {
if (this.containHelpCommand(parsedArgs)) {
const command = this.matchCommand(parsedArgs[1]);
return this.showCommandHelp(executable, command, presenter);
}
const commandName = lodash_1.default.first(parsedArgs);
const command = this.matchCommand(commandName);
if (command !== undefined) {
if (this.shouldShowCommandHelp(parsedArgs.slice(1), globalOptions)) {
return this.showCommandHelp(executable, command, presenter);
}
return await command.execute(parsedArgs.slice(1), parsedOptions, presenter, logger);
}
// If Application do not have a default Command, It means user typed wrong
// Command name.
if (!this.defaultCommand) {
throw new CommandNotFound_1.CommandNotFound(commandName);
}
}
// Global help, version
if (parsedArgs.length === 0) {
if (globalOptions['--version']) {
return this.showVersion(presenter);
}
if (globalOptions['--help']) {
return this.showHelp(executable, presenter);
}
}
// Default Command
if (this.defaultCommand) {
return await this.defaultCommand.execute(parsedArgs, parsedOptions, presenter, logger);
}
return this.showHelp(executable, presenter);
}
async parse(argv, customPresenter, customLogger) {
const presenter = customPresenter ? customPresenter : this.makeDefaultPresenter();
const logger = customLogger ? customLogger : this.makeDefaultLogger();
const executable = parser_1.extractExecutableNameFromArgv(argv);
try {
const [parsedArgs, parsedOptions] = parser_1.parseArgv(argv.slice(2));
return await this.execute(executable, parsedArgs, parsedOptions, presenter, logger);
}
catch (err) {
presenter.renderError(err instanceof Error ? err.message : `${err}`);
if (err instanceof MissingArgument_1.MissingArgument
|| err instanceof MissingOption_1.MissingOption
|| err instanceof SanitizationError_1.SanitizationError
|| err instanceof CommandNotFound_1.CommandNotFound) {
this.showHelp(executable, presenter);
}
}
}
renderApplicationInfo(executable, presenter) {
presenter.renderApplicationInfo(executable, this.name, this.description, this.version);
}
shouldShowCommandHelp(parsedArgs, globalOptions) {
if (parsedArgs.length > 0) {
return false;
}
return globalOptions['--help'] == true;
}
showHelp(executable, presenter) {
this.renderApplicationInfo(executable, presenter);
presenter.renderApplicationUsage(executable, this.commands, this.defaultCommand);
presenter.renderCommandList(this.commands);
presenter.renderGlobalOptions(this.getGlobalOptionRequirements());
presenter.renderEnding();
}
showCommandHelp(executable, command, presenter) {
this.renderApplicationInfo(executable, presenter);
command.showHelp(executable, presenter);
presenter.renderGlobalOptions(this.getGlobalOptionRequirements());
}
showVersion(presenter) {
presenter.renderVersion(lodash_1.default.defaultTo(this.version, ''));
presenter.renderEnding();
}
}
exports.Application = Application;