@zowe/imperative
Version:
framework for building configurable CLIs
297 lines • 15.4 kB
JavaScript
"use strict";
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.YargsConfigurer = void 0;
const util_1 = require("util");
const logger_1 = require("../../../logger");
const constants_1 = require("../../../constants");
const AbstractCommandYargs_1 = require("./AbstractCommandYargs");
const CommandProcessor_1 = require("../CommandProcessor");
const CommandUtils_1 = require("../utils/CommandUtils");
const utilities_1 = require("../../../utilities");
const fastest_levenshtein_1 = require("fastest-levenshtein");
/**
* Before invoking commands, this class configures some settings and callbacks in Yargs,
* including what happens on syntax failures.
*/
class YargsConfigurer {
constructor(rootCommand, yargs, commandRespParms, helpGeneratorFactory, experimentalCommandDescription, rootCommandName, commandLine, envVariablePrefix, promptPhrase) {
this.rootCommand = rootCommand;
this.yargs = yargs;
this.commandRespParms = commandRespParms;
this.helpGeneratorFactory = helpGeneratorFactory;
this.experimentalCommandDescription = experimentalCommandDescription;
this.rootCommandName = rootCommandName;
this.commandLine = commandLine;
this.envVariablePrefix = envVariablePrefix;
this.promptPhrase = promptPhrase;
}
configure() {
/**
* Add the command definitions to yargs
*/
const logger = logger_1.Logger.getImperativeLogger();
this.yargs.help(false);
this.yargs.version(false);
this.yargs.showHelpOnFail(false);
this.yargs.parserConfiguration(YargsConfigurer.yargsConfiguration);
// finally, catch any undefined commands
this.yargs.command({
command: "*",
description: "Unknown group",
handler: (argv) => {
const attemptedCommand = argv._.join(" ");
if (attemptedCommand.trim().length === 0) {
if (argv.V) {
argv.version = true;
}
// Allocate a help generator from the factory
const rootHelpGenerator = this.helpGeneratorFactory.getHelpGenerator({
commandDefinition: this.rootCommand,
fullCommandTree: this.rootCommand,
experimentalCommandsDescription: this.experimentalCommandDescription
});
new CommandProcessor_1.CommandProcessor({
definition: this.rootCommand, fullDefinition: this.rootCommand,
helpGenerator: rootHelpGenerator,
rootCommandName: this.rootCommandName,
commandLine: this.commandLine,
envVariablePrefix: this.envVariablePrefix,
promptPhrase: this.promptPhrase,
}).invoke({ arguments: argv, silent: false, responseFormat: this.getResponseFormat(argv) })
.then((_response) => {
logger_1.Logger.getImperativeLogger().debug("Root help complete.");
})
.catch((rejected) => {
var _a;
const daemonStream = (_a = utilities_1.ImperativeConfig.instance.daemonContext) === null || _a === void 0 ? void 0 : _a.stream;
if (daemonStream) {
daemonStream.write(utilities_1.DaemonRequest.create({ stderr: `Internal Imperative Error: Root command help ` +
`error occurred: ${rejected.message}\n` }));
}
else {
process.stderr.write("Internal Imperative Error: Root command help error occurred: "
+ rejected.message + "\n");
}
logger_1.Logger.getImperativeLogger().error(`Root unexpected help error: ${(0, util_1.inspect)(rejected)}`);
});
}
else {
// unknown command, not successful
process.exitCode = constants_1.Constants.ERROR_EXIT_CODE;
const closestCommand = this.getClosestCommand(attemptedCommand);
argv.failureMessage = this.buildFailureMessage(closestCommand);
const failedCommandDefinition = this.buildFailedCommandDefinition();
// Allocate a help generator from the factory
const rootHelpGenerator = this.helpGeneratorFactory.getHelpGenerator({
commandDefinition: failedCommandDefinition,
fullCommandTree: failedCommandDefinition,
experimentalCommandsDescription: this.experimentalCommandDescription
});
// Create the command processor for the fail command
const failCommand = new CommandProcessor_1.CommandProcessor({
definition: failedCommandDefinition,
fullDefinition: failedCommandDefinition,
helpGenerator: rootHelpGenerator,
rootCommandName: this.rootCommandName,
commandLine: utilities_1.ImperativeConfig.instance.commandLine,
envVariablePrefix: this.envVariablePrefix,
promptPhrase: this.promptPhrase
});
// Invoke the fail command
failCommand.invoke({ arguments: argv, silent: false, responseFormat: this.getResponseFormat(argv) })
.then((_failedCommandResponse) => {
logger.debug("Finished invoking the 'FailedCommand' handler");
}).catch((err) => {
logger.error("%s", err.msg);
});
}
}
});
this.yargs.fail((msg, error, _failedYargs) => {
process.exitCode = constants_1.Constants.ERROR_EXIT_CODE;
AbstractCommandYargs_1.AbstractCommandYargs.STOP_YARGS = true; // todo: figure out a better way
error = error || new Error(msg);
const failedCommandDefinition = this.buildFailedCommandDefinition();
// Allocate a help generator from the factory
const failHelpGenerator = this.helpGeneratorFactory.getHelpGenerator({
commandDefinition: failedCommandDefinition,
fullCommandTree: failedCommandDefinition,
experimentalCommandsDescription: this.experimentalCommandDescription
});
// Create the command processor for the fail command
const failCommand = new CommandProcessor_1.CommandProcessor({
definition: failedCommandDefinition,
fullDefinition: failedCommandDefinition,
helpGenerator: failHelpGenerator,
rootCommandName: this.rootCommandName,
commandLine: this.commandLine,
envVariablePrefix: this.envVariablePrefix,
promptPhrase: this.promptPhrase,
daemonContext: utilities_1.ImperativeConfig.instance.daemonContext
});
const failureMessage = this.buildFailureMessage();
// Construct the fail command arguments
const argv = {
failureMessage,
error,
_: [],
$0: constants_1.Constants.PRIMARY_COMMAND
};
// Invoke the fail command
failCommand.invoke({ arguments: argv, silent: false, responseFormat: this.getResponseFormat(argv) })
.then((_failedCommandResponse) => {
logger.debug("Finished invoking the 'FailedCommand' handler");
}).catch((err) => {
logger.error("%s", err.msg);
});
});
process.on("uncaughtException", (error) => {
process.exitCode = constants_1.Constants.ERROR_EXIT_CODE;
const failedCommandDefinition = this.buildFailedCommandDefinition();
// Allocate a help generator from the factory
const failHelpGenerator = this.helpGeneratorFactory.getHelpGenerator({
commandDefinition: failedCommandDefinition,
fullCommandTree: failedCommandDefinition,
experimentalCommandsDescription: this.experimentalCommandDescription
});
// Create the command processor for failure
let failureMessage = "Imperative encountered an unexpected exception";
const failCommand = new CommandProcessor_1.CommandProcessor({
definition: failedCommandDefinition,
fullDefinition: failedCommandDefinition,
helpGenerator: failHelpGenerator,
rootCommandName: this.rootCommandName,
commandLine: utilities_1.ImperativeConfig.instance.commandLine,
envVariablePrefix: this.envVariablePrefix,
promptPhrase: this.promptPhrase
});
failureMessage += `\nCommand entered: "${this.rootCommandName} ${utilities_1.ImperativeConfig.instance.commandLine}"`;
const groupValues = utilities_1.ImperativeConfig.instance.commandLine.split(" ", 2);
failureMessage += `\nUse "${this.rootCommandName} ${groupValues[0]} ${groupValues[1]} --help" to view groups, commands, and options.`;
// Construct the arguments
const argv = {
failureMessage,
error,
_: [],
$0: constants_1.Constants.PRIMARY_COMMAND
};
// Invoke the fail command processor
failCommand.invoke({ arguments: argv, silent: false, responseFormat: this.getResponseFormat(argv) })
.then((_failedCommandResponse) => {
logger.debug("Finished invoking the 'FailedCommand' handler");
}).catch((err) => {
logger.error("%s", err.msg);
});
});
}
/**
* Builds the failure message that is passed to the failedCommand handler
* @return {string} - Returns the failure message
*/
buildFailureMessage(closestCommand) {
const three = 3;
let commands = "";
let groups = " "; // default to " " for proper spacing in message
let delimiter = ""; // used to delimit between possible 'command' values
const groupValues = utilities_1.ImperativeConfig.instance.commandLine.split(" ", three);
const commandToCheck = groupValues.join(" ");
const nearestCommand = closestCommand || this.getClosestCommand(commandToCheck);
let failureMessage = "Command failed due to improper syntax";
if (closestCommand != null) {
failureMessage += (0, util_1.format)("\nUnknown group: %s", groupValues[0]);
}
if (closestCommand != null || !commandToCheck.includes(nearestCommand)) {
failureMessage += (0, util_1.format)("\nDid you mean: %s?\n", nearestCommand);
}
failureMessage += `\nCommand entered: "${utilities_1.ImperativeConfig.instance.commandLine}"`;
// limit to three to include two levels of group and command value, if present
// loop through the top level groups
for (const group of this.rootCommand.children) {
if (group.name.trim() === groupValues[0] || group.aliases[0] === groupValues[0]) {
groups += groupValues[0] + " ";
// found the top level group so loop to see if second level group valid
for (const group2 of group.children) {
if (group2.name.trim() === groupValues[1] || group2.aliases[0] === groupValues[1]) {
groups += groupValues[1] + " ";
// second level group valid so command provided is invalid, retrieve the valid command(s)
for (let i = 0; i < group2.children.length; i++) {
if (i > 0) {
delimiter = ", ";
}
commands += delimiter + group2.children[i].name;
}
break;
}
}
break;
}
}
if (commands.length > 0) {
failureMessage += `\nAvailable commands are "${commands}".`;
}
failureMessage += `\nUse "${this.rootCommandName}${groups}--help" to view groups, commands, and options.`;
return failureMessage;
}
/**
* Define failed command with the current command line arguments.
* @returns Failed command definition object
*/
buildFailedCommandDefinition() {
return {
name: this.rootCommandName + " " + utilities_1.ImperativeConfig.instance.commandLine,
handler: __dirname + "/../handlers/FailedCommandHandler",
type: "command",
description: "The command you tried to invoke failed"
};
}
// /**
// * Constructs the response object for invoking help and error command handlers.
// * @param {boolean} silent - Enable silent mode
// * @param {boolean} printJsonResponse - Print a JSON response if requested.
// * @return {CommandResponse} - Returns the constructed command response object
// */
// private buildResponseObject(silent = false, printJsonResponse = false): CommandResponse {
// return new CommandResponse({
// log: this.commandRespParms.log,
// silent: silent,
// printJsonOnResponse: printJsonResponse,
// primaryTextColor: this.commandRespParms.primaryTextColor,
// progressBarSpinner: this.commandRespParms.progressBarSpinner,
// progressBarPollFrequency: this.commandRespParms.progressBarPollFrequency
// });
// }
getClosestCommand(attemptedCommand) {
const commandTree = CommandUtils_1.CommandUtils.flattenCommandTree(this.rootCommand, true);
const commands = [];
for (const commandEntry of commandTree) {
if (commandEntry.fullName.trim().length === 0) {
continue;
}
commands.push(commandEntry.fullName);
}
return (0, fastest_levenshtein_1.closest)(attemptedCommand, commands);
}
/**
* Get the command response format based on whether `--rfj` is set.
* @param argv Yargs arguments object
*/
getResponseFormat(argv) {
return argv[constants_1.Constants.JSON_OPTION] ? "json" : "default";
}
}
exports.YargsConfigurer = YargsConfigurer;
YargsConfigurer.yargsConfiguration = {
"parse-numbers": false,
"parse-positional-numbers": false
};
//# sourceMappingURL=YargsConfigurer.js.map