Repository: webpack/webpack (JavaScript)

Issue:
Destructured values from an imported namespace are mangled and returning undefined
<!-- Please don't delete this template because we'll close your issue -->
<!-- Before creating an issue please make sure you are using the latest version of webpack. -->

# Bug report

There seems to be a strange issue with name mangling change that was introduced in 5.90.2. This caused our build to fail because something that was bundled correctly in 5.90.1 was no longer bundled correctly. I've narrowed it down to [this change](https://github.com/webpack/webpack/commit/2a063f88da046377ee4eac1e57fc2ea9966e55c8#diff-96335405b47eef62b17ba6fe88f6b35a1e37c9cf98fd762fe74599aca1c65d94R195), but I haven't really been successful in figuring out what is going on, no doubt because I'm pretty new to spelunking through webpack.

I've created a [repo here](https://github.com/imccausl/webpack-5.90.2-bug-repro-repo) to reproduce the behaviour, although there is one minor difference between this repo and the way our webpack config is set up at my company: in the linked repo, I can only reproduce the behaviour if I set `optimization.sideEffects` to `false`. It's my understanding that the default for `sideEffects` is `true` and as far as I can tell, `sideEffects` was not set to false when this issue occurred so I might be missing something. Either way, I thought I'd submit this here in case others with more experience with the internals of webpack have any ideas.

**What is the current behavior?**

