diff --git a/lib/config/browserslistTargetHandler.js b/lib/config/browserslistTargetHandler.js index 196cfa143..6b42b54ac 100644 --- a/lib/config/browserslistTargetHandler.js +++ b/lib/config/browserslistTargetHandler.js @@ -28,19 +28,26 @@ const inputRx = /^(?:((?:[A-Z]:)?[/\\].*?))?(?::(.+?))?$/i; * @returns {BrowserslistHandlerConfig} config */ const parse = (input, context) => { + // browserslist if (!input) { return {}; } + // browserslist:path-to-config + // browserslist:path-to-config:env if (path.isAbsolute(input)) { const [, configPath, env] = inputRx.exec(input) || []; + return { configPath, env }; } const config = browserslist.findConfig(context); - if (config && Object.keys(config).includes(input)) { - return { env: input }; + // browserslist:query + // browserslist:env + // When we have a configuration file and value after `:` it can be env or query + if (config) { + return { env: input, query: input }; } return { query: input }; @@ -54,20 +61,26 @@ const parse = (input, context) => { const load = (input, context) => { const { configPath, env, query } = parse(input, context); - // if a query is specified, then use it, else - // if a path to a config is specified then load it, else - // find a nearest config - const config = - query || - (configPath - ? browserslist.loadConfig({ - config: configPath, - env - }) - : browserslist.loadConfig({ path: context, env })); + // TODO refactor me after resolve - https://github.com/browserslist/browserslist/issues/906 + // Try to apply query firstly, + if (query) { + try { + return browserslist(query); + } catch (_err) { + // Nothing + } + } + + // if a path to a config is specified then load it, else find a nearest config + const config = configPath + ? browserslist.loadConfig({ + config: configPath, + env + }) + : browserslist.loadConfig({ path: context, env }); if (!config) return; - return browserslist(config); + return browserslist(config, { env, throwOnMissing: true }); }; /** diff --git a/lib/config/defaults.js b/lib/config/defaults.js index 742704b88..ce4bf3980 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -1326,8 +1326,13 @@ const applyOutputDefaults = ( if (tp.importScripts) return "array-push"; throw new Error( "For the selected environment is no default script chunk format available:\n" + - "JSONP Array push can be chosen when 'document' or 'importScripts' is available.\n" + - `CommonJs exports can be chosen when 'require' or node builtins are available.\n${ + `${ + tp.module + ? "Module ('module') can be chosen when ES modules are available (please set 'experiments.outputModule' and 'output.module' to `true`)" + : "" + }\n` + + "JSONP Array push ('array-push') can be chosen when 'document' or 'importScripts' is available.\n" + + `CommonJs exports ('commonjs') can be chosen when 'require' or node builtins are available.\n${ helpMessage }` ); diff --git a/test/configCases/ecmaVersion/browserslist-config-env-extends/.browserslistrc b/test/configCases/ecmaVersion/browserslist-config-env-extends/.browserslistrc new file mode 100644 index 000000000..4f63a5aa8 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-env-extends/.browserslistrc @@ -0,0 +1 @@ +extends browserslist-config-mycompany diff --git a/test/configCases/ecmaVersion/browserslist-config-env-extends/index.js b/test/configCases/ecmaVersion/browserslist-config-env-extends/index.js new file mode 100644 index 000000000..bbd9de415 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-env-extends/index.js @@ -0,0 +1 @@ +it("should compile and run the test", function() {}); diff --git a/test/configCases/ecmaVersion/browserslist-config-env-extends/test.config.js b/test/configCases/ecmaVersion/browserslist-config-env-extends/test.config.js new file mode 100644 index 000000000..178756de2 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-env-extends/test.config.js @@ -0,0 +1,37 @@ +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +const rootPath = path.resolve(__dirname, "../../../../"); +const rootNodeModules = path.resolve(rootPath, "./node_modules"); +const browserslistPackage = path.resolve( + rootNodeModules, + "browserslist-config-mycompany" +); +const content = ` +module.exports = { + development: [ + 'last 1 version' + ], + production: [ + 'ie 9', + ] +} +`; +const browserslistFile = path.resolve(browserslistPackage, "./index.js"); + +try { + fs.mkdirSync(browserslistPackage); +} catch (_err) { + // Nothing +} + +fs.writeFileSync(browserslistFile, content); + +module.exports = { + afterExecute() { + fs.rmSync(browserslistFile); + fs.rmdirSync(browserslistPackage); + } +}; diff --git a/test/configCases/ecmaVersion/browserslist-config-env-extends/webpack.config.js b/test/configCases/ecmaVersion/browserslist-config-env-extends/webpack.config.js new file mode 100644 index 000000000..e346b3584 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-env-extends/webpack.config.js @@ -0,0 +1,43 @@ +"use strict"; + +const path = require("path"); + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: `browserslist:${path.join(__dirname, ".browserslistrc")}:production`, + plugins: [ + (compiler) => { + compiler.hooks.compilation.tap("Test", (compilation) => { + expect(compilation.outputOptions.environment).toMatchInlineSnapshot(` + Object { + "arrowFunction": false, + "asyncFunction": false, + "bigIntLiteral": false, + "const": false, + "destructuring": false, + "document": true, + "dynamicImport": false, + "dynamicImportInWorker": false, + "forOf": false, + "globalThis": false, + "module": false, + "nodePrefixForCoreModules": false, + "optionalChaining": false, + "templateLiteral": false, + } + `); + expect(compilation.options.externalsPresets).toMatchInlineSnapshot(` + Object { + "electron": false, + "electronMain": false, + "electronPreload": false, + "electronRenderer": false, + "node": false, + "nwjs": false, + "web": true, + } + `); + }); + } + ] +}; diff --git a/test/configCases/ecmaVersion/browserslist-config-extends/.browserslistrc b/test/configCases/ecmaVersion/browserslist-config-extends/.browserslistrc new file mode 100644 index 000000000..7d26717a1 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-extends/.browserslistrc @@ -0,0 +1 @@ +extends browserslist-config-mycompany1 diff --git a/test/configCases/ecmaVersion/browserslist-config-extends/index.js b/test/configCases/ecmaVersion/browserslist-config-extends/index.js new file mode 100644 index 000000000..bbd9de415 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-extends/index.js @@ -0,0 +1 @@ +it("should compile and run the test", function() {}); diff --git a/test/configCases/ecmaVersion/browserslist-config-extends/test.config.js b/test/configCases/ecmaVersion/browserslist-config-extends/test.config.js new file mode 100644 index 000000000..6efed67a2 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-extends/test.config.js @@ -0,0 +1,38 @@ +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +const rootPath = path.resolve(__dirname, "../../../../"); +const rootNodeModules = path.resolve(rootPath, "./node_modules"); +const browserslistPackage = path.resolve( + rootNodeModules, + "browserslist-config-mycompany1" +); +const content = ` +module.exports = { + development: [ + 'last 1 version' + ], + // We are in tests, so 'process.env.NODE_ENV' has the 'test' value (browserslist respects the 'process.env.NODE_ENV' value) + test: [ + 'ie 9', + ] +} +`; +const browserslistFile = path.resolve(browserslistPackage, "./index.js"); + +try { + fs.mkdirSync(browserslistPackage); +} catch (_err) { + // Nothing +} + +fs.writeFileSync(browserslistFile, content); + +module.exports = { + afterExecute() { + fs.rmSync(browserslistFile); + fs.rmdirSync(browserslistPackage); + } +}; diff --git a/test/configCases/ecmaVersion/browserslist-config-extends/webpack.config.js b/test/configCases/ecmaVersion/browserslist-config-extends/webpack.config.js new file mode 100644 index 000000000..ab3cae79d --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-config-extends/webpack.config.js @@ -0,0 +1,43 @@ +"use strict"; + +const path = require("path"); + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: `browserslist:${path.join(__dirname, ".browserslistrc")}`, + plugins: [ + (compiler) => { + compiler.hooks.compilation.tap("Test", (compilation) => { + expect(compilation.outputOptions.environment).toMatchInlineSnapshot(` + Object { + "arrowFunction": false, + "asyncFunction": false, + "bigIntLiteral": false, + "const": false, + "destructuring": false, + "document": true, + "dynamicImport": false, + "dynamicImportInWorker": false, + "forOf": false, + "globalThis": false, + "module": false, + "nodePrefixForCoreModules": false, + "optionalChaining": false, + "templateLiteral": false, + } + `); + expect(compilation.options.externalsPresets).toMatchInlineSnapshot(` + Object { + "electron": false, + "electronMain": false, + "electronPreload": false, + "electronRenderer": false, + "node": false, + "nwjs": false, + "web": true, + } + `); + }); + } + ] +}; diff --git a/test/configCases/ecmaVersion/browserslist-env/index.js b/test/configCases/ecmaVersion/browserslist-env/index.js new file mode 100644 index 000000000..bbd9de415 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-env/index.js @@ -0,0 +1 @@ +it("should compile and run the test", function() {}); diff --git a/test/configCases/ecmaVersion/browserslist-env/package.json b/test/configCases/ecmaVersion/browserslist-env/package.json new file mode 100644 index 000000000..b4dfe86f3 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-env/package.json @@ -0,0 +1,10 @@ +{ + "browserslist": { + "development": [ + "last 1 version" + ], + "production": [ + "ie 9" + ] + } +} diff --git a/test/configCases/ecmaVersion/browserslist-env/webpack.config.js b/test/configCases/ecmaVersion/browserslist-env/webpack.config.js new file mode 100644 index 000000000..3b47ecbd9 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-env/webpack.config.js @@ -0,0 +1,41 @@ +"use strict"; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "browserslist:production", + plugins: [ + (compiler) => { + compiler.hooks.compilation.tap("Test", (compilation) => { + expect(compilation.outputOptions.environment).toMatchInlineSnapshot(` + Object { + "arrowFunction": false, + "asyncFunction": false, + "bigIntLiteral": false, + "const": false, + "destructuring": false, + "document": true, + "dynamicImport": false, + "dynamicImportInWorker": false, + "forOf": false, + "globalThis": false, + "module": false, + "nodePrefixForCoreModules": false, + "optionalChaining": false, + "templateLiteral": false, + } + `); + expect(compilation.options.externalsPresets).toMatchInlineSnapshot(` + Object { + "electron": false, + "electronMain": false, + "electronPreload": false, + "electronRenderer": false, + "node": false, + "nwjs": false, + "web": true, + } + `); + }); + } + ] +}; diff --git a/test/configCases/ecmaVersion/browserslist-extends/index.js b/test/configCases/ecmaVersion/browserslist-extends/index.js new file mode 100644 index 000000000..bbd9de415 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-extends/index.js @@ -0,0 +1 @@ +it("should compile and run the test", function() {}); diff --git a/test/configCases/ecmaVersion/browserslist-extends/package.json b/test/configCases/ecmaVersion/browserslist-extends/package.json new file mode 100644 index 000000000..3670aa399 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-extends/package.json @@ -0,0 +1,5 @@ +{ + "browserslist": [ + "extends browserslist-config-mycompany2" + ] +} diff --git a/test/configCases/ecmaVersion/browserslist-extends/test.config.js b/test/configCases/ecmaVersion/browserslist-extends/test.config.js new file mode 100644 index 000000000..cd9899f23 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-extends/test.config.js @@ -0,0 +1,38 @@ +"use strict"; + +const fs = require("fs"); +const path = require("path"); + +const rootPath = path.resolve(__dirname, "../../../../"); +const rootNodeModules = path.resolve(rootPath, "./node_modules"); +const browserslistPackage = path.resolve( + rootNodeModules, + "browserslist-config-mycompany2" +); +const content = ` +module.exports = { + development: [ + 'last 1 version' + ], + // We are in tests, so 'process.env.NODE_ENV' has the 'test' value (browserslist respects the 'process.env.NODE_ENV' value) + test: [ + 'ie 9', + ] +} +`; +const browserslistFile = path.resolve(browserslistPackage, "./index.js"); + +try { + fs.mkdirSync(browserslistPackage); +} catch (_err) { + // Nothing +} + +fs.writeFileSync(browserslistFile, content); + +module.exports = { + afterExecute() { + fs.rmSync(browserslistFile); + fs.rmdirSync(browserslistPackage); + } +}; diff --git a/test/configCases/ecmaVersion/browserslist-extends/webpack.config.js b/test/configCases/ecmaVersion/browserslist-extends/webpack.config.js new file mode 100644 index 000000000..6f37b9440 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-extends/webpack.config.js @@ -0,0 +1,41 @@ +"use strict"; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "browserslist", + plugins: [ + (compiler) => { + compiler.hooks.compilation.tap("Test", (compilation) => { + expect(compilation.outputOptions.environment).toMatchInlineSnapshot(` + Object { + "arrowFunction": false, + "asyncFunction": false, + "bigIntLiteral": false, + "const": false, + "destructuring": false, + "document": true, + "dynamicImport": false, + "dynamicImportInWorker": false, + "forOf": false, + "globalThis": false, + "module": false, + "nodePrefixForCoreModules": false, + "optionalChaining": false, + "templateLiteral": false, + } + `); + expect(compilation.options.externalsPresets).toMatchInlineSnapshot(` + Object { + "electron": false, + "electronMain": false, + "electronPreload": false, + "electronRenderer": false, + "node": false, + "nwjs": false, + "web": true, + } + `); + }); + } + ] +}; diff --git a/test/configCases/ecmaVersion/browserslist-query-with-config-file/index.js b/test/configCases/ecmaVersion/browserslist-query-with-config-file/index.js new file mode 100644 index 000000000..bbd9de415 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-query-with-config-file/index.js @@ -0,0 +1 @@ +it("should compile and run the test", function() {}); diff --git a/test/configCases/ecmaVersion/browserslist-query-with-config-file/package.json b/test/configCases/ecmaVersion/browserslist-query-with-config-file/package.json new file mode 100644 index 000000000..b4dfe86f3 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-query-with-config-file/package.json @@ -0,0 +1,10 @@ +{ + "browserslist": { + "development": [ + "last 1 version" + ], + "production": [ + "ie 9" + ] + } +} diff --git a/test/configCases/ecmaVersion/browserslist-query-with-config-file/webpack.config.js b/test/configCases/ecmaVersion/browserslist-query-with-config-file/webpack.config.js new file mode 100644 index 000000000..9d91b6823 --- /dev/null +++ b/test/configCases/ecmaVersion/browserslist-query-with-config-file/webpack.config.js @@ -0,0 +1,41 @@ +"use strict"; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "browserslist:maintained node versions", + plugins: [ + (compiler) => { + compiler.hooks.compilation.tap("Test", (compilation) => { + expect(compilation.outputOptions.environment).toMatchInlineSnapshot(` + Object { + "arrowFunction": true, + "asyncFunction": true, + "bigIntLiteral": true, + "const": true, + "destructuring": true, + "document": false, + "dynamicImport": true, + "dynamicImportInWorker": false, + "forOf": true, + "globalThis": true, + "module": true, + "nodePrefixForCoreModules": true, + "optionalChaining": true, + "templateLiteral": true, + } + `); + expect(compilation.options.externalsPresets).toMatchInlineSnapshot(` + Object { + "electron": false, + "electronMain": false, + "electronPreload": false, + "electronRenderer": false, + "node": true, + "nwjs": false, + "web": false, + } + `); + }); + } + ] +};