UPDATE:
Actually there is a couple of different ways to handle remotes availability:
- Promise Based Dynamic Remotes: You can pass a promise to Module Federation plugin config instead of plain url of your remote. This promise and your errors handling logic there will be resolved at runtime. It can be done as in that example:
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'host',
remotes: {
app1: `promise new Promise(resolve => {
const urlParams = new URLSearchParams(window.location.search)
const version = urlParams.get('app1VersionParam')
// This part depends on how you plan on hosting and versioning your federated modules
const remoteUrlWithVersion = 'http://localhost:3001/' + version + '/remoteEntry.js'
const script = document.createElement('script')
script.src = remoteUrlWithVersion
script.onload = () => {
// the injected script has loaded and is available on window
// we can now resolve this Promise
const proxy = {
get: (request) => window.app1.get(request),
init: (arg) => {
try {
return window.app1.init(arg)
} catch(e) {
console.log('remote container already initialized')
}
}
}
resolve(proxy)
}
// inject this script with the src set to the versioned remoteEntry.js
document.head.appendChild(script);
})
`,
},
// ...
}),
],
};
I think module federation plugin supposed to be used as build time dependency and not includes dynamic modules resolving and failures on purpose.
So at build time it's nice to know that some asset is not there in your app.
But if you really need to have dynamic imports and handle availability of endpoints with your MF's at runtime, than there is nice example of that in module-federation-examples repo
Or simplified version of it:
const loadScope = (url, scope) => {
const element = document.createElement('script');
const promise = new Promise((resolve, reject) => {
element.src = url
element.type = 'text/javascript'
element.async = true
element.onload = () => resolve(window[scope])
element.onerror = reject
})`enter code here`
document.head.appendChild(element)
promise.finally(() => document.head.removeChild(element))
return promise
}
const loadModule = async (url, scope, module) => {
try {
const container = await loadScope(url, scope)
await __webpack_init_sharing__('default')
await container.init(__webpack_share_scopes__.default)
const factory = await container.get(module)
return factory()
} catch (error) {
console.error('Error loading module:', error)
throw error
}
}
const RemoteButton = React.lazy(() => loadModule(
'http://localhost:3002/remoteEntry.js',
'app2',
'./Button'
))