"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