-
-
Notifications
You must be signed in to change notification settings - Fork 784
fix(mf): use unresolved request to match shared module #11478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(mf): use unresolved request to match shared module #11478
Conversation
✅ Deploy Preview for rspack canceled.Built without sensitive environment variables
|
CodSpeed Performance ReportMerging #11478 will not alter performanceComparing 🎉 Hooray!
|
d316723
to
a11b5d0
Compare
a11b5d0
to
df5f9d9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes an issue with Module Federation's shared module matching by using the unresolved dependency request instead of the raw request from create data. This resolves problems where custom resolvers that hook into normalModuleFactory.hooks.resolve
would cause fully qualified paths to be used, preventing proper matching against MF shared configuration.
- Uses
data.dependencies[0].request()
instead ofcreate_data.raw_request
for shared module matching - Restores behavior that was changed in v1.4.7 via PR #10999
- Fixes issue where custom resolvers would break shared module detection
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
return Ok(()); | ||
} | ||
let request = &create_data.raw_request; | ||
let dependency = data.dependencies[0] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a better way is use data.request
here, should also solve the issue since dependency.request
is assigned from data.request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👋 I tried to do that but it seems that data.request
contains the mutated resolved path instead of the original request (since this change?). I could not find another practical way of retrieving it except extracting it from dependency[0].request
which does contain the pristine specifier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, have you tried webpack that they use the unresolved request or the changed request (changed by NormalModuleFactory.hooks.afterResolve
)? If I remember correctly they will use the changed request. So at this point I'd like to keep the consistent with webpack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh there is another issue about ConsumeSharedPlugin not using the changed request #11437 (your PR is about ProvideSharedPlugin not using original request)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see this is more complicated than expected.
I see, have you tried webpack that they use the unresolved request or the changed request (changed by NormalModuleFactory.hooks.afterResolve)? If I remember correctly they will use the changed request. So at this point I'd like to keep the consistent with webpack
Oh, I can double check 👍.
What worries me is that if the logic is kept like this, it would make it very complicated to use a custom resolver - like with packages having subpath "exports" fields - because I'm not sure resolving all these paths ahead of time when generating the MF config + using the import
field would be straightforward.
Especially with packages that only export deep subpaths, without root exports. ("exports": {"." { … } }
)
Oh there is another issue about ConsumeSharedPlugin not using the changed request #11437 (your PR is about ProvideSharedPlugin not using original request)
I'm wondering, what would be the expected outcome in this case? The consumer would load the module from the resolved path and assume that it is not provided by a producer?
In addition to this PR changes, I tried modifying the request to use the resolved path &data.request
in the factorize hook here and it seems to solve both issues but I'm not fully sure that I get all the implications yet.
I'll try to dig into it and get a better understanding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious how you use the custom resolver that resolves paths ahead of time. If you resolve paths before compile, you can use the resolved paths to generate a MF plugin (configured with resolved paths instead of the original paths) and apply to the compiler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this is precisely the problem, I don't think that I can easily resolve all the possible subpaths for shared modules before compilation because I have no knowledge of the imports in the codebase. For simple cases it works, but as soon as I try to share a module exporting only subpaths it breaks (and the trailing slash syntax - module/
- does not work when using an absolute path fallback).
For instance, for these kind of packages…
{ "name": "package", "main": "index.js" }
…I cannot guess what the imports will look like in the provider code…
// any subpath is technically possible
import json from "package/package.json";
import stuff from "package/stuff";
import otherStuff from "package/other/stuff";
import someOtherStuff from "package/some/other/stuff";
…and it becomes very hard to generate the MF config:
new ModuleFederationPlugin({
name: "host",
shared: {
package: {
version: "1.0.0",
import: // I can add the resolved path here but what about nested paths??
},
"package/": {
version: "1.0.0",
import: // won't work
},
// Does every possible import paths have to be added as keys here?
},
}),
Whereas this config used to work just fine with v1.4.6
:
new ModuleFederationPlugin({
name: "host",
shared: {
package: {
version: "1.0.0",
},
"package/": {
version: "1.0.0",
},
},
}),
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And need a test case to avoid regression
Summary
Use
data.dependencies[0].request
instead ofcreate_data.raw_request
to check if a module is declared in the MFshared
configuration.Without it and when using a custom resolver hooking into
normalModuleFactory.hooks.resolve
the fully qualified path would be used and would end up not matching anything.This PR should restore the same behaviour as before
v1.4.7
and specifically #10999Related links
Checklist