Please see [the repo I set up](https://github.com/imccausl/webpack-5.90.2-bug-repro-repo) for steps to reproduce the issue. What effectively happens is that I import a namespaced module, and then I destructure a few items from this module after importing. Webpack mangles the names after the import, so that the subsequent destructured values are undefined.

**What is the expected behavior?**

The expected behaviour is that these values are not undefined and can be accessed correctly. 

**Other relevant information:**
webpack version:  5.90.2
Node.js version: 20.11.1
Operating System: MacOS/Linux
Additional tools:



Hints:

Patch:
diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js
--- a/lib/dependencies/HarmonyImportSpecifierDependency.js
+++ b/lib/dependencies/HarmonyImportSpecifierDependency.js
@@ -143,7 +143,8 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
 	 */
 	getReferencedExports(moduleGraph, runtime) {
 		let ids = this.getIds(moduleGraph);
-		if (ids.length === 0) return this._getReferencedExportsInDestructuring();
+		if (ids.length === 0)
+			return this._getReferencedExportsInDestructuring(moduleGraph);
 		let namespaceObjectAsContext = this.namespaceObjectAsContext;
 		if (ids[0] === "default") {
 			const selfModule = moduleGraph.getParentModule(this);
@@ -160,7 +161,7 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
 				case "default-only":
 				case "default-with-named":
 					if (ids.length === 1)
-						return this._getReferencedExportsInDestructuring();
+						return this._getReferencedExportsInDestructuring(moduleGraph);
 					ids = ids.slice(1);
 					namespaceObjectAsContext = true;
 					break;
@@ -178,21 +179,30 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
 			ids = ids.slice(0, -1);
 		}
 
-		return this._getReferencedExportsInDestructuring(ids);
+		return this._getReferencedExportsInDestructuring(moduleGraph, ids);
 	}
 
 	/**
+	 * @param {ModuleGraph} moduleGraph module graph
 	 * @param {string[]=} ids ids
 	 * @returns {(string[] | ReferencedExport)[]} referenced exports
 	 */
-	_getReferencedExportsInDestructuring(ids) {
+	_getReferencedExportsInDestructuring(moduleGraph, ids) {
 		if (this.referencedPropertiesInDestructuring) {
 			/** @type {ReferencedExport[]} */
 			const refs = [];
+			const importedModule = moduleGraph.getModule(this);
+			const canMangle =
+				Array.isArray(ids) &&
+				ids.length > 0 &&
+				!moduleGraph
+					.getExportsInfo(importedModule)
+					.getExportInfo(ids[0])
+					.isReexport();
 			for (const key of this.referencedPropertiesInDestructuring) {
 				refs.push({
 					name: ids ? ids.concat([key]) : [key],
-					canMangle: Array.isArray(ids) && ids.length > 0
+					canMangle
 				});
 			}
 			return refs;


Test patch:
diff --git a/test/configCases/mangle/mangle-with-destructuring-assignment/index.js b/test/configCases/mangle/mangle-with-destructuring-assignment/index.js
--- a/test/configCases/mangle/mangle-with-destructuring-assignment/index.js
+++ b/test/configCases/mangle/mangle-with-destructuring-assignment/index.js
@@ -1,4 +1,5 @@
 import * as module from "./module";
+import { obj3, obj3CanMangle, obj4, obj4CanMangle } from "./reexport?side-effects" // enable side effects to ensure reexport is not skipped
 
 it("should not mangle export when destructuring module", () => {
 	const { obj: { a, b }, objCanMangle } = module
@@ -19,3 +20,18 @@ it("should mangle export when using module dot property", () => {
 	expect(module.aaa).toBe("aaa");
 	expect(module.aaaCanMangle).toBe(true)
 });
+
+it("should not mangle export when destructuring module's property is a module", () => {
+	const { aaa, bbb } = obj3;
+	expect(aaa).toBe("a");
+	expect(bbb).toBe("b");
+	expect(obj3CanMangle).toBe(false)
+});
+
+it("should not mangle export when destructuring module's nested property is a module", () => {
+	const { nested: { obj5, obj5CanMangle } } = obj4;
+	expect(obj5.aaa).toBe("a");
+	expect(obj5.bbb).toBe("b");
+	expect(obj4CanMangle).toBe(true);
+	expect(obj5CanMangle).toBe(false)
+});
diff --git a/test/configCases/mangle/mangle-with-destructuring-assignment/module2.js b/test/configCases/mangle/mangle-with-destructuring-assignment/module2.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/mangle/mangle-with-destructuring-assignment/module2.js
@@ -0,0 +1,2 @@
+export const aaa = "a";
+export const bbb = "b";
diff --git a/test/configCases/mangle/mangle-with-destructuring-assignment/module3.js b/test/configCases/mangle/mangle-with-destructuring-assignment/module3.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/mangle/mangle-with-destructuring-assignment/module3.js
@@ -0,0 +1,2 @@
+export const aaa = "a";
+export const bbb = "b";
diff --git a/test/configCases/mangle/mangle-with-destructuring-assignment/reexport.js b/test/configCases/mangle/mangle-with-destructuring-assignment/reexport.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/mangle/mangle-with-destructuring-assignment/reexport.js
@@ -0,0 +1,6 @@
+export * as obj3 from "./module2"
+export const obj3CanMangle = __webpack_exports_info__.obj3.canMangle;
+
+import * as reexport2 from "./reexport2?side-effects"
+export const obj4 = { nested: reexport2 }
+export const obj4CanMangle = __webpack_exports_info__.reexport2.canMangle;
diff --git a/test/configCases/mangle/mangle-with-destructuring-assignment/reexport2.js b/test/configCases/mangle/mangle-with-destructuring-assignment/reexport2.js
new file mode 100644
--- /dev/null
+++ b/test/configCases/mangle/mangle-with-destructuring-assignment/reexport2.js
@@ -0,0 +1,2 @@
+export * as obj5 from "./module3"
+export const obj5CanMangle = __webpack_exports_info__.obj5.canMangle;
diff --git a/test/configCases/mangle/mangle-with-destructuring-assignment/webpack.config.js b/test/configCases/mangle/mangle-with-destructuring-assignment/webpack.config.js
--- a/test/configCases/mangle/mangle-with-destructuring-assignment/webpack.config.js
+++ b/test/configCases/mangle/mangle-with-destructuring-assignment/webpack.config.js
@@ -1,5 +1,13 @@
 /** @type {import("../../../../").Configuration} */
 module.exports = {
+	module: {
+		rules: [
+			{
+				resourceQuery: /side-effects/,
+				sideEffects: true
+			}
+		]
+	},
 	optimization: {
 		mangleExports: true,
 		usedExports: true,