From b7a2288ae455d709e242b934eaa5fe38ad817bfd Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 18 Apr 2023 22:55:38 +0530 Subject: [PATCH 01/19] feat: pickup extends from cli and prepend to configs --- packages/webpack-cli/src/webpack-cli.ts | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index c5ab34cd6bb..253ce40a1fc 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -962,6 +962,17 @@ class WebpackCLI implements IWebpackCLI { ], description: "Stop webpack-cli process with non-zero exit code on warnings from webpack", }, + { + name: "extends", + alias: "e", + configs: [ + { + type: "string", + }, + ], + multiple: true, + description: "Extend webpack configuration", + }, ]; const minimumHelpFlags = [ @@ -1937,6 +1948,21 @@ class WebpackCLI implements IWebpackCLI { } } + if (options.extends && options.extends.length > 0) { + // load the config from the extends option + options.extends = Array.isArray(options.extends) ? options.extends : [options.extends]; + options.extends = await Promise.all( + options.extends.map((configPath: string) => + loadConfigByPath(path.resolve(configPath), options.argv), + ), + ); + config.options = options.extends.concat( + Array.isArray(config.options) ? config.options : [config.options], + ); + + options.merge = true; + } + if (options.merge) { const merge = await this.tryRequireThenImport("webpack-merge"); From 01b6f9001fd881dbefdf9539353c06062bb6d7bd Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Thu, 20 Apr 2023 09:17:07 +0530 Subject: [PATCH 02/19] feat: handle cli param as well as webpack config file extends property --- packages/webpack-cli/src/types.ts | 1 + packages/webpack-cli/src/webpack-cli.ts | 89 +++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/packages/webpack-cli/src/types.ts b/packages/webpack-cli/src/types.ts index b51d7e0485d..09dbf30819e 100644 --- a/packages/webpack-cli/src/types.ts +++ b/packages/webpack-cli/src/types.ts @@ -178,6 +178,7 @@ type WebpackDevServerOptions = DevServerConfig & config: string[]; configName?: string[]; disableInterpret?: boolean; + extends?: string[]; argv: Argv; }; diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 253ce40a1fc..66625f7e52c 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1950,17 +1950,94 @@ class WebpackCLI implements IWebpackCLI { if (options.extends && options.extends.length > 0) { // load the config from the extends option - options.extends = Array.isArray(options.extends) ? options.extends : [options.extends]; - options.extends = await Promise.all( + const extendedConfigs = await Promise.all( options.extends.map((configPath: string) => loadConfigByPath(path.resolve(configPath), options.argv), ), ); - config.options = options.extends.concat( - Array.isArray(config.options) ? config.options : [config.options], - ); - options.merge = true; + const merge = await this.tryRequireThenImport("webpack-merge"); + + const mergedConfig = extendedConfigs.reduce((accumulator: object, config) => { + return merge(accumulator, config.options); + }, {}); + + if (Array.isArray(config.options)) { + // merge extended config with all configs + config.options = config.options.map((options) => { + return merge(mergedConfig, options); + }); + } else { + config.options = merge(mergedConfig, config.options); + } + } else if ( + !Array.isArray(config.options) && + // @ts-expect-error TODO remove when extends property type is added to webpack + config.options.extends + ) { + const merge = await this.tryRequireThenImport("webpack-merge"); + + let extendedConfig = {}; + + // @ts-expect-error TODO remove when extends property type is added to webpack + if (Array.isArray(config.options.extends)) { + const extendedConfigs: { + options: ConfigOptions | ConfigOptions[]; + path: string; + }[] = await Promise.all( + // @ts-expect-error TODO remove when extends property type is added to webpack + config.options.extends.map((configPath: string) => + loadConfigByPath(path.resolve(configPath), options.argv), + ), + ); + + extendedConfig = extendedConfigs.reduce((accumulator: object, config) => { + return merge(accumulator, config.options); + }, {}); + } else { + // load the config from the extends option + // @ts-expect-error TODO remove when extends property type is added to webpack + extendedConfig = loadConfigByPath(path.resolve(config.options.extends), options.argv); + } + + config.options = merge(extendedConfig, config.options); + } else if ( + Array.isArray(config.options) && + // @ts-expect-error TODO remove when extends property type is added to webpack + config.options.some((options) => options.extends) + ) { + const merge = await this.tryRequireThenImport("webpack-merge"); + + for (let index = 0; index < config.options.length; index++) { + const configOptions = config.options[index]; + // @ts-expect-error TODO remove when extends property type is added to webpack + if (configOptions.extends) { + let extendedConfig = {}; + + // @ts-expect-error TODO remove when extends property type is added to webpack + if (Array.isArray(configOptions.extends)) { + const extendedConfigs: { + options: ConfigOptions | ConfigOptions[]; + path: string; + }[] = await Promise.all( + // @ts-expect-error TODO remove when extends property type is added to webpack + configOptions.extends.map((configPath: string) => + loadConfigByPath(path.resolve(configPath), options.argv), + ), + ); + + extendedConfig = extendedConfigs.reduce((accumulator: object, config) => { + return merge(accumulator, config.options); + }, {}); + } else { + // load the config from the extends option + // @ts-expect-error TODO remove when extends property type is added to webpack + extendedConfig = loadConfigByPath(path.resolve(configOptions.extends), options.argv); + } + + config.options[index] = merge(extendedConfig, configOptions); + } + } } if (options.merge) { From 476bb7a5f3d2f5dbac20faad948e1897cb13c97d Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Thu, 20 Apr 2023 09:27:29 +0530 Subject: [PATCH 03/19] refactor: simplify to two cases --- packages/webpack-cli/src/webpack-cli.ts | 45 ++++++------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 66625f7e52c..aea226561eb 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1948,6 +1948,7 @@ class WebpackCLI implements IWebpackCLI { } } + // extends param in CLI gets priority over extends in config file if (options.extends && options.extends.length > 0) { // load the config from the extends option const extendedConfigs = await Promise.all( @@ -1970,42 +1971,18 @@ class WebpackCLI implements IWebpackCLI { } else { config.options = merge(mergedConfig, config.options); } - } else if ( - !Array.isArray(config.options) && - // @ts-expect-error TODO remove when extends property type is added to webpack - config.options.extends - ) { - const merge = await this.tryRequireThenImport("webpack-merge"); - - let extendedConfig = {}; - - // @ts-expect-error TODO remove when extends property type is added to webpack - if (Array.isArray(config.options.extends)) { - const extendedConfigs: { - options: ConfigOptions | ConfigOptions[]; - path: string; - }[] = await Promise.all( - // @ts-expect-error TODO remove when extends property type is added to webpack - config.options.extends.map((configPath: string) => - loadConfigByPath(path.resolve(configPath), options.argv), - ), - ); - - extendedConfig = extendedConfigs.reduce((accumulator: object, config) => { - return merge(accumulator, config.options); - }, {}); - } else { - // load the config from the extends option + } + // if no extends option is passed, check if the config file has extends + else if ( + (!Array.isArray(config.options) && // @ts-expect-error TODO remove when extends property type is added to webpack - extendedConfig = loadConfigByPath(path.resolve(config.options.extends), options.argv); - } - - config.options = merge(extendedConfig, config.options); - } else if ( - Array.isArray(config.options) && - // @ts-expect-error TODO remove when extends property type is added to webpack - config.options.some((options) => options.extends) + config.options.extends) || + (Array.isArray(config.options) && + // @ts-expect-error TODO remove when extends property type is added to webpack + config.options.some((options) => options.extends)) ) { + config.options = Array.isArray(config.options) ? config.options : [config.options]; + const merge = await this.tryRequireThenImport("webpack-merge"); for (let index = 0; index < config.options.length; index++) { From 6e81a15bb0925940f1175e7d8026b5b53f1838cf Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Thu, 20 Apr 2023 09:35:41 +0530 Subject: [PATCH 04/19] refactor: temporarily override webpack types to add extends --- packages/webpack-cli/src/types.ts | 6 ++++-- packages/webpack-cli/src/webpack-cli.ts | 17 +++-------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/webpack-cli/src/types.ts b/packages/webpack-cli/src/types.ts index 09dbf30819e..34250a5d405 100644 --- a/packages/webpack-cli/src/types.ts +++ b/packages/webpack-cli/src/types.ts @@ -187,8 +187,10 @@ type Callback = (...args: T) => void; /** * Webpack */ - -type WebpackConfiguration = Configuration; +type WebpackConfiguration = Configuration & { + // TODO add extends to webpack types + extends?: string | string[]; +}; type ConfigOptions = PotentialPromise; type CallableOption = (env: Env | undefined, argv: Argv) => WebpackConfiguration; type WebpackCompiler = Compiler | MultiCompiler; diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index aea226561eb..00971dc74a1 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1974,12 +1974,8 @@ class WebpackCLI implements IWebpackCLI { } // if no extends option is passed, check if the config file has extends else if ( - (!Array.isArray(config.options) && - // @ts-expect-error TODO remove when extends property type is added to webpack - config.options.extends) || - (Array.isArray(config.options) && - // @ts-expect-error TODO remove when extends property type is added to webpack - config.options.some((options) => options.extends)) + (!Array.isArray(config.options) && config.options.extends) || + (Array.isArray(config.options) && config.options.some((options) => options.extends)) ) { config.options = Array.isArray(config.options) ? config.options : [config.options]; @@ -1987,17 +1983,11 @@ class WebpackCLI implements IWebpackCLI { for (let index = 0; index < config.options.length; index++) { const configOptions = config.options[index]; - // @ts-expect-error TODO remove when extends property type is added to webpack if (configOptions.extends) { let extendedConfig = {}; - // @ts-expect-error TODO remove when extends property type is added to webpack if (Array.isArray(configOptions.extends)) { - const extendedConfigs: { - options: ConfigOptions | ConfigOptions[]; - path: string; - }[] = await Promise.all( - // @ts-expect-error TODO remove when extends property type is added to webpack + const extendedConfigs = await Promise.all( configOptions.extends.map((configPath: string) => loadConfigByPath(path.resolve(configPath), options.argv), ), @@ -2008,7 +1998,6 @@ class WebpackCLI implements IWebpackCLI { }, {}); } else { // load the config from the extends option - // @ts-expect-error TODO remove when extends property type is added to webpack extendedConfig = loadConfigByPath(path.resolve(configOptions.extends), options.argv); } From 333e9d32839e8d0c7d0db9a7fbcd757a7e59b583 Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Thu, 20 Apr 2023 11:10:52 +0530 Subject: [PATCH 05/19] fix: missing promise await --- packages/webpack-cli/src/webpack-cli.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 00971dc74a1..50c252fc9bb 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1998,7 +1998,10 @@ class WebpackCLI implements IWebpackCLI { }, {}); } else { // load the config from the extends option - extendedConfig = loadConfigByPath(path.resolve(configOptions.extends), options.argv); + extendedConfig = await loadConfigByPath( + path.resolve(configOptions.extends), + options.argv, + ); } config.options[index] = merge(extendedConfig, configOptions); From 2c633df4329a7a9ea0515ba8c52ebab4aa879b93 Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Thu, 20 Apr 2023 23:00:39 +0530 Subject: [PATCH 06/19] refactor: simplify cases --- packages/webpack-cli/src/webpack-cli.ts | 66 ++++++++++++------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 50c252fc9bb..7a9728a0bf5 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1948,29 +1948,42 @@ class WebpackCLI implements IWebpackCLI { } } - // extends param in CLI gets priority over extends in config file - if (options.extends && options.extends.length > 0) { - // load the config from the extends option - const extendedConfigs = await Promise.all( - options.extends.map((configPath: string) => + const flattenConfigs = async ( + configPaths: string | string[], + ): Promise => { + configPaths = Array.isArray(configPaths) ? configPaths : [configPaths]; + + // fetch configs by path + const configs = await Promise.all( + configPaths.map((configPath: string) => loadConfigByPath(path.resolve(configPath), options.argv), ), ); - const merge = await this.tryRequireThenImport("webpack-merge"); + // extract options from configs + const extendedConfigOptions = configs.map((extendedConfig) => + Array.isArray(extendedConfig.options) ? extendedConfig.options : [extendedConfig.options], + ); - const mergedConfig = extendedConfigs.reduce((accumulator: object, config) => { - return merge(accumulator, config.options); + const merge = await this.tryRequireThenImport("webpack-merge"); + return extendedConfigOptions.reduce((accumulator: object, options) => { + return merge(accumulator, options); }, {}); + }; - if (Array.isArray(config.options)) { - // merge extended config with all configs - config.options = config.options.map((options) => { - return merge(mergedConfig, options); - }); - } else { - config.options = merge(mergedConfig, config.options); - } + // extends param in CLI gets priority over extends in config file + if (options.extends && options.extends.length > 0) { + // load the config from the extends option + const extendedConfig = await flattenConfigs(options.extends); + + const merge = await this.tryRequireThenImport("webpack-merge"); + + config.options = Array.isArray(config.options) ? config.options : [config.options]; + + // merge extended config with all configs + config.options = config.options.map((options) => { + return merge(extendedConfig, options); + }); } // if no extends option is passed, check if the config file has extends else if ( @@ -1984,27 +1997,10 @@ class WebpackCLI implements IWebpackCLI { for (let index = 0; index < config.options.length; index++) { const configOptions = config.options[index]; if (configOptions.extends) { - let extendedConfig = {}; - - if (Array.isArray(configOptions.extends)) { - const extendedConfigs = await Promise.all( - configOptions.extends.map((configPath: string) => - loadConfigByPath(path.resolve(configPath), options.argv), - ), - ); - - extendedConfig = extendedConfigs.reduce((accumulator: object, config) => { - return merge(accumulator, config.options); - }, {}); - } else { - // load the config from the extends option - extendedConfig = await loadConfigByPath( - path.resolve(configOptions.extends), - options.argv, - ); - } + const extendedConfig = await flattenConfigs(configOptions.extends); config.options[index] = merge(extendedConfig, configOptions); + delete config.options[index].extends; } } } From f7c6c7116d013a38c655de9b40fc0d804ce22043 Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Thu, 20 Apr 2023 23:11:25 +0530 Subject: [PATCH 07/19] feat: recursively merge extended configs --- packages/webpack-cli/src/webpack-cli.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 7a9728a0bf5..d1be318b18d 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1965,10 +1965,25 @@ class WebpackCLI implements IWebpackCLI { Array.isArray(extendedConfig.options) ? extendedConfig.options : [extendedConfig.options], ); + // recursively flatten the configs by looking for extends property + for (let i = 0; i < extendedConfigOptions.length; i++) { + const extendedConfigs = extendedConfigOptions[i]; + for (let j = 0; j < extendedConfigs.length; j++) { + const extendedConfig = extendedConfigs[j] as WebpackConfiguration; + if (extendedConfig.extends) { + const flattenedExtendedConfig = await flattenConfigs(extendedConfig.extends); + extendedConfigOptions[i][j] = webpackMerge(flattenedExtendedConfig, extendedConfig); + } + } + } + const merge = await this.tryRequireThenImport("webpack-merge"); - return extendedConfigOptions.reduce((accumulator: object, options) => { + const mergedConfig = extendedConfigOptions.reduce((accumulator: object, options) => { return merge(accumulator, options); }, {}); + + delete (mergedConfig as WebpackConfiguration).extends; + return mergedConfig; }; // extends param in CLI gets priority over extends in config file From 64be604ed813625470bc5a8378ceafb3c1b740b0 Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 25 Apr 2023 09:46:04 +0530 Subject: [PATCH 08/19] fix: bug where configs were not flattened --- packages/webpack-cli/src/webpack-cli.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index d1be318b18d..7cfb7089a91 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1977,8 +1977,16 @@ class WebpackCLI implements IWebpackCLI { } } + const flattenedConfigs = extendedConfigOptions.reduce( + (acc: Array, options) => { + acc.push(options[0]); + return acc; + }, + [], + ); + const merge = await this.tryRequireThenImport("webpack-merge"); - const mergedConfig = extendedConfigOptions.reduce((accumulator: object, options) => { + const mergedConfig = flattenedConfigs.reduce((accumulator: object, options) => { return merge(accumulator, options); }, {}); From 1e77f8605f229f7d808fea94e3f022ecdee7f4db Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 25 Apr 2023 09:46:28 +0530 Subject: [PATCH 09/19] test: add tests for webpack config option --- test/build/extends/extends.test.js | 41 +++++++++++++++++++ .../base1.webpack.config.js | 12 ++++++ .../base2.webpack.config.js | 8 ++++ .../extends/multi-level-extends/src/index1.js | 1 + .../multi-level-extends/webpack.config.js | 10 +++++ .../multiple-extends/base1.webpack.config.js | 8 ++++ .../multiple-extends/base2.webpack.config.js | 8 ++++ .../extends/multiple-extends/src/index2.js | 1 + .../multiple-extends/webpack.config.js | 10 +++++ .../simple-case/base.webpack.config.js | 8 ++++ test/build/extends/simple-case/src/index.js | 1 + .../extends/simple-case/webpack.config.js | 10 +++++ 12 files changed, 118 insertions(+) create mode 100644 test/build/extends/extends.test.js create mode 100644 test/build/extends/multi-level-extends/base1.webpack.config.js create mode 100644 test/build/extends/multi-level-extends/base2.webpack.config.js create mode 100644 test/build/extends/multi-level-extends/src/index1.js create mode 100644 test/build/extends/multi-level-extends/webpack.config.js create mode 100644 test/build/extends/multiple-extends/base1.webpack.config.js create mode 100644 test/build/extends/multiple-extends/base2.webpack.config.js create mode 100644 test/build/extends/multiple-extends/src/index2.js create mode 100644 test/build/extends/multiple-extends/webpack.config.js create mode 100644 test/build/extends/simple-case/base.webpack.config.js create mode 100644 test/build/extends/simple-case/src/index.js create mode 100644 test/build/extends/simple-case/webpack.config.js diff --git a/test/build/extends/extends.test.js b/test/build/extends/extends.test.js new file mode 100644 index 00000000000..6d992133e92 --- /dev/null +++ b/test/build/extends/extends.test.js @@ -0,0 +1,41 @@ +"use strict"; + +const { run } = require("../../utils/test-utils"); + +describe("extends property", () => { + it("extends a provided webpack config correctly", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/simple-case"); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("entry: './src/index.js'"); + expect(stdout).toContain("mode: 'development'"); + }); + + it("extends a provided array of webpack configs correctly", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/multiple-extends"); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base1.webpack.config.js"); + expect(stdout).toContain("base2.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("entry: './src/index2.js'"); + expect(stdout).toContain("mode: 'production'"); + }); + + it("extends a multilevel config correctly", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/multi-level-extends"); + console.log(stdout); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base1.webpack.config.js"); + expect(stdout).toContain("base2.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("entry: './src/index1.js'"); + expect(stdout).toContain("mode: 'production'"); + }); +}); diff --git a/test/build/extends/multi-level-extends/base1.webpack.config.js b/test/build/extends/multi-level-extends/base1.webpack.config.js new file mode 100644 index 00000000000..8160b9a9665 --- /dev/null +++ b/test/build/extends/multi-level-extends/base1.webpack.config.js @@ -0,0 +1,12 @@ +module.exports = () => { + console.log("base1.webpack.config.js"); + + return { + extends: ["./base2.webpack.config.js"], + mode: "production", + entry: "./src/index1.js", + output: { + filename: "bundle1.js", + }, + }; +}; diff --git a/test/build/extends/multi-level-extends/base2.webpack.config.js b/test/build/extends/multi-level-extends/base2.webpack.config.js new file mode 100644 index 00000000000..06d39e409c5 --- /dev/null +++ b/test/build/extends/multi-level-extends/base2.webpack.config.js @@ -0,0 +1,8 @@ +module.exports = () => { + console.log("base2.webpack.config.js"); + + return { + mode: "development", + entry: "./src/index2.js", + }; +}; diff --git a/test/build/extends/multi-level-extends/src/index1.js b/test/build/extends/multi-level-extends/src/index1.js new file mode 100644 index 00000000000..7e6f6d3db95 --- /dev/null +++ b/test/build/extends/multi-level-extends/src/index1.js @@ -0,0 +1 @@ +console.log("i am index1"); diff --git a/test/build/extends/multi-level-extends/webpack.config.js b/test/build/extends/multi-level-extends/webpack.config.js new file mode 100644 index 00000000000..d5ffc2fc5f9 --- /dev/null +++ b/test/build/extends/multi-level-extends/webpack.config.js @@ -0,0 +1,10 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("derived.webpack.config.js"); + + return { + extends: ["./base1.webpack.config.js"], + plugins: [new WebpackCLITestPlugin()], + }; +}; diff --git a/test/build/extends/multiple-extends/base1.webpack.config.js b/test/build/extends/multiple-extends/base1.webpack.config.js new file mode 100644 index 00000000000..d444adca471 --- /dev/null +++ b/test/build/extends/multiple-extends/base1.webpack.config.js @@ -0,0 +1,8 @@ +module.exports = () => { + console.log("base1.webpack.config.js"); + + return { + mode: "development", + entry: "./src/index.js", + }; +}; diff --git a/test/build/extends/multiple-extends/base2.webpack.config.js b/test/build/extends/multiple-extends/base2.webpack.config.js new file mode 100644 index 00000000000..1a3b3212b3c --- /dev/null +++ b/test/build/extends/multiple-extends/base2.webpack.config.js @@ -0,0 +1,8 @@ +module.exports = () => { + console.log("base2.webpack.config.js"); + + return { + mode: "production", + entry: "./src/index2.js", + }; +}; diff --git a/test/build/extends/multiple-extends/src/index2.js b/test/build/extends/multiple-extends/src/index2.js new file mode 100644 index 00000000000..8ba053a7356 --- /dev/null +++ b/test/build/extends/multiple-extends/src/index2.js @@ -0,0 +1 @@ +console.log("i am index2"); diff --git a/test/build/extends/multiple-extends/webpack.config.js b/test/build/extends/multiple-extends/webpack.config.js new file mode 100644 index 00000000000..20a16784aac --- /dev/null +++ b/test/build/extends/multiple-extends/webpack.config.js @@ -0,0 +1,10 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("derived.webpack.config.js"); + + return { + extends: ["./base1.webpack.config.js", "./base2.webpack.config.js"], + plugins: [new WebpackCLITestPlugin()], + }; +}; diff --git a/test/build/extends/simple-case/base.webpack.config.js b/test/build/extends/simple-case/base.webpack.config.js new file mode 100644 index 00000000000..c1fa0f74627 --- /dev/null +++ b/test/build/extends/simple-case/base.webpack.config.js @@ -0,0 +1,8 @@ +module.exports = () => { + console.log("base.webpack.config.js"); + + return { + mode: "development", + entry: "./src/index.js", + }; +}; diff --git a/test/build/extends/simple-case/src/index.js b/test/build/extends/simple-case/src/index.js new file mode 100644 index 00000000000..7a21c1b9d7d --- /dev/null +++ b/test/build/extends/simple-case/src/index.js @@ -0,0 +1 @@ +console.log("index.js") diff --git a/test/build/extends/simple-case/webpack.config.js b/test/build/extends/simple-case/webpack.config.js new file mode 100644 index 00000000000..2a761ad2b1a --- /dev/null +++ b/test/build/extends/simple-case/webpack.config.js @@ -0,0 +1,10 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("derived.webpack.config.js"); + + return { + extends: "./base.webpack.config.js", + plugins: [new WebpackCLITestPlugin()], + }; +}; From 46bd66aa8382ce75b5cd460bdbdd6a3eb7daa4cf Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 25 Apr 2023 09:51:39 +0530 Subject: [PATCH 10/19] test: add testcase for cli option --- .../extends-cli-option/base.webpack.config.js | 8 ++++++++ .../build/extends/extends-cli-option/src/index.js | 1 + .../extends/extends-cli-option/webpack.config.js | 9 +++++++++ test/build/extends/extends.test.js | 15 ++++++++++++++- 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test/build/extends/extends-cli-option/base.webpack.config.js create mode 100644 test/build/extends/extends-cli-option/src/index.js create mode 100644 test/build/extends/extends-cli-option/webpack.config.js diff --git a/test/build/extends/extends-cli-option/base.webpack.config.js b/test/build/extends/extends-cli-option/base.webpack.config.js new file mode 100644 index 00000000000..c1fa0f74627 --- /dev/null +++ b/test/build/extends/extends-cli-option/base.webpack.config.js @@ -0,0 +1,8 @@ +module.exports = () => { + console.log("base.webpack.config.js"); + + return { + mode: "development", + entry: "./src/index.js", + }; +}; diff --git a/test/build/extends/extends-cli-option/src/index.js b/test/build/extends/extends-cli-option/src/index.js new file mode 100644 index 00000000000..26d7e0297e4 --- /dev/null +++ b/test/build/extends/extends-cli-option/src/index.js @@ -0,0 +1 @@ +console.log("i am index.js") diff --git a/test/build/extends/extends-cli-option/webpack.config.js b/test/build/extends/extends-cli-option/webpack.config.js new file mode 100644 index 00000000000..9fe563fec36 --- /dev/null +++ b/test/build/extends/extends-cli-option/webpack.config.js @@ -0,0 +1,9 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("derived.webpack.config.js"); + + return { + plugins: [new WebpackCLITestPlugin()], + }; +}; diff --git a/test/build/extends/extends.test.js b/test/build/extends/extends.test.js index 6d992133e92..5901458c8c8 100644 --- a/test/build/extends/extends.test.js +++ b/test/build/extends/extends.test.js @@ -28,7 +28,6 @@ describe("extends property", () => { it("extends a multilevel config correctly", async () => { const { exitCode, stderr, stdout } = await run(__dirname + "/multi-level-extends"); - console.log(stdout); expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); @@ -38,4 +37,18 @@ describe("extends property", () => { expect(stdout).toContain("entry: './src/index1.js'"); expect(stdout).toContain("mode: 'production'"); }); + + it("extends a provided webpack config passed in the cli correctly", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/extends-cli-option", [ + "--extends", + "./base.webpack.config.js", + ]); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("entry: './src/index.js'"); + expect(stdout).toContain("mode: 'development'"); + }); }); From ff6c569979bcbf20df05b9a5989891b8ac365976 Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 25 Apr 2023 19:11:39 +0530 Subject: [PATCH 11/19] fix: add missing help level property --- packages/webpack-cli/src/webpack-cli.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 1f26088cb74..7e9b1b18ee3 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -985,6 +985,7 @@ class WebpackCLI implements IWebpackCLI { ], multiple: true, description: "Extend webpack configuration", + helpLevel: "minimum", }, ]; From 1c044cc7a3e66a8ee8c5a46627d3ba42e65f695f Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 25 Apr 2023 20:42:54 +0530 Subject: [PATCH 12/19] test: update snapshots --- .../help.test.js.snap.devServer4.webpack5 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 b/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 index 6bfca3640fc..d72de05f8db 100644 --- a/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 +++ b/test/help/__snapshots__/help.test.js.snap.devServer4.webpack5 @@ -105,6 +105,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -165,6 +166,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -225,6 +227,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -284,6 +287,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -331,6 +335,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -378,6 +383,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -425,6 +431,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -472,6 +479,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -519,6 +527,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1500,6 +1509,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1545,6 +1555,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1590,6 +1601,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1635,6 +1647,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1680,6 +1693,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1725,6 +1739,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1771,6 +1786,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -1831,6 +1847,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -2081,6 +2098,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. @@ -2139,6 +2157,7 @@ Options: --progress [value] Print compilation progress during build. -j, --json [value] Prints result as JSON or store it in a file. --fail-on-warnings Stop webpack-cli process with non-zero exit code on warnings from webpack + -e, --extends Extend webpack configuration -d, --devtool A developer tool to enhance debugging (false | eval | [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map). --no-devtool Negative 'devtool' option. --entry A module that is loaded upon startup. Only the last one is exported. From 146d10780114ee105a380138ea870e4171c763a4 Mon Sep 17 00:00:00 2001 From: Burhanuddin Udaipurwala Date: Tue, 25 Apr 2023 21:03:30 +0530 Subject: [PATCH 13/19] test: increase coverage --- .../extends-cli-option/base.webpack.config.js | 1 + test/build/extends/extends.test.js | 22 +++++++++++++++---- .../base1.webpack.config.js | 1 + .../base2.webpack.config.js | 1 + .../multiple-configs/base.webpack.config.js | 15 +++++++++++++ .../extends/multiple-configs/src/index1.js | 1 + .../extends/multiple-configs/src/index2.js | 1 + .../multiple-configs/webpack.config.js | 16 ++++++++++++++ .../multiple-extends/base1.webpack.config.js | 1 + .../multiple-extends/base2.webpack.config.js | 1 + .../simple-case/base.webpack.config.js | 1 + 11 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test/build/extends/multiple-configs/base.webpack.config.js create mode 100644 test/build/extends/multiple-configs/src/index1.js create mode 100644 test/build/extends/multiple-configs/src/index2.js create mode 100644 test/build/extends/multiple-configs/webpack.config.js diff --git a/test/build/extends/extends-cli-option/base.webpack.config.js b/test/build/extends/extends-cli-option/base.webpack.config.js index c1fa0f74627..db299e40fa5 100644 --- a/test/build/extends/extends-cli-option/base.webpack.config.js +++ b/test/build/extends/extends-cli-option/base.webpack.config.js @@ -2,6 +2,7 @@ module.exports = () => { console.log("base.webpack.config.js"); return { + name: "base_config", mode: "development", entry: "./src/index.js", }; diff --git a/test/build/extends/extends.test.js b/test/build/extends/extends.test.js index 5901458c8c8..23be093ffe0 100644 --- a/test/build/extends/extends.test.js +++ b/test/build/extends/extends.test.js @@ -10,7 +10,7 @@ describe("extends property", () => { expect(stderr).toBeFalsy(); expect(stdout).toContain("base.webpack.config.js"); expect(stdout).toContain("derived.webpack.config.js"); - expect(stdout).toContain("entry: './src/index.js'"); + expect(stdout).toContain("name: 'base_config'"); expect(stdout).toContain("mode: 'development'"); }); @@ -22,7 +22,7 @@ describe("extends property", () => { expect(stdout).toContain("base1.webpack.config.js"); expect(stdout).toContain("base2.webpack.config.js"); expect(stdout).toContain("derived.webpack.config.js"); - expect(stdout).toContain("entry: './src/index2.js'"); + expect(stdout).toContain("name: 'base_config2'"); expect(stdout).toContain("mode: 'production'"); }); @@ -34,7 +34,7 @@ describe("extends property", () => { expect(stdout).toContain("base1.webpack.config.js"); expect(stdout).toContain("base2.webpack.config.js"); expect(stdout).toContain("derived.webpack.config.js"); - expect(stdout).toContain("entry: './src/index1.js'"); + expect(stdout).toContain("name: 'base_config1'"); expect(stdout).toContain("mode: 'production'"); }); @@ -48,7 +48,21 @@ describe("extends property", () => { expect(stderr).toBeFalsy(); expect(stdout).toContain("base.webpack.config.js"); expect(stdout).toContain("derived.webpack.config.js"); - expect(stdout).toContain("entry: './src/index.js'"); + expect(stdout).toContain("name: 'base_config'"); expect(stdout).toContain("mode: 'development'"); }); + + it("extends a provided webpack config for multiple configs correctly", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/multiple-configs"); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("name: 'derived_config1'"); + expect(stdout).toContain("name: 'derived_config2'"); + expect(stdout).not.toContain("name: 'base_config'"); + expect(stdout).toContain("mode: 'development'"); + expect(stdout).toContain("topLevelAwait: true"); + }); }); diff --git a/test/build/extends/multi-level-extends/base1.webpack.config.js b/test/build/extends/multi-level-extends/base1.webpack.config.js index 8160b9a9665..d9535e8ab71 100644 --- a/test/build/extends/multi-level-extends/base1.webpack.config.js +++ b/test/build/extends/multi-level-extends/base1.webpack.config.js @@ -2,6 +2,7 @@ module.exports = () => { console.log("base1.webpack.config.js"); return { + name: "base_config1", extends: ["./base2.webpack.config.js"], mode: "production", entry: "./src/index1.js", diff --git a/test/build/extends/multi-level-extends/base2.webpack.config.js b/test/build/extends/multi-level-extends/base2.webpack.config.js index 06d39e409c5..3e494c9e813 100644 --- a/test/build/extends/multi-level-extends/base2.webpack.config.js +++ b/test/build/extends/multi-level-extends/base2.webpack.config.js @@ -2,6 +2,7 @@ module.exports = () => { console.log("base2.webpack.config.js"); return { + name: "base_config2", mode: "development", entry: "./src/index2.js", }; diff --git a/test/build/extends/multiple-configs/base.webpack.config.js b/test/build/extends/multiple-configs/base.webpack.config.js new file mode 100644 index 00000000000..f10862022b4 --- /dev/null +++ b/test/build/extends/multiple-configs/base.webpack.config.js @@ -0,0 +1,15 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("base.webpack.config.js"); + + return { + name: "base_config", + mode: "development", + plugins: [new WebpackCLITestPlugin()], + + experiments: { + topLevelAwait: true, + }, + }; +}; diff --git a/test/build/extends/multiple-configs/src/index1.js b/test/build/extends/multiple-configs/src/index1.js new file mode 100644 index 00000000000..9fc41fb4f1f --- /dev/null +++ b/test/build/extends/multiple-configs/src/index1.js @@ -0,0 +1 @@ +console.log("i am index1.js"); diff --git a/test/build/extends/multiple-configs/src/index2.js b/test/build/extends/multiple-configs/src/index2.js new file mode 100644 index 00000000000..7fa5b7a517e --- /dev/null +++ b/test/build/extends/multiple-configs/src/index2.js @@ -0,0 +1 @@ +console.log("i am index2.js"); diff --git a/test/build/extends/multiple-configs/webpack.config.js b/test/build/extends/multiple-configs/webpack.config.js new file mode 100644 index 00000000000..b2db2f158da --- /dev/null +++ b/test/build/extends/multiple-configs/webpack.config.js @@ -0,0 +1,16 @@ +module.exports = () => { + console.log("derived.webpack.config.js"); + + return [ + { + name: "derived_config1", + extends: "./base.webpack.config.js", + entry: "./src/index1.js", + }, + { + name: "derived_config2", + extends: "./base.webpack.config.js", + entry: "./src/index2.js", + }, + ]; +}; diff --git a/test/build/extends/multiple-extends/base1.webpack.config.js b/test/build/extends/multiple-extends/base1.webpack.config.js index d444adca471..7fb6424a698 100644 --- a/test/build/extends/multiple-extends/base1.webpack.config.js +++ b/test/build/extends/multiple-extends/base1.webpack.config.js @@ -2,6 +2,7 @@ module.exports = () => { console.log("base1.webpack.config.js"); return { + name: "base_config1", mode: "development", entry: "./src/index.js", }; diff --git a/test/build/extends/multiple-extends/base2.webpack.config.js b/test/build/extends/multiple-extends/base2.webpack.config.js index 1a3b3212b3c..2dc58a5a6ea 100644 --- a/test/build/extends/multiple-extends/base2.webpack.config.js +++ b/test/build/extends/multiple-extends/base2.webpack.config.js @@ -2,6 +2,7 @@ module.exports = () => { console.log("base2.webpack.config.js"); return { + name: "base_config2", mode: "production", entry: "./src/index2.js", }; diff --git a/test/build/extends/simple-case/base.webpack.config.js b/test/build/extends/simple-case/base.webpack.config.js index c1fa0f74627..db299e40fa5 100644 --- a/test/build/extends/simple-case/base.webpack.config.js +++ b/test/build/extends/simple-case/base.webpack.config.js @@ -2,6 +2,7 @@ module.exports = () => { console.log("base.webpack.config.js"); return { + name: "base_config", mode: "development", entry: "./src/index.js", }; From 78d99ed5d1fa7b741e478bea2e52f06171c84e4a Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Sat, 29 Apr 2023 03:29:49 +0300 Subject: [PATCH 14/19] refactor: code --- packages/webpack-cli/src/webpack-cli.ts | 113 +++++++++++------------- 1 file changed, 51 insertions(+), 62 deletions(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 7e9b1b18ee3..31388fa6ab2 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1826,6 +1826,7 @@ class WebpackCLI implements IWebpackCLI { return { options, path: configPath }; }; + // TODO better name const config: WebpackCLIConfig = { options: {} as WebpackConfiguration, path: new WeakMap(), @@ -1941,84 +1942,72 @@ class WebpackCLI implements IWebpackCLI { } } - const flattenConfigs = async ( - configPaths: string | string[], + const resolveExtends = async ( + config: WebpackConfiguration, + extendsPaths: string[], ): Promise => { - configPaths = Array.isArray(configPaths) ? configPaths : [configPaths]; - - // fetch configs by path - const configs = await Promise.all( - configPaths.map((configPath: string) => - loadConfigByPath(path.resolve(configPath), options.argv), + const loadedConfigs = await Promise.all( + extendsPaths.map((extendsPath) => + loadConfigByPath(path.resolve(extendsPath), options.argv), ), ); - // extract options from configs - const extendedConfigOptions = configs.map((extendedConfig) => - Array.isArray(extendedConfig.options) ? extendedConfig.options : [extendedConfig.options], - ); + const merge = await this.tryRequireThenImport("webpack-merge"); + const loadedOptions = loadedConfigs.flatMap((config) => config.options); - // recursively flatten the configs by looking for extends property - for (let i = 0; i < extendedConfigOptions.length; i++) { - const extendedConfigs = extendedConfigOptions[i]; - for (let j = 0; j < extendedConfigs.length; j++) { - const extendedConfig = extendedConfigs[j] as WebpackConfiguration; - if (extendedConfig.extends) { - const flattenedExtendedConfig = await flattenConfigs(extendedConfig.extends); - extendedConfigOptions[i][j] = webpackMerge(flattenedExtendedConfig, extendedConfig); - } - } + if (loadedOptions.length > 0) { + config = merge( + ...(loadedOptions as [WebpackConfiguration, ...WebpackConfiguration[]]), + config, + ); } - const flattenedConfigs = extendedConfigOptions.reduce( - (acc: Array, options) => { - acc.push(options[0]); - return acc; - }, - [], - ); + if (config.extends) { + const extendsPaths = typeof config.extends === "string" ? [config.extends] : config.extends; - const merge = await this.tryRequireThenImport("webpack-merge"); - const mergedConfig = flattenedConfigs.reduce((accumulator: object, options) => { - return merge(accumulator, options); - }, {}); + delete config.extends; + + config = await resolveExtends(config, extendsPaths); + } - delete (mergedConfig as WebpackConfiguration).extends; - return mergedConfig; + return config; }; + // TODO set paths for all configs // extends param in CLI gets priority over extends in config file if (options.extends && options.extends.length > 0) { - // load the config from the extends option - const extendedConfig = await flattenConfigs(options.extends); - - const merge = await this.tryRequireThenImport("webpack-merge"); - - config.options = Array.isArray(config.options) ? config.options : [config.options]; + const extendsPaths = options.extends; - // merge extended config with all configs - config.options = config.options.map((options) => { - return merge(extendedConfig, options); - }); + if (Array.isArray(config.options)) { + config.options = await Promise.all( + config.options.map((config) => resolveExtends(config, extendsPaths)), + ); + } else { + // load the config from the extends option + config.options = await resolveExtends(config.options, extendsPaths); + } } // if no extends option is passed, check if the config file has extends - else if ( - (!Array.isArray(config.options) && config.options.extends) || - (Array.isArray(config.options) && config.options.some((options) => options.extends)) - ) { - config.options = Array.isArray(config.options) ? config.options : [config.options]; - - const merge = await this.tryRequireThenImport("webpack-merge"); - - for (let index = 0; index < config.options.length; index++) { - const configOptions = config.options[index]; - if (configOptions.extends) { - const extendedConfig = await flattenConfigs(configOptions.extends); - - config.options[index] = merge(extendedConfig, configOptions); - delete config.options[index].extends; - } - } + else if (Array.isArray(config.options) && config.options.some((options) => options.extends)) { + config.options = await Promise.all( + config.options.map((config) => { + if (config.extends) { + return resolveExtends( + config, + typeof config.extends === "string" ? [config.extends] : config.extends, + ); + } else { + return config; + } + }), + ); + } else if (!Array.isArray(config.options) && config.options.extends) { + config.options = await resolveExtends( + config.options, + typeof config.options.extends === "string" + ? [config.options.extends] + : config.options.extends, + ); } if (options.merge) { From 8547cf8d4a20e520fe47fc9d6f0d693eeacb39da Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Sat, 29 Apr 2023 04:05:08 +0300 Subject: [PATCH 15/19] refactor: fix problems with cache --- packages/webpack-cli/src/webpack-cli.ts | 33 +++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 31388fa6ab2..77a20d2b8ad 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1944,6 +1944,7 @@ class WebpackCLI implements IWebpackCLI { const resolveExtends = async ( config: WebpackConfiguration, + configPaths: WeakMap, extendsPaths: string[], ): Promise => { const loadedConfigs = await Promise.all( @@ -1956,10 +1957,23 @@ class WebpackCLI implements IWebpackCLI { const loadedOptions = loadedConfigs.flatMap((config) => config.options); if (loadedOptions.length > 0) { + const prevConfigPath = + typeof configPaths.get(config) === "string" + ? [configPaths.get(config)] + : configPaths.get(config); + config = merge( ...(loadedOptions as [WebpackConfiguration, ...WebpackConfiguration[]]), config, ); + + if (prevConfigPath) { + // @ts-ignore + configPaths.set(config, [ + ...prevConfigPath, + ...loadedConfigs.flatMap((config) => config.path), + ]); + } } if (config.extends) { @@ -1967,43 +1981,46 @@ class WebpackCLI implements IWebpackCLI { delete config.extends; - config = await resolveExtends(config, extendsPaths); + config = await resolveExtends(config, configPaths, extendsPaths); } return config; }; // TODO set paths for all configs + // TODO check with options // extends param in CLI gets priority over extends in config file if (options.extends && options.extends.length > 0) { const extendsPaths = options.extends; if (Array.isArray(config.options)) { config.options = await Promise.all( - config.options.map((config) => resolveExtends(config, extendsPaths)), + config.options.map((options) => resolveExtends(options, config.path, extendsPaths)), ); } else { // load the config from the extends option - config.options = await resolveExtends(config.options, extendsPaths); + config.options = await resolveExtends(config.options, config.path, extendsPaths); } } // if no extends option is passed, check if the config file has extends else if (Array.isArray(config.options) && config.options.some((options) => options.extends)) { config.options = await Promise.all( - config.options.map((config) => { - if (config.extends) { + config.options.map((options) => { + if (options.extends) { return resolveExtends( - config, - typeof config.extends === "string" ? [config.extends] : config.extends, + options, + config.path, + typeof options.extends === "string" ? [options.extends] : options.extends, ); } else { - return config; + return options; } }), ); } else if (!Array.isArray(config.options) && config.options.extends) { config.options = await resolveExtends( config.options, + config.path, typeof config.options.extends === "string" ? [config.options.extends] : config.options.extends, From 0f35d9218969ea637546fce1367bd4b0fe324908 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Sat, 29 Apr 2023 04:18:06 +0300 Subject: [PATCH 16/19] refactor: refacor paths --- packages/configtest/src/index.ts | 12 +++++++--- .../webpack-cli/src/plugins/cli-plugin.ts | 6 ++++- packages/webpack-cli/src/types.ts | 4 ++-- packages/webpack-cli/src/webpack-cli.ts | 24 +++++++++---------- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/configtest/src/index.ts b/packages/configtest/src/index.ts index 53fa9c1ebc8..7fa6297bd2e 100644 --- a/packages/configtest/src/index.ts +++ b/packages/configtest/src/index.ts @@ -21,13 +21,19 @@ class ConfigTestCommand { if (Array.isArray(config.options)) { config.options.forEach((options) => { - if (config.path.get(options)) { - configPaths.add(config.path.get(options) as string); + const loadedConfigPaths = config.path.get(options); + + if (loadedConfigPaths) { + loadedConfigPaths.forEach((path) => configPaths.add(path)); } }); } else { if (config.path.get(config.options)) { - configPaths.add(config.path.get(config.options) as string); + const loadedConfigPaths = config.path.get(config.options); + + if (loadedConfigPaths) { + loadedConfigPaths.forEach((path) => configPaths.add(path)); + } } } diff --git a/packages/webpack-cli/src/plugins/cli-plugin.ts b/packages/webpack-cli/src/plugins/cli-plugin.ts index f53cc835187..a9d819c3c62 100644 --- a/packages/webpack-cli/src/plugins/cli-plugin.ts +++ b/packages/webpack-cli/src/plugins/cli-plugin.ts @@ -53,7 +53,11 @@ export class CLIPlugin { logCompilation(`Compiler${name ? ` ${name}` : ""} starting... `); if (configPath) { - this.logger.log(`Compiler${name ? ` ${name}` : ""} is using config: '${configPath}'`); + this.logger.log( + `Compiler${name ? ` ${name}` : ""} is using config: ${configPath + .map((path) => `'${path}'`) + .join(", ")}`, + ); } }); diff --git a/packages/webpack-cli/src/types.ts b/packages/webpack-cli/src/types.ts index f15bfb518ac..ec31fa9c364 100644 --- a/packages/webpack-cli/src/types.ts +++ b/packages/webpack-cli/src/types.ts @@ -94,7 +94,7 @@ interface WebpackCLICommandOption extends CommanderOption { interface WebpackCLIConfig { options: WebpackConfiguration | WebpackConfiguration[]; - path: WeakMap; + path: WeakMap; } interface WebpackCLICommand extends Command { @@ -239,7 +239,7 @@ interface BasicPackageJsonContent { */ interface CLIPluginOptions { - configPath?: string; + configPath?: string[]; helpfulOutput: boolean; hot?: boolean | "only"; progress?: boolean | "profile"; diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 77a20d2b8ad..2f9f3863406 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1826,7 +1826,7 @@ class WebpackCLI implements IWebpackCLI { return { options, path: configPath }; }; - // TODO better name + // TODO better name and better type const config: WebpackCLIConfig = { options: {} as WebpackConfiguration, path: new WeakMap(), @@ -1863,10 +1863,10 @@ class WebpackCLI implements IWebpackCLI { if (isArray) { (loadedConfig.options as ConfigOptions[]).forEach((options) => { - config.path.set(options, loadedConfig.path); + config.path.set(options, [loadedConfig.path]); }); } else { - config.path.set(loadedConfig.options, loadedConfig.path); + config.path.set(loadedConfig.options, [loadedConfig.path]); } }); @@ -1905,10 +1905,10 @@ class WebpackCLI implements IWebpackCLI { if (Array.isArray(config.options)) { config.options.forEach((item) => { - config.path.set(item, loadedConfig.path); + config.path.set(item, [loadedConfig.path]); }); } else { - config.path.set(loadedConfig.options, loadedConfig.path); + config.path.set(loadedConfig.options, [loadedConfig.path]); } } } @@ -1944,7 +1944,7 @@ class WebpackCLI implements IWebpackCLI { const resolveExtends = async ( config: WebpackConfiguration, - configPaths: WeakMap, + configPaths: WebpackCLIConfig["path"], extendsPaths: string[], ): Promise => { const loadedConfigs = await Promise.all( @@ -1957,10 +1957,7 @@ class WebpackCLI implements IWebpackCLI { const loadedOptions = loadedConfigs.flatMap((config) => config.options); if (loadedOptions.length > 0) { - const prevConfigPath = - typeof configPaths.get(config) === "string" - ? [configPaths.get(config)] - : configPaths.get(config); + const prevConfigPath = configPaths.get(config); config = merge( ...(loadedOptions as [WebpackConfiguration, ...WebpackConfiguration[]]), @@ -1968,7 +1965,6 @@ class WebpackCLI implements IWebpackCLI { ); if (prevConfigPath) { - // @ts-ignore configPaths.set(config, [ ...prevConfigPath, ...loadedConfigs.flatMap((config) => config.path), @@ -2044,11 +2040,13 @@ class WebpackCLI implements IWebpackCLI { const configPath = config.path.get(options); const mergedOptions = merge(accumulator, options); - mergedConfigPaths.push(configPath as string); + if (configPath) { + mergedConfigPaths.push(...configPath); + } return mergedOptions; }, {}); - config.path.set(config.options, mergedConfigPaths as unknown as string); + config.path.set(config.options, mergedConfigPaths); } return config; From f8ba736f8118085bfb98038b95739320a4cbf826 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Sat, 29 Apr 2023 04:30:09 +0300 Subject: [PATCH 17/19] refactor: catch recursive calls --- packages/webpack-cli/src/webpack-cli.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index 2f9f3863406..a9b233b38d7 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1957,18 +1957,25 @@ class WebpackCLI implements IWebpackCLI { const loadedOptions = loadedConfigs.flatMap((config) => config.options); if (loadedOptions.length > 0) { - const prevConfigPath = configPaths.get(config); + const prevPaths = configPaths.get(config); + const loadedPaths = loadedConfigs.flatMap((config) => config.path); + + if (prevPaths) { + const intersection = loadedPaths.filter((element) => prevPaths.includes(element)); + + if (intersection.length > 0) { + this.logger.error(`Recursive configuration detected, exiting.`); + process.exit(2); + } + } config = merge( ...(loadedOptions as [WebpackConfiguration, ...WebpackConfiguration[]]), config, ); - if (prevConfigPath) { - configPaths.set(config, [ - ...prevConfigPath, - ...loadedConfigs.flatMap((config) => config.path), - ]); + if (prevPaths) { + configPaths.set(config, [...prevPaths, ...loadedPaths]); } } @@ -1983,9 +1990,7 @@ class WebpackCLI implements IWebpackCLI { return config; }; - // TODO set paths for all configs - // TODO check with options - // extends param in CLI gets priority over extends in config file + // The `extends` param in CLI gets priority over extends in config file if (options.extends && options.extends.length > 0) { const extendsPaths = options.extends; From c348c6697a42b97ac20e09b36cc00f92631fd5c0 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Sat, 29 Apr 2023 19:13:43 +0300 Subject: [PATCH 18/19] fix: bug and tests --- packages/webpack-cli/src/webpack-cli.ts | 4 +- .../deep.base.webpack.config.js | 10 ++++ test/build/extends/extends.test.js | 57 +++++++++++++++---- .../multiple-extends/base2.webpack.config.js | 4 +- .../extends/recursive-extends/other.config.js | 8 +++ .../recursive-extends/webpack.config.js | 8 +++ .../extends/simple-case/override.config.js | 9 +++ 7 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 test/build/extends/extends-cli-option/deep.base.webpack.config.js create mode 100644 test/build/extends/recursive-extends/other.config.js create mode 100644 test/build/extends/recursive-extends/webpack.config.js create mode 100644 test/build/extends/simple-case/override.config.js diff --git a/packages/webpack-cli/src/webpack-cli.ts b/packages/webpack-cli/src/webpack-cli.ts index a9b233b38d7..90c8f1ff402 100644 --- a/packages/webpack-cli/src/webpack-cli.ts +++ b/packages/webpack-cli/src/webpack-cli.ts @@ -1947,6 +1947,8 @@ class WebpackCLI implements IWebpackCLI { configPaths: WebpackCLIConfig["path"], extendsPaths: string[], ): Promise => { + delete config.extends; + const loadedConfigs = await Promise.all( extendsPaths.map((extendsPath) => loadConfigByPath(path.resolve(extendsPath), options.argv), @@ -1982,8 +1984,6 @@ class WebpackCLI implements IWebpackCLI { if (config.extends) { const extendsPaths = typeof config.extends === "string" ? [config.extends] : config.extends; - delete config.extends; - config = await resolveExtends(config, configPaths, extendsPaths); } diff --git a/test/build/extends/extends-cli-option/deep.base.webpack.config.js b/test/build/extends/extends-cli-option/deep.base.webpack.config.js new file mode 100644 index 00000000000..46c91d0207f --- /dev/null +++ b/test/build/extends/extends-cli-option/deep.base.webpack.config.js @@ -0,0 +1,10 @@ +module.exports = () => { + console.log("deep.base.webpack.config.js"); + + return { + name: "base_config", + mode: "development", + entry: "./src/index.js", + bail: true, + }; +}; diff --git a/test/build/extends/extends.test.js b/test/build/extends/extends.test.js index 23be093ffe0..48c06ca41be 100644 --- a/test/build/extends/extends.test.js +++ b/test/build/extends/extends.test.js @@ -23,7 +23,7 @@ describe("extends property", () => { expect(stdout).toContain("base2.webpack.config.js"); expect(stdout).toContain("derived.webpack.config.js"); expect(stdout).toContain("name: 'base_config2'"); - expect(stdout).toContain("mode: 'production'"); + expect(stdout).toContain("mode: 'development'"); }); it("extends a multilevel config correctly", async () => { @@ -38,31 +38,68 @@ describe("extends property", () => { expect(stdout).toContain("mode: 'production'"); }); - it("extends a provided webpack config passed in the cli correctly", async () => { + it("extends a provided webpack config for multiple configs correctly", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/multiple-configs"); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("name: 'derived_config1'"); + expect(stdout).toContain("name: 'derived_config2'"); + expect(stdout).not.toContain("name: 'base_config'"); + expect(stdout).toContain("mode: 'development'"); + expect(stdout).toContain("topLevelAwait: true"); + }); + + it("multiple extends a provided webpack config passed in the cli correctly", async () => { const { exitCode, stderr, stdout } = await run(__dirname + "/extends-cli-option", [ "--extends", "./base.webpack.config.js", + "--extends", + "./deep.base.webpack.config.js", ]); expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); - expect(stdout).toContain("base.webpack.config.js"); expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("deep.base.webpack.config.js"); expect(stdout).toContain("name: 'base_config'"); expect(stdout).toContain("mode: 'development'"); + expect(stdout).toContain("bail: true"); }); - it("extends a provided webpack config for multiple configs correctly", async () => { - const { exitCode, stderr, stdout } = await run(__dirname + "/multiple-configs"); + it("CLI `extends` should override `extends` in a configuration", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/simple-case", [ + "--extends", + "./override.config.js", + ]); expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); - expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("override.config.js"); expect(stdout).toContain("derived.webpack.config.js"); - expect(stdout).toContain("name: 'derived_config1'"); - expect(stdout).toContain("name: 'derived_config2'"); - expect(stdout).not.toContain("name: 'base_config'"); + expect(stdout).toContain("name: 'override_config'"); expect(stdout).toContain("mode: 'development'"); - expect(stdout).toContain("topLevelAwait: true"); + }); + + it("should throw an error on recursive", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/recursive-extends"); + + expect(exitCode).toBe(2); + expect(stderr).toContain("Recursive configuration detected, exiting"); + expect(stdout).toBeFalsy(); + }); + + it("should throw an error on recursive #2", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/recursive-extends", [ + "--config", + "other.config.js", + ]); + + expect(exitCode).toBe(2); + expect(stderr).toContain("Recursive configuration detected, exiting"); + expect(stdout).toBeFalsy(); }); }); diff --git a/test/build/extends/multiple-extends/base2.webpack.config.js b/test/build/extends/multiple-extends/base2.webpack.config.js index 2dc58a5a6ea..7661abf08a7 100644 --- a/test/build/extends/multiple-extends/base2.webpack.config.js +++ b/test/build/extends/multiple-extends/base2.webpack.config.js @@ -3,7 +3,9 @@ module.exports = () => { return { name: "base_config2", - mode: "production", entry: "./src/index2.js", + externals: { + jquery: "jQuery", + }, }; }; diff --git a/test/build/extends/recursive-extends/other.config.js b/test/build/extends/recursive-extends/other.config.js new file mode 100644 index 00000000000..c918158aeea --- /dev/null +++ b/test/build/extends/recursive-extends/other.config.js @@ -0,0 +1,8 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + return { + extends: "./webpack.config.js", + plugins: [new WebpackCLITestPlugin()], + }; +}; diff --git a/test/build/extends/recursive-extends/webpack.config.js b/test/build/extends/recursive-extends/webpack.config.js new file mode 100644 index 00000000000..c918158aeea --- /dev/null +++ b/test/build/extends/recursive-extends/webpack.config.js @@ -0,0 +1,8 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + return { + extends: "./webpack.config.js", + plugins: [new WebpackCLITestPlugin()], + }; +}; diff --git a/test/build/extends/simple-case/override.config.js b/test/build/extends/simple-case/override.config.js new file mode 100644 index 00000000000..bf79ce64650 --- /dev/null +++ b/test/build/extends/simple-case/override.config.js @@ -0,0 +1,9 @@ +module.exports = () => { + console.log("override.config.js"); + + return { + name: "override_config", + mode: "development", + entry: "./src/index.js", + }; +}; From ce7c6cb245f44350e797bfc1f4e6b1274c322bc7 Mon Sep 17 00:00:00 2001 From: "alexander.akait" Date: Sat, 29 Apr 2023 19:42:40 +0300 Subject: [PATCH 19/19] test: more --- test/build/extends/extends.test.js | 31 +++++++++++++++++++ .../extends/multiple-configs/other.config.js | 11 +++++++ .../multiple-configs1/base.webpack.config.js | 15 +++++++++ .../extends/multiple-configs1/other.config.js | 11 +++++++ .../extends/multiple-configs1/src/index1.js | 1 + .../extends/multiple-configs1/src/index2.js | 1 + .../multiple-configs1/webpack.config.js | 14 +++++++++ .../multiple-configs2/base.webpack.config.js | 16 ++++++++++ .../extends/multiple-configs2/src/index1.js | 1 + .../extends/multiple-configs2/src/index2.js | 1 + .../multiple-configs2/webpack.config.js | 17 ++++++++++ 11 files changed, 119 insertions(+) create mode 100644 test/build/extends/multiple-configs/other.config.js create mode 100644 test/build/extends/multiple-configs1/base.webpack.config.js create mode 100644 test/build/extends/multiple-configs1/other.config.js create mode 100644 test/build/extends/multiple-configs1/src/index1.js create mode 100644 test/build/extends/multiple-configs1/src/index2.js create mode 100644 test/build/extends/multiple-configs1/webpack.config.js create mode 100644 test/build/extends/multiple-configs2/base.webpack.config.js create mode 100644 test/build/extends/multiple-configs2/src/index1.js create mode 100644 test/build/extends/multiple-configs2/src/index2.js create mode 100644 test/build/extends/multiple-configs2/webpack.config.js diff --git a/test/build/extends/extends.test.js b/test/build/extends/extends.test.js index 48c06ca41be..e3b6b3a735d 100644 --- a/test/build/extends/extends.test.js +++ b/test/build/extends/extends.test.js @@ -52,6 +52,19 @@ describe("extends property", () => { expect(stdout).toContain("topLevelAwait: true"); }); + it("extends a provided webpack config for multiple configs correctly #2", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/multiple-configs2"); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("name: 'base_config'"); + expect(stdout).toContain("name: 'derived_config2'"); + expect(stdout).toContain("mode: 'development'"); + expect(stdout).toContain("topLevelAwait: true"); + }); + it("multiple extends a provided webpack config passed in the cli correctly", async () => { const { exitCode, stderr, stdout } = await run(__dirname + "/extends-cli-option", [ "--extends", @@ -70,6 +83,24 @@ describe("extends property", () => { expect(stdout).toContain("bail: true"); }); + it("should work with multiple extends and multiple configuration", async () => { + const { exitCode, stderr, stdout } = await run(__dirname + "/multiple-configs1", [ + "--extends", + "./base.webpack.config.js", + "--extends", + "./other.config.js", + ]); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain("derived.webpack.config.js"); + expect(stdout).toContain("base.webpack.config.js"); + expect(stdout).toContain("other.config.js"); + expect(stdout).toContain("name: 'derived_config1'"); + expect(stdout).toContain("name: 'derived_config2'"); + expect(stdout).toContain("topLevelAwait: true"); + }); + it("CLI `extends` should override `extends` in a configuration", async () => { const { exitCode, stderr, stdout } = await run(__dirname + "/simple-case", [ "--extends", diff --git a/test/build/extends/multiple-configs/other.config.js b/test/build/extends/multiple-configs/other.config.js new file mode 100644 index 00000000000..80c2fc96e94 --- /dev/null +++ b/test/build/extends/multiple-configs/other.config.js @@ -0,0 +1,11 @@ +module.exports = () => { + console.log("other.config.js"); + + return { + name: "other_config", + mode: "development", + experiments: { + topLevelAwait: true, + }, + }; +}; diff --git a/test/build/extends/multiple-configs1/base.webpack.config.js b/test/build/extends/multiple-configs1/base.webpack.config.js new file mode 100644 index 00000000000..f10862022b4 --- /dev/null +++ b/test/build/extends/multiple-configs1/base.webpack.config.js @@ -0,0 +1,15 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("base.webpack.config.js"); + + return { + name: "base_config", + mode: "development", + plugins: [new WebpackCLITestPlugin()], + + experiments: { + topLevelAwait: true, + }, + }; +}; diff --git a/test/build/extends/multiple-configs1/other.config.js b/test/build/extends/multiple-configs1/other.config.js new file mode 100644 index 00000000000..80c2fc96e94 --- /dev/null +++ b/test/build/extends/multiple-configs1/other.config.js @@ -0,0 +1,11 @@ +module.exports = () => { + console.log("other.config.js"); + + return { + name: "other_config", + mode: "development", + experiments: { + topLevelAwait: true, + }, + }; +}; diff --git a/test/build/extends/multiple-configs1/src/index1.js b/test/build/extends/multiple-configs1/src/index1.js new file mode 100644 index 00000000000..9fc41fb4f1f --- /dev/null +++ b/test/build/extends/multiple-configs1/src/index1.js @@ -0,0 +1 @@ +console.log("i am index1.js"); diff --git a/test/build/extends/multiple-configs1/src/index2.js b/test/build/extends/multiple-configs1/src/index2.js new file mode 100644 index 00000000000..7fa5b7a517e --- /dev/null +++ b/test/build/extends/multiple-configs1/src/index2.js @@ -0,0 +1 @@ +console.log("i am index2.js"); diff --git a/test/build/extends/multiple-configs1/webpack.config.js b/test/build/extends/multiple-configs1/webpack.config.js new file mode 100644 index 00000000000..f12adb72cf6 --- /dev/null +++ b/test/build/extends/multiple-configs1/webpack.config.js @@ -0,0 +1,14 @@ +module.exports = () => { + console.log("derived.webpack.config.js"); + + return [ + { + name: "derived_config1", + entry: "./src/index1.js", + }, + { + name: "derived_config2", + entry: "./src/index2.js", + }, + ]; +}; diff --git a/test/build/extends/multiple-configs2/base.webpack.config.js b/test/build/extends/multiple-configs2/base.webpack.config.js new file mode 100644 index 00000000000..68ec8d384a4 --- /dev/null +++ b/test/build/extends/multiple-configs2/base.webpack.config.js @@ -0,0 +1,16 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("base.webpack.config.js"); + + return { + entry: "./src/index1.js", + name: "base_config", + mode: "development", + plugins: [new WebpackCLITestPlugin()], + + experiments: { + topLevelAwait: true, + }, + }; +}; diff --git a/test/build/extends/multiple-configs2/src/index1.js b/test/build/extends/multiple-configs2/src/index1.js new file mode 100644 index 00000000000..9fc41fb4f1f --- /dev/null +++ b/test/build/extends/multiple-configs2/src/index1.js @@ -0,0 +1 @@ +console.log("i am index1.js"); diff --git a/test/build/extends/multiple-configs2/src/index2.js b/test/build/extends/multiple-configs2/src/index2.js new file mode 100644 index 00000000000..7fa5b7a517e --- /dev/null +++ b/test/build/extends/multiple-configs2/src/index2.js @@ -0,0 +1 @@ +console.log("i am index2.js"); diff --git a/test/build/extends/multiple-configs2/webpack.config.js b/test/build/extends/multiple-configs2/webpack.config.js new file mode 100644 index 00000000000..05b311b14e2 --- /dev/null +++ b/test/build/extends/multiple-configs2/webpack.config.js @@ -0,0 +1,17 @@ +const WebpackCLITestPlugin = require("../../../utils/webpack-cli-test-plugin"); + +module.exports = () => { + console.log("derived.webpack.config.js"); + + return [ + { + extends: "./base.webpack.config.js", + }, + { + name: "derived_config2", + mode: "development", + entry: "./src/index2.js", + plugins: [new WebpackCLITestPlugin()], + }, + ]; +};