@@ -5,6 +5,7 @@ import * as path from 'node:path';
55
66import type { AsyncSeriesHook } from 'tapable' ;
77
8+ import type { CommandLineParameter } from '@rushstack/ts-command-line' ;
89import {
910 FileSystem ,
1011 type IPackageJson ,
@@ -45,6 +46,9 @@ export class GlobalScriptAction extends BaseScriptAction<IGlobalCommandConfig> {
4546 private readonly _autoinstallerName : string ;
4647 private readonly _autoinstallerFullPath : string ;
4748
49+ private _customParametersByLongName : ReadonlyMap < string , CommandLineParameter > | undefined ;
50+ private _isHandled : boolean = false ;
51+
4852 public constructor ( options : IGlobalScriptActionOptions ) {
4953 super ( options ) ;
5054 this . _shellCommand = options . shellCommand ;
@@ -93,6 +97,37 @@ export class GlobalScriptAction extends BaseScriptAction<IGlobalCommandConfig> {
9397 this . defineScriptParameters ( ) ;
9498 }
9599
100+ /**
101+ * {@inheritDoc IGlobalCommand.setHandled }
102+ */
103+ public setHandled ( ) : void {
104+ this . _isHandled = true ;
105+ }
106+
107+ /**
108+ * {@inheritDoc IGlobalCommand.getCustomParametersByLongName }
109+ */
110+ public getCustomParametersByLongName < TParameter extends CommandLineParameter > (
111+ longName : string
112+ ) : TParameter {
113+ if ( ! this . _customParametersByLongName ) {
114+ const map : Map < string , CommandLineParameter > = new Map ( ) ;
115+ for ( const [ parameterJson , parameter ] of this . customParameters ) {
116+ map . set ( parameterJson . longName , parameter ) ;
117+ }
118+ this . _customParametersByLongName = map ;
119+ }
120+
121+ const parameter : CommandLineParameter | undefined = this . _customParametersByLongName . get ( longName ) ;
122+ if ( ! parameter ) {
123+ throw new Error (
124+ `The command "${ this . actionName } " does not have a custom parameter with long name "${ longName } ".`
125+ ) ;
126+ }
127+
128+ return parameter as TParameter ;
129+ }
130+
96131 private async _prepareAutoinstallerNameAsync ( ) : Promise < void > {
97132 const autoInstaller : Autoinstaller = new Autoinstaller ( {
98133 autoinstallerName : this . _autoinstallerName ,
@@ -117,6 +152,20 @@ export class GlobalScriptAction extends BaseScriptAction<IGlobalCommandConfig> {
117152 await hookForAction . promise ( this ) ;
118153 }
119154
155+ // If a plugin hook called setHandled(), the command has been fully handled.
156+ // Skip the default shell command execution.
157+ if ( this . _isHandled ) {
158+ return ;
159+ }
160+
161+ if ( this . _shellCommand === '' ) {
162+ throw new Error (
163+ `The custom command "${ this . actionName } " has an empty "shellCommand" value, but no plugin ` +
164+ 'called setHandled() for this command. An empty "shellCommand" is intended for global ' +
165+ 'commands whose implementation is provided entirely by a Rush plugin.'
166+ ) ;
167+ }
168+
120169 const additionalPathFolders : string [ ] =
121170 this . commandLineConfiguration ?. additionalPathFolders . slice ( ) || [ ] ;
122171
0 commit comments