| Server IP : 34.87.99.72 / Your IP : 216.73.217.31 Web Server : Apache/2.4.46 (Unix) OpenSSL/1.1.1n System : Linux zlock-bitnami-lamp-prod-v2-vm 4.19.0-16-cloud-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 User : bitnami ( 1000) PHP Version : 7.4.18 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /opt/bitnami/nami/node_modules/provisioner/node_modules/cmd-parser/ |
Upload File : |
'use strict';
const _ = require('lodash');
const printf = require('printf');
const pluralize = require('pluralize');
const fs = require('fs');
function pickWithDefaults(opts, defaults, aliases) {
const provided = _.pick(opts || {}, _.keys(defaults));
_.each(aliases, function(original, alias) {
if (_.has(opts, alias) && !_.has(opts, original)) provided[original] = opts[alias];
});
return _.defaults(provided, defaults);
}
class ParserError extends Error {
constructor(msg, options) {
options = _.defaults(options || {}, {showHelp: true});
super(msg);
this.showHelp = options.showHelp;
}
}
class ParserExit extends ParserError {
constructor(msg, options) {
options = _.defaults(options || {}, {exitCode: 0});
super(msg, options);
this.exitCode = options.exitCode;
}
}
class ShowHelpException extends ParserExit {
}
class ParserExitOnCallback extends ParserExit {
constructor(msg, options) {
options = _.defaults(options || {}, {showHelp: false});
super(msg, options);
}
}
class Option {
_createAliasedAttribute(name, alias, defaultValue) {
let _data = defaultValue;
_.each([name, alias], k => {
Object.defineProperty(this, k, {
get: () => _data,
set: val => _data = val
});
});
}
constructor(opts) {
opts = opts || {};
const supportedOptions = {
name: null,
alias: null,
cliName: null,
type: 'string',
defaultValue: null,
required: false,
value: null,
allowedValues: null,
hiddenValidValues: null,
secret: false,
description: '',
detailedDescription: '',
multivalued: false,
callback: null,
exitAfterCallBack: false,
allowNegated: null,
envvar: null,
canBeEmpty: true
};
const aliases = {validValues: 'allowedValues', default: 'defaultValue'};
this.provided = false;
_.each(aliases, (original, alias) => this._createAliasedAttribute(original, alias, supportedOptions[original]));
_.extend(this, pickWithDefaults(opts, supportedOptions, aliases));
if (!this.name) {
throw new ParserError('You did not provide a name for the option');
}
if (this.type === 'choice') {
if (this.validValues === null) {
this.validValues = [];
}
}
if (this.defaultValue === null) {
this.defaultValue = this._getInitialDefaultValue();
}
if (this.value === null) {
this.value = this.defaultValue;
}
if (this.allowNegated === null) {
this.allowNegated = (this.type === 'boolean');
}
if (this.envvar !== null && _.has(process.env, this.envvar)) {
this.setValue(process.env[this.envvar], {silentValidation: true});
}
}
_getInitialDefaultValue() {
let value = '';
switch (this.type) {
case 'boolean':
value = false;
break;
case 'choice':
value = this.validValues[0];
break;
default:
value = '';
break;
}
return value;
}
isEmpty() {
const value = this.getValue();
if (this.type === 'boolean') {
if (value === false) {
return true;
} else {
return false;
}
} else {
return _.isEmpty(value);
}
}
getHelpText(options) {
options = _.defaults(options || {}, {compact: false});
let text = '';
let valuesText = '';
const optName = this.getCliName();
if (this.type !== 'boolean') {
valuesText = ` <${optName}>`;
}
text = printf('%-*s %s\n', `--${optName}${valuesText}`, 60, this.detailedDescription || this.description);
if (this.type !== 'boolean') {
text += printf('%-*s Default: %s\n', '', 60, `${this.defaultValue}`);
}
if (this.type === 'choice') {
text += printf('%-*s Allowed: %s\n', '', 60, `${this.validValues.join(', ')}`);
}
text += '\n';
return text;
}
getCliName() {
return this.cliName || this.name;
}
getValue() {
let value = this.value;
if (!this.provided && (value === null || value === '') && this.defaultValue !== null) {
value = this.defaultValue;
}
return value;
}
allAllowedValues() {
return this.validValues.concat(this.hiddenValidValues || []);
}
validateValue(val) {
const cliName = this.getCliName();
switch (this.type) {
case 'choice':
if (_.size(this.allAllowedValues()) === 0) {
throw new Error(`Choice '${cliName}' does not allow any valid value`);
} else if (!_.includes(this.allAllowedValues(), val)) {
throw new Error(`'${val}' is not a valid value for '${cliName}'. Allowed: ${this.validValues.join(', ')}`);
}
break;
default:
break;
}
if (!this.canBeEmpty && _.isEmpty(val)) {
throw new Error(`'${cliName}' cannot be empty`);
}
}
setValue(val, options) {
options = _.defaults(options || {}, {silentValidation: false});
try {
this.validateValue(val);
} catch (e) {
if (options.silentValidation) {
return;
} else {
throw e;
}
}
this.provided = true;
this.value = val;
if (this.callback) {
this.callback(this);
if (this.exitAfterCallBack) {
throw new ParserExitOnCallback();
}
}
}
}
class OptionsContainer {
constructor(options) {
options = _.defaults(options || {}, {printer: {info: console.log, error: console.error}});
if (_.isFunction(options.printer)) {
this._printer = {info: options.printer, error: options.printer};
} else {
this._printer = options.printer;
}
this._options = {};
this._aliases = {};
this._cliNames = {};
// These are the populated values after parsing
this.selectedFlags = null;
this.extraFlags = null;
}
reset() {
this.selectedFlags = null;
this.extraFlags = null;
}
printError(e) {
const msg = (e instanceof Error) ? e.message : e;
this._printer.error(msg);
}
print(msg) {
this._printer.info(msg);
}
parseFlags(argv, opts) {
opts = _.defaults(opts || {}, {abortOnUnknown: false, abortIfRequiredAndNotProvided: true});
const result = {};
let i = 0;
result.extraArgs = null;
result.flags = [];
while (i < argv.length) {
const match = argv[i].match(/^--([^=]*)(=(.*))?/);
if (!match) break;
const name = match[1];
let value = null;
// This tell us to stop looking for more '--'
if (name === '') {
i += 1;
break;
}
if (match[2]) {
value = match[3];
}
const optionInfo = this._getOptionInfo(name);
const option = optionInfo.option;
if (option) {
if (option.type === 'boolean') {
if (value === null) {
value = true;
} else {
if (_.includes(['0', 'false', 'no'], String(value).toLowerCase())) {
value = false;
} else {
value = !!value;
}
}
if (optionInfo.negated) {
value = !value;
}
} else {
if (value === null) {
i += 1;
if (i >= argv.length) {
throw new ParserError(`Option '${name}' requires a value`);
} else {
value = argv[i];
}
}
}
option.setValue(value);
} else {
if (opts.abortOnUnknown) {
throw new ParserError(this._unknownFlagsMessage([`--${name}`]));
} else {
break;
}
}
i += 1;
}
if (opts.abortIfRequiredAndNotProvided) {
const requiredNotProvided = this.getRequiredAndNotProvidedOptions();
if (_.keys(requiredNotProvided).length !== 0) {
this.throwRequiredOptionsError(_.map(requiredNotProvided, 'name'));
}
}
result.extraArgs = argv.slice(i) || [];
return result;
}
_unknownFlagsMessage(flags) {
return `Unknown ${pluralize('flag', flags.length)}: ${flags.join(' ')}`;
}
dump() {
return {flags: this._options};
}
throwRequiredOptionsError(optionList) {
throw new ParserError(`The following options are required: ${optionList.join(',')}`, {showHelp: false});
}
_formatText(text, options) {
options = _.defaults(options || {}, {spacing: 0});
return text;
}
getPublicOptions() {
return _.filter(this._options, {secret: false});
}
_normalizeOptDefinition(info) {
let option = {};
if (_.isString(info)) {
option.value = info;
} else if (_.isBoolean(info)) {
option.value = info;
option.type = 'boolean';
} else {
option = _.cloneDeep(info);
}
return option;
}
addOptions(options) {
const that = this;
// 'options' may be an array of well formated options:
// [{name: 'a', defaultValue: true, type: 'boolean'}, {name: 'b', ...}]
//
// A hash with name as id:
// {a: {defaultValue: true, type: 'boolean'}, b: {...}}
//
// A hash with plain values, letting the system deduce the type:
// { a: true, b: 'sample' }
_.each(options, (data, name) => {
const opt = this._normalizeOptDefinition(data);
if (!opt.name) opt.name = name;
that.addOption(opt);
});
}
addOption(opts) {
const opt = new Option(opts);
this._options[opt.name] = opt;
if (!!opt.alias) {
this._aliases[opt.alias] = opt;
}
if (!!opt.cliName) {
this._cliNames[opt.cliName] = opt;
}
return opt;
}
getProvidedOptions() {
return this.getFilteredOptions(function(opt) {
return opt.provided === true;
});
}
getRequiredOptions() {
return this.getFilteredOptions(function(opt) {
return opt.required === true;
});
}
getRequiredAndNotProvidedOptions() {
return this.getFilteredOptions(function(opt) {
return opt.required === true && opt.provided === false && opt.isEmpty();
});
}
getFilteredOptions(fn) {
const res = [];
_.each(this._options, function(opt) {
if (fn(opt)) {
res.push(opt);
}
});
return res;
}
getFlattenOptions(options) {
// By default, a built-in help option is added to all parsers. We filter it here.
options = _.defaults(options || {}, {camelize: false, includeHelp: false, includeOptionFile: false});
const toExclude = [];
if (!options.includeHelp) toExclude.push('help');
if (!options.includeOptionFile && this.optionFileIsEnabled()) toExclude.push('option-file');
const res = {};
_.each(this._options, function(opt, key) {
if (_.includes(toExclude, key)) return;
const resKey = options.camelize ? _.camelCase(key) : key;
res[resKey] = opt.getValue();
});
return res;
}
getOptionValue(name) {
const opt = this.getOption(name);
if (opt === null) {
throw new Error(`Cannot find option ${name}`);
} else {
return opt.getValue();
}
}
_getJsonData(jsonFile) {
let stats = null;
try {
stats = fs.statSync(jsonFile);
} catch (e) {
throw new Error(`File ${jsonFile} does not exists`);
}
if (!stats.isFile(jsonFile)) {
throw new Error(`'${jsonFile}' is not a file`);
}
try {
const text = fs.readFileSync(jsonFile);
return JSON.parse(text);
} catch (e) {
throw new Error(`Error loading JSON file ${e.message}`);
}
}
parseJSON(jsonFile, options) {
this.parseData(this._getJsonData(jsonFile), options);
}
parseData(data, options) {
options = _.defaults(options || {}, {force: true});
const that = this;
_.each(data, function(val, k) {
const option = that.getOption(k);
if (!option) {
throw new ParserError(`Unknown option '${k}'`);
}
if (option.provided && !options.force) return;
option.setValue(val);
});
}
getOption(name) {
if (_.has(this._options, name)) {
return this._options[name];
} else if (_.has(this._aliases, name)) {
return this._aliases[name];
} else if (_.has(this._cliNames, name)) {
return this._cliNames[name];
} else {
return null;
}
}
_getOptionInfo(name) {
let option = this.getOption(name);
if (option) {
return {option: option};
} else {
const match = name.match(/^no-(.+)$/);
if (match) {
option = this.getOption(match[1]);
if (option && option.type === 'boolean' && option.allowNegated) {
return {option: option, negated: true};
}
}
}
return {option: null};
}
isFlag(str) {
return !!str.match(/^--([^=]*)/);
}
}
class CommandsContainer extends OptionsContainer {
constructor(options) {
super(options);
this._commands = {};
// These are the populated values after parsing
this.selectedCommand = null;
}
optionFileIsEnabled() {
return !!this.getOption('option-file');
}
enableOptionFile(options) {
if (this.optionFileIsEnabled()) throw new Error(`--option-file is already enabled`);
options = _.defaults(options || {}, {description: 'JSON file containing command line options'});
const optionFileOpts = _.omit(options, 'name');
const _this = this;
optionFileOpts.callback = function() {
_this.parseJSON(this.value);
};
optionFileOpts.name = 'option-file';
this.addOption(optionFileOpts);
}
reset() {
super.reset();
this.selectedCommand = null;
}
addCommand(cmdDefinition, options) {
options = options || [];
/* eslint-disable no-use-before-define */
const cmd = new Command(cmdDefinition);
/* eslint-enable no-use-before-define */
this._commands[cmd.name] = cmd;
cmd.addOptions(options);
return cmd;
}
getPublicCommands() {
return _.filter(this._commands, {secret: false});
}
showHelp(options) {
options = _.defaults(options || {}, {});
this.print(this.getHelpText());
}
getCommand(name) {
if (_.has(this._commands, name)) {
return this._commands[name];
} else {
return null;
}
}
parseCommands(argv, options) {
options = _.defaults(options || {}, {abortOnUnknown: false, silent: false});
if (argv.length === 0) {
return null;
} else if (this.isFlag(argv[0])) {
if (!options.silent && options.abortOnUnknown) {
throw new ShowHelpException(this._unknownFlagsMessage(argv), {exitCode: 1});
}
return null;
}
const cmd = this.getCommand(argv[0]);
if (cmd) {
cmd.parse(argv.slice(1));
this.selectedCommand = cmd;
if (this.selectedCommand.callback !== null) {
this.selectedCommand.callback({container: this});
if (this.selectedCommand.exitAfterCallBack) {
throw new ParserExitOnCallback();
}
}
return null;
} else {
throw new ParserError(`Unknown command '${argv[0]}'`);
}
}
getHelpText(options) {
options = _.defaults(options || {}, {compact: false, commandName: this.name});
let headText = `\nUsage: ${options.commandName}`;
let text = '';
const cmds = this.getPublicCommands();
const opts = this.getPublicOptions();
if (!_.isEmpty(opts)) {
headText += ' <options>';
text += `where <options> include:\n\n`;
_.each(this.getPublicOptions(), function(opt) {
text += opt.getHelpText();
});
}
if (!_.isEmpty(cmds)) {
headText += ' <command>';
text += `\nAnd <command> is one of: ${this._formatText(_.map(cmds, 'name').join(', '))}`;
text += `\n\nTo get more information about a command, you can execute:
${options.commandName} <command> --help\n`;
}
return `${headText}\n\n ${text}`;
}
}
class Command extends CommandsContainer {
constructor(opts) {
opts = opts || {};
super(opts);
const supportedOptions = {
name: null,
alias: null,
secret: false,
description: '',
minArgs: 0,
maxArgs: 0,
namedArgs: [],
callback: null,
exitAfterCallBack: false,
enableHelpMenu: true
};
_.extend(this, pickWithDefaults(opts, supportedOptions));
if (!this.name) {
throw new ParserError('You did not provide a name for the command');
}
// these are the extra unrecognized argumets
this.extraArgs = [];
this.arguments = {};
this.providedArguments = [];
if (this.enableHelpMenu) {
this.addOption({
name: 'help',
type: 'boolean',
description: 'Display this help menu',
allowNegated: false,
callback: function() {
throw new ShowHelpException();
}
});
}
}
dump() {
const that = this;
const result = super.dump();
_.each(['minArgs', 'maxArgs', 'extraArgs'], function(k) {
result[k] = that[k];
});
return result;
}
parse(argv, options) {
options = _.defaults(options || {}, {abortOnUnknown: true, abortIfRequiredAndNotProvided: true});
try {
const rest = this.parseFlags(argv, options).extraArgs;
const cmdArgs = [];
let i = 0;
while (i < rest.length && (this.maxArgs === -1 || i < this.maxArgs)) {
if (this.isFlag(rest[i]) && i >= this.minArgs) {
// It is already valid, so we finish here
break;
}
cmdArgs.push(rest[i]);
if (this.namedArgs.length > i) {
const namedArg = this.namedArgs[i];
this.arguments[namedArg] = rest[i];
}
this.providedArguments.push(rest[i]);
i += 1;
}
if (cmdArgs.length < this.minArgs) {
throw new ParserError(
`Command '${this.name}' expects at least ${this.minArgs} argument but you provided ${cmdArgs.length}`
);
}
if (_.isEmpty(this._commands)) {
this.extraArgs = rest.slice(i);
} else {
this.parseCommands(rest.slice(i));
}
} catch (e) {
if (e instanceof ShowHelpException) {
this.showHelp({code: 1, error: e.message});
e.showHelp = false;
}
throw e;
}
}
getHelpText(options) {
options = _.defaults(options || {}, {compact: false, commandName: this.name});
let headText = '';
if (this.description !== '') {
headText += `\n${this.description}\n`;
}
headText += `\nUsage: ${options.commandName}`;
let text = '';
const cmds = this.getPublicCommands();
const opts = this.getPublicOptions();
if (!_.isEmpty(opts)) {
headText += ' <options>';
text += `where <options> include: \n\n`;
_.each(this.getPublicOptions(), function(opt) {
text += opt.getHelpText();
});
}
if (this.minArgs !== 0 || this.maxArgs !== 0 || this.namedArgs.length > 0) {
if (this.namedArgs.length > 0) {
headText += ` ${this.namedArgs.map(function(e) { return `<${e}>`; }).join(' ')}`;
}
}
if (!_.isEmpty(cmds)) {
headText += ' <command>';
text += `\nAnd <command> is one of: ${this._formatText(_.map(cmds, 'name').join(', '))}`;
text += `\n\nTo get more information about a command, you can execute:
${options.commandName} help <command>\n`;
}
return `${headText}\n\n ${text}`;
}
throwRequiredOptionsError(optionList) {
throw new ParserError(`The following command options are required: ${optionList.join(',')}`);
}
}
class Parser extends CommandsContainer {
constructor(options) {
options = _.defaults(
options || {},
{
toolName: process.argv[1] || process.argv[0],
enableBuiltInHelp: true,
throwOnExit: false,
allowProcessExit: true
}
);
super(options);
this.throwOnExit = options.throwOnExit;
this.allowProcessExit = options.allowProcessExit;
this.toolName = options.toolName;
this.unknownFlags = [];
if (options.enableBuiltInHelp) {
this.addOption({
name: 'help', type: 'boolean', allowNegated: false, callback: function() {
throw new ShowHelpException();
}
});
// const that = this;
// this.addCommand({name: 'help', minArgs: 1, maxArgs: 1, namedArgs: ['command'], callback: function() {
// var command = this.command;
// that.showCommandHelp(command);
// }});
}
}
reset() {
super.reset();
this.unknownFlags = [];
}
showCommandHelp(name) {
const cmd = this.getCommand(name);
if (cmd === null) {
throw new ParserError(`Unknow command ${name}`);
}
if (cmd.enableHelpMenu) {
throw new ShowHelpException();
}
}
getHelpText(options) {
options = _.defaults(options || {}, {compact: false, commandName: this.toolName});
return super.getHelpText(options);
}
parse(argv, options) {
options = _.defaults(
options || {},
{
silent: false,
abortOnError: true,
abortOnUnknown: true,
throwOnExit: this.throwOnExit,
allowProcessExit: this.allowProcessExit,
onlyFlags: false,
reset: true,
abortIfRequiredAndNotProvided: true
}
);
// Clean the state between different parse calls
if (options.reset) this.reset();
try {
this._argv = argv;
const rest = this.parseFlags(
argv, _.pick(options, ['abortOnUnknown', 'abortIfRequiredAndNotProvided'])
);
if (!options.onlyFlags) {
this.parseCommands(rest.extraArgs, options);
}
} catch (e) {
if (options.silent) {
return;
}
if (e instanceof ShowHelpException) {
if (e.showHelp) {
this.showHelp({code: e.exitCode, error: e.message, throw: options.throwOnExit});
e.showHelp = false;
}
}
if (e instanceof ParserExit) {
if (options.throwOnExit) {
throw e;
} else if (options.allowProcessExit) {
// Returning makes possible to mock process.exit while testing
// (replacing it with a function that just captures the exit code, for example)
// while still aborting the execution of the function. The return won't ever
// execute in regular circumstances.
process.exit(e.exitCode);
return;
} else {
return;
}
} else if (e instanceof ParserError && e.showHelp) {
this.showHelp();
e.showHelp = false;
if (options.allowProcessExit) {
this.printError(e);
// Returning makes possible to mock process.exit while testing
// (replacing it with a function that just captures the exit code, for example)
// while still aborting the execution of the function. The return won't ever
// execute in regular circumstances.
process.exit(e.exitCode);
return;
}
}
if (options.abortOnError) {
throw e;
}
}
}
dump() {
const dumpInfo = super.dump();
dumpInfo.commands = {};
_.each(this._commands, function(command) {
dumpInfo.commands[command.name] = command.dump();
});
return dumpInfo;
}
}
exports.Parser = Parser;
exports.Option = Option;
exports.Command = Command;
exports.ParserError = ParserError;
exports.ParserExit = ParserExit;