module-federation-examples
module-federation-examples copied to clipboard
External Templates evaluated too early
So I'm trying to create a setup by where the URL of a remoteEntry.js (Foo) is set at runtime. This works completely fine when I set the URL property on the window in the initial chunk.
In my setup I want to set the URL for my remoteEntry.js by a package which is also module federated (Foundation). In the example below the "init" call is responsible for creating the "federationsMap" object and adding the URL for Foo to it.
The problem is that webpack seems to be evaluating this statement before the import statement is called in the call stack, and is evaluating the URL before init is called. This logs a warning, before init is run. Then when the import is actually attempted in code, it logs an error.
Any help is appreciated, I can't understand why the External Template is being evaluated before the init call - I assume I've missed some fundamental understanding here.
index.js:1 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'Foo')
while loading "./public" from webpack/container/reference/Foo
index.js
import('./bootstrap');
bootstrap.js
(async () => {
await init();
const { setup } = await import('./bootstrapbootstrap');
await setup;
start();
})();
bootstrapbootstrap.js
const setup = (async () => {
const { setup } = await import("Foo/public");
await setup.complete;
})();
export { setup };
Federation Config
{
"name": "ExampleProject",
"filename": "remoteEntry.js",
"exposes": {
},
"remotes": {
"Foundation": "Foundation@http://placeholder:3000/remoteEntry.js",
"Foo": "Foo@[window.federationsMap.Foo]"
},
"shared": {
}
}
}
Promise new promise would be a better syntax, the Square braces way of trying to eval things is probably flat in the webpack runtime or module tree, that can lead to quirks.
Promise new promise would ensure it’s called at the right time
Thank you for your reply.
I have tried out using the promise new Promise syntax.
In my setup I've dispatched an event in window to say that init has finished. I am then listening for this event in my promise new Promise syntax.
`promise new Promise(resolve => {
window.addEventListener('init-finished', () => {
const remoteUrl= 'http://placeholder:3002/remoteEntry.js'
const script = document.createElement('script')
script.src = remoteUrl
script.onload = () => {
const proxy = {
get: (request) => window.Foo.get(request),
init: (arg) => {
try {
return window.Foo.init(arg)
} catch(e) {
console.log('remote container already initialized')
}
}
}
resolve(proxy)
}
document.head.appendChild(script);
});
})
In this setup I get a similar scenario, the code gets stuck waiting for this event to be fired as it appears to be waiting for the promise to resolve before moving on.
I assume I've done something wrong, is there a better pattern I can follow to allow the import to be resolved only when it is actually run in code?
Dm me on twitter, let's do a zoom call.
It's possible to use webpacks own script loader to manage actually loading the file.
Doing it manually in the promise is prone to a lot of async quirks which are hard to nail down.
But it waiting for the promise to resolve. That's kinda how dependency management is supposed to work, app can't start till it's got the tree it needs. But this depends on how your app boots, who fires that event, where you have async import boundaries.
You can try putting another dynamic import lower in the tree. So that the "top" of the app can start running. Then hold on a lower dynamic import
Thanks for the help,
My company is just approving finance for consulting (for some other problems that we've got) - I'll be in contact soon.
I've got a consulting firm so we also do provide staff augmentation if you need longer tee assistance