diff --git a/generate-types-config.js b/generate-types-config.js index a3ce7af1250..89205e3496b 100644 --- a/generate-types-config.js +++ b/generate-types-config.js @@ -4,7 +4,8 @@ module.exports = { nameMapping: { FsStats: /^Stats Import fs/, validateFunction: /^validate Import/, - Configuration: /^WebpackOptions / + Configuration: /^WebpackOptions /, + MultiConfiguration: /^MultiWebpackOptions / }, exclude: [/^devServer in WebpackOptions /], include: [/^(_module|_compilation|_compiler) in NormalModuleLoaderContext /] diff --git a/lib/MultiCompiler.js b/lib/MultiCompiler.js index dfec39920c1..3ab64e4affc 100644 --- a/lib/MultiCompiler.js +++ b/lib/MultiCompiler.js @@ -16,6 +16,7 @@ const ArrayQueue = require("./util/ArrayQueue"); /** @template T @typedef {import("tapable").AsyncSeriesHook} AsyncSeriesHook */ /** @template T @template R @typedef {import("tapable").SyncBailHook} SyncBailHook */ +/** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */ /** @typedef {import("../declarations/WebpackOptions").WatchOptions} WatchOptions */ /** @typedef {import("./Compiler")} Compiler */ /** @typedef {import("./Stats")} Stats */ @@ -44,6 +45,8 @@ const ArrayQueue = require("./util/ArrayQueue"); * @property {number=} parallelism how many Compilers are allows to run at the same time in parallel */ +/** @typedef {ReadonlyArray & MultiCompilerOptions} MultiWebpackOptions */ + const CLASS_NAME = "MultiCompiler"; module.exports = class MultiCompiler { @@ -576,7 +579,7 @@ module.exports = class MultiCompiler { } /** - * @param {WatchOptions|WatchOptions[]} watchOptions the watcher's options + * @param {WatchOptions | WatchOptions[]} watchOptions the watcher's options * @param {Callback} handler signals when the call finishes * @returns {MultiWatching} a compiler watcher */ diff --git a/lib/MultiStats.js b/lib/MultiStats.js index 50571cdf4d2..62504ab8234 100644 --- a/lib/MultiStats.js +++ b/lib/MultiStats.js @@ -25,6 +25,8 @@ const indent = (str, prefix) => { return prefix + rem; }; +/** @typedef {undefined | string | boolean | StatsOptions} ChildrenStatsOptions */ +/** @typedef {Omit & { children?: ChildrenStatsOptions | ChildrenStatsOptions[] }} MultiStatsOptions */ /** @typedef {{ version: boolean, hash: boolean, errorsCount: boolean, warningsCount: boolean, errors: boolean, warnings: boolean, children: NormalizedStatsOptions[] }} ChildOptions */ class MultiStats { @@ -54,7 +56,7 @@ class MultiStats { } /** - * @param {string | boolean | StatsOptions | undefined} options stats options + * @param {undefined | string | boolean | MultiStatsOptions} options stats options * @param {CreateStatsOptionsContext} context context * @returns {ChildOptions} context context */ @@ -80,6 +82,9 @@ class MultiStats { const childOptions = Array.isArray(childrenOptions) ? childrenOptions[idx] : childrenOptions; + if (typeof childOptions === "boolean") { + return stat.compilation.createStatsOptions(childOptions, context); + } return stat.compilation.createStatsOptions( { ...baseOptions, @@ -104,7 +109,7 @@ class MultiStats { } /** - * @param {(string | boolean | StatsOptions)=} options stats options + * @param {(string | boolean | MultiStatsOptions)=} options stats options * @returns {StatsCompilation} json output */ toJson(options) { @@ -179,7 +184,7 @@ class MultiStats { } /** - * @param {(string | boolean | StatsOptions)=} options stats options + * @param {(string | boolean | MultiStatsOptions)=} options stats options * @returns {string} string output */ toString(options) { diff --git a/lib/cli.js b/lib/cli.js index 4bc007fc26d..1d38af32bc8 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -840,7 +840,12 @@ const init = (open, close, replace) => }} Colors */ /** - * @param {{ useColor?: boolean }=} options options + * @typedef {object} ColorsOptions + * @property {boolean=} useColor force use colors + */ + +/** + * @param {ColorsOptions=} options options * @returns {Colors} colors */ const createColors = ({ useColor = isColorSupported() } = {}) => ({ diff --git a/lib/index.js b/lib/index.js index 651460befe2..a4003a0f0ec 100644 --- a/lib/index.js +++ b/lib/index.js @@ -49,7 +49,9 @@ const memoize = require("./util/memoize"); /** @typedef {import("./Compiler").AssetEmittedInfo} AssetEmittedInfo */ /** @typedef {import("./Entrypoint")} Entrypoint */ /** @typedef {import("./MultiCompiler").MultiCompilerOptions} MultiCompilerOptions */ +/** @typedef {import("./MultiCompiler").MultiWebpackOptions} MultiConfiguration */ /** @typedef {import("./MultiStats")} MultiStats */ +/** @typedef {import("./MultiStats").MultiStatsOptions} MultiStatsOptions */ /** @typedef {import("./NormalModuleFactory").ResolveData} ResolveData */ /** @typedef {import("./Parser").ParserState} ParserState */ /** @typedef {import("./ResolverFactory").ResolvePluginInstance} ResolvePluginInstance */ @@ -57,6 +59,8 @@ const memoize = require("./util/memoize"); /** @typedef {import("./Watching")} Watching */ /** @typedef {import("./cli").Argument} Argument */ /** @typedef {import("./cli").Problem} Problem */ +/** @typedef {import("./cli").Colors} Colors */ +/** @typedef {import("./cli").ColorsOptions} ColorsOptions */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsAsset} StatsAsset */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsChunk} StatsChunk */ /** @typedef {import("./stats/DefaultStatsFactoryPlugin").StatsChunkGroup} StatsChunkGroup */ @@ -132,16 +136,16 @@ module.exports = mergeExports(fn, { return require("./webpack"); }, /** - * @returns {(configuration: Configuration | Configuration[]) => void} validate fn + * @returns {(configuration: Configuration | MultiConfiguration) => void} validate fn */ get validate() { const webpackOptionsSchemaCheck = - /** @type {(configuration: Configuration | Configuration[]) => boolean} */ + /** @type {(configuration: Configuration | MultiConfiguration) => boolean} */ (require("../schemas/WebpackOptions.check")); const getRealValidate = memoize( /** - * @returns {(configuration: Configuration | Configuration[]) => void} validate fn + * @returns {(configuration: Configuration | MultiConfiguration) => void} validate fn */ () => { const validateSchema = require("./validateSchema"); diff --git a/lib/javascript/JavascriptModulesPlugin.js b/lib/javascript/JavascriptModulesPlugin.js index 01ea2798b53..783e35183fe 100644 --- a/lib/javascript/JavascriptModulesPlugin.js +++ b/lib/javascript/JavascriptModulesPlugin.js @@ -186,7 +186,7 @@ const printGeneratedCodeForStack = (module, code) => { * @property {SyncWaterfallHook<[Source, RenderContext]>} render * @property {SyncWaterfallHook<[Source, Module, StartupRenderContext]>} renderStartup * @property {SyncWaterfallHook<[string, RenderBootstrapContext]>} renderRequire - * @property {SyncBailHook<[Module, RenderBootstrapContext], string | void>} inlineInRuntimeBailout + * @property {SyncBailHook<[Module, Partial], string | void>} inlineInRuntimeBailout * @property {SyncBailHook<[Module, RenderContext], string | void>} embedInRuntimeBailout * @property {SyncBailHook<[RenderContext], string | void>} strictRuntimeBailout * @property {SyncHook<[Chunk, Hash, ChunkHashContext]>} chunkHash diff --git a/lib/library/ModuleLibraryPlugin.js b/lib/library/ModuleLibraryPlugin.js index 2adc9342de7..6fd9faa8c58 100644 --- a/lib/library/ModuleLibraryPlugin.js +++ b/lib/library/ModuleLibraryPlugin.js @@ -8,6 +8,7 @@ const { ConcatSource } = require("webpack-sources"); const RuntimeGlobals = require("../RuntimeGlobals"); const Template = require("../Template"); +const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin"); const ConcatenatedModule = require("../optimize/ConcatenatedModule"); const propertyAccess = require("../util/propertyAccess"); const AbstractLibraryPlugin = require("./AbstractLibraryPlugin"); @@ -68,6 +69,10 @@ class ModuleLibraryPlugin extends AbstractLibraryPlugin { const { exportsDefinitions } = ConcatenatedModule.getCompilationHooks(compilation); exportsDefinitions.tap(PLUGIN_NAME, (definitions, module) => { + const bailout = JavascriptModulesPlugin.getCompilationHooks( + compilation + ).inlineInRuntimeBailout.call(module, {}); + if (bailout) return false; // If we have connections not all modules were concatenated, so we need the wrapper const connections = compilation.moduleGraph.getIncomingConnections(module); diff --git a/lib/stats/DefaultStatsPresetPlugin.js b/lib/stats/DefaultStatsPresetPlugin.js index 65a04bee68d..016e7d5064a 100644 --- a/lib/stats/DefaultStatsPresetPlugin.js +++ b/lib/stats/DefaultStatsPresetPlugin.js @@ -144,31 +144,31 @@ const NAMED_PRESETS = { }; /** - * @param {Partial} all stats option + * @param {Partial} all stats options * @returns {boolean} true when enabled, otherwise false */ const NORMAL_ON = ({ all }) => all !== false; /** - * @param {Partial} all stats option + * @param {Partial} all stats options * @returns {boolean} true when enabled, otherwise false */ const NORMAL_OFF = ({ all }) => all === true; /** - * @param {Partial} all stats option + * @param {Partial} all stats options * @param {CreateStatsOptionsContext} forToString stats options context * @returns {boolean} true when enabled, otherwise false */ const ON_FOR_TO_STRING = ({ all }, { forToString }) => forToString ? all !== false : all === true; /** - * @param {Partial} all stats option + * @param {Partial} all stats options * @param {CreateStatsOptionsContext} forToString stats options context * @returns {boolean} true when enabled, otherwise false */ const OFF_FOR_TO_STRING = ({ all }, { forToString }) => forToString ? all === true : all !== false; /** - * @param {Partial} all stats option + * @param {Partial} all stats options * @param {CreateStatsOptionsContext} forToString stats options context * @returns {boolean | "auto"} true when enabled, otherwise false */ diff --git a/lib/webpack.js b/lib/webpack.js index f6e56f2e39b..75b93687bd0 100644 --- a/lib/webpack.js +++ b/lib/webpack.js @@ -23,6 +23,7 @@ const memoize = require("./util/memoize"); /** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */ /** @typedef {import("./Compiler").WatchOptions} WatchOptions */ /** @typedef {import("./MultiCompiler").MultiCompilerOptions} MultiCompilerOptions */ +/** @typedef {import("./MultiCompiler").MultiWebpackOptions} MultiWebpackOptions */ /** @typedef {import("./MultiStats")} MultiStats */ /** @typedef {import("./Stats")} Stats */ @@ -100,14 +101,14 @@ const createCompiler = (rawOptions, compilerIndex) => { * @callback WebpackFunctionSingle * @param {WebpackOptions} options options object * @param {Callback=} callback callback - * @returns {Compiler} the compiler object + * @returns {Compiler | null} the compiler object */ /** * @callback WebpackFunctionMulti - * @param {ReadonlyArray & MultiCompilerOptions} options options objects + * @param {MultiWebpackOptions} options options objects * @param {Callback=} callback callback - * @returns {MultiCompiler} the multi compiler object + * @returns {MultiCompiler | null} the multi compiler object */ /** @@ -118,12 +119,15 @@ const createCompiler = (rawOptions, compilerIndex) => { const asArray = (options) => Array.isArray(options) ? [...options] : [options]; +/** + * @callback WebpackCallback + * @param {WebpackOptions | MultiWebpackOptions} options options + * @param {Callback & Callback=} callback callback + * @returns {Compiler | MultiCompiler | null} Compiler or MultiCompiler + */ + const webpack = /** @type {WebpackFunctionSingle & WebpackFunctionMulti} */ ( - /** - * @param {WebpackOptions | (ReadonlyArray & MultiCompilerOptions)} options options - * @param {Callback & Callback=} callback callback - * @returns {Compiler | MultiCompiler | null} Compiler or MultiCompiler - */ + /** @type {WebpackCallback} */ (options, callback) => { const create = () => { if (!asArray(options).every(webpackOptionsSchemaCheck)) { diff --git a/test/MultiStats.test.js b/test/MultiStats.test.js index 6fcb31376fe..6d1f4630b4c 100644 --- a/test/MultiStats.test.js +++ b/test/MultiStats.test.js @@ -4,11 +4,24 @@ require("./helpers/warmup-webpack"); const { Volume, createFsFromVolume } = require("memfs"); -describe("MultiStats", () => { - it("should create JSON of children stats", (done) => { +const compile = (options) => + new Promise((resolve, reject) => { const webpack = require(".."); - const compiler = webpack([ + const compiler = webpack(options); + compiler.outputFileSystem = createFsFromVolume(new Volume()); + compiler.run((err, stats) => { + if (err) { + reject(err); + } else { + resolve(stats); + } + }); + }); + +describe("MultiStats", () => { + it("should create JSON of children stats", async () => { + const stats = await compile([ { context: __dirname, entry: "./fixtures/a" @@ -18,19 +31,278 @@ describe("MultiStats", () => { entry: "./fixtures/b" } ]); - compiler.outputFileSystem = createFsFromVolume(new Volume()); - compiler.run((err, stats) => { - if (err) return done(err); - try { - const statsObject = stats.toJson(); - expect(statsObject).toEqual( - expect.objectContaining({ children: expect.any(Array) }) - ); - expect(statsObject.children).toHaveLength(2); - done(); - } catch (err) { - done(err); + + const statsObject = stats.toJson(); + expect(statsObject).toEqual( + expect.objectContaining({ children: expect.any(Array) }) + ); + expect(statsObject.children).toHaveLength(2); + }); + + it("should work with a boolean value", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" } - }); + ]); + + expect(stats.toJson(false)).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "name": undefined, + }, + Object { + "name": undefined, + }, + ], + } + `); + expect(stats.toString(false)).toMatchInlineSnapshot('""'); + }); + + it("should work with a string value", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" + } + ]); + + expect(stats.toJson("none")).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "name": undefined, + }, + Object { + "name": undefined, + }, + ], + } + `); + expect(stats.toString("none")).toMatchInlineSnapshot('""'); + }); + + it("should work with an object value", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" + } + ]); + + expect( + stats.toJson({ + all: false, + version: false, + errorsCount: true, + warningsCount: true + }) + ).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "errorsCount": 0, + "name": undefined, + "warningsCount": 1, + }, + Object { + "errorsCount": 0, + "name": undefined, + "warningsCount": 1, + }, + ], + "errorsCount": 0, + "warningsCount": 2, + } + `); + expect( + stats.toString({ + all: false, + version: false, + errorsCount: true, + warningsCount: true + }) + ).toMatchInlineSnapshot(` + "webpack compiled with 1 warning + + webpack compiled with 1 warning" + `); + }); + + it("should work with a boolean value for each children", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" + } + ]); + + const statsOptions = { + children: [false, false] + }; + + expect(stats.toJson(statsOptions)).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "name": undefined, + }, + Object { + "name": undefined, + }, + ], + } + `); + expect(stats.toString(statsOptions)).toMatchInlineSnapshot('""'); + }); + + it("should work with a string value for each children", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" + } + ]); + + const statsOptions = { + children: ["none", "none"] + }; + + expect(stats.toJson(statsOptions)).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "name": undefined, + }, + Object { + "name": undefined, + }, + ], + } + `); + expect(stats.toString(statsOptions)).toMatchInlineSnapshot('""'); + }); + + it("should work with an object value for each children", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" + } + ]); + + const statsOptions = { + children: [ + { + all: false, + publicPath: true, + version: false, + errorsCount: true, + warningsCount: true + }, + { + all: false, + version: false, + errorsCount: true, + warningsCount: true + } + ] + }; + + expect(stats.toJson(statsOptions)).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "errorsCount": 0, + "name": undefined, + "publicPath": "auto", + "warningsCount": 1, + }, + Object { + "errorsCount": 0, + "name": undefined, + "warningsCount": 1, + }, + ], + "errorsCount": 0, + "warningsCount": 2, + } + `); + expect(stats.toString(statsOptions)).toMatchInlineSnapshot(` + "PublicPath: auto + webpack compiled with 1 warning + + webpack compiled with 1 warning" + `); + }); + + it("should work with an mixed values for each children", async () => { + const stats = await compile([ + { + context: __dirname, + entry: "./fixtures/a" + }, + { + context: __dirname, + entry: "./fixtures/b" + } + ]); + + const statsOptions = { + children: [ + false, + { + all: false, + version: false, + errorsCount: true, + warningsCount: true + } + ] + }; + + expect(stats.toJson(statsOptions)).toMatchInlineSnapshot(` + Object { + "children": Array [ + Object { + "name": undefined, + }, + Object { + "errorsCount": 0, + "name": undefined, + "warningsCount": 1, + }, + ], + } + `); + expect(stats.toString(statsOptions)).toMatchInlineSnapshot( + '"webpack compiled with 1 warning"' + ); }); }); diff --git a/test/Stats.test.js b/test/Stats.test.js index 009b5eb4258..a939161535b 100644 --- a/test/Stats.test.js +++ b/test/Stats.test.js @@ -20,6 +20,52 @@ const compile = (options) => }); describe("Stats", () => { + it("should work with a boolean value", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/a" + }); + expect(stats.toJson(false)).toMatchInlineSnapshot("Object {}"); + expect(stats.toString(false)).toMatchInlineSnapshot('""'); + }); + + it("should work with a string value", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/a" + }); + expect(stats.toJson("none")).toMatchInlineSnapshot("Object {}"); + expect(stats.toString("none")).toMatchInlineSnapshot('""'); + }); + + it("should work with an object value", async () => { + const stats = await compile({ + context: __dirname, + entry: "./fixtures/a" + }); + expect( + stats.toJson({ + all: false, + version: false, + errorsCount: true, + warningsCount: true + }) + ).toMatchInlineSnapshot(` + Object { + "errorsCount": 0, + "warningsCount": 1, + } + `); + expect( + stats.toString({ + all: false, + version: false, + errorsCount: true, + warningsCount: true + }) + ).toMatchInlineSnapshot('"webpack compiled with 1 warning"'); + }); + it("should print env string in stats", async () => { const stats = await compile({ context: __dirname, diff --git a/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap b/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap index 3d5a1526b01..f38dbd5ef75 100644 --- a/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap +++ b/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap @@ -3498,6 +3498,8 @@ exports[`ConfigCacheTestCases css css-modules-no-space exported tests should all exports[`ConfigCacheTestCases css css-order exported tests keep consistent css order 1`] = `".button-module { padding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px;}.teaser-module { padding: 20px; border: 1px solid #ddd; border-radius: 8px; margin: 16px;}.teaser-module { background-color: orange;}"`; +exports[`ConfigCacheTestCases css css-order-concatenate-modules exported tests keep consistent css order 1`] = `".a { color: red;}.b{ color: green;}.c{ color: black;}"`; + exports[`ConfigCacheTestCases css css-order-reexport exported tests keep consistent css order 1`] = `".dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; exports[`ConfigCacheTestCases css css-order2 exported tests keep consistent css order 1`] = `".dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; diff --git a/test/__snapshots__/ConfigTestCases.basictest.js.snap b/test/__snapshots__/ConfigTestCases.basictest.js.snap index fc6de606181..3542d2527fa 100644 --- a/test/__snapshots__/ConfigTestCases.basictest.js.snap +++ b/test/__snapshots__/ConfigTestCases.basictest.js.snap @@ -3498,6 +3498,8 @@ exports[`ConfigTestCases css css-modules-no-space exported tests should allow to exports[`ConfigTestCases css css-order exported tests keep consistent css order 1`] = `".button-module { padding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px;}.teaser-module { padding: 20px; border: 1px solid #ddd; border-radius: 8px; margin: 16px;}.teaser-module { background-color: orange;}"`; +exports[`ConfigTestCases css css-order-concatenate-modules exported tests keep consistent css order 1`] = `".a { color: red;}.b{ color: green;}.c{ color: black;}"`; + exports[`ConfigTestCases css css-order-reexport exported tests keep consistent css order 1`] = `".dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; exports[`ConfigTestCases css css-order2 exported tests keep consistent css order 1`] = `".dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; diff --git a/test/configCases/css/css-order-concatenate-modules/index.js b/test/configCases/css/css-order-concatenate-modules/index.js new file mode 100644 index 00000000000..faf7574346a --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/index.js @@ -0,0 +1,15 @@ +import { c, b, a } from "dep"; + +c() +b() +a() + +it("keep consistent css order", function() { + const fs = __non_webpack_require__("fs"); + let source = fs.readFileSync(__dirname + "/main.css", "utf-8"); + expect(removeComments(source)).toMatchSnapshot() +}); + +function removeComments(source) { + return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\n/g, ""); +} diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/a.css b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/a.css new file mode 100644 index 00000000000..04af14f0c01 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/a.css @@ -0,0 +1,3 @@ +.a { + color: red; +} \ No newline at end of file diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/a.js b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/a.js new file mode 100644 index 00000000000..de7acb49ee7 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/a.js @@ -0,0 +1,2 @@ +import "./a.css" +export function a() {} \ No newline at end of file diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/b.css b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/b.css new file mode 100644 index 00000000000..43c4cc6d378 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/b.css @@ -0,0 +1,3 @@ +.b{ + color: green; +} \ No newline at end of file diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/b.js b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/b.js new file mode 100644 index 00000000000..82aff859974 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/b.js @@ -0,0 +1,2 @@ +import "./b.css" +export function b() {} \ No newline at end of file diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/c.css b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/c.css new file mode 100644 index 00000000000..8c7886af387 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/c.css @@ -0,0 +1,3 @@ +.c{ + color: black; +} \ No newline at end of file diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/c.js b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/c.js new file mode 100644 index 00000000000..0813f955fdd --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/c.js @@ -0,0 +1,2 @@ +import "./c.css" +export function c() {} \ No newline at end of file diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/index.js b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/index.js new file mode 100644 index 00000000000..6fc01ace2a8 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/index.js @@ -0,0 +1,3 @@ +export * from "./a.js" +export * from "./b.js" +export * from "./c.js" diff --git a/test/configCases/css/css-order-concatenate-modules/node_modules/dep/package.json b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/package.json new file mode 100644 index 00000000000..644d902d8e0 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/node_modules/dep/package.json @@ -0,0 +1,6 @@ +{ + "name": "dep", + "version": "1.0.0", + "type": "module", + "sideEffects": false +} diff --git a/test/configCases/css/css-order-concatenate-modules/package.json b/test/configCases/css/css-order-concatenate-modules/package.json new file mode 100644 index 00000000000..f9f7ed01bb1 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/package.json @@ -0,0 +1,8 @@ +{ + "name": "css-order", + "version": "1.0.0", + "sideEffects": false, + "devDependencies": { + "mini-css-extract-plugin": "^2.9.0" + } +} diff --git a/test/configCases/css/css-order-concatenate-modules/webpack.config.js b/test/configCases/css/css-order-concatenate-modules/webpack.config.js new file mode 100644 index 00000000000..5decd0f98e5 --- /dev/null +++ b/test/configCases/css/css-order-concatenate-modules/webpack.config.js @@ -0,0 +1,46 @@ +"use strict"; + +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +/** @type {import("../../../../").Configuration} */ +module.exports = { + devtool: false, + target: "web", + entry: "./index.js", + mode: "development", + optimization: { + concatenateModules: true + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + { + loader: MiniCssExtractPlugin.loader + }, + { + loader: "css-loader", + options: { + esModule: true, + modules: { + namedExport: false, + localIdentName: "[name]" + } + } + } + ], + sideEffects: true + } + ] + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css" + }) + ], + node: { + __dirname: false, + __filename: false + } +}; diff --git a/test/configCases/externals/devtool-eval-concatenate-modules/imported.js b/test/configCases/externals/devtool-eval-concatenate-modules/imported.js new file mode 100644 index 00000000000..aef22247d75 --- /dev/null +++ b/test/configCases/externals/devtool-eval-concatenate-modules/imported.js @@ -0,0 +1 @@ +export default 1; diff --git a/test/configCases/externals/devtool-eval-concatenate-modules/index.js b/test/configCases/externals/devtool-eval-concatenate-modules/index.js new file mode 100644 index 00000000000..69d26e2947c --- /dev/null +++ b/test/configCases/externals/devtool-eval-concatenate-modules/index.js @@ -0,0 +1,7 @@ +import imported from "./imported.mjs"; + +it("should allow to use externals in concatenated modules", () => { + expect(imported).toBe(1); +}); + +export { imported } \ No newline at end of file diff --git a/test/configCases/externals/devtool-eval-concatenate-modules/test.config.js b/test/configCases/externals/devtool-eval-concatenate-modules/test.config.js new file mode 100644 index 00000000000..04d5e6a4b35 --- /dev/null +++ b/test/configCases/externals/devtool-eval-concatenate-modules/test.config.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + findBundle() { + return "./main.mjs"; + } +}; diff --git a/test/configCases/externals/devtool-eval-concatenate-modules/webpack.config.js b/test/configCases/externals/devtool-eval-concatenate-modules/webpack.config.js new file mode 100644 index 00000000000..2b8f2064663 --- /dev/null +++ b/test/configCases/externals/devtool-eval-concatenate-modules/webpack.config.js @@ -0,0 +1,38 @@ +"use strict"; + +/** @type {import("../../../../types").Configuration} */ +module.exports = { + devtool: "eval", + module: { + parser: { + javascript: { + importMeta: false + } + } + }, + entry: { + main: "./index.js", + imported: { + import: "./imported.js", + library: { + type: "module" + } + } + }, + target: "node14", + output: { + filename: "[name].mjs", + module: true, + library: { + type: "module" + } + }, + externals: "./imported.mjs", + externalsType: "module", + experiments: { + outputModule: true + }, + optimization: { + concatenateModules: true + } +}; diff --git a/types.d.ts b/types.d.ts index 2fa4312278f..df39b3f509c 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1180,6 +1180,7 @@ declare interface CallbackWebpack { (err: null | Error, stats?: T): void; } type Cell = undefined | T; +type ChildrenStatsOptions = undefined | string | boolean | StatsOptions; declare class Chunk { constructor(name?: null | string, backCompat?: boolean); id: null | string | number; @@ -1994,6 +1995,12 @@ declare interface Colors { bgCyanBright: (value?: any) => string; bgWhiteBright: (value?: any) => string; } +declare interface ColorsOptions { + /** + * force use colors + */ + useColor?: boolean; +} declare interface Comparator { (a: T, b: T): 0 | 1 | -1; } @@ -2562,7 +2569,7 @@ declare interface CompilationHooksJavascriptModulesPlugin { renderStartup: SyncWaterfallHook<[Source, Module, StartupRenderContext]>; renderRequire: SyncWaterfallHook<[string, RenderBootstrapContext]>; inlineInRuntimeBailout: SyncBailHook< - [Module, RenderBootstrapContext], + [Module, Partial], string | void >; embedInRuntimeBailout: SyncBailHook< @@ -10460,14 +10467,18 @@ declare interface MultiCompilerOptions { */ parallelism?: number; } +type MultiConfiguration = ReadonlyArray & MultiCompilerOptions; declare abstract class MultiStats { stats: Stats[]; get hash(): string; hasErrors(): boolean; hasWarnings(): boolean; - toJson(options?: string | boolean | StatsOptions): StatsCompilation; - toString(options?: string | boolean | StatsOptions): string; + toJson(options?: string | boolean | MultiStatsOptions): StatsCompilation; + toString(options?: string | boolean | MultiStatsOptions): string; } +type MultiStatsOptions = Omit & { + children?: string | boolean | StatsOptions | ChildrenStatsOptions[]; +}; declare abstract class MultiWatching { watchings: Watching[]; compiler: MultiCompiler; @@ -10920,43 +10931,43 @@ declare class NormalModuleReplacementPlugin { type NormalizedStatsOptions = KnownNormalizedStatsOptions & Omit< StatsOptions, - | "context" - | "chunkGroups" - | "requestShortener" - | "chunksSort" - | "modulesSort" - | "chunkModulesSort" - | "nestedModulesSort" | "assetsSort" - | "ids" - | "cachedAssets" - | "groupAssetsByEmitStatus" - | "groupAssetsByPath" - | "groupAssetsByExtension" | "assetsSpace" - | "excludeAssets" - | "excludeModules" - | "warningsFilter" + | "cachedAssets" | "cachedModules" - | "orphanModules" + | "chunkGroupAuxiliary" + | "chunkGroupChildren" + | "chunkGroupMaxAssets" + | "chunkGroups" + | "chunkModulesSpace" + | "chunksSort" + | "context" | "dependentModules" - | "runtimeModules" + | "entrypoints" + | "excludeAssets" + | "excludeModules" + | "groupAssetsByEmitStatus" + | "groupAssetsByExtension" + | "groupAssetsByPath" + | "groupModulesByAttributes" | "groupModulesByCacheStatus" + | "groupModulesByExtension" | "groupModulesByLayer" - | "groupModulesByAttributes" | "groupModulesByPath" - | "groupModulesByExtension" | "groupModulesByType" - | "entrypoints" - | "chunkGroupAuxiliary" - | "chunkGroupChildren" - | "chunkGroupMaxAssets" - | "modulesSpace" - | "chunkModulesSpace" - | "nestedModulesSpace" + | "ids" | "logging" | "loggingDebug" | "loggingTrace" + | "modulesSort" + | "modulesSpace" + | "nestedModulesSpace" + | "orphanModules" + | "runtimeModules" + | "warningsFilter" + | "requestShortener" + | "chunkModulesSort" + | "nestedModulesSort" | "_env" > & Record; @@ -12617,7 +12628,7 @@ declare class ProgressPlugin { showModules?: boolean; showDependencies?: boolean; showActiveModules?: boolean; - percentBy?: null | "entries" | "modules" | "dependencies"; + percentBy?: null | "modules" | "entries" | "dependencies"; apply(compiler: Compiler | MultiCompiler): void; static getReporter( compiler: Compiler @@ -12682,7 +12693,7 @@ declare interface ProgressPluginOptions { /** * Collect percent algorithm. By default it calculates by a median from modules, entries and dependencies percent. */ - percentBy?: null | "entries" | "modules" | "dependencies"; + percentBy?: null | "modules" | "entries" | "dependencies"; /** * Collect profile data for progress steps. Default: false. @@ -17432,21 +17443,24 @@ declare interface WriteStreamOptions { declare function exports( options: Configuration, callback?: CallbackWebpack -): Compiler; +): null | Compiler; declare function exports( - options: ReadonlyArray & MultiCompilerOptions, + options: MultiConfiguration, callback?: CallbackWebpack -): MultiCompiler; +): null | MultiCompiler; declare namespace exports { export const webpack: { - (options: Configuration, callback?: CallbackWebpack): Compiler; ( - options: ReadonlyArray & MultiCompilerOptions, + options: Configuration, + callback?: CallbackWebpack + ): null | Compiler; + ( + options: MultiConfiguration, callback?: CallbackWebpack - ): MultiCompiler; + ): null | MultiCompiler; }; export const validate: ( - configuration: Configuration | Configuration[] + configuration: Configuration | MultiConfiguration ) => void; export const validateSchema: ( schema: Parameters[0], @@ -17455,7 +17469,7 @@ declare namespace exports { ) => void; export const version: string; export namespace cli { - export let createColors: (__0?: { useColor?: boolean }) => Colors; + export let createColors: (__0?: ColorsOptions) => Colors; export let getArguments: ( schema?: | (JSONSchema4 & { @@ -18145,7 +18159,9 @@ declare namespace exports { AssetEmittedInfo, Entrypoint, MultiCompilerOptions, + MultiConfiguration, MultiStats, + MultiStatsOptions, ResolveData, ParserState, ResolvePluginInstance, @@ -18153,6 +18169,8 @@ declare namespace exports { Watching, Argument, Problem, + Colors, + ColorsOptions, StatsAsset, StatsChunk, StatsChunkGroup,