From 532b35675d5567b1e22c2616444c18eb41434c07 Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Tue, 7 Oct 2025 17:11:22 +0300 Subject: [PATCH] fix: defer import mangling --- lib/RuntimeTemplate.js | 11 ++++++++++- .../defer-import/import-attributes/file.ext | 4 ++++ .../defer-import/import-attributes/index.js | 18 ++++++++++++++++++ .../import-attributes/test.filter.js | 5 +++++ .../import-attributes/webpack.config.js | 9 +++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 test/configCases/defer-import/import-attributes/file.ext create mode 100644 test/configCases/defer-import/import-attributes/index.js create mode 100644 test/configCases/defer-import/import-attributes/test.filter.js create mode 100644 test/configCases/defer-import/import-attributes/webpack.config.js diff --git a/lib/RuntimeTemplate.js b/lib/RuntimeTemplate.js index 5aa4f2e254e..6c4b0aba981 100644 --- a/lib/RuntimeTemplate.js +++ b/lib/RuntimeTemplate.js @@ -949,7 +949,16 @@ class RuntimeTemplate { // when the defaultInterop is used (when a ESM imports a CJS module), if (exportName.length > 0 && exportName[0] === "default") { if (isDeferred && exportsType !== "namespace") { - const access = `${importVar}.a${propertyAccess(exportName, 1)}`; + const exportsInfo = moduleGraph.getExportsInfo(module); + const name = exportName.slice(1); + const used = exportsInfo.getUsedName(name, runtime); + if (!used) { + const comment = Template.toNormalComment( + `unused export ${propertyAccess(exportName)}` + ); + return `${comment} undefined`; + } + const access = `${importVar}.a${propertyAccess(used)}`; if (isCall || asiSafe === undefined) { return access; } diff --git a/test/configCases/defer-import/import-attributes/file.ext b/test/configCases/defer-import/import-attributes/file.ext new file mode 100644 index 00000000000..879aeeaf4bf --- /dev/null +++ b/test/configCases/defer-import/import-attributes/file.ext @@ -0,0 +1,4 @@ +{ + "foo": "bar", + "nested": { "foo": "bar" } +} diff --git a/test/configCases/defer-import/import-attributes/index.js b/test/configCases/defer-import/import-attributes/index.js new file mode 100644 index 00000000000..60df4a38608 --- /dev/null +++ b/test/configCases/defer-import/import-attributes/index.js @@ -0,0 +1,18 @@ +import defer * as mod1 from "./file.ext" with { type: "bytes" }; +import defer * as mod2 from "./file.ext" with { type: "json" }; +import * as mod3 from "./file.ext" with { type: "bytes" }; +import * as mod4 from "./file.ext" with { type: "json" }; + +it("should work with defer and import attributes", () => { + const decoder = new TextDecoder('utf-8'); + const mod1Decoded = JSON.parse(decoder.decode(mod1.default)); + expect(mod1Decoded.foo).toBe("bar"); + expect(mod1Decoded.nested.foo).toBe("bar"); + expect(mod2.default.foo).toBe("bar"); + expect(mod2.default.nested.foo).toBe("bar"); + const mod2Decoded = JSON.parse(decoder.decode(mod3.default)); + expect(mod2Decoded.foo).toBe("bar"); + expect(mod2Decoded.nested.foo).toBe("bar"); + expect(mod4.default.foo).toBe("bar"); + expect(mod4.default.nested.foo).toBe("bar"); +}); diff --git a/test/configCases/defer-import/import-attributes/test.filter.js b/test/configCases/defer-import/import-attributes/test.filter.js new file mode 100644 index 00000000000..03c5581fcb2 --- /dev/null +++ b/test/configCases/defer-import/import-attributes/test.filter.js @@ -0,0 +1,5 @@ +"use strict"; + +const supportsTextDecoder = require("../../../helpers/supportsTextDecoder"); + +module.exports = () => supportsTextDecoder(); diff --git a/test/configCases/defer-import/import-attributes/webpack.config.js b/test/configCases/defer-import/import-attributes/webpack.config.js new file mode 100644 index 00000000000..c3897eb5054 --- /dev/null +++ b/test/configCases/defer-import/import-attributes/webpack.config.js @@ -0,0 +1,9 @@ +"use strict"; + +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: [`async-node${process.versions.node.split(".").map(Number)[0]}`], + experiments: { + deferImport: true + } +};