diff --git a/lib/Compilation.js b/lib/Compilation.js index 20dca8e7184..b5f7558ac61 100644 --- a/lib/Compilation.js +++ b/lib/Compilation.js @@ -292,7 +292,7 @@ const { isSourceEqual } = require("./util/source"); * @property {boolean=} development true, when asset is only used for development and doesn't count towards user-facing assets * @property {boolean=} hotModuleReplacement true, when asset ships data for updating an existing application (HMR) * @property {boolean=} javascriptModule true, when asset is javascript and an ESM - * @property {Record=} related object of pointers to other assets, keyed by type of relation (only points from parent to child) + * @property {Record=} related object of pointers to other assets, keyed by type of relation (only points from parent to child) */ /** @typedef {KnownAssetInfo & Record} AssetInfo */ diff --git a/lib/ModuleGraph.js b/lib/ModuleGraph.js index c9cd7cfbe40..554bbde0493 100644 --- a/lib/ModuleGraph.js +++ b/lib/ModuleGraph.js @@ -10,7 +10,7 @@ const ExportsInfo = require("./ExportsInfo"); const ModuleGraphConnection = require("./ModuleGraphConnection"); const SortableSet = require("./util/SortableSet"); const WeakTupleMap = require("./util/WeakTupleMap"); -const { compareNumbers, compareSelect } = require("./util/comparators"); +const { sortWithSourceOrder } = require("./util/comparators"); /** @typedef {import("./Compilation").ModuleMemCaches} ModuleMemCaches */ /** @typedef {import("./DependenciesBlock")} DependenciesBlock */ @@ -22,6 +22,7 @@ const { compareNumbers, compareSelect } = require("./util/comparators"); /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */ /** @typedef {import("./dependencies/HarmonyImportSideEffectDependency")} HarmonyImportSideEffectDependency */ /** @typedef {import("./dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */ +/** @typedef {import("./util/comparators").DependencySourceOrder} DependencySourceOrder */ /** * @callback OptimizationBailoutFunction @@ -31,17 +32,6 @@ const { compareNumbers, compareSelect } = require("./util/comparators"); const EMPTY_SET = new Set(); -/** - * @param {number} num the input number (should be less than or equal to total) - * @param {number} total the total number used to determine decimal places - * @returns {number} the decimal representation of num - */ -function numberToDecimal(num, total) { - const totalDigitCount = total.toString().length; - const divisor = 10 ** totalDigitCount; - return num / divisor; -} - /** * @param {SortableSet} set input * @returns {readonly Map} mapped by origin module @@ -174,7 +164,7 @@ class ModuleGraph { this._cacheStage = undefined; /** - * @type {WeakMap} + * @type {WeakMap} * @private */ this._dependencySourceOrderMap = new WeakMap(); @@ -308,14 +298,17 @@ class ModuleGraph { return; } const originDependency = connection.dependency; + // src/index.js // import { c } from "lib/c" -> c = 0 - // import { a, b } from "lib": a and b have the same source order -> a = b = 1 + // import { a, b } from "lib" -> a and b have the same source order -> a = b = 1 + // import { d } from "lib/d" -> d = 2 const currentSourceOrder = /** @type { HarmonyImportSideEffectDependency | HarmonyImportSpecifierDependency} */ ( dependency ).sourceOrder; - // lib/index.js + + // lib/index.js (reexport) // import { a } from "lib/a" -> a = 0 // import { b } from "lib/b" -> b = 1 const originSourceOrder = @@ -328,26 +321,19 @@ class ModuleGraph { ) { // src/index.js // import { c } from "lib/c" -> c = 0 - // import { a } from "lib/a" -> a = 1 + 0.0 - // import { b } from "lib/b" -> b = 1 + 0.1 - const newSourceOrder = - currentSourceOrder + - numberToDecimal(originSourceOrder, parentModule.dependencies.length); - - this._dependencySourceOrderMap.set(dependency, newSourceOrder); + // import { a } from "lib/a" -> a = 1.0 = 1(main) + 0.0(sub) + // import { b } from "lib/b" -> b = 1.1 = 1(main) + 0.1(sub) + // import { d } from "lib/d" -> d = 2 + this._dependencySourceOrderMap.set(dependency, { + main: currentSourceOrder, + sub: originSourceOrder + }); // If dependencies like HarmonyImportSideEffectDependency and HarmonyImportSpecifierDependency have a SourceOrder, // we sort based on it; otherwise, we preserve the original order. - parentModule.dependencies.sort( - compareSelect( - a => - this._dependencySourceOrderMap.has(a) - ? this._dependencySourceOrderMap.get(a) - : /** @type { HarmonyImportSideEffectDependency | HarmonyImportSpecifierDependency} */ ( - a - ).sourceOrder, - compareNumbers - ) + sortWithSourceOrder( + parentModule.dependencies, + this._dependencySourceOrderMap ); for (const [index, dep] of parentModule.dependencies.entries()) { diff --git a/lib/NormalModule.js b/lib/NormalModule.js index 7f9676bc52a..20ff21b3b74 100644 --- a/lib/NormalModule.js +++ b/lib/NormalModule.js @@ -38,10 +38,10 @@ const { isSubset } = require("./util/SetHelpers"); const { getScheme } = require("./util/URLAbsoluteSpecifier"); const { compareLocations, - compareNumbers, compareSelect, concatComparators, - keepOriginalOrder + keepOriginalOrder, + sortWithSourceOrder } = require("./util/comparators"); const createHash = require("./util/createHash"); const { createFakeHook } = require("./util/deprecation"); @@ -1220,20 +1220,11 @@ class NormalModule extends Module { const handleParseResult = () => { this.dependencies.sort( concatComparators( - // For HarmonyImportSideEffectDependency and HarmonyImportSpecifierDependency, we should prioritize import order to match the behavior of running modules directly in a JS engine without a bundler. - // For other types like ConstDependency, we can instead prioritize usage order. - // https://github.com/webpack/webpack/pull/19686 - compareSelect( - a => - /** @type {HarmonyImportSideEffectDependency | HarmonyImportSpecifierDependency} */ ( - a - ).sourceOrder, - compareNumbers - ), compareSelect(a => a.loc, compareLocations), keepOriginalOrder(this.dependencies) ) ); + sortWithSourceOrder(this.dependencies, new WeakMap()); this._initBuildHash(compilation); this._lastSuccessfulBuildMeta = /** @type {BuildMeta} */ diff --git a/lib/asset/AssetGenerator.js b/lib/asset/AssetGenerator.js index f4c2fae54f6..90cb71b23ae 100644 --- a/lib/asset/AssetGenerator.js +++ b/lib/asset/AssetGenerator.js @@ -57,8 +57,8 @@ const nonNumericOnlyHash = require("../util/nonNumericOnlyHash"); /** * @template T * @template U - * @param {string | Array | Set | undefined} a a - * @param {string | Array | Set | undefined} b b + * @param {null | string | Array | Set | undefined} a a + * @param {null | string | Array | Set | undefined} b b * @returns {Array & Array} array */ const mergeMaybeArrays = (a, b) => { diff --git a/lib/stats/DefaultStatsFactoryPlugin.js b/lib/stats/DefaultStatsFactoryPlugin.js index dce67f1dae4..2e71085ad3b 100644 --- a/lib/stats/DefaultStatsFactoryPlugin.js +++ b/lib/stats/DefaultStatsFactoryPlugin.js @@ -796,6 +796,7 @@ const SIMPLE_EXTRACTORS = { ? relatedEntry : [relatedEntry]; for (const dep of deps) { + if (!dep) continue; const depItem = assetMap.get(dep); if (!depItem) continue; assets.delete(depItem); diff --git a/lib/util/comparators.js b/lib/util/comparators.js index ed01f939cc5..ce8cd40e5bf 100644 --- a/lib/util/comparators.js +++ b/lib/util/comparators.js @@ -13,9 +13,18 @@ const { compareRuntime } = require("./runtime"); /** @typedef {import("../ChunkGraph").ModuleId} ModuleId */ /** @typedef {import("../ChunkGroup")} ChunkGroup */ /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */ +/** @typedef {import("../Dependency")} Dependency */ +/** @typedef {import("../dependencies/HarmonyImportSideEffectDependency")} HarmonyImportSideEffectDependency */ +/** @typedef {import("../dependencies/HarmonyImportSpecifierDependency")} HarmonyImportSpecifierDependency */ /** @typedef {import("../Module")} Module */ /** @typedef {import("../ModuleGraph")} ModuleGraph */ +/** + * @typedef {object} DependencySourceOrder + * @property {number} main the main source order + * @property {number} sub the sub source order + */ + /** * @template T * @typedef {(a: T, b: T) => -1 | 0 | 1} Comparator @@ -497,6 +506,95 @@ const compareChunksNatural = chunkGraph => { ); }; +/** + * For HarmonyImportSideEffectDependency and HarmonyImportSpecifierDependency, we should prioritize import order to match the behavior of running modules directly in a JS engine without a bundler. + * For other types like ConstDependency, we can instead prioritize usage order. + * https://github.com/webpack/webpack/pull/19686 + * @param {Dependency[]} dependencies dependencies + * @param {WeakMap} dependencySourceOrderMap dependency source order map + * @returns {void} + */ +const sortWithSourceOrder = (dependencies, dependencySourceOrderMap) => { + /** + * @param {Dependency} dep dependency + * @returns {number} source order + */ + const getSourceOrder = dep => { + if (dependencySourceOrderMap.has(dep)) { + const { main } = /** @type {DependencySourceOrder} */ ( + dependencySourceOrderMap.get(dep) + ); + return main; + } + return /** @type { HarmonyImportSideEffectDependency | HarmonyImportSpecifierDependency} */ ( + dep + ).sourceOrder; + }; + + /** + * If the sourceOrder is a number, it means the dependency needs to be sorted. + * @param {number | undefined} sourceOrder sourceOrder + * @returns {boolean} needReSort + */ + const needReSort = sourceOrder => { + if (typeof sourceOrder === "number") { + return true; + } + return false; + }; + + // Extract dependencies with sourceOrder and sort them + const withSourceOrder = []; + + // First pass: collect dependencies with sourceOrder + for (let i = 0; i < dependencies.length; i++) { + const dep = dependencies[i]; + const sourceOrder = getSourceOrder(dep); + + if (needReSort(sourceOrder)) { + withSourceOrder.push({ dep, sourceOrder, originalIndex: i }); + } + } + + if (withSourceOrder.length === 0) { + return; + } + + // Sort dependencies with sourceOrder + withSourceOrder.sort((a, b) => { + // Handle both dependencies in map case + if ( + dependencySourceOrderMap.has(a.dep) && + dependencySourceOrderMap.has(b.dep) + ) { + const { main: mainA, sub: subA } = /** @type {DependencySourceOrder} */ ( + dependencySourceOrderMap.get(a.dep) + ); + const { main: mainB, sub: subB } = /** @type {DependencySourceOrder} */ ( + dependencySourceOrderMap.get(b.dep) + ); + if (mainA === mainB) { + return compareNumbers(subA, subB); + } + return compareNumbers(mainA, mainB); + } + + return compareNumbers(a.sourceOrder, b.sourceOrder); + }); + + // Second pass: build result array + let sortedIndex = 0; + for (let i = 0; i < dependencies.length; i++) { + const dep = dependencies[i]; + const sourceOrder = getSourceOrder(dep); + + if (needReSort(sourceOrder)) { + dependencies[i] = withSourceOrder[sortedIndex].dep; + sortedIndex++; + } + } +}; + module.exports.compareChunkGroupsByIndex = compareChunkGroupsByIndex; /** @type {ParameterizedComparator} */ module.exports.compareChunks = @@ -548,3 +646,4 @@ module.exports.compareStringsNumeric = compareStringsNumeric; module.exports.concatComparators = concatComparators; module.exports.keepOriginalOrder = keepOriginalOrder; +module.exports.sortWithSourceOrder = sortWithSourceOrder; diff --git a/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap b/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap index de50ab8aa01..c574aba6957 100644 --- a/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap +++ b/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap @@ -3500,6 +3500,8 @@ exports[`ConfigCacheTestCases css css-order exported tests keep consistent css o exports[`ConfigCacheTestCases css css-order2 exported tests keep consistent css order 1`] = `".dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; +exports[`ConfigCacheTestCases css css-order3 exported tests keep consistent css order 1`] = `".dependency3::before { content: \\"dependency3\\";}.dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; + exports[`ConfigCacheTestCases css escape-unescape exported tests should work with URLs in CSS: classes 1`] = ` Object { "#": "_style_modules_css-#", diff --git a/test/__snapshots__/ConfigTestCases.basictest.js.snap b/test/__snapshots__/ConfigTestCases.basictest.js.snap index dd1352d381e..4589d2ef1f6 100644 --- a/test/__snapshots__/ConfigTestCases.basictest.js.snap +++ b/test/__snapshots__/ConfigTestCases.basictest.js.snap @@ -3500,6 +3500,8 @@ exports[`ConfigTestCases css css-order exported tests keep consistent css order exports[`ConfigTestCases css css-order2 exported tests keep consistent css order 1`] = `".dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; +exports[`ConfigTestCases css css-order3 exported tests keep consistent css order 1`] = `".dependency3::before { content: \\"dependency3\\";}.dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; + exports[`ConfigTestCases css escape-unescape exported tests should work with URLs in CSS: classes 1`] = ` Object { "#": "_style_modules_css-#", diff --git a/test/compareSourceOrder.unittest.js b/test/compareSourceOrder.unittest.js new file mode 100644 index 00000000000..06974832ec5 --- /dev/null +++ b/test/compareSourceOrder.unittest.js @@ -0,0 +1,123 @@ +"use strict"; + +const { sortWithSourceOrder } = require("../lib/util/comparators"); + +describe("sortWithSourceOrder", () => { + let dependencySourceOrderMap; + + beforeEach(() => { + dependencySourceOrderMap = new WeakMap(); + }); + + it("dependency without the sourceOrder attribute must keep their original index in the array", () => { + const deps = [ + // HarmonyImportSpecifierDependency + { name: "b", sourceOrder: 10 }, + // CommonJSRequireDependency + { name: "a" }, + // CommonJSRequireDependency + { name: "d" }, + // HarmonyImportSpecifierDependency + { name: "c", sourceOrder: 5 } + ]; + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["c", "a", "d", "b"]); + }); + + it("should sort dependencies by main order when both in map", () => { + const deps = [ + { name: "b", sourceOrder: 5 }, + { name: "a", sourceOrder: 10 }, + { name: "c", sourceOrder: 3 } + ]; + + // Add to map with main and sub orders + dependencySourceOrderMap.set(deps[0], { main: 5, sub: 0 }); + dependencySourceOrderMap.set(deps[1], { main: 10, sub: 0 }); + dependencySourceOrderMap.set(deps[2], { main: 3, sub: 0 }); + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["c", "b", "a"]); + }); + + it("should sort by sub order when main order is same", () => { + const deps = [ + { name: "b", sourceOrder: 5 }, + { name: "a", sourceOrder: 5 }, + { name: "c", sourceOrder: 5 } + ]; + + // Add to map with same main but different sub orders + dependencySourceOrderMap.set(deps[0], { main: 5, sub: 3 }); + dependencySourceOrderMap.set(deps[1], { main: 5, sub: 1 }); + dependencySourceOrderMap.set(deps[2], { main: 5, sub: 2 }); + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["a", "c", "b"]); + }); + + it("should sort mixed dependencies - some in map, some not", () => { + const deps = [ + { name: "b", sourceOrder: 10 }, + { name: "a", sourceOrder: 5 }, + { name: "c", sourceOrder: 15 } + ]; + + // Only add one to map + dependencySourceOrderMap.set(deps[0], { main: 10, sub: 0 }); + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["a", "b", "c"]); + }); + + it("should sort by sourceOrder when none in map", () => { + const deps = [ + { name: "b", sourceOrder: 10 }, + { name: "a", sourceOrder: 5 }, + { name: "c", sourceOrder: 15 } + ]; + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["a", "b", "c"]); + }); + + it("should sort complex scenario with negative and decimal values", () => { + const deps = [ + { name: "f", sourceOrder: 10 }, + { name: "e", sourceOrder: 5 }, + { name: "d", sourceOrder: 20 }, + { name: "c", sourceOrder: 10 }, + { name: "b", sourceOrder: 5 }, + { name: "a", sourceOrder: 3 } + ]; + + dependencySourceOrderMap.set(deps[0], { main: 10, sub: 0.5 }); + dependencySourceOrderMap.set(deps[1], { main: 5, sub: 0.5 }); + dependencySourceOrderMap.set(deps[2], { main: 20, sub: 0 }); + dependencySourceOrderMap.set(deps[3], { main: 10, sub: 0.25 }); + dependencySourceOrderMap.set(deps[4], { main: 5, sub: 0.25 }); + dependencySourceOrderMap.set(deps[5], { main: 3, sub: 0 }); + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["a", "b", "e", "c", "f", "d"]); + }); + + it("should maintain stable sort for equal values", () => { + const deps = [ + { name: "b", sourceOrder: 5 }, + { name: "a", sourceOrder: 5 }, + { name: "c", sourceOrder: 5 } + ]; + + sortWithSourceOrder(deps, dependencySourceOrderMap); + + expect(deps.map(d => d.name)).toEqual(["b", "a", "c"]); + }); +}); diff --git a/test/configCases/assets/prevent-related-deletion/index.js b/test/configCases/assets/prevent-related-deletion/index.js new file mode 100644 index 00000000000..da3c15d23b6 --- /dev/null +++ b/test/configCases/assets/prevent-related-deletion/index.js @@ -0,0 +1,13 @@ +const fs = require("fs") +const path = require("path") + +try { + import("./module.js"); +} catch (_err) { + // Nothing +} + +it("should prevent related deletion", async () => { + expect(fs.existsSync(path.join(__STATS__.outputPath, "module_js.bundle0.js"))).toBe(false); + expect(fs.existsSync(path.join(__STATS__.outputPath, "module_js.bundle0.js.map"))).toBe(true); +}); diff --git a/test/configCases/assets/prevent-related-deletion/module.js b/test/configCases/assets/prevent-related-deletion/module.js new file mode 100644 index 00000000000..503bca78c72 --- /dev/null +++ b/test/configCases/assets/prevent-related-deletion/module.js @@ -0,0 +1 @@ +export default "module"; diff --git a/test/configCases/assets/prevent-related-deletion/webpack.config.js b/test/configCases/assets/prevent-related-deletion/webpack.config.js new file mode 100644 index 00000000000..5f55eeaca0a --- /dev/null +++ b/test/configCases/assets/prevent-related-deletion/webpack.config.js @@ -0,0 +1,31 @@ +const { Compilation } = require("../../../../"); + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "node", + optimization: { + minimize: true, + chunkIds: "named" + }, + devtool: "source-map", + plugins: [ + compiler => { + compiler.hooks.compilation.tap("Test", compilation => { + compilation.hooks.processAssets.tap( + { + name: "Test", + stage: Compilation.PROCESS_ASSETS_STAGE_ANALYSE + }, + () => { + compilation.updateAsset( + "module_js.bundle0.js", + compilation.assets["module_js.bundle0.js"], + { related: { sourceMap: null } } + ); + compilation.deleteAsset("module_js.bundle0.js"); + } + ); + }); + } + ] +}; diff --git a/test/configCases/css/css-order3/component.js b/test/configCases/css/css-order3/component.js new file mode 100644 index 00000000000..05fbc35874b --- /dev/null +++ b/test/configCases/css/css-order3/component.js @@ -0,0 +1,8 @@ +const { dependency3 } = require("./dependency/dependency3"); +import { dependency, dependency2 } from "./dependency"; + +export function component() { + dependency(); + dependency2(); + dependency3(); +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/dependency.css b/test/configCases/css/css-order3/dependency/dependency.css new file mode 100644 index 00000000000..776c3714d84 --- /dev/null +++ b/test/configCases/css/css-order3/dependency/dependency.css @@ -0,0 +1,3 @@ +.dependency::before { + content: "dependency"; +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/dependency.js b/test/configCases/css/css-order3/dependency/dependency.js new file mode 100644 index 00000000000..66dbef95df9 --- /dev/null +++ b/test/configCases/css/css-order3/dependency/dependency.js @@ -0,0 +1,5 @@ +import styles from "./dependency.css"; + +export function dependency() { + return styles !== undefined; +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/dependency2.css b/test/configCases/css/css-order3/dependency/dependency2.css new file mode 100644 index 00000000000..f882c894fdd --- /dev/null +++ b/test/configCases/css/css-order3/dependency/dependency2.css @@ -0,0 +1,3 @@ +.dependency2::before { + content: "dependency2"; +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/dependency2.js b/test/configCases/css/css-order3/dependency/dependency2.js new file mode 100644 index 00000000000..f59b231020f --- /dev/null +++ b/test/configCases/css/css-order3/dependency/dependency2.js @@ -0,0 +1,5 @@ +import styles from "./dependency2.css"; + +export function dependency2() { + return styles !== undefined; +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/dependency3.css b/test/configCases/css/css-order3/dependency/dependency3.css new file mode 100644 index 00000000000..943e21d23af --- /dev/null +++ b/test/configCases/css/css-order3/dependency/dependency3.css @@ -0,0 +1,3 @@ +.dependency3::before { + content: "dependency3"; +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/dependency3.js b/test/configCases/css/css-order3/dependency/dependency3.js new file mode 100644 index 00000000000..5a7124ffa4e --- /dev/null +++ b/test/configCases/css/css-order3/dependency/dependency3.js @@ -0,0 +1,5 @@ +import styles from "./dependency3.css"; + +export function dependency3() { + return styles !== undefined; +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/dependency/index.js b/test/configCases/css/css-order3/dependency/index.js new file mode 100644 index 00000000000..035ddd4d727 --- /dev/null +++ b/test/configCases/css/css-order3/dependency/index.js @@ -0,0 +1,2 @@ +export * from "./dependency2"; +export * from "./dependency"; diff --git a/test/configCases/css/css-order3/dependency/package.json b/test/configCases/css/css-order3/dependency/package.json new file mode 100644 index 00000000000..ea2fc66bebe --- /dev/null +++ b/test/configCases/css/css-order3/dependency/package.json @@ -0,0 +1,7 @@ +{ + "name": "dependency", + "version": "1.0.0", + "private": true, + "sideEffects": false, + "main": "index.js" +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/index.js b/test/configCases/css/css-order3/index.js new file mode 100644 index 00000000000..6fd0e91c385 --- /dev/null +++ b/test/configCases/css/css-order3/index.js @@ -0,0 +1,14 @@ +const { component } = require("./component"); +component() + +// https://github.com/webpack/webpack/issues/18961 +// https://github.com/jantimon/reproduction-webpack-css-order +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, ""); +} \ No newline at end of file diff --git a/test/configCases/css/css-order3/package.json b/test/configCases/css/css-order3/package.json new file mode 100644 index 00000000000..0afb3f14972 --- /dev/null +++ b/test/configCases/css/css-order3/package.json @@ -0,0 +1,8 @@ +{ + "name": "css-order2", + "version": "1.0.0", + "sideEffects": false, + "devDependencies": { + "mini-css-extract-plugin": "^2.9.0" + } + } \ No newline at end of file diff --git a/test/configCases/css/css-order3/webpack.config.js b/test/configCases/css/css-order3/webpack.config.js new file mode 100644 index 00000000000..4231389feea --- /dev/null +++ b/test/configCases/css/css-order3/webpack.config.js @@ -0,0 +1,43 @@ +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +/** @type {import("../../../../").Configuration} */ +module.exports = { + devtool: false, + target: "web", + entry: "./index.js", + mode: "development", + optimization: { + concatenateModules: false + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + { + loader: MiniCssExtractPlugin.loader + }, + { + loader: "css-loader", + options: { + esModule: true, + modules: { + namedExport: false, + localIdentName: "[name]" + } + } + } + ] + } + ] + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: "[name].css" + }) + ], + node: { + __dirname: false, + __filename: false + } +}; diff --git a/types.d.ts b/types.d.ts index 84005457074..885aca9fa47 100644 --- a/types.d.ts +++ b/types.d.ts @@ -3720,6 +3720,17 @@ declare interface DependencyConstructor { new (...args: any[]): Dependency; } type DependencyLocation = SyntheticDependencyLocation | RealDependencyLocation; +declare interface DependencySourceOrder { + /** + * the main source order + */ + main: number; + + /** + * the sub source order + */ + sub: number; +} declare class DependencyTemplate { constructor(); apply( @@ -8149,7 +8160,7 @@ declare interface KnownAssetInfo { /** * object of pointers to other assets, keyed by type of relation (only points from parent to child) */ - related?: Record; + related?: Record; } declare interface KnownBuildInfo { cacheable?: boolean; @@ -17801,6 +17812,10 @@ declare namespace exports { ...cRest: Comparator[] ) => Comparator; export let keepOriginalOrder: (iterable: Iterable) => Comparator; + export let sortWithSourceOrder: ( + dependencies: Dependency[], + dependencySourceOrderMap: WeakMap + ) => void; } export namespace runtime { export let compareRuntime: (a: RuntimeSpec, b: RuntimeSpec) => 0 | 1 | -1;