@@ -847,7 +847,7 @@ class WebpackCLI {
847847 options . entry = [ ...entries , ...( options . entry || [ ] ) ] ;
848848 }
849849
850- await this . buildCommand ( options , isWatchCommandUsed ) ;
850+ await this . runWebpack ( options , isWatchCommandUsed ) ;
851851 } ,
852852 ) ;
853853 } else if ( isCommand ( commandName , helpCommandOptions ) ) {
@@ -1521,107 +1521,124 @@ class WebpackCLI {
15211521 await this . program . parseAsync ( args , parseOptions ) ;
15221522 }
15231523
1524- async resolveConfig ( options ) {
1525- const loadConfig = async ( configPath ) => {
1526- const { interpret } = this . utils ;
1527- const ext = path . extname ( configPath ) ;
1528- const interpreted = Object . keys ( interpret . jsVariants ) . find (
1529- ( variant ) => variant === ext ,
1530- ) ;
1524+ async loadConfig ( configPath , argv = { } ) {
1525+ const { interpret } = this . utils ;
1526+ const ext = path . extname ( configPath ) ;
1527+ const interpreted = Object . keys ( interpret . jsVariants ) . find ( ( variant ) => variant === ext ) ;
15311528
1532- if ( interpreted ) {
1533- const { rechoir } = this . utils ;
1534-
1535- try {
1536- rechoir . prepare ( interpret . extensions , configPath ) ;
1537- } catch ( error ) {
1538- if ( error . failures ) {
1539- this . logger . error ( `Unable load '${ configPath } '` ) ;
1540- this . logger . error ( error . message ) ;
1541-
1542- error . failures . forEach ( ( failure ) => {
1543- this . logger . error ( failure . error . message ) ;
1544- } ) ;
1545- this . logger . error ( "Please install one of them" ) ;
1546- process . exit ( 2 ) ;
1547- }
1548-
1549- this . logger . error ( error ) ;
1550- process . exit ( 2 ) ;
1551- }
1552- }
1553-
1554- let options ;
1529+ if ( interpreted ) {
1530+ const { rechoir } = this . utils ;
15551531
15561532 try {
1557- options = await this . tryRequireThenImport ( configPath , false ) ;
1533+ rechoir . prepare ( interpret . extensions , configPath ) ;
15581534 } catch ( error ) {
1559- this . logger . error ( `Failed to load '${ configPath } ' config` ) ;
1560-
1561- if ( this . isValidationError ( error ) ) {
1535+ if ( error . failures ) {
1536+ this . logger . error ( `Unable load '${ configPath } '` ) ;
15621537 this . logger . error ( error . message ) ;
1563- } else {
1564- this . logger . error ( error ) ;
1538+
1539+ error . failures . forEach ( ( failure ) => {
1540+ this . logger . error ( failure . error . message ) ;
1541+ } ) ;
1542+ this . logger . error ( "Please install one of them" ) ;
1543+ process . exit ( 2 ) ;
15651544 }
15661545
1546+ this . logger . error ( error ) ;
15671547 process . exit ( 2 ) ;
15681548 }
1549+ }
15691550
1570- return { options, path : configPath } ;
1571- } ;
1551+ let options ;
15721552
1573- const evaluateConfig = async ( loadedConfig , argv ) => {
1574- const isMultiCompiler = Array . isArray ( loadedConfig . options ) ;
1575- const config = isMultiCompiler ? loadedConfig . options : [ loadedConfig . options ] ;
1553+ try {
1554+ options = await this . tryRequireThenImport ( configPath , false ) ;
1555+ } catch ( error ) {
1556+ this . logger . error ( `Failed to load '${ configPath } ' config` ) ;
1557+
1558+ if ( this . isValidationError ( error ) ) {
1559+ this . logger . error ( error . message ) ;
1560+ } else {
1561+ this . logger . error ( error ) ;
1562+ }
15761563
1577- const evaluatedConfig = await Promise . all (
1578- config . map ( async ( rawConfig ) => {
1579- if ( typeof rawConfig . then === "function" ) {
1580- rawConfig = await rawConfig ;
1564+ process . exit ( 2 ) ;
1565+ }
1566+
1567+ if ( Array . isArray ( options ) ) {
1568+ await Promise . all (
1569+ options . map ( async ( _ , i ) => {
1570+ if ( typeof options [ i ] . then === "function" ) {
1571+ options [ i ] = await options [ i ] ;
15811572 }
15821573
15831574 // `Promise` may return `Function`
1584- if ( typeof rawConfig === "function" ) {
1575+ if ( typeof options [ i ] === "function" ) {
15851576 // when config is a function, pass the env from args to the config function
1586- rawConfig = await rawConfig ( argv . env , argv ) ;
1577+ options [ i ] = await options [ i ] ( argv . env , argv ) ;
15871578 }
1588-
1589- return rawConfig ;
15901579 } ) ,
15911580 ) ;
1581+ } else {
1582+ if ( typeof options . then === "function" ) {
1583+ options = await options ;
1584+ }
15921585
1593- loadedConfig . options = isMultiCompiler ? evaluatedConfig : evaluatedConfig [ 0 ] ;
1586+ // `Promise` may return `Function`
1587+ if ( typeof options === "function" ) {
1588+ // when config is a function, pass the env from args to the config function
1589+ options = await options ( argv . env , argv ) ;
1590+ }
1591+ }
15941592
1595- const isObject = ( value ) => typeof value === "object" && value !== null ;
1593+ const isObject = ( value ) => typeof value === "object" && value !== null ;
15961594
1597- if ( ! isObject ( loadedConfig . options ) && ! Array . isArray ( loadedConfig . options ) ) {
1598- this . logger . error ( `Invalid configuration in '${ loadedConfig . path } '` ) ;
1599- process . exit ( 2 ) ;
1600- }
1595+ if ( ! isObject ( options ) && ! Array . isArray ( options ) ) {
1596+ this . logger . error ( `Invalid configuration in '${ configPath } '` ) ;
16011597
1602- return loadedConfig ;
1603- } ;
1598+ process . exit ( 2 ) ;
1599+ }
16041600
1601+ return { options, path : configPath } ;
1602+ }
1603+
1604+ async resolveConfig ( options ) {
16051605 const config = { options : { } , path : new WeakMap ( ) } ;
16061606
16071607 if ( options . config && options . config . length > 0 ) {
1608- const evaluatedConfigs = await Promise . all (
1609- options . config . map ( async ( value ) =>
1610- evaluateConfig ( await loadConfig ( path . resolve ( value ) ) , options . argv || { } ) ,
1608+ const loadedConfigs = await Promise . all (
1609+ options . config . map ( ( configPath ) =>
1610+ this . loadConfig ( path . resolve ( configPath ) , options . argv ) ,
16111611 ) ,
16121612 ) ;
16131613
16141614 config . options = [ ] ;
16151615
1616- evaluatedConfigs . forEach ( ( evaluatedConfig ) => {
1617- if ( Array . isArray ( evaluatedConfig . options ) ) {
1618- evaluatedConfig . options . forEach ( ( options ) => {
1619- config . options . push ( options ) ;
1620- config . path . set ( options , evaluatedConfig . path ) ;
1616+ loadedConfigs . forEach ( ( loadedConfig ) => {
1617+ const isArray = Array . isArray ( loadedConfig . options ) ;
1618+
1619+ // TODO we should run webpack multiple times when the `--config` options have multiple values with `--merge`, need to solve for the next major release
1620+ if ( config . options . length === 0 ) {
1621+ config . options = loadedConfig . options ;
1622+ } else {
1623+ if ( ! Array . isArray ( config . options ) ) {
1624+ config . options = [ config . options ] ;
1625+ }
1626+
1627+ if ( isArray ) {
1628+ loadedConfig . options . forEach ( ( item ) => {
1629+ config . options . push ( item ) ;
1630+ } ) ;
1631+ } else {
1632+ config . options . push ( loadedConfig . options ) ;
1633+ }
1634+ }
1635+
1636+ if ( isArray ) {
1637+ loadedConfig . options . forEach ( ( options ) => {
1638+ config . path . set ( options , loadedConfig . path ) ;
16211639 } ) ;
16221640 } else {
1623- config . options . push ( evaluatedConfig . options ) ;
1624- config . path . set ( evaluatedConfig . options , evaluatedConfig . path ) ;
1641+ config . path . set ( loadedConfig . options , loadedConfig . path ) ;
16251642 }
16261643 } ) ;
16271644
@@ -1657,23 +1674,25 @@ class WebpackCLI {
16571674 }
16581675
16591676 if ( foundDefaultConfigFile ) {
1660- const loadedConfig = await loadConfig ( foundDefaultConfigFile . path ) ;
1661- const evaluatedConfig = await evaluateConfig ( loadedConfig , options . argv || { } ) ;
1677+ const loadedConfig = await this . loadConfig (
1678+ foundDefaultConfigFile . path ,
1679+ options . argv ,
1680+ ) ;
16621681
1663- config . options = evaluatedConfig . options ;
1682+ config . options = loadedConfig . options ;
16641683
16651684 if ( Array . isArray ( config . options ) ) {
1666- config . options . forEach ( ( options ) => {
1667- config . path . set ( options , evaluatedConfig . path ) ;
1685+ config . options . forEach ( ( item ) => {
1686+ config . path . set ( item , loadedConfig . path ) ;
16681687 } ) ;
16691688 } else {
1670- config . path . set ( evaluatedConfig . options , evaluatedConfig . path ) ;
1689+ config . path . set ( loadedConfig . options , loadedConfig . path ) ;
16711690 }
16721691 }
16731692 }
16741693
16751694 if ( options . configName ) {
1676- const notfoundConfigNames = [ ] ;
1695+ const notFoundConfigNames = [ ] ;
16771696
16781697 config . options = options . configName . map ( ( configName ) => {
16791698 let found ;
@@ -1685,15 +1704,15 @@ class WebpackCLI {
16851704 }
16861705
16871706 if ( ! found ) {
1688- notfoundConfigNames . push ( configName ) ;
1707+ notFoundConfigNames . push ( configName ) ;
16891708 }
16901709
16911710 return found ;
16921711 } ) ;
16931712
1694- if ( notfoundConfigNames . length > 0 ) {
1713+ if ( notFoundConfigNames . length > 0 ) {
16951714 this . logger . error (
1696- notfoundConfigNames
1715+ notFoundConfigNames
16971716 . map (
16981717 ( configName ) =>
16991718 `Configuration with the name "${ configName } " was not found.` ,
@@ -1731,6 +1750,18 @@ class WebpackCLI {
17311750 return config ;
17321751 }
17331752
1753+ runFunctionOnOptions ( options , fn ) {
1754+ if ( Array . isArray ( options ) ) {
1755+ for ( let item of options ) {
1756+ item = fn ( item ) ;
1757+ }
1758+ } else {
1759+ options = fn ( options ) ;
1760+ }
1761+
1762+ return options ;
1763+ }
1764+
17341765 // TODO refactor
17351766 async applyOptions ( config , options ) {
17361767 if ( options . analyze ) {
@@ -1786,9 +1817,7 @@ class WebpackCLI {
17861817 return configOptions ;
17871818 } ;
17881819
1789- config . options = Array . isArray ( config . options )
1790- ? config . options . map ( ( options ) => outputHints ( options ) )
1791- : outputHints ( config . options ) ;
1820+ this . runFunctionOnOptions ( config . options , outputHints ) ;
17921821
17931822 if ( this . webpack . cli ) {
17941823 const processArguments = ( configOptions ) => {
@@ -1850,9 +1879,7 @@ class WebpackCLI {
18501879 return configOptions ;
18511880 } ;
18521881
1853- config . options = Array . isArray ( config . options )
1854- ? config . options . map ( ( options ) => processArguments ( options ) )
1855- : processArguments ( config . options ) ;
1882+ this . runFunctionOnOptions ( config . options , processArguments ) ;
18561883
18571884 const setupDefaultOptions = ( configOptions ) => {
18581885 // No need to run for webpack@4
@@ -1881,9 +1908,7 @@ class WebpackCLI {
18811908 return configOptions ;
18821909 } ;
18831910
1884- config . options = Array . isArray ( config . options )
1885- ? config . options . map ( ( options ) => setupDefaultOptions ( options ) )
1886- : setupDefaultOptions ( config . options ) ;
1911+ this . runFunctionOnOptions ( config . options , setupDefaultOptions ) ;
18871912 }
18881913
18891914 // Logic for webpack@4
@@ -1943,12 +1968,10 @@ class WebpackCLI {
19431968 return configOptions ;
19441969 } ;
19451970
1946- config . options = Array . isArray ( config . options )
1947- ? config . options . map ( ( options ) => processLegacyArguments ( options ) )
1948- : processLegacyArguments ( config . options ) ;
1971+ this . runFunctionOnOptions ( config . options , processLegacyArguments ) ;
19491972
19501973 // Apply `stats` and `stats.colors` options
1951- const applyStatsColors = ( configOptions ) => {
1974+ const applyStatsOption = ( configOptions ) => {
19521975 // TODO remove after drop webpack@4
19531976 const statsForWebpack4 = this . webpack . Stats && this . webpack . Stats . presetToOptions ;
19541977
@@ -2005,24 +2028,22 @@ class WebpackCLI {
20052028 return configOptions ;
20062029 } ;
20072030
2008- config . options = Array . isArray ( config . options )
2009- ? config . options . map ( ( options ) => applyStatsColors ( options ) )
2010- : applyStatsColors ( config . options ) ;
2031+ this . runFunctionOnOptions ( config . options , applyStatsOption ) ;
20112032
20122033 return config ;
20132034 }
20142035
20152036 async applyCLIPlugin ( config , cliOptions ) {
20162037 const CLIPlugin = await this . tryRequireThenImport ( "./plugins/CLIPlugin" ) ;
20172038
2018- const addCLIPlugin = ( configOptions ) => {
2019- if ( ! configOptions . plugins ) {
2020- configOptions . plugins = [ ] ;
2039+ const addCLIPlugin = ( options ) => {
2040+ if ( ! options . plugins ) {
2041+ options . plugins = [ ] ;
20212042 }
20222043
2023- configOptions . plugins . unshift (
2044+ options . plugins . unshift (
20242045 new CLIPlugin ( {
2025- configPath : config . path . get ( configOptions ) ,
2046+ configPath : config . path . get ( options ) ,
20262047 helpfulOutput : ! cliOptions . json ,
20272048 hot : cliOptions . hot ,
20282049 progress : cliOptions . progress ,
@@ -2031,12 +2052,10 @@ class WebpackCLI {
20312052 } ) ,
20322053 ) ;
20332054
2034- return configOptions ;
2055+ return options ;
20352056 } ;
20362057
2037- config . options = Array . isArray ( config . options )
2038- ? config . options . map ( ( options ) => addCLIPlugin ( options ) )
2039- : addCLIPlugin ( config . options ) ;
2058+ this . runFunctionOnOptions ( config . options , addCLIPlugin ) ;
20402059
20412060 return config ;
20422061 }
@@ -2102,7 +2121,7 @@ class WebpackCLI {
21022121 return compiler ;
21032122 }
21042123
2105- async buildCommand ( options , isWatchCommand ) {
2124+ async runWebpack ( options , isWatchCommand ) {
21062125 // eslint-disable-next-line prefer-const
21072126 let compiler ;
21082127 let createJsonStringifyStream ;
0 commit comments