fix ESM default export handling for .mjs files in Module Federation#20189
Conversation
🦋 Changeset detectedLatest commit: 76c88e4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
CodSpeed Performance ReportMerging #20189 will not alter performanceComparing Summary
|
d32be4e to
b7a7123
Compare
|
@alexander-akait Thank you for approving! It seems that CI is saying "Waiting for status to be reported". Do I need to do anything here? |
|
@y-okt No, I am waiting for review from other developers and we can merge |
|
@alexander-akait I see, thank you for letting me know! |
|
Looks like the issue involves strict ESM module (.mjs) importing another strict ESM module (.mjs). In this case, using the |
|
@hai-x Hi, thank you for reviewing. My understanding on what happens on module federation is that: The format can differ when the host has a different package version that uses a different module format (e.g., older version before ESM was added), or when resolve conditions differ between host and consumer. Furthermore, I think this matches how For these reasons, my guess is that Could you share your opinion? |
|
I think the root reason is that we can’t determine whether the shared module is actually CJS or ESM, so we don’t assign
Yeah, in most scenarios, But when a module(assumed CJS here) is imported by a strict ESM file (.mjs), webpack’s interop behavior aligned with
We have Back to the origin issue, it’s |
use namespace rather than dynamic in js importing mjs case Fixes: webpack#20189
b7a7123 to
64ddd69
Compare
|
@hai-x Thank you, I now understand your concern. I updated my implementation as follows.
Could you please review again? |
|
For 1, I think we can handle it in a separate PR and we also need some input from @ScriptedAlchemy . To be honest, I’m not fully understand it, and concerned it might break interop. For 2 and 3 look good to me. |
|
@y-okt Yeah, let's remove changes for 1 and will wait feedback from @ScriptedAlchemy here (will solve it in a separate PR) |
remove module federation implementation to separately do so in another MR Fixes: webpack#20189
|
@hai-x @alexander-akait Thank you, changed the handling for 1 to deal with The logic:
|
When .mjs files import a default export from a shared/remote module in Module Federation, they receive the ESM namespace object instead of the actual default export value. The solution of this approach is to override the getExportsType() method in ConsumeSharedModule and RemoteModule to always return "dynamic".
fix failed linting for lib classes
as pointed out in PR review, their values are less so remove these
use namespace rather than dynamic in js importing mjs case Fixes: webpack#20189
remove module federation implementation to separately do so in another MR Fixes: webpack#20189
d57edf1 to
76c88e4
Compare
|
Sorry, rebased because codecov was failing - could you retrigger the pipeline? |
|
@y-okt Sometimes codecov has false positive reports due our codebase is complex and many things are async, so ignore it, I always check it before merge and ask to adding test cases if found something uncovered that should be tested |
|
Thank you @alexander-akait for the information🙇 |
|
@y-okt @alexander-akait Sorry for the delay. I just checked the logic of And we could probably refine it further, but that can go into a separate PR. For now, we can go ahead and merge this one; at least the output won’t be any worse. |
|
@y-okt Thanks for fix |
|
This PR is packaged and the instant preview is available (50e0997). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@50e0997
yarn add -D webpack@https://pkg.pr.new/webpack@50e0997
pnpm add -D webpack@https://pkg.pr.new/webpack@50e0997 |
|
@alexander-akait @hai-x Thank you for reviewing and merging! |
Summary
Fixes: #16125
When .mjs files import a default export from a shared/remote module in Module Federation, they receive the ESM namespace object instead of the actual default export value.
The root cause was that ConsumeSharedModule and RemoteModule have empty buildMeta (no exportsType). When .mjs files (which have strictHarmonyModule: true) import from these modules, getExportsType() returns "default-with-named", causing webpack to generate code that expects .default to be pre-unwrapped. But the federation runtime simply passes through the namespace object.
Explaining in more detail,
_defaultwrapper is created uniquely for "dynamic", but not for "default-with-named" (code)when
import something from "shared-pkg",exportNamewill be ["default"], and when "default-with-named",exportNamewill be an empty array (code). By this logic, the entireimportVarwill be returned (code).Module Federation's ConsumeSharedModule returns shared module's export object entirely (code).
What kind of change does this PR introduce?
As aforementioned in the detailed section,
dynamiccan insert handlings for the default import. If we avoid using "default-with-named" but "dynamic", we can resolve this issue.Another solution is to unwrap the default export at runtime, but this breaks the original webpack's approach to process at compilation time, and also can complicate the implementation due to its complexity.
Did you add tests for your changes?
Yes
Does this PR introduce a breaking change?
No. This will fix the bug reported in #16125
If relevant, what needs to be documented once your changes are merged or what have you already documented?
No