Skip to content

feat: enforce global singleton for all exposed modules via module federation #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions src/virtualModules/virtualExposes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,31 @@ export const VIRTUAL_EXPOSES = 'virtual:mf-exposes';
export function generateExposes() {
const options = getNormalizeModuleFederationOptions();
return `
const GLOBAL_KEY = '__MF_SINGLETONS__';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not add more globals, this entire thing already exists in FEDERATION
should be using loadShare('lib', {from: 'name of current app'}); or at least from share_scope_map off federation

This will also not work with module layers which is being introduced, where you can have concurrent parallel share scopes who still respect singletons, but within the scope - so in cases like next, i have 13 singletons for react.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the tests I have made, the instance is not unique, so the instance may be recreated. I'd like to know what your approach is to avoid the problem of multiple instances

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be 1 instance per app. Recreation of instances shouldn't happen.
If thats is happening theres a bigger problem

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instance is stored in memory / state and registered in the federation global, we search for the instance by name.

These arch specs may be useful. https://github.com/module-federation/core/pull/3955/files#diff-5596198c9d0ae99e768d378410829b221f2ac61b098aa86e5c55a994d6efb964

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be the case for Webpack, but not for Vite. Based on my tests, I created an Axios instance with Keycloak for the token part and shared this instance with the other remotes. However, doing so resulted in the instances being created twice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah then its a bug in vite implementation. all webpack does is implement the runtime package.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gioboa what do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can totally trust @ScriptedAlchemy

Copy link
Author

@aachik-trimoz aachik-trimoz Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gioboa do you think you can do an investigation to know why we don't have this in the Vite version

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Id check why instances are recreated. In webpack. We do have 1 aspect where we assign the instance associated with that runtime to webpack_require. So we can access the instance throughout the application.
However I can also call init() multiple times over and it doesn't recreate the instance because we store it in a scoped variable - but thats what /runtime init does.
We did introduce createInstance now as well to runtime.

const globalObj = typeof globalThis !== 'undefined' ? globalThis : window;
globalObj[GLOBAL_KEY] = globalObj[GLOBAL_KEY] || {};

export default {
${Object.keys(options.exposes)
.map((key) => {
const singletonKey = JSON.stringify(key);
return `
${JSON.stringify(key)}: async () => {
const importModule = await import(${JSON.stringify(options.exposes[key].import)})
const exportModule = {}
Object.assign(exportModule, importModule)
Object.defineProperty(exportModule, "__esModule", {
value: true,
enumerable: false
})
return exportModule
${singletonKey}: async () => {
if (!globalObj[GLOBAL_KEY][${singletonKey}]) {
const importModule = await import(${JSON.stringify(options.exposes[key].import)});
const exportModule = {};
Object.assign(exportModule, importModule);
Object.defineProperty(exportModule, "__esModule", {
value: true,
enumerable: false
});
globalObj[GLOBAL_KEY][${singletonKey}] = exportModule;
}
return globalObj[GLOBAL_KEY][${singletonKey}];
}
`;
})
.join(',')}
}
}
`;
}