647 lines
31 KiB
JavaScript
647 lines
31 KiB
JavaScript
"use strict";
|
|
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
|
// See LICENSE in the project root for license information.
|
|
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;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.CommandLineParameterProvider = void 0;
|
|
const argparse = __importStar(require("argparse"));
|
|
const BaseClasses_1 = require("../parameters/BaseClasses");
|
|
const CommandLineChoiceParameter_1 = require("../parameters/CommandLineChoiceParameter");
|
|
const CommandLineChoiceListParameter_1 = require("../parameters/CommandLineChoiceListParameter");
|
|
const CommandLineIntegerParameter_1 = require("../parameters/CommandLineIntegerParameter");
|
|
const CommandLineIntegerListParameter_1 = require("../parameters/CommandLineIntegerListParameter");
|
|
const CommandLineFlagParameter_1 = require("../parameters/CommandLineFlagParameter");
|
|
const CommandLineStringParameter_1 = require("../parameters/CommandLineStringParameter");
|
|
const CommandLineStringListParameter_1 = require("../parameters/CommandLineStringListParameter");
|
|
const CommandLineRemainder_1 = require("../parameters/CommandLineRemainder");
|
|
const Constants_1 = require("../Constants");
|
|
const CommandLineParserExitError_1 = require("./CommandLineParserExitError");
|
|
const SCOPE_GROUP_NAME = 'scope';
|
|
const LONG_NAME_GROUP_NAME = 'longName';
|
|
const POSSIBLY_SCOPED_LONG_NAME_REGEXP = /^--((?<scope>[a-z0-9]+(-[a-z0-9]+)*):)?(?<longName>[a-z0-9]+((-[a-z0-9]+)+)?)$/;
|
|
/**
|
|
* This is the common base class for CommandLineAction and CommandLineParser
|
|
* that provides functionality for defining command-line parameters.
|
|
*
|
|
* @public
|
|
*/
|
|
class CommandLineParameterProvider {
|
|
/** @internal */
|
|
// Third party code should not inherit subclasses or call this constructor
|
|
constructor() {
|
|
this._parameters = [];
|
|
this._parametersByLongName = new Map();
|
|
this._parametersByShortName = new Map();
|
|
this._parameterGroupsByName = new Map();
|
|
this._ambiguousParameterParserKeysByName = new Map();
|
|
this._registeredParameterParserKeysByName = new Map();
|
|
this._parametersHaveBeenRegistered = false;
|
|
this._parametersHaveBeenProcessed = false;
|
|
}
|
|
/**
|
|
* Returns a collection of the parameters that were defined for this object.
|
|
*/
|
|
get parameters() {
|
|
return this._parameters;
|
|
}
|
|
/**
|
|
* Informs the caller if the argparse data has been processed into parameters.
|
|
*/
|
|
get parametersProcessed() {
|
|
return this._parametersHaveBeenProcessed;
|
|
}
|
|
/**
|
|
* If {@link CommandLineParameterProvider.defineCommandLineRemainder} was called,
|
|
* this object captures any remaining command line arguments after the recognized portion.
|
|
*/
|
|
get remainder() {
|
|
return this._remainder;
|
|
}
|
|
/**
|
|
* Defines a command-line parameter whose value must be a string from a fixed set of
|
|
* allowable choices (similar to an enum).
|
|
*
|
|
* @remarks
|
|
* Example of a choice parameter:
|
|
* ```
|
|
* example-tool --log-level warn
|
|
* ```
|
|
*/
|
|
defineChoiceParameter(definition) {
|
|
const parameter = new CommandLineChoiceParameter_1.CommandLineChoiceParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Returns the CommandLineChoiceParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getChoiceParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.Choice, parameterScope);
|
|
}
|
|
/**
|
|
* Defines a command-line parameter whose value must be a string from a fixed set of
|
|
* allowable choices (similar to an enum). The parameter can be specified multiple times to
|
|
* build a list.
|
|
*
|
|
* @remarks
|
|
* Example of a choice list parameter:
|
|
* ```
|
|
* example-tool --allow-color red --allow-color green
|
|
* ```
|
|
*/
|
|
defineChoiceListParameter(definition) {
|
|
const parameter = new CommandLineChoiceListParameter_1.CommandLineChoiceListParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Returns the CommandLineChoiceListParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getChoiceListParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.ChoiceList, parameterScope);
|
|
}
|
|
/**
|
|
* Defines a command-line switch whose boolean value is true if the switch is provided,
|
|
* and false otherwise.
|
|
*
|
|
* @remarks
|
|
* Example usage of a flag parameter:
|
|
* ```
|
|
* example-tool --debug
|
|
* ```
|
|
*/
|
|
defineFlagParameter(definition) {
|
|
const parameter = new CommandLineFlagParameter_1.CommandLineFlagParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Returns the CommandLineFlagParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getFlagParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.Flag, parameterScope);
|
|
}
|
|
/**
|
|
* Defines a command-line parameter whose argument is an integer.
|
|
*
|
|
* @remarks
|
|
* Example usage of an integer parameter:
|
|
* ```
|
|
* example-tool --max-attempts 5
|
|
* ```
|
|
*/
|
|
defineIntegerParameter(definition) {
|
|
const parameter = new CommandLineIntegerParameter_1.CommandLineIntegerParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Returns the CommandLineIntegerParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getIntegerParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.Integer, parameterScope);
|
|
}
|
|
/**
|
|
* Defines a command-line parameter whose argument is an integer. The parameter can be specified
|
|
* multiple times to build a list.
|
|
*
|
|
* @remarks
|
|
* Example usage of an integer list parameter:
|
|
* ```
|
|
* example-tool --avoid 4 --avoid 13
|
|
* ```
|
|
*/
|
|
defineIntegerListParameter(definition) {
|
|
const parameter = new CommandLineIntegerListParameter_1.CommandLineIntegerListParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Returns the CommandLineIntegerParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getIntegerListParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.IntegerList, parameterScope);
|
|
}
|
|
/**
|
|
* Defines a command-line parameter whose argument is a single text string.
|
|
*
|
|
* @remarks
|
|
* Example usage of a string parameter:
|
|
* ```
|
|
* example-tool --message "Hello, world!"
|
|
* ```
|
|
*/
|
|
defineStringParameter(definition) {
|
|
const parameter = new CommandLineStringParameter_1.CommandLineStringParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Returns the CommandLineStringParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getStringParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.String, parameterScope);
|
|
}
|
|
/**
|
|
* Defines a command-line parameter whose argument is a single text string. The parameter can be
|
|
* specified multiple times to build a list.
|
|
*
|
|
* @remarks
|
|
* Example usage of a string list parameter:
|
|
* ```
|
|
* example-tool --add file1.txt --add file2.txt --add file3.txt
|
|
* ```
|
|
*/
|
|
defineStringListParameter(definition) {
|
|
const parameter = new CommandLineStringListParameter_1.CommandLineStringListParameter(definition);
|
|
this._defineParameter(parameter);
|
|
return parameter;
|
|
}
|
|
/**
|
|
* Defines a rule that captures any remaining command line arguments after the recognized portion.
|
|
*
|
|
* @remarks
|
|
* This feature is useful for commands that pass their arguments along to an external tool, relying on
|
|
* that tool to perform validation. (It could also be used to parse parameters without any validation
|
|
* or documentation, but that is not recommended.)
|
|
*
|
|
* Example of capturing the remainder after an optional flag parameter.
|
|
* ```
|
|
* example-tool --my-flag this is the remainder
|
|
* ```
|
|
*
|
|
* In the "--help" documentation, the remainder rule will be represented as "...".
|
|
*/
|
|
defineCommandLineRemainder(definition) {
|
|
if (this._remainder) {
|
|
throw new Error('defineRemainingArguments() has already been called for this provider');
|
|
}
|
|
this._remainder = new CommandLineRemainder_1.CommandLineRemainder(definition);
|
|
return this._remainder;
|
|
}
|
|
/**
|
|
* Returns the CommandLineStringListParameter with the specified long name.
|
|
* @remarks
|
|
* This method throws an exception if the parameter is not defined.
|
|
*/
|
|
getStringListParameter(parameterLongName, parameterScope) {
|
|
return this._getParameter(parameterLongName, BaseClasses_1.CommandLineParameterKind.StringList, parameterScope);
|
|
}
|
|
/**
|
|
* Generates the command-line help text.
|
|
*/
|
|
renderHelpText() {
|
|
const initialState = {
|
|
parentParameterNames: new Set()
|
|
};
|
|
this._registerDefinedParameters(initialState);
|
|
return this._getArgumentParser().formatHelp();
|
|
}
|
|
/**
|
|
* Generates the command-line usage text.
|
|
*/
|
|
renderUsageText() {
|
|
const initialState = {
|
|
parentParameterNames: new Set()
|
|
};
|
|
this._registerDefinedParameters(initialState);
|
|
return this._getArgumentParser().formatUsage();
|
|
}
|
|
/**
|
|
* Returns a object which maps the long name of each parameter in this.parameters
|
|
* to the stringified form of its value. This is useful for logging telemetry, but
|
|
* it is not the proper way of accessing parameters or their values.
|
|
*/
|
|
getParameterStringMap() {
|
|
const parameterMap = {};
|
|
for (const parameter of this.parameters) {
|
|
const parameterName = parameter.scopedLongName || parameter.longName;
|
|
switch (parameter.kind) {
|
|
case BaseClasses_1.CommandLineParameterKind.Flag:
|
|
case BaseClasses_1.CommandLineParameterKind.Choice:
|
|
case BaseClasses_1.CommandLineParameterKind.String:
|
|
case BaseClasses_1.CommandLineParameterKind.Integer:
|
|
parameterMap[parameterName] = JSON.stringify(parameter.value);
|
|
break;
|
|
case BaseClasses_1.CommandLineParameterKind.StringList:
|
|
case BaseClasses_1.CommandLineParameterKind.IntegerList:
|
|
case BaseClasses_1.CommandLineParameterKind.ChoiceList:
|
|
const arrayValue = parameter.values;
|
|
parameterMap[parameterName] = arrayValue ? arrayValue.join(',') : '';
|
|
break;
|
|
}
|
|
}
|
|
return parameterMap;
|
|
}
|
|
/**
|
|
* Returns an object with the parsed scope (if present) and the long name of the parameter.
|
|
*/
|
|
parseScopedLongName(scopedLongName) {
|
|
const result = POSSIBLY_SCOPED_LONG_NAME_REGEXP.exec(scopedLongName);
|
|
if (!result || !result.groups) {
|
|
throw new Error(`The parameter long name "${scopedLongName}" is not valid.`);
|
|
}
|
|
return {
|
|
longName: `--${result.groups[LONG_NAME_GROUP_NAME]}`,
|
|
scope: result.groups[SCOPE_GROUP_NAME]
|
|
};
|
|
}
|
|
/** @internal */
|
|
_registerDefinedParameters(state) {
|
|
if (this._parametersHaveBeenRegistered) {
|
|
// We prevent new parameters from being defined after the first call to _registerDefinedParameters,
|
|
// so we can already ensure that all parameters were registered.
|
|
return;
|
|
}
|
|
// First, loop through all parameters with short names. If there are any duplicates, disable the short names
|
|
// since we can't prefix scopes to short names in order to deduplicate them. The duplicate short names will
|
|
// be reported as errors if the user attempts to use them.
|
|
const parametersWithDuplicateShortNames = new Set();
|
|
for (const [shortName, shortNameParameters] of this._parametersByShortName.entries()) {
|
|
if (shortNameParameters.length > 1) {
|
|
for (const parameter of shortNameParameters) {
|
|
this._defineAmbiguousParameter(shortName);
|
|
parametersWithDuplicateShortNames.add(parameter);
|
|
}
|
|
}
|
|
}
|
|
// Then, loop through all parameters and register them. If there are any duplicates, ensure that they have
|
|
// provided a scope and register them with the scope. The duplicate long names will be reported as an error
|
|
// if the user attempts to use them.
|
|
for (const longNameParameters of this._parametersByLongName.values()) {
|
|
const useScopedLongName = longNameParameters.length > 1;
|
|
for (const parameter of longNameParameters) {
|
|
if (useScopedLongName) {
|
|
if (!parameter.parameterScope) {
|
|
throw new Error(`The parameter "${parameter.longName}" is defined multiple times with the same long name. ` +
|
|
'Parameters with the same long name must define a scope.');
|
|
}
|
|
this._defineAmbiguousParameter(parameter.longName);
|
|
}
|
|
const ignoreShortName = parametersWithDuplicateShortNames.has(parameter);
|
|
this._registerParameter(parameter, useScopedLongName, ignoreShortName);
|
|
}
|
|
}
|
|
// Register the existing parameters as ambiguous parameters. These are generally provided by the
|
|
// parent action.
|
|
const { parentParameterNames } = state;
|
|
for (const parentParameterName of parentParameterNames) {
|
|
this._defineAmbiguousParameter(parentParameterName);
|
|
}
|
|
// We also need to loop through the defined ambiguous parameters and register them. These will be reported
|
|
// as errors if the user attempts to use them.
|
|
for (const [ambiguousParameterName, parserKey] of this._ambiguousParameterParserKeysByName) {
|
|
// Only register the ambiguous parameter if it hasn't already been registered. We will still handle these
|
|
// already-registered parameters as ambiguous, but by avoiding registering again, we will defer errors
|
|
// until the user actually attempts to use the parameter.
|
|
if (!this._registeredParameterParserKeysByName.has(ambiguousParameterName)) {
|
|
this._registerAmbiguousParameter(ambiguousParameterName, parserKey);
|
|
}
|
|
}
|
|
// Need to add the remainder parameter last
|
|
if (this._remainder) {
|
|
const argparseOptions = {
|
|
help: this._remainder.description,
|
|
nargs: argparse.Const.REMAINDER,
|
|
metavar: '"..."'
|
|
};
|
|
this._getArgumentParser().addArgument(argparse.Const.REMAINDER, argparseOptions);
|
|
}
|
|
this._parametersHaveBeenRegistered = true;
|
|
}
|
|
/** @internal */
|
|
_processParsedData(parserOptions, data) {
|
|
if (!this._parametersHaveBeenRegistered) {
|
|
throw new Error('Parameters have not been registered');
|
|
}
|
|
if (this._parametersHaveBeenProcessed) {
|
|
throw new Error('Command Line Parser Data was already processed');
|
|
}
|
|
// Search for any ambiguous parameters and throw an error if any are found
|
|
for (const [parameterName, parserKey] of this._ambiguousParameterParserKeysByName) {
|
|
if (data[parserKey]) {
|
|
// When the parser key matches the actually registered parameter, we know that this is an ambiguous
|
|
// parameter sourced from the parent action or tool
|
|
if (this._registeredParameterParserKeysByName.get(parameterName) === parserKey) {
|
|
this._throwParserExitError(parserOptions, data, 1, `Ambiguous option: "${parameterName}".`);
|
|
}
|
|
// Determine if the ambiguous parameter is a short name or a long name, since the process of finding
|
|
// the non-ambiguous name is different for each.
|
|
const duplicateShortNameParameters = this._parametersByShortName.get(parameterName);
|
|
if (duplicateShortNameParameters) {
|
|
// We also need to make sure we get the non-ambiguous long name for the parameter, since it is
|
|
// possible for that the long name is ambiguous as well.
|
|
const nonAmbiguousLongNames = [];
|
|
for (const parameter of duplicateShortNameParameters) {
|
|
const matchingLongNameParameters = this._parametersByLongName.get(parameter.longName);
|
|
if (!(matchingLongNameParameters === null || matchingLongNameParameters === void 0 ? void 0 : matchingLongNameParameters.length)) {
|
|
// This should never happen
|
|
throw new Error(`Unable to find long name parameters for ambiguous short name parameter "${parameterName}".`);
|
|
}
|
|
// If there is more than one matching long name parameter, then we know that we need to use the
|
|
// scoped long name for the parameter. The scoped long name should always be provided.
|
|
if (matchingLongNameParameters.length > 1) {
|
|
if (!parameter.scopedLongName) {
|
|
// This should never happen
|
|
throw new Error(`Unable to find scoped long name for ambiguous short name parameter "${parameterName}".`);
|
|
}
|
|
nonAmbiguousLongNames.push(parameter.scopedLongName);
|
|
}
|
|
else {
|
|
nonAmbiguousLongNames.push(parameter.longName);
|
|
}
|
|
}
|
|
// Throw an error including the non-ambiguous long names for the parameters that have the ambiguous
|
|
// short name, ex.
|
|
// Error: Ambiguous option "-p" could match "--param1", "--param2"
|
|
this._throwParserExitError(parserOptions, data, 1, `Ambiguous option: "${parameterName}" could match ${nonAmbiguousLongNames.join(', ')}.`);
|
|
}
|
|
const duplicateLongNameParameters = this._parametersByLongName.get(parameterName);
|
|
if (duplicateLongNameParameters) {
|
|
const nonAmbiguousLongNames = duplicateLongNameParameters.map((p) => {
|
|
// The scoped long name should always be provided
|
|
if (!p.scopedLongName) {
|
|
// This should never happen
|
|
throw new Error(`Unable to find scoped long name for ambiguous long name parameter "${parameterName}".`);
|
|
}
|
|
return p.scopedLongName;
|
|
});
|
|
// Throw an error including the non-ambiguous scoped long names for the parameters that have the
|
|
// ambiguous long name, ex.
|
|
// Error: Ambiguous option: "--param" could match --scope1:param, --scope2:param
|
|
this._throwParserExitError(parserOptions, data, 1, `Ambiguous option: "${parameterName}" could match ${nonAmbiguousLongNames.join(', ')}.`);
|
|
}
|
|
// This shouldn't happen, but we also shouldn't allow the user to use the ambiguous parameter
|
|
this._throwParserExitError(parserOptions, data, 1, `Ambiguous option: "${parameterName}".`);
|
|
}
|
|
}
|
|
// Fill in the values for the parameters
|
|
for (const parameter of this._parameters) {
|
|
const value = data[parameter._parserKey]; // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
parameter._setValue(value);
|
|
}
|
|
if (this.remainder) {
|
|
this.remainder._setValue(data[argparse.Const.REMAINDER]);
|
|
}
|
|
this._parametersHaveBeenProcessed = true;
|
|
}
|
|
/** @internal */
|
|
_defineParameter(parameter) {
|
|
if (this._parametersHaveBeenRegistered) {
|
|
throw new Error('Parameters have already been registered for this provider');
|
|
}
|
|
// Generate and set the parser key at definition time
|
|
parameter._parserKey = this._generateKey();
|
|
this._parameters.push(parameter);
|
|
// Collect all parameters with the same long name. We will perform conflict resolution at registration.
|
|
let longNameParameters = this._parametersByLongName.get(parameter.longName);
|
|
if (!longNameParameters) {
|
|
longNameParameters = [];
|
|
this._parametersByLongName.set(parameter.longName, longNameParameters);
|
|
}
|
|
longNameParameters.push(parameter);
|
|
// Collect all parameters with the same short name. We will perform conflict resolution at registration.
|
|
if (parameter.shortName) {
|
|
let shortNameParameters = this._parametersByShortName.get(parameter.shortName);
|
|
if (!shortNameParameters) {
|
|
shortNameParameters = [];
|
|
this._parametersByShortName.set(parameter.shortName, shortNameParameters);
|
|
}
|
|
shortNameParameters.push(parameter);
|
|
}
|
|
}
|
|
/** @internal */
|
|
_defineAmbiguousParameter(name) {
|
|
if (this._parametersHaveBeenRegistered) {
|
|
throw new Error('Parameters have already been registered for this provider');
|
|
}
|
|
// Only generate a new parser key if the ambiguous parameter hasn't been defined yet,
|
|
// either as an existing parameter or as another ambiguous parameter
|
|
let existingParserKey = this._registeredParameterParserKeysByName.get(name) ||
|
|
this._ambiguousParameterParserKeysByName.get(name);
|
|
if (!existingParserKey) {
|
|
existingParserKey = this._generateKey();
|
|
}
|
|
this._ambiguousParameterParserKeysByName.set(name, existingParserKey);
|
|
return existingParserKey;
|
|
}
|
|
/** @internal */
|
|
_registerParameter(parameter, useScopedLongName, ignoreShortName) {
|
|
var _a;
|
|
const names = [];
|
|
if (parameter.shortName && !ignoreShortName) {
|
|
names.push(parameter.shortName);
|
|
}
|
|
// Use the original long name unless otherwise requested
|
|
if (!useScopedLongName) {
|
|
names.push(parameter.longName);
|
|
}
|
|
// Add the scoped long name if it exists
|
|
if (parameter.scopedLongName) {
|
|
names.push(parameter.scopedLongName);
|
|
}
|
|
let finalDescription = parameter.description;
|
|
const supplementaryNotes = [];
|
|
parameter._getSupplementaryNotes(supplementaryNotes);
|
|
if (supplementaryNotes.length > 0) {
|
|
// If they left the period off the end of their sentence, then add one.
|
|
if (finalDescription.match(/[a-z0-9]"?\s*$/i)) {
|
|
finalDescription = finalDescription.trimRight() + '.';
|
|
}
|
|
// Append the supplementary text
|
|
finalDescription += ' ' + supplementaryNotes.join(' ');
|
|
}
|
|
// NOTE: Our "environmentVariable" feature takes precedence over argparse's "defaultValue",
|
|
// so we have to reimplement that feature.
|
|
const argparseOptions = {
|
|
help: finalDescription,
|
|
dest: parameter._parserKey,
|
|
metavar: parameter.argumentName || undefined,
|
|
required: parameter.required
|
|
};
|
|
switch (parameter.kind) {
|
|
case BaseClasses_1.CommandLineParameterKind.Choice: {
|
|
const choiceParameter = parameter;
|
|
argparseOptions.choices = choiceParameter.alternatives;
|
|
break;
|
|
}
|
|
case BaseClasses_1.CommandLineParameterKind.ChoiceList: {
|
|
const choiceParameter = parameter;
|
|
argparseOptions.choices = choiceParameter.alternatives;
|
|
argparseOptions.action = 'append';
|
|
break;
|
|
}
|
|
case BaseClasses_1.CommandLineParameterKind.Flag:
|
|
argparseOptions.action = 'storeTrue';
|
|
break;
|
|
case BaseClasses_1.CommandLineParameterKind.Integer:
|
|
argparseOptions.type = 'int';
|
|
break;
|
|
case BaseClasses_1.CommandLineParameterKind.IntegerList:
|
|
argparseOptions.type = 'int';
|
|
argparseOptions.action = 'append';
|
|
break;
|
|
case BaseClasses_1.CommandLineParameterKind.String:
|
|
break;
|
|
case BaseClasses_1.CommandLineParameterKind.StringList:
|
|
argparseOptions.action = 'append';
|
|
break;
|
|
}
|
|
let argumentGroup;
|
|
if (parameter.parameterGroup) {
|
|
argumentGroup = this._parameterGroupsByName.get(parameter.parameterGroup);
|
|
if (!argumentGroup) {
|
|
let parameterGroupName;
|
|
if (typeof parameter.parameterGroup === 'string') {
|
|
parameterGroupName = parameter.parameterGroup;
|
|
}
|
|
else if (parameter.parameterGroup === Constants_1.SCOPING_PARAMETER_GROUP) {
|
|
parameterGroupName = 'scoping';
|
|
}
|
|
else {
|
|
throw new Error('Unexpected parameter group: ' + parameter.parameterGroup);
|
|
}
|
|
argumentGroup = this._getArgumentParser().addArgumentGroup({
|
|
title: `Optional ${parameterGroupName} arguments`
|
|
});
|
|
this._parameterGroupsByName.set(parameter.parameterGroup, argumentGroup);
|
|
}
|
|
}
|
|
else {
|
|
argumentGroup = this._getArgumentParser();
|
|
}
|
|
argumentGroup.addArgument(names, Object.assign({}, argparseOptions));
|
|
if ((_a = parameter.undocumentedSynonyms) === null || _a === void 0 ? void 0 : _a.length) {
|
|
argumentGroup.addArgument(parameter.undocumentedSynonyms, Object.assign(Object.assign({}, argparseOptions), { help: argparse.Const.SUPPRESS }));
|
|
}
|
|
// Register the parameter names so that we can detect ambiguous parameters
|
|
for (const name of [...names, ...(parameter.undocumentedSynonyms || [])]) {
|
|
this._registeredParameterParserKeysByName.set(name, parameter._parserKey);
|
|
}
|
|
}
|
|
_registerAmbiguousParameter(name, parserKey) {
|
|
this._getArgumentParser().addArgument(name, {
|
|
dest: parserKey,
|
|
// We don't know if this argument takes parameters or not, so we need to accept any number of args
|
|
nargs: '*',
|
|
// Ensure that the argument is not shown in the help text, since these parameters are only included
|
|
// to inform the user that ambiguous parameters are present
|
|
help: argparse.Const.SUPPRESS
|
|
});
|
|
}
|
|
_generateKey() {
|
|
return 'key_' + (CommandLineParameterProvider._keyCounter++).toString();
|
|
}
|
|
_getParameter(parameterLongName, expectedKind, parameterScope) {
|
|
// Support the parameter long name being prefixed with the scope
|
|
const { scope, longName } = this.parseScopedLongName(parameterLongName);
|
|
parameterLongName = longName;
|
|
parameterScope = scope || parameterScope;
|
|
const parameters = this._parametersByLongName.get(parameterLongName);
|
|
if (!parameters) {
|
|
throw new Error(`The parameter "${parameterLongName}" is not defined`);
|
|
}
|
|
let parameter = parameters.find((p) => p.parameterScope === parameterScope);
|
|
if (!parameter) {
|
|
if (parameterScope !== undefined) {
|
|
throw new Error(`The parameter "${parameterLongName}" with scope "${parameterScope}" is not defined.`);
|
|
}
|
|
if (parameters.length !== 1) {
|
|
throw new Error(`The parameter "${parameterLongName}" is ambiguous. You must specify a scope.`);
|
|
}
|
|
parameter = parameters[0];
|
|
}
|
|
if (parameter.kind !== expectedKind) {
|
|
throw new Error(`The parameter "${parameterLongName}" is of type "${BaseClasses_1.CommandLineParameterKind[parameter.kind]}"` +
|
|
` whereas the caller was expecting "${BaseClasses_1.CommandLineParameterKind[expectedKind]}".`);
|
|
}
|
|
return parameter;
|
|
}
|
|
_throwParserExitError(parserOptions, data, errorCode, message) {
|
|
// Write out the usage text to make it easier for the user to find the correct parameter name
|
|
const targetActionName = data.aliasAction || data.action || '';
|
|
const errorPrefix = `Error: ${parserOptions.toolFilename}` +
|
|
// Handle aliases, actions, and actionless parameter providers
|
|
`${targetActionName ? ' ' : ''}${targetActionName}: error: `;
|
|
// eslint-disable-next-line no-console
|
|
console.log(this.renderUsageText());
|
|
throw new CommandLineParserExitError_1.CommandLineParserExitError(errorCode, `${errorPrefix}${message.trimStart().trimEnd()}\n`);
|
|
}
|
|
}
|
|
CommandLineParameterProvider._keyCounter = 0;
|
|
exports.CommandLineParameterProvider = CommandLineParameterProvider;
|
|
//# sourceMappingURL=CommandLineParameterProvider.js.map
|