Module Federation: unable to share externalized dependency between host and remote
See original GitHub issueBug report
TLDR: ModuleFederationPlugin
won’t expose a host’s dependency to a remote if the dependency is an external
.
To be honest, I’m not completely sure if this is a bug, a missing feature, or just a technical limitation of ModuleFederationPlugin
… but i’d like to be able to do that 😄
NOTE: i’m using React as an example, but this behavior occurs regardless of what library i’m trying to externalize+share.
Context
Our module federation setup is pretty standard (except for the externals, apparently). We’ve got two apps:
(Host) (Remote)
SomePage ----> SomeWidget
Both of these are React apps, with React stuff shared via a typical ModuleFederationPlugin
config:
shared: {
'react': { singleton: true },
'react-dom': { singleton: true }
}
This all works fine.
Now, we’d like SomePage
to get React from a CDN link, so we tried externalizing it in SomePage
via a typical
externals: {
'react': 'SomeGlobalVar',
'react-dom': 'AnotherGlobalVar',
}
the idea being that React would get wired up everywhere like this:
Script tag <script src="/our-cdn/react-stuff.js>
|
|
| webpack `external` (global var)
|
v
SomePage (Host app)
|
|
| ModuleFederationPlugin's `shared` exposes React
|
v
SomeWidget (Remote component)
What is the current behavior?
Unfortunately, despite SomePage
successfully loading React from the CDN, this doesn’t work.
The externalized React doesn’t end up getting get shared between SomePage
and SomeWidget
, so we get two copies of React in one tree, which leads to a crash for the usual reasons. From my testing, it appears that despite the ModuleFederationPlugin
config, SomePage
never puts React into the shared WMF scope, so SomeWidget
understandably loads its fallback. The versions are the same between both, and removing them doesn’t change anything. I’ve also tried this with lodash, and that also results in two copies, one from the CDN, and one loaded via SomeWidget
’s fallback.
What is the expected behavior?
Dependencies should be shared between host and remote even if they’re externals, as illustrated in the diagram a few paragraphs up.
Steps to reproduce
I’ve added some externals to basic-host-remote
from module-federation-examples
to reproduce this setup:
https://github.com/lubieowoce/module-federation-examples/tree/externals-test/basic-host-remote
the change is very small. You can run it via cd basic-host-remote && yarn && yarn start
, then navigating to http://localhost:3001/. It’ll flash and then turn blank, because when you when mix two copies of React in the same tree, it’ll crash with “Error: Invalid hook call”. This demonstrates that the library isn’t actually being shared between the host and remote.
Other relevant information:
webpack version: 5.70.0
Node.js version: 14.17.6
Operating System: macOS 11.6, ARM
Issue Analytics
- State:
- Created 2 years ago
- Reactions:5
- Comments:10 (6 by maintainers)
Top GitHub Comments
I think it should be reopened, this can cause strange regressions where there doesn’t need to be. I believe what would need to change is the SharingRuntimeModule.js
Regarding sharing externals in userspace; something like this might work.
But we would need a way to ensure it isnt tree shaken out as being “unused” in the graph.
In the webpack runtime, what we need is something like
register("react", "18.0.0", function() { return React});
So the problem here. I think, is the remote container initialization phase. If a host marks it as external the runtime should still register react, and just point to the global variable. So the share scope still contains the keys to pass them into remote containers. Otherwise if my remote has import:false set - it crashes because share scope is missing keys, but loadSingleton is still part of the logic