With module federation, submodules of shared modules are not actually shared if they are imported individually
Bug report
What is the current behavior?
Importing a submodule of a shared module yields a different instance of the submodule in each ModuleFederationPlugin, instead of a single shared instance.
If the current behavior is a bug, please provide the steps to reproduce.
I created a repository to showcase the issue: module-federation-with-shared-submodules.
It is based on the shared context module federation example, with the difference that on the main branch the different module federation plugins import the shared context from the shared library through a submodule import rather than a root import:
- import { NameContext } from "@shared-context/shared-library";
+ import { NameContext } from "@shared-context/shared-library/src/context/NameContext";
The problem is that when doing this, the context is not actually shared, and each module federation plugin gets its own instance: app1 provides "Billy" as context value above the Welcome component taken from app2, but Welcome uses a different context instance in its call to useContext - the one bundled with app2.
=> This leads to seeing "No name provided" in app1 instead of "Billy".
Not being able to rely on submodules imports is a problem, because importing the whole shared library might make the bundle size much bigger in real life cases.
For example, importing just the <Button /> component from ant-design instead of the whole library makes a huge difference:
- import { Button } from "antd";
+ import Button from "antd/lib/button";
What is the expected behavior? I would expect the submodules to be shared between the different module federation plugins.
Possible workaround Sharing the submodule itself in the webpack configs of the module federation plugins seems to work as expected:
shared: [
"react",
"react-dom",
{
- "@shared-context/shared-library": {
- import: "@shared-context/shared-library",
+ "@shared-context/shared-library/src/context/NameContext": {
+ import: "@shared-context/shared-library/src/context/NameContext",
requiredVersion: require("../shared-library/package.json").version,
},
},
]
You can check that on the shared-submodule branch of the repo. After rebuilding, "Billy" is rendered as expected in app1:
git checkout shared-submodule
yarn clean-install && yarn clean && yarn build && yarn start
However, having to do this for every submodule of a shared library would be painful, and not scale well. Please let me know if there is a better to way to approach this issue.
Other relevant information: webpack version: 5.52.1 Node.js version: 14.17.1 Operating System: Windows 10
I want to say it is expected
@ScriptedAlchemy your opinion on this would be greatly appreciated.
Put a trailing / on the end of whatever your sharing. This will cause webpack to reference anything that matches the partial path, not just the index file / main resolution point
Shared:{“somePackage/“:{}}
@ScriptedAlchemy thank you.
To put more context to this. The use case we were having was regarding antd , making it a shared dep and transforming imports like import { ConfigProvider } from "antd" to import ConfigProvider from "antd/lib/config-provider" led to this issue. Adding trailing / solves the prob.
I think we should move it to our doc site and improve this
Agreed. I'm planning to open another PR to the docs in response of growing complaints about missing information ℹ️
@messaooudi Just be careful, if you did it on something like material-ui/icons/ it'll add 5000 keys to the remote container.
I think in production mode if you share something but never actually use it, webpack may tree shake it out. But I can't confirm this and haven't tested that scenario in several months and only tested this in the context of an angular build which may have aggressive optimization. Just keep an eye on the file size of the remote entry container itself when using the trailing slash
@ScriptedAlchemy I can confirm that only the resources that are actually used ( imported somewhere in the code ) are part of the container.
Put a trailing / on the end of whatever your sharing.
It's working ! When you add the documentation, would you mind adding a link to it in this issue ?
In any case, thanks a lot 👍
Someone feel free to open pr. I have no capacity but can suggest edits if someone will get the PR started
@ScriptedAlchemy & @alexander-akait I just opened a PR https://github.com/webpack/webpack.js.org/pull/6519, since this took me a long time to figure out.
Feel free to leave feedback.
Is this a regression? It seems adding / at the end of a shared package causes it not to be shared (including the sub path imports and index import)
It seems like adding the trailing / to the shared package will cause direct package import not to be shared. Is there a way to shared both subpath imports and index / main imports?