Module Federation Redesign #1170
Replies: 24 comments 70 replies
-
I think runtimePlugins can combine with delegate module ,and may like this structure: export const RuntimePlugin = (config)=>{
return {
beforeLoadRemoteEntry(){
// can programmatically return remoteEntry url
},
loadRemoteEntry(){},
beforeInit(){
// before remote entry / shared init
},
init(){
// init remote entry and shared
},
loadRemote(){
// call remoteEntry.get('remote module')
},
errorLoadRemote(){
// error catch
},
}
} But don't sure whether we can programmatically control shared loading process. |
Beta Was this translation helpful? Give feedback.
-
We should also support dynamic remotes as in a remote URL should/can be able to fetch from a |
Beta Was this translation helpful? Give feedback.
-
Can it be made as easy to use as esm's importmap? Just build the commonjs module and do the mapping of the module to the remote paths via something like require-map . |
Beta Was this translation helpful? Give feedback.
-
I think this is the settings for the host right?
|
Beta Was this translation helpful? Give feedback.
-
my 2 cents -
|
Beta Was this translation helpful? Give feedback.
-
Looking forward to rspack support 😁 |
Beta Was this translation helpful? Give feedback.
-
Looking forward to to a simpler interface design for sharing files across Angular projects, something like this: {
exposes: [],
imports: [],
} Ideally, it could even be configured to be compatible with Angular's Dependency Injection (DI). With this design, I could directly import the exposed entities as if the files were locally available in my projects. Just to clarify, the exposes and imports arrays in this configuration object are placeholders. In a real use case, we would replace them with strings representing the paths to the modules we want to expose or import. Such a simple interface saves a lot of time when reusing my code and my teammates' code from other Angular projects. Moreover, this approach would eliminate the need for me to grapple with the complex concepts underlying Module Federation. |
Beta Was this translation helpful? Give feedback.
-
My suggestions: Middleware/Runtime API
I do like the shared scope idea, I feel like this could be another great place to introduce hooks into the process. It would be very useful at the very least for logging to know how the shared scopes are being negotiated and determined. I also agree that the consumer should not try to specify how a remote handles eager shared modules as shown below.
Does this fit with something like a MF/core concept at all? @ScriptedAlchemy |
Beta Was this translation helpful? Give feedback.
-
Support federated isomorphic client, so we can write something like this: import Client from '@aegis/core'
import { IModel, IAdapter, IDataSource } from './component-model'
const url = 'https://raw.githubusercontent.com/module-federation/aegis-app/master/dist/remoteEntry.js'
const client = new Client(url)
const order = await client.import<IModel>('models/order')
const fedex = await client.import<IAdapter>('adapters/fedex')
const store = await client.import<IDataSource>('datasources/orderdb')
order
.addAdapter('ship', fedex)
.addDataSource('orderdb', store)
.createInstance()
.list({ status: 'APPROVED' })
.then(list => list.forEach(order => order.ship())) |
Beta Was this translation helpful? Give feedback.
-
...and maybe something like this: new CentralFederationConfigPlugin ({
awsOrderContainer: {
module: 'models',
exportName: 'orderService',
url: 'https://my-bucket.s3-us-west-2.amazonaws.com/remoteEntry.js',
},
cloudflareOrderContainer: {
module: 'adapters',
exportName: 'orderCacheDurableObject',
url: 'https://cloudflare.com/my-cdn/remoteEntry.js',
}
})
|
Beta Was this translation helpful? Give feedback.
-
I would also add the ability to completely exclude libraries from the bundle, to speed up its compilation time, and give the user the possibility to choose the CDN that will be used to download that dependency. I'm already doing this with import maps, would be awesome to have a simple interface to it with module federation! |
Beta Was this translation helpful? Give feedback.
-
@ScriptedAlchemy I'm not sure if my edge case enters on this scenario but i had a rough time dealing with multiple service workers from different mfes to a single host. Is it viable we enhance this a bit more? Besides that, had a good experience using module fed 🚀. |
Beta Was this translation helpful? Give feedback.
-
Any way we can support cache-busting features out of the box for remoteEntry.js? |
Beta Was this translation helpful? Give feedback.
-
Will this redesign change the webpack implementation? Or it will implement by external webpack plugin? Although we can add JavaScript's plugin in rspack rather than rust code, performance will not good . |
Beta Was this translation helpful? Give feedback.
-
Reposting for tracking through my personal account - aka I'm @stacey-guild as well. Hello @ScriptedAlchemy! Took me a minute to get bandwidth, here's what we're doing now plus a few suggestions for improvements. I feel like a lot of these have been mentioned already which is awesome! We’re building a host platform where many different teams can plug in remote pages and add homepage widgets. The remotes span many teams across the company and require their own development/deployment cycles. Our host had become a deployment bottleneck for every time someone wanted to add a new remote, even just in staging because of review cycles and general "too many moving parts, not enough context" syndrome. As a result, we are retrieving our remote list from an api endpoint where each team can add their remote location and important details like “title, permissions, etc” to include for context in a database. This allows us dynamic locations and a changing number of remotes without redeploying our host as well as having different sets of remotes in dev/staging/prod. We are using the importRemote utility to load the pages at runtime. Example return object from our api:
Generic example here of loading a remote list and adding remotes to routes - https://github.com/staceyshk/module-federation-examples/pull/1/files#diff-831816579a766d2738d58fcb01416a8e11606b16f17bd5ee022f2a5b43295dc5R29 The benefit of this is we're using the user token permissions to decide what the user isn't allowed to see before it even gets to the host. For security reasons checking permissions within your client-side javascript isn't the best idea if you can avoid it (we have security on the backend as well, it's always "when you get hacked, not if"). For our use case some remotes expose 2 components from the same location, one that is a page and one that is a widget used on the homepage which share functionality. Right now
Ideally importRemote becomes a core feature where you can call
Alternatively, we could also solve this with middleware prefetching as you’ve got in the queue above and some browser caching or with server-side prefetching and rendering (that’s a big lift for us though, all of our apps are client-side only at the moment). Thanks a lot for taking the time to read our use case! Let me know if you have any follow up questions. |
Beta Was this translation helpful? Give feedback.
-
I have a few suggestions:
{
"exposes": [
{
"moduleName": "Button",
"assets": {
"js": {
"sync": [
"static/js/async/__vmok_expose_Button.eb1083b5.js"
],
"async": [
"static/js/async/page.df6d5d28.js"
]
},
"css": {
"sync": [],
"async": []
}
}
}
],
"shared": [] // Same data structure
}
import { init } from '@module-federation/utilities/runtime';
init({
remotes: [
{
name: "@demo/app3",
alias: "app3",
entry: "http:://xxx/remoteEntry.js"
}
],
shared: {
react: {
version: "17.0.0",
scope: "default",
lib: ()=> React,
shareConfig: {
singleton: true,
requiredVersion: "^17.0.0"
}
},
},
plugins: [],
}) Load remote modules, support the same reference method aliases and names as import, so that the runtime api also supports type hints |
Beta Was this translation helpful? Give feedback.
-
a few suggestions Offer clearer error notifications, such as when micro-module B relies on a module that is not exported in micro-module A, display an error message within module A to indicate the source of the reference.
better to "./@chanjet/chanjet-h5-biz-components/lib/voucher-number-input/util" does not exist in app-A source of the reference from app-B |
Beta Was this translation helpful? Give feedback.
This comment was marked as off-topic.
This comment was marked as off-topic.
-
My team has found that it's very difficult to introduce token-based authentication (e.g. Bearer authentication) for remote module assets, in the context of the importRemote library. Authentication of requests for code assets can be important in certain environments.
Our conclusion is that while injecting authentication is likely possible via workarounds, doing so is overly burdensome and it would be better to have first-class support for authenticated remote modules. |
Beta Was this translation helpful? Give feedback.
-
Is chunkMap used for preloading for performance optimization? In addition to including shared modules, it should be better to include other remote references, right? chunkMap: {
integrity: "sha384",
"home_header": {
moduleId: 345, //to help with something like loadable/component
chunks: ["/chunks/home_header.js", "/css/chunks/home_header.css"],
requires: [
{name: 'app2/App'}, {name: 'app3/Button'}, // dependent remote
{name: 'react', info: [0, 18, 0, 2]},//[strictVersion,major,minor,patch]
{name: 'lodash', info: [0, 4, 17, 3], chunks: ['/chunks/lodash.js']}
],
},
} |
Beta Was this translation helpful? Give feedback.
-
I think that in order to enhance the DX, we should also create a "database" of known dependencies that needs to be singleton to work properly, e.g.: react and react-dom. |
Beta Was this translation helpful? Give feedback.
-
First of all, thank you! One thing that's not clear to me right now is when to use a remote vs shared. For example, I can publish my The benefit of a remote is that I don't need to redeploy my host to update a remote. I watched this video on module federation versioning which shows the concept of loading a semver remote. I took that a step further and loaded esm modules in a promise-based dynamic remote. // mf-app-one/webpack.config.js
const fetchUselessLib = (resolve) => {
import('http://localhost:3000/npm/@local/jamime-usless-lib/^3.0.0/dist/browser/remote-entry.js').then(module => {
resolve({
get: (request) => module.get(request),
init: (scope) => {
try {
return module.init(scope);
} catch (e) {
console.log("localJamimeUslessLib has already been loaded");
}
},
});
})
};
const remotes = {
'@local/jamime-usless-lib': `promise new Promise(${fetchUselessLib.toString()})`,
} // mf-app-two/webpack.config.js
const fetchUselessLib = (resolve) => {
import('http://localhost:3000/npm/@local/jamime-usless-lib/3.0.1/dist/browser/remote-entry.js').then(module => {
resolve({
get: (request) => module.get(request),
init: (scope) => {
try {
return module.init(scope);
} catch (e) {
console.log("localJamimeUslessLib has already been loaded");
}
},
});
})
};
const remotes = {
'@local/jamime-usless-lib': `promise new Promise(${fetchUselessLib.toString()})`,
} // jamime-useless-lib/webpack.config.js
plugins: [
new webpack.container.ModuleFederationPlugin({
name,
filename: "remote-entry.js",
library: { type: "module" },
exposes: {
"./version": "./src/version.js",
},
shared: {
...pkg.dependencies,
},
}),
],
output: {
uniqueName: `${name}-${pkg.version}}`,
}, Using promise-based remotes allows me to use multiple different versions of the same package by defining a uniquename for the output. If I was to export the library as a var it would race as the names would be the same. If I was to change the name based on the version, the mfe wouldn't know the name of the global to use. I currently lack the ability to re-use the remote - we don't have any information about the resolved version and it's not added to the shared cache. I could replicate the If we had this functionality, could we then look to merge the concepts of shared modules and remotes? Is there another difference that I'm missing? For context - this is the problem I'm trying to solve. When using different major versions it works well When the CDN resolves to the same version it works well When using different sem ver ranges that could use the same module, we end up with duplication.
|
Beta Was this translation helpful? Give feedback.
-
It would be helpful to be able to hook into runtime shared resolution to know what dependency version and chunk a federated module actually resolved to from the share scope. This would allow us to create better runtime inspection tooling. I noticed this runtime hook in your blog post: Do you have an intention of including this resolution information there or making that ShareInfos context object extensible? |
Beta Was this translation helpful? Give feedback.
-
@ScriptedAlchemy congrats on the release of MF 1.5! 🎉 One thing I could not find there was this proposal for shared:
Is that something that is still to come or is there another way of achieving the same? |
Beta Was this translation helpful? Give feedback.
-
note on backward compatibility: the existing interface will remain compatible. Moreover we will support a webpack plugin with parity to rspack and may implement the new federation design as a webpack plugin to start
Ive posted a blog regarding the broader future: https://medium.com/@scriptedalchemy/the-future-of-module-federation-665dd33947cb and more depth
Plugin api:
What should the plugin give us?
remotes already accepts an array like:
If the first item fails, or throws error, it will loop over the array of options till one resolves.
remotes
Shared
Share scope should allow "quantum modules" concept - allowing us to make a dependnecy eager/lazy per runtime instead of globally.
This would allow for react to be eager:true in a host, but eager: false in a remote runtime.
Share scope runtime api today:
If duplicate shared modules offered, keep them in array and "pick" top one by default
Doing so allows us to avoid overwriting share scope, we can then "pick" what version is chosen by changing the runtime modules slightly, similar to how @2heal1 has suggested for lifecycle middleware.
I have actually done this before, but as a hack - by making share_scope a Proxy(), through that i was able to choose what "react" actually returns to a consumer during negotiations.
Middleware/runtimeplugins:
Node
Node apis / plugins need enhancements.
We node federation should support filesystem calls that are relative / local volume on network as well. Like EFS from aws.
This offers alternatives to my traditional remote code execution and a better pattern than offering a hidden chunk load global patch.
globalThis.__remote_scope__
needs something better within the node plugin.We need to register the known paths to remoteEntry in memory, so that when node needs to fetch chunk, it knows what the origin path is. @2heal1
Runtime API
Currently returns
{get,init}
api, but this is not enough for modern demands. Federation interface needs additional root interfaces.chunkMap
- reflection of chunks needed mapped to a exposed module, represents both css and js chunks as well as section for shared module chunks that it depends on as well. Useful for SSR manifests to remove waterfallinfo
- anything that cant/shouldnt go into share scope. Ideally things like tracking who is using what and where, things we would want chrome devtools to read, info about whats in use, and so on.get
- like typical federationget
- however, there should be the option to decorate it.Additional metadata about what module is being fetched would be very helpful, for example if its a react component or util function.
Are there any other aspects we would want to have runtime control? from both global and independent remote interfaces?
__webpack_require__.federation
for example:federation.getAllConnectedRemotes = ()=>{}
federation.getAllSharedModulesInUse = ()=>{}
federation.uninstallRemote = ()=>{}
federation.useFallbackof(remote)
Global Runtime Api?
Should there be a global runtime api for things like
federation.getAllConnectedRemotes
federation.getAllSharedModules
federation.registerRemote
federation.import
@web-infra-dev has put a lot work into "VMOK" which is similar to medusa. The runtime api / module info provided to users is very comprehensive.
Dynamic, pluggable api:
@2heal1 has already shared some api, this is great start
WIP
Compile Time Features
import('myremote/${someVar}')
Beta Was this translation helpful? Give feedback.
All reactions