@@ -100,7 +100,6 @@ interface CommandOptions<
100100 usage ?: string ;
101101 dependencies ?: string [ ] ;
102102 pkg ?: string ;
103- external ?: boolean ;
104103 preload ?: ( ) => Promise < C > ;
105104 options ?:
106105 | CommandOption [ ]
@@ -543,13 +542,13 @@ class WebpackCLI {
543542
544543 async makeCommand < A = void , O extends CommanderArgs = CommanderArgs , C extends Context = Context > (
545544 options : CommandOptions < A , O , C > ,
546- ) : Promise < Command | undefined > {
545+ ) : Promise < Command > {
547546 const alreadyLoaded = this . program . commands . find (
548547 ( command ) => command . name ( ) === options . rawName ,
549548 ) ;
550549
551550 if ( alreadyLoaded ) {
552- return ;
551+ return alreadyLoaded as Command ;
553552 }
554553
555554 const command = this . program . command ( options . name , {
@@ -1163,9 +1162,7 @@ class WebpackCLI {
11631162 } else {
11641163 const [ name ] = options ;
11651164
1166- await this . #loadCommandByName( name ) ;
1167-
1168- const command = this . #findCommandByName( name ) ;
1165+ const command = await this . #loadCommandByName( name ) ;
11691166
11701167 if ( ! command ) {
11711168 const builtInCommandUsed = Object . values ( this . #commands) . find (
@@ -1202,9 +1199,9 @@ class WebpackCLI {
12021199 outputIncorrectUsageOfHelp ( ) ;
12031200 }
12041201
1205- await this . #loadCommandByName ( commandName ) ;
1206-
1207- const command = isGlobalOption ( optionName ) ? program : this . #findCommandByName ( commandName ) ;
1202+ const command = isGlobalOption ( optionName )
1203+ ? program
1204+ : await this . #loadCommandByName ( commandName ) ;
12081205
12091206 if ( ! command ) {
12101207 this . logger . error ( `Can't find and load command '${ commandName } '` ) ;
@@ -1916,81 +1913,58 @@ class WebpackCLI {
19161913 ) ;
19171914 }
19181915
1919- async #loadCommandByName( commandName : string , allowToInstall = false ) {
1916+ async #loadCommandByName( commandName : string ) : Promise < Command | undefined > {
19201917 if ( this . #isCommand( commandName , this . #commands. build ) ) {
1921- await this . makeCommand ( this . #commands. build ) ;
1918+ return await this . makeCommand ( this . #commands. build ) ;
19221919 } else if ( this . #isCommand( commandName , this . #commands. serve ) ) {
1923- await this . makeCommand ( this . #commands. serve ) ;
1920+ return await this . makeCommand ( this . #commands. serve ) ;
19241921 } else if ( this . #isCommand( commandName , this . #commands. watch ) ) {
1925- await this . makeCommand ( this . #commands. watch ) ;
1922+ return await this . makeCommand ( this . #commands. watch ) ;
19261923 } else if ( this . #isCommand( commandName , this . #commands. help ) ) {
19271924 // Stub for the `help` command
1928- await this . makeCommand ( this . #commands. help ) ;
1925+ return await this . makeCommand ( this . #commands. help ) ;
19291926 } else if ( this . #isCommand( commandName , this . #commands. version ) ) {
1930- await this . makeCommand ( this . #commands. version ) ;
1927+ return await this . makeCommand ( this . #commands. version ) ;
19311928 } else if ( this . #isCommand( commandName , this . #commands. info ) ) {
1932- await this . makeCommand ( this . #commands. info ) ;
1929+ return await this . makeCommand ( this . #commands. info ) ;
19331930 } else if ( this . #isCommand( commandName , this . #commands. configtest ) ) {
1934- await this . makeCommand ( this . #commands. configtest ) ;
1935- } else {
1936- const builtInExternalCommandInfo = Object . values ( this . #commands)
1937- . filter ( ( item ) => item . external )
1938- . find (
1939- ( externalBuiltInCommandInfo ) =>
1940- externalBuiltInCommandInfo . rawName === commandName ||
1941- ( Array . isArray ( externalBuiltInCommandInfo . alias )
1942- ? externalBuiltInCommandInfo . alias . includes ( commandName )
1943- : externalBuiltInCommandInfo . alias === commandName ) ,
1944- ) ;
1945-
1946- let pkg : string ;
1947-
1948- if ( builtInExternalCommandInfo && builtInExternalCommandInfo . pkg ) {
1949- ( { pkg } = builtInExternalCommandInfo ) ;
1950- } else {
1951- pkg = commandName ;
1952- }
1953-
1954- if ( pkg !== "webpack-cli" && ! ( await this . isPackageInstalled ( pkg ) ) ) {
1955- if ( ! allowToInstall ) {
1956- return ;
1957- }
1958-
1959- pkg = await this . installPackage ( pkg , {
1960- preMessage : ( ) => {
1961- this . logger . error (
1962- `For using this command you need to install: '${ this . colors . green ( pkg ) } ' package.` ,
1963- ) ;
1964- } ,
1965- } ) ;
1966- }
1967-
1968- type Instantiable <
1969- InstanceType = unknown ,
1970- ConstructorParameters extends unknown [ ] = unknown [ ] ,
1971- > = new ( ...args : ConstructorParameters ) => InstanceType ;
1972-
1973- let LoadedCommand : Instantiable < ( ) => void > ;
1931+ return await this . makeCommand ( this . #commands. configtest ) ;
1932+ }
19741933
1975- try {
1976- LoadedCommand = ( await import ( pkg ) ) . default ;
1977- } catch {
1978- // Ignore, command is not installed
1979- return ;
1980- }
1934+ const pkg : string = commandName ;
19811935
1982- let command ;
1936+ type Instantiable <
1937+ InstanceType = unknown ,
1938+ ConstructorParameters extends unknown [ ] = unknown [ ] ,
1939+ > = new ( ...args : ConstructorParameters ) => InstanceType & { apply ( cli : WebpackCLI ) : Command } ;
19831940
1984- try {
1985- command = new LoadedCommand ( ) ;
1941+ let LoadedCommand : Instantiable < ( ) => void > ;
19861942
1987- await command . apply ( this ) ;
1988- } catch ( error ) {
1943+ try {
1944+ LoadedCommand = ( await import ( pkg ) ) . default ;
1945+ } catch ( error ) {
1946+ if ( ( error as NodeJS . ErrnoException ) . code !== "ERR_MODULE_NOT_FOUND" ) {
19891947 this . logger . error ( `Unable to load '${ pkg } ' command` ) ;
19901948 this . logger . error ( error ) ;
19911949 process . exit ( 2 ) ;
19921950 }
1951+
1952+ return ;
1953+ }
1954+
1955+ let command ;
1956+ let externalCommand : Command ;
1957+
1958+ try {
1959+ command = new LoadedCommand ( ) ;
1960+ externalCommand = await command . apply ( this ) ;
1961+ } catch ( error ) {
1962+ this . logger . error ( `Unable to load '${ pkg } ' command` ) ;
1963+ this . logger . error ( error ) ;
1964+ process . exit ( 2 ) ;
19931965 }
1966+
1967+ return externalCommand ;
19941968 }
19951969
19961970 async run ( args : readonly string [ ] , parseOptions : ParseOptions ) {
@@ -2123,25 +2097,25 @@ class WebpackCLI {
21232097 process . exit ( 0 ) ;
21242098 }
21252099
2126- let commandNameToRun = operand ;
2127- let commandOperands = operands . slice ( 1 ) ;
2128-
21292100 let isKnownCommand = false ;
21302101
21312102 for ( const command of Object . values ( this . #commands) ) {
21322103 if (
2133- command . rawName === commandNameToRun ||
2104+ command . rawName === operand ||
21342105 ( Array . isArray ( command . alias )
2135- ? command . alias . includes ( commandNameToRun )
2136- : command . alias === commandNameToRun )
2106+ ? command . alias . includes ( operand )
2107+ : command . alias === operand )
21372108 ) {
21382109 isKnownCommand = true ;
21392110 break ;
21402111 }
21412112 }
21422113
2114+ let command : Command | undefined ;
2115+ let commandOperands = operands . slice ( 1 ) ;
2116+
21432117 if ( isKnownCommand ) {
2144- await this . #loadCommandByName( commandNameToRun , true ) ;
2118+ command = await this . #loadCommandByName( operand ) ;
21452119 } else {
21462120 let isEntrySyntax : boolean ;
21472121
@@ -2153,33 +2127,39 @@ class WebpackCLI {
21532127 }
21542128
21552129 if ( isEntrySyntax ) {
2156- commandNameToRun = defaultCommandNameToRun ;
21572130 commandOperands = operands ;
21582131
2159- await this . #loadCommandByName( commandNameToRun ) ;
2132+ command = await this . #loadCommandByName( defaultCommandNameToRun ) ;
21602133 } else {
2161- this . logger . error ( `Unknown command or entry '${ operand } '` ) ;
2134+ // Try to load external command
2135+ try {
2136+ command = await this . #loadCommandByName( operand ) ;
2137+ } catch {
2138+ // Nothing
2139+ }
21622140
2163- const found = Object . values ( this . #commands) . find (
2164- ( commandOptions ) => distance ( operand , commandOptions . rawName ) < 3 ,
2165- ) ;
2141+ if ( ! command ) {
2142+ this . logger . error ( `Unknown command or entry '${ operand } '` ) ;
21662143
2167- if ( found ) {
2168- this . logger . error (
2169- `Did you mean '${ found . rawName } ' (alias '${ Array . isArray ( found . alias ) ? found . alias . join ( ", " ) : found . alias } ')?` ,
2144+ const found = Object . values ( this . #commands) . find (
2145+ ( commandOptions ) => distance ( operand , commandOptions . rawName ) < 3 ,
21702146 ) ;
2171- }
21722147
2173- this . logger . error ( "Run 'webpack --help' to see available commands and options" ) ;
2174- process . exit ( 2 ) ;
2148+ if ( found ) {
2149+ this . logger . error (
2150+ `Did you mean '${ found . rawName } ' (alias '${ Array . isArray ( found . alias ) ? found . alias . join ( ", " ) : found . alias } ')?` ,
2151+ ) ;
2152+ }
2153+
2154+ this . logger . error ( "Run 'webpack --help' to see available commands and options" ) ;
2155+ process . exit ( 2 ) ;
2156+ }
21752157 }
21762158 }
21772159
2178- const command = this . #findCommandByName( commandNameToRun ) ;
2179-
21802160 if ( ! command ) {
21812161 throw new Error (
2182- `Internal error: Registered command "${ commandNameToRun } " is missing an action handler.` ,
2162+ `Internal error: Registered command "${ operand } " is missing an action handler.` ,
21832163 ) ;
21842164 }
21852165
0 commit comments