204 lines
10 KiB
JavaScript
204 lines
10 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.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ScopedCommandLineAction = void 0;
|
|
const Constants_1 = require("../Constants");
|
|
const CommandLineAction_1 = require("./CommandLineAction");
|
|
const CommandLineParser_1 = require("./CommandLineParser");
|
|
const CommandLineParserExitError_1 = require("./CommandLineParserExitError");
|
|
/**
|
|
* A CommandLineParser used exclusively to parse the scoped command-line parameters
|
|
* for a ScopedCommandLineAction.
|
|
*/
|
|
class InternalScopedCommandLineParser extends CommandLineParser_1.CommandLineParser {
|
|
get canExecute() {
|
|
return this._canExecute;
|
|
}
|
|
constructor(options) {
|
|
const { actionOptions, unscopedActionParameters, toolFilename, aliasAction, aliasDocumentation } = options;
|
|
const toolCommand = `${toolFilename} ${actionOptions.actionName}`;
|
|
// When coming from an alias command, we want to show the alias command name in the help text
|
|
const toolCommandForLogging = `${toolFilename} ${aliasAction !== null && aliasAction !== void 0 ? aliasAction : actionOptions.actionName}`;
|
|
const scopingArgs = [];
|
|
for (const parameter of unscopedActionParameters) {
|
|
parameter.appendToArgList(scopingArgs);
|
|
}
|
|
const scope = scopingArgs.join(' ');
|
|
// We can run the parser directly because we are not going to use it for any other actions,
|
|
// so construct a special options object to make the "--help" text more useful.
|
|
const scopedCommandLineParserOptions = {
|
|
// Strip the scoping args if coming from an alias command, since they are not applicable
|
|
// to the alias command itself
|
|
toolFilename: `${toolCommandForLogging}${scope && !aliasAction ? ` ${scope} --` : ''}`,
|
|
toolDescription: aliasDocumentation !== null && aliasDocumentation !== void 0 ? aliasDocumentation : actionOptions.documentation,
|
|
toolEpilog: `For more information on available unscoped parameters, use "${toolCommand} --help"`,
|
|
enableTabCompletionAction: false
|
|
};
|
|
super(scopedCommandLineParserOptions);
|
|
this._canExecute = false;
|
|
this._internalOptions = options;
|
|
this._internalOptions.onDefineScopedParameters(this);
|
|
}
|
|
_registerDefinedParameters(state) {
|
|
// Since we are in a separate parser, we need to register the parameters using the state
|
|
// from the parent parser.
|
|
super._registerDefinedParameters(this._internalOptions.registerDefinedParametersState);
|
|
}
|
|
async onExecute() {
|
|
// override
|
|
// Only set if we made it this far, which may not be the case if an error occurred or
|
|
// if '--help' was specified.
|
|
this._canExecute = true;
|
|
}
|
|
}
|
|
/**
|
|
* Represents a sub-command that is part of the CommandLineParser command-line.
|
|
* Applications should create subclasses of ScopedCommandLineAction corresponding to
|
|
* each action that they want to expose.
|
|
*
|
|
* The action name should be comprised of lower case words separated by hyphens
|
|
* or colons. The name should include an English verb (e.g. "deploy"). Use a
|
|
* hyphen to separate words (e.g. "upload-docs"). A group of related commands
|
|
* can be prefixed with a colon (e.g. "docs:generate", "docs:deploy",
|
|
* "docs:serve", etc).
|
|
*
|
|
* Scoped commands allow for different parameters to be specified for different
|
|
* provided scoping values. For example, the "scoped-action --scope A" command
|
|
* may allow for different scoped arguments to be specified than the "scoped-action
|
|
* --scope B" command.
|
|
*
|
|
* Scoped arguments are specified after the "--" pseudo-argument. For example,
|
|
* "scoped-action --scope A -- --scopedFoo --scopedBar".
|
|
*
|
|
* @public
|
|
*/
|
|
class ScopedCommandLineAction extends CommandLineAction_1.CommandLineAction {
|
|
constructor(options) {
|
|
super(options);
|
|
this._options = options;
|
|
this._scopingParameters = [];
|
|
}
|
|
/**
|
|
* {@inheritDoc CommandLineParameterProvider.parameters}
|
|
*/
|
|
get parameters() {
|
|
if (this._scopedCommandLineParser) {
|
|
return [...super.parameters, ...this._scopedCommandLineParser.parameters];
|
|
}
|
|
else {
|
|
return super.parameters;
|
|
}
|
|
}
|
|
/**
|
|
* {@inheritdoc CommandLineAction._processParsedData}
|
|
* @internal
|
|
*/
|
|
_processParsedData(parserOptions, data) {
|
|
// override
|
|
super._processParsedData(parserOptions, data);
|
|
// This should never happen because the super method should throw if parameters haven't been registered,
|
|
// but guard against this just in-case.
|
|
if (this._subparserState === undefined) {
|
|
throw new Error('Parameters have not been registered');
|
|
}
|
|
this._unscopedParserOptions = parserOptions;
|
|
// Generate the scoped parser using the parent parser information. We can only create this after we
|
|
// have parsed the data, since the parameter values are used during construction.
|
|
this._scopedCommandLineParser = new InternalScopedCommandLineParser(Object.assign(Object.assign({}, parserOptions), { actionOptions: this._options, aliasAction: data.aliasAction, aliasDocumentation: data.aliasDocumentation, unscopedActionParameters: this.parameters, registerDefinedParametersState: this._subparserState, onDefineScopedParameters: this.onDefineScopedParameters.bind(this) }));
|
|
}
|
|
/**
|
|
* {@inheritdoc CommandLineAction._execute}
|
|
* @internal
|
|
*/
|
|
async _execute() {
|
|
// override
|
|
if (!this._unscopedParserOptions || !this._scopedCommandLineParser) {
|
|
throw new Error('The CommandLineAction parameters must be processed before execution.');
|
|
}
|
|
if (!this.remainder) {
|
|
throw new Error('CommandLineAction.onDefineParameters must be called before execution.');
|
|
}
|
|
// The '--' argument is required to separate the action parameters from the scoped parameters,
|
|
// so it needs to be trimmed. If remainder values are provided but no '--' is found, then throw.
|
|
const scopedArgs = [];
|
|
if (this.remainder.values.length) {
|
|
if (this.remainder.values[0] !== '--') {
|
|
throw new CommandLineParserExitError_1.CommandLineParserExitError(
|
|
// argparse sets exit code 2 for invalid arguments
|
|
2,
|
|
// model the message off of the built-in "unrecognized arguments" message
|
|
`${this.renderUsageText()}\n${this._unscopedParserOptions.toolFilename} ${this.actionName}: ` +
|
|
`error: Unrecognized arguments: ${this.remainder.values[0]}.\n`);
|
|
}
|
|
for (const scopedArg of this.remainder.values.slice(1)) {
|
|
scopedArgs.push(scopedArg);
|
|
}
|
|
}
|
|
// Call the scoped parser using only the scoped args to handle parsing
|
|
await this._scopedCommandLineParser.executeWithoutErrorHandling(scopedArgs);
|
|
// Only call execute if the parser reached the execute stage. This may not be true if
|
|
// the parser exited early due to a specified '--help' parameter.
|
|
if (this._scopedCommandLineParser.canExecute) {
|
|
await super._execute();
|
|
}
|
|
return;
|
|
}
|
|
/** @internal */
|
|
_registerDefinedParameters(state) {
|
|
super._registerDefinedParameters(state);
|
|
const { parentParameterNames } = state;
|
|
const updatedParentParameterNames = new Set([
|
|
...parentParameterNames,
|
|
...this._registeredParameterParserKeysByName.keys()
|
|
]);
|
|
this._subparserState = Object.assign(Object.assign({}, state), { parentParameterNames: updatedParentParameterNames });
|
|
}
|
|
/**
|
|
* {@inheritdoc CommandLineParameterProvider.onDefineParameters}
|
|
*/
|
|
onDefineParameters() {
|
|
var _a;
|
|
(_a = this.onDefineUnscopedParameters) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
if (!this._scopingParameters.length) {
|
|
throw new Error('No scoping parameters defined. At least one scoping parameter must be defined. ' +
|
|
'Scoping parameters are defined by setting the parameterGroupName to ' +
|
|
'ScopedCommandLineAction.ScopingParameterGroupName.');
|
|
}
|
|
if (this.remainder) {
|
|
throw new Error('Unscoped remainder parameters are not allowed. Remainder parameters can only be defined on ' +
|
|
'the scoped parameter provider in onDefineScopedParameters().');
|
|
}
|
|
// Consume the remainder of the command-line, which will later be passed the scoped parser.
|
|
// This will also prevent developers from calling this.defineCommandLineRemainder(...) since
|
|
// we will have already defined it.
|
|
this.defineCommandLineRemainder({
|
|
description: 'Scoped parameters. Must be prefixed with "--", ex. "-- --scopedParameter ' +
|
|
'foo --scopedFlag". For more information on available scoped parameters, use "-- --help".'
|
|
});
|
|
}
|
|
/**
|
|
* Retrieves the scoped CommandLineParser, which is populated after the ScopedCommandLineAction is executed.
|
|
* @internal
|
|
*/
|
|
_getScopedCommandLineParser() {
|
|
if (!this._scopedCommandLineParser) {
|
|
throw new Error('The scoped CommandLineParser is only populated after the action is executed.');
|
|
}
|
|
return this._scopedCommandLineParser;
|
|
}
|
|
/** @internal */
|
|
_defineParameter(parameter) {
|
|
super._defineParameter(parameter);
|
|
if (parameter.parameterGroup === ScopedCommandLineAction.ScopingParameterGroup) {
|
|
this._scopingParameters.push(parameter);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* The required group name to apply to all scoping parameters. At least one parameter
|
|
* must be defined with this group name.
|
|
*/
|
|
ScopedCommandLineAction.ScopingParameterGroup = Constants_1.SCOPING_PARAMETER_GROUP;
|
|
exports.ScopedCommandLineAction = ScopedCommandLineAction;
|
|
//# sourceMappingURL=ScopedCommandLineAction.js.map
|