diff --git a/common/changes/@rushstack/ts-command-line/copilot-remove-abstract-modifier-commandlineparser_2026-02-03-01-07.json b/common/changes/@rushstack/ts-command-line/copilot-remove-abstract-modifier-commandlineparser_2026-02-03-01-07.json new file mode 100644 index 00000000000..21bc7e334e0 --- /dev/null +++ b/common/changes/@rushstack/ts-command-line/copilot-remove-abstract-modifier-commandlineparser_2026-02-03-01-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/ts-command-line", + "comment": "Remove `abstract` modifier from `CommandLineParser` class to allow direct instantiation. Remove redundant `_actions` array field.", + "type": "minor" + } + ], + "packageName": "@rushstack/ts-command-line" +} \ No newline at end of file diff --git a/common/reviews/api/ts-command-line.api.md b/common/reviews/api/ts-command-line.api.md index 3541c82bb73..d0288e7b7df 100644 --- a/common/reviews/api/ts-command-line.api.md +++ b/common/reviews/api/ts-command-line.api.md @@ -249,7 +249,7 @@ export abstract class CommandLineParameterWithArgument extends CommandLineParame } // @public -export abstract class CommandLineParser extends CommandLineParameterProvider { +export class CommandLineParser extends CommandLineParameterProvider { constructor(options: ICommandLineParserOptions); get actions(): ReadonlyArray; addAction(action: CommandLineAction): void; diff --git a/libraries/ts-command-line/src/providers/CommandLineParser.ts b/libraries/ts-command-line/src/providers/CommandLineParser.ts index ab717adc3a0..26e3b1037d1 100644 --- a/libraries/ts-command-line/src/providers/CommandLineParser.ts +++ b/libraries/ts-command-line/src/providers/CommandLineParser.ts @@ -48,13 +48,13 @@ export interface ICommandLineParserOptions { * The "argparse" library is a relatively advanced command-line parser with features such * as word-wrapping and intelligible error messages (that are lacking in other similar * libraries such as commander, yargs, and nomnom). Unfortunately, its ruby-inspired API - * is awkward to use. The abstract base classes CommandLineParser and CommandLineAction + * is awkward to use. The base classes CommandLineParser and CommandLineAction * provide a wrapper for "argparse" that makes defining and consuming arguments quick * and simple, and enforces that appropriate documentation is provided for each parameter. * * @public */ -export abstract class CommandLineParser extends CommandLineParameterProvider { +export class CommandLineParser extends CommandLineParameterProvider { /** * Reports which CommandLineAction was specified on the command line. * @remarks @@ -65,7 +65,6 @@ export abstract class CommandLineParser extends CommandLineParameterProvider { private readonly _argumentParser: argparse.ArgumentParser; private _actionsSubParser: argparse.SubParser | undefined; private readonly _options: ICommandLineParserOptions; - private readonly _actions: CommandLineAction[]; private readonly _actionsByName: Map; private _executed: boolean = false; private _tabCompleteActionWasAdded: boolean = false; @@ -74,7 +73,6 @@ export abstract class CommandLineParser extends CommandLineParameterProvider { super(); this._options = options; - this._actions = []; this._actionsByName = new Map(); const { toolFilename, toolDescription, toolEpilog } = options; @@ -95,7 +93,7 @@ export abstract class CommandLineParser extends CommandLineParameterProvider { * Returns the list of actions that were defined for this CommandLineParser object. */ public get actions(): ReadonlyArray { - return this._actions; + return Array.from(this._actionsByName.values()); } /** @@ -110,7 +108,6 @@ export abstract class CommandLineParser extends CommandLineParameterProvider { } action._buildParser(this._actionsSubParser); - this._actions.push(action); this._actionsByName.set(action.actionName, action); } @@ -314,7 +311,7 @@ export abstract class CommandLineParser extends CommandLineParameterProvider { ...state, parentParameterNames: updatedParentParameterNames }; - for (const action of this._actions) { + for (const action of this._actionsByName.values()) { action._registerDefinedParameters(parentState); } } diff --git a/libraries/ts-command-line/src/test/CommandLineParser.test.ts b/libraries/ts-command-line/src/test/CommandLineParser.test.ts index 4f9aec25aa2..4fffb15bafa 100644 --- a/libraries/ts-command-line/src/test/CommandLineParser.test.ts +++ b/libraries/ts-command-line/src/test/CommandLineParser.test.ts @@ -58,4 +58,22 @@ describe(CommandLineParser.name, () => { const action: TestAction = commandLineParser.selectedAction as TestAction; expect(action.done).toBe(true); }); + + it('can be instantiated directly without subclassing', () => { + // This test verifies that CommandLineParser can be instantiated directly, + // which is useful for test scenarios + const commandLineParser = new CommandLineParser({ + toolFilename: 'test-tool', + toolDescription: 'A test tool' + }); + + expect(commandLineParser).toBeDefined(); + expect(commandLineParser.actions).toEqual([]); + + // Verify that addAction can be called on the direct instance + const action = new TestAction(); + commandLineParser.addAction(action); + expect(commandLineParser.actions).toHaveLength(1); + expect(commandLineParser.actions[0]).toBe(action); + }); });