From 89eb6a3c8109563050c5da42ed99de51782e6501 Mon Sep 17 00:00:00 2001 From: Natsu Xiao <784487301@qq.com> Date: Tue, 15 Jul 2025 17:53:58 +0800 Subject: [PATCH] fix: keep module traversal consistent across reexport scenarios (#19702) --- lib/optimize/SideEffectsFlagPlugin.js | 11 ++++- lib/util/comparators.js | 2 +- .../ConfigCacheTestCases.longtest.js.snap | 2 + .../ConfigTestCases.basictest.js.snap | 2 + .../css/css-order-reexport/component.js | 5 +++ .../dependency/dependency.css | 3 ++ .../dependency/dependency.js | 5 +++ .../dependency/dependency2.css | 3 ++ .../dependency/dependency2.js | 5 +++ .../css-order-reexport/dependency/index.js | 2 + .../dependency/package.json | 7 +++ .../css/css-order-reexport/index.js | 14 ++++++ .../css/css-order-reexport/package.json | 8 ++++ .../css/css-order-reexport/webpack.config.js | 43 +++++++++++++++++++ 14 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 test/configCases/css/css-order-reexport/component.js create mode 100644 test/configCases/css/css-order-reexport/dependency/dependency.css create mode 100644 test/configCases/css/css-order-reexport/dependency/dependency.js create mode 100644 test/configCases/css/css-order-reexport/dependency/dependency2.css create mode 100644 test/configCases/css/css-order-reexport/dependency/dependency2.js create mode 100644 test/configCases/css/css-order-reexport/dependency/index.js create mode 100644 test/configCases/css/css-order-reexport/dependency/package.json create mode 100644 test/configCases/css/css-order-reexport/index.js create mode 100644 test/configCases/css/css-order-reexport/package.json create mode 100644 test/configCases/css/css-order-reexport/webpack.config.js diff --git a/lib/optimize/SideEffectsFlagPlugin.js b/lib/optimize/SideEffectsFlagPlugin.js index e07788c7b..1c882ef79 100644 --- a/lib/optimize/SideEffectsFlagPlugin.js +++ b/lib/optimize/SideEffectsFlagPlugin.js @@ -320,8 +320,17 @@ class SideEffectsFlagPlugin { ({ module }) => module.getSideEffectsConnectionState(moduleGraph) === false, - ({ module: newModule, export: exportName }) => { + ({ + module: newModule, + export: exportName, + connection: targetConnection + }) => { moduleGraph.updateModule(dep, newModule); + moduleGraph.updateParent( + dep, + targetConnection, + /** @type {Module} */ (connection.originModule) + ); moduleGraph.addExplanation( dep, "(skipped side-effect-free modules)" diff --git a/lib/util/comparators.js b/lib/util/comparators.js index ce8cd40e5..034a17404 100644 --- a/lib/util/comparators.js +++ b/lib/util/comparators.js @@ -556,7 +556,7 @@ const sortWithSourceOrder = (dependencies, dependencySourceOrderMap) => { } } - if (withSourceOrder.length === 0) { + if (withSourceOrder.length <= 1) { return; } diff --git a/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap b/test/__snapshots__/ConfigCacheTestCases.longtest.js.snap index c574aba69..3d5a1526b 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-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\\";}"`; exports[`ConfigCacheTestCases css css-order3 exported tests keep consistent css order 1`] = `".dependency3::before { content: \\"dependency3\\";}.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 4589d2ef1..fc6de6061 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-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\\";}"`; exports[`ConfigTestCases css css-order3 exported tests keep consistent css order 1`] = `".dependency3::before { content: \\"dependency3\\";}.dependency2::before { content: \\"dependency2\\";}.dependency::before { content: \\"dependency\\";}"`; diff --git a/test/configCases/css/css-order-reexport/component.js b/test/configCases/css/css-order-reexport/component.js new file mode 100644 index 000000000..bf962afd1 --- /dev/null +++ b/test/configCases/css/css-order-reexport/component.js @@ -0,0 +1,5 @@ +export { dependency, dependency2 } from "./dependency"; + +export function component(...args) { + console.log(args); +} \ No newline at end of file diff --git a/test/configCases/css/css-order-reexport/dependency/dependency.css b/test/configCases/css/css-order-reexport/dependency/dependency.css new file mode 100644 index 000000000..776c3714d --- /dev/null +++ b/test/configCases/css/css-order-reexport/dependency/dependency.css @@ -0,0 +1,3 @@ +.dependency::before { + content: "dependency"; +} \ No newline at end of file diff --git a/test/configCases/css/css-order-reexport/dependency/dependency.js b/test/configCases/css/css-order-reexport/dependency/dependency.js new file mode 100644 index 000000000..66dbef95d --- /dev/null +++ b/test/configCases/css/css-order-reexport/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-order-reexport/dependency/dependency2.css b/test/configCases/css/css-order-reexport/dependency/dependency2.css new file mode 100644 index 000000000..f882c894f --- /dev/null +++ b/test/configCases/css/css-order-reexport/dependency/dependency2.css @@ -0,0 +1,3 @@ +.dependency2::before { + content: "dependency2"; +} \ No newline at end of file diff --git a/test/configCases/css/css-order-reexport/dependency/dependency2.js b/test/configCases/css/css-order-reexport/dependency/dependency2.js new file mode 100644 index 000000000..f59b23102 --- /dev/null +++ b/test/configCases/css/css-order-reexport/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-order-reexport/dependency/index.js b/test/configCases/css/css-order-reexport/dependency/index.js new file mode 100644 index 000000000..035ddd4d7 --- /dev/null +++ b/test/configCases/css/css-order-reexport/dependency/index.js @@ -0,0 +1,2 @@ +export * from "./dependency2"; +export * from "./dependency"; diff --git a/test/configCases/css/css-order-reexport/dependency/package.json b/test/configCases/css/css-order-reexport/dependency/package.json new file mode 100644 index 000000000..ea2fc66be --- /dev/null +++ b/test/configCases/css/css-order-reexport/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-order-reexport/index.js b/test/configCases/css/css-order-reexport/index.js new file mode 100644 index 000000000..6901b689e --- /dev/null +++ b/test/configCases/css/css-order-reexport/index.js @@ -0,0 +1,14 @@ +import { component, dependency, dependency2 } from "./component"; +component(dependency, dependency2); + +// 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-order-reexport/package.json b/test/configCases/css/css-order-reexport/package.json new file mode 100644 index 000000000..0afb3f149 --- /dev/null +++ b/test/configCases/css/css-order-reexport/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-order-reexport/webpack.config.js b/test/configCases/css/css-order-reexport/webpack.config.js new file mode 100644 index 000000000..4d2ad0fd3 --- /dev/null +++ b/test/configCases/css/css-order-reexport/webpack.config.js @@ -0,0 +1,43 @@ +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); + +/** @type {import("../../../../types").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 + } +};