module-federation-examples
module-federation-examples copied to clipboard
Dynamic import only work when direct passed named
Hi i am working on a offline approach to use with module federation and to achieve this i must load and cache all the components from my remotes. But when i try to load the component with dynamic import and dynamic string, the webpack cant find it module. I am targeting to do
const modules = ["Cronograma", "Sampler", "Wrapper"]
modules.forEach(module => import(`cronograma/${module}`))
I saw it didnt work and try to simplify it in order to test. I've tried a simples dummy test like:
const moduleName = "cronograma/Cronograma";
import(moduleName);
At the browser i am getting the error:
src_bootstrap_js.js:2169 Uncaught (in promise) Error: Cannot find module 'cronograma/Cronograma'
According with the Dynamic import specs it accepts a string, so it appears to be something related with module federation :/
Here the full code of my test
// const modules = ["Cronograma", "Sampler", "Wrapper"]
const modules = ["Cronograma"]
setTimeout(() => {
//modules.forEach(module => import(`cronograma/${module}`)) Does not work either
console.log("Trying to import from string ")
const moduleName = "cronograma/Cronograma";
import(moduleName); //Dont work
setTimeout(() => {
console.log("Trying to import the same but directly string ")
import("cronograma/Cronograma"); // Work
console.log("Imported")
}, 1000)
}, 3000)
Above the screencapture of the browser (and the componented rendered using the plain import withtout the string)
I am letting something pass by? or should work in a different way with string dynamic imports with module federation?
Did you try to dynamically load modules using something like https://github.com/module-federation/module-federation-examples/tree/master/advanced-api/dynamic-remotes?
Thanks @Amirko28 , yeah i was thinking of doing that, in fact i've done and worked, but i wanted to know there was a specific problem with import('') and MFP.
Sorry to chime in on the same issue, trying to use the example in dynamic-remotes and getting __webpack_init_sharing__ is undefined
. what am I doing wrong? could it be a webpack version problem?
(using CRA5 with Craco to get the MF going)
Yeah Webpack cannot create a factory for unknown modules. When you use a partial import - webpack crawls the directory and bundles all possible modules. So the graph still has full module maps.
In Federation it's not possible for webpack to generate possible references or create factories for modules it doesn't know about. If you need dynamic loading you have to use the low level interface directly.
If init sharing isn't defined you either don't have federation plugin enabled or are not sharing any modules so the runtime requirement isn't added to webpack since there's nothing for it to do.
So it just started working. somehow. I've used the code from the example. Maybe some config suddenly took effect. 🤷♂️
So while i cannot support partial remote import paths, i do think you can create a proxy trap to a fake module (maybe) and possibly catch the requested remote, then use promise new promise to patch it to the low level API
something like remote: promise new Promise(()=>{ const trap { get(module, scope)=> { window.remote.get(module)
} ,init: } resolve(trap) })
Webpack would not be able to initialize it correctly but you could init it manually in the promise and inject the remote yourself. Havent tested with partial remote paths but i think it might be possible
PR an example on this repo and ill see if i can write a trap for you
Nice @ScriptedAlchemy thanks a lot. Also do you think that is possible to have any type of property to show all the modules availabe at a remote? I've tried to "schema" something like it on here, for local usage and test and it works like i needed. Do you think it can be something integrated at webpack? Maybe could i open a PR?
Integration into webpack would be tough but possible.
I am working on adding chunk maps to the federation interface to allow SSR and so on to read the used chunks directly off the webpack runtime.
I should be able to easily add the capability you want as well.
Alternatively, you could use DefinePlugin and make process.env.allChunks which just reads the MF config and writes an object - then expose that as a remote. So you could crawl all remotes and they'll return the map of their federated modules.
What im planning to do is extend federation interfaces directly. {get,init,chunkMap}
Id open a issue about webpack integration before starting a PR - ensure Tobias will sign off on such a modification. Just state you're willing to work on it.
For SSR chunk maps, i was just planning to add another module.export to the top of the remote. Client-side would need to actually alter the runtime requirements.
You could also attach it as a separate runtime requirement. It is trickier to pull maps out of the runtime. I did create the ability to hook into the remotes and run the federation script loading code on demand so i can dynamically figure out what a path needs to load in order to work. Similar to dynamic remotes but you're using the build in federation runtime to load load a remote at runtime, assuming the remote is listed in the federation plugin's remotes to begin with.
Hi zack, first of all. The dynamic load, i managed to make, withtout the trap. For while i am defining and naming all remotes and its components.
I take a better look at the Post about Dynamic Remotes, from the readme and make a similar approad, and from its example.
- get from my env all the Remotes URL
const getAllRemotesURL = async () => {
const remoteEnvs = Object.keys(process.env).filter(name => name.toUpperCase().indexOf("REMOTE_") != -1)
await Promise.all(remoteEnvs.map(name => addScript(process.env[name])))
}
- In it i call the addScipt to load the
remoteEntry
files
const addScript = async (src) => {
return new Promise((resolve) => {
var s = document.createElement('script');
s.setAttribute('src', src);
s.onload = function () {
resolve()
};
document.body.appendChild(s);
})
}
- Then, from my list of modules and components i call the
loadComponent
function, as on the example:
const loadComponent = function (scope, module) {
return async () => {
await __webpack_init_sharing__('default');
const container = window[scope]; // or get the container somewhere else
await container.init(__webpack_share_scopes__.default);
const factory = await window[scope].get(`./${module}`);
const Module = factory();
return Module;
};
}
- So my init function is as:
export const initOffline = async () => {
await getAllRemotesURL()
const remotes = data.default
const remoteNames = Object.keys(remotes)
remoteNames.forEach(remoteName => {
const components = remotes[remoteName];
components.forEach(async component => {
loadComponent(remoteName, component)()
})
})
}
I've done this way to be "ready ASAP" even with a little effort of defining the components and remotes names. But i am planning on try and experiment something more like you've said.
Ahhh okay so using the low level api. So I'm actually working on a solution that exposes Federation and the internal chunk loading directly to you so you can call Remotes.home.get
Check the matchedFederatedPage to see an example. It's on the SSR example under shared in index.js
I'll be releasing that function for end users. But you can achieve it with my new SSR plugin and just loop over the entries and execute them, they'll return the get init interface for all remotes connected to the network. And you don't have to do any manual script injection since I hooked into webpack Federation loading mechanics
@schirrel coming back to this. Since webpack will not accept your idea, how'd you feel about us creating a plugin under this organization that extends MF with some augmented extras?
Remotes being one of them. Possibly another could be chunkMap? Any interest?
Hey @ScriptedAlchemy i've thinking about it, i've started to study and was thinking about something like the AutomaticVendorFederation would be simpler and usefull, what you think?
@ScriptedAlchemy i've manage to make 3 thinks here
exposes moduleMap
and remoteMap
listing exposed files and available remotes, within the remoteEntry
, such:
and for the chunkMap, because of my knowledge of the webpack "internal work" i've create a file after build listing all the chunks name, that can be fetched or something like it by http://localhost:3006/chunkMap.json
, here is the output:
I didnt knew what name it, but maybe we could use the extended-module-federation-plugin
and further in the future add other abilities to it that are not enoughe to be on webpack core codebase.
Hey @ScriptedAlchemy what you think?
Book some time and let's talk. I'm thinking ModuleFederationEnhacedPlugin
https://calendly.com/scripted-alchemy/special-pair-programming-session
Done @ScriptedAlchemy, in fact the name you said is more suitable
Also how familiar are you with webpack internal apis? I've been looking for some help on my SSR plugin for next but needz the skillz
Funding wise I'm working on private capital so I could comp you. Takes a while to procure but once I've got cash I'll be looking for some hands to help on specific problem
i have little experience, i am having now to develop this plugin, and about the ssr i need to confess that i had never play around with ssr, even next or nuxt, but i am a hell of a curious person, maybe i can learn around some things
Find another slot on my calendar :D
@ScriptedAlchemy i took a while to re-schedule, but now i booked ;)
I would love to have this access to remotes too for dynamic imports
@alessioerosferri we are willing to make it possible thru https://github.com/schirrel/extended-module-federation-plugin
So I may have a way to do this now. It would involve inspecting the runtime module that creates this request, and see if it can take a dynamic expression for it.
@ScriptedAlchemy as we saw this is not a thing related to MF and yes to JS itself and webpack, i think is right this to be closed.
I'll be releasing that function for end users. But you can achieve it with my new SSR plugin and just loop over the entries and execute them, they'll return the get init interface for all remotes connected to the network. And you don't have to do any manual script injection since I hooked into webpack Federation loading mechanics
@ScriptedAlchemy Could you share an example of this please. Thanks!