A while ago, we made a shell and remote according to Module Federation. All are Angular applications (v14). Both applications share an Angular library, which is set as a singleton
, strictVersion
, with the requiredVersion
being ^2.2.1. So minor higher versions are allowed.
The remote usually has a higher version, due to much more development work being done there.
My understanding is that when the shell loads, it loads the remoteEntry.js of the remote, and registers the library-version of both the shell and remote. It then takes the higher version of the library, and uses that (according to what is allowed in requiredVersion
).
This does seem to be the case in the majority of cases. But like 1 in 20 times, I see that the lower version is loaded (due to an error in the console of a function that cannot be found in the older library version).
This is concerning. I want to be able to count on always having the higher version loaded. Of course, we could always also update the shell to the newest version, but this is tedious, and with more remotes being added to this shell only more so.
The shell recently got a second remote, and that is when I saw this error for the first time. However, that can be a coincidence, as the function that causes the error has only been recently added as well. It might very well be the case that this was happening before, but that I just didn't notice yet, as there was no clear error. Even with the new remote being offline, I get this error. So I don't think that the new remote's library version is being chosen, as its remoteEntry.js is never loaded.
My question is: what causes the wrong (older) version of the shell being loaded instead of the remote (higher) one (race condition?), and how can I guarantee that always the higher version is chosen, without having to update both library-versions to match? Also, the shared library is not an eager one. I'm not completely sure if that is needed and what it does (and do you set it on eager in both webpack-files, or only that of the shell?)
The main.ts file of the shell looks like this:
Promise.all([
loadRemoteEntry({
type: 'module',
remoteEntry: `${environment.incompany}/remoteEntry.js`
}),
loadRemoteEntry({
type: 'module',
remoteEntry: `${environment.catalog}/remoteEntry.js`
})
])
.catch((err) => console.error('Error loading remote entries', err))
.then(() => import('./bootstrap'))
.catch((err) => console.error(err));
The webpack of the shell:
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const mf = require('@angular-architects/module-federation/webpack');
const path = require('path');
const sharedMappings = new mf.SharedMappings();
sharedMappings.register(path.join(__dirname, 'tsconfig.json'), []);
module.exports = {
output: {
uniqueName: 'adminPortal',
publicPath: 'auto'
},
experiments: {
outputModule: true
},
optimization: {
runtimeChunk: false
},
resolve: {
alias: {
...sharedMappings.getAliases()
}
},
plugins: [
new ModuleFederationPlugin({
library: { type: 'module' },
remotes: {},
shared: {
(... some angular packages and other libraries not important for the question)
'@myLibrary': {
singleton: true,
strictVersion: true,
requiredVersion: '^2.2.1'
},
...sharedMappings.getDescriptors()
}
}),
sharedMappings.getPlugin()
] };
Related question: How does module federation choose which dependency version to use?
Documentation about versions: https://www.angulararchitects.io/en/blog/getting-out-of-version-mismatch-hell-with-module-federation/