ChunkID collisions when using Federated Module Plugin
Bug report
What is the current behavior?
I am trying to import two modules using the federated module plugin with a setup like this:
new ModuleFederationPlugin({
name: "module-a",
filename: "remoteEntry.js",
library: { type: "var", name: "module_a" },
exposes: {
"./lib": "./bootstrap",
},
})
and a nearly identical setup for module-b.
However, since both of the federated modules use the same file name at their root module (e.g. bootstrap.ts), both get assigned the same chunkId ("626", which is just the hash of './node_modules/ts-loader/index.js!./bootstrap.ts' using the numberHash function)
In a normal webpack build, the file paths for each chunk would be unique, but since I have multiple federated modules that expose a ./bootstrap.ts, each get the same chunk id (656, in this case). This causes an id collision in the runtime, as both federated modules request the same chunkid, and as result there is a race condition, wherein the first chunk with the id 626 to be requested gets by both federated modules.
I tried setting outputs.chunkFilename: "module-a-[id].js" (and likewise for module-b) but this doesn't seems to change the chunkId, and in the network activity, I can see that only one of the two chunks gets loaded and is imported (erroneously) into both federated modules.
Reading the code, it looks like the chunk IDs get set by the DeterministicChunkIdsPlugin, which doesn't expose a hashSalt option or make use of the options.hashSalt, so there doesn't seem to be a way change the hash to prevent id collisions.
Is there any way to prevent this kind of id collisions with federated modules?
If the current behavior is a bug, please provide the steps to reproduce.
This repo illustrates the race condition. In the example, there are two nearly identical federated modules that expose a src/index.js file which are simple enough:
// module_a/src/index.js
export const message = "Hi from Module A"
and
// module_b/src/index.js
export const message = "Hi from Module B"
The host app simply imports these and displays the results with:
function App() {
const [a, set_a] = useState()
const [b, set_b] = useState()
useEffect(() => {
import(`module_a/Message`).then((m) => { set_a(m.message) })
import(`module_b/Message`).then((m) => { set_b(m.message) })
}, [])
return (
<>
<p>Module A says: {a}</p>
<p>Module B says: {b}</p>
</>
)
}
When the app is run, the messages read either
Module A says: Hi from Module A
Module B says: Hi from Module A
or:
Module A says: Hi from Module B
Module B says: Hi from Module B
Depending on whether module_a/remoteEntry.js or module_b/remoteEntry.js finishes loading first. (The Nginx server in the repo introduces a random delay to help make the switching behavior more obvious)
What is the expected behavior?
When two federated modules are loaded onto the page, the correct module is reliably loaded
Other relevant information: webpack version: 5.68.0 Node.js version: 14.16.0 Operating System: Windows, Linux, OSX Additional tools:
Give different names here https://github.com/jdthorpe/federated-module-race-condition/blob/main/module_a/package.json#L2 and here https://github.com/jdthorpe/federated-module-race-condition/blob/main/module_b/package.json#L2, we have this in readme
Thanks! That worked, but I don't see it mentioned in the official docs Is there another readme I should be reading?
We have this on another page, but yes make sense to improve this on this page too
I'm experiencing the same problem, but in my case, I have a shell that uses a the library A and I load multiple federated components that use also the library A (but they might use a different version).
In the shell and federated component, I've setup the module federation plugin to not share the library A. It seems that the module id for the library A is the same for the shell and the federated components. When the shell loads and calls a function in library A, it seems to refer to the wrong library A chunk (it was three shaked differently) and the method that I call is missing, but looking in the other chunks with same module id (loaded later in the network tab), the function is there.
I think this is a bug
Shell -> Library A Federated Page -> Library A Federated Component -> Library A
Library A is not shared cause it contains a context API that we won't share between MFEs
All instance of Library A have 805 as module id
When the Shell initializes and call a hook defined in the Library A, I get an error saying it does not exist, but it is there in another chunk with the same module id.
Check you don't have different version of Library-A
the shell loads 2.9.1 the page loads 2.9.1 the component loads 2.9.0
This is the shell that fails to load
As you can see you have different versions, you can use version (as prefix or suffic) in name to prevent such cases
we set splitChuks like this in module A and B ,the remoteEntry.js of them has the same chunk id of vendor, they influence each other.
optimization: { runtimeChunk: false, splitChunks: { chunks: 'async', cacheGroups: { vendors: { test: /[\/]node_modules[\/]/, priority: -10, }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true, }, }, }, }