core icon indicating copy to clipboard operation
core copied to clipboard

loading a remote wipes out the registered shared modules by the host even if they are declared singleton

Open MadaraUchiha-314 opened this issue 1 year ago • 2 comments

Describe the bug

When a host is initialized with some shared modules say react and then host loads a remote which also shares react, the module registered by the host is removed from the shared scope (__FEDERATION__.__SHARE__) and is replaced with the one brought in by the remote.

This happens even if the host registers the module as "singleton".

The problem is that host doesn't "load" the shared module before the remote is loaded.

Expected behavior: Since the host registers a module in the shared scope first and marks it as singleton, subsequent remotes which depend on the same module should use the one provided by the host.

Reproduction

https://stackblitz.com/edit/stackblitz-starters-rsuexa?file=packages%2Fhost-shell%2Fhost-shell.js

Used Package Manager

npm

System Info

- NA

Validations

MadaraUchiha-314 avatar Jul 24 '24 06:07 MadaraUchiha-314

federation is assign based, it object assigns new over old, since the keys (version) is the same - it doesn't matter to init order. You can put it into loaded-first which may prevent this.

import type { FederationRuntimePlugin } from '@module-federation/enhanced/runtime';

const sharedStrategy: () => FederationRuntimePlugin = () => ({
  name: 'shared-strategy-plugin',
  beforeInit(args) {
    const { userOptions } = args;
    const shared = userOptions.shared;
    if (shared) {
      Object.keys(shared).forEach((sharedKey) => {
        const sharedConfigs = shared[sharedKey];
        const arraySharedConfigs = Array.isArray(sharedConfigs)
          ? sharedConfigs
          : [sharedConfigs];
        arraySharedConfigs.forEach((s) => {
          s.strategy = 'loaded-first';
        });
      });
    }
    return args;
  },
});
export default sharedStrategy;

ScriptedAlchemy avatar Jul 26 '24 13:07 ScriptedAlchemy

The solution works! but .....

It works when I use @module-federation/enhanced/webpack. But when I use webpack/lib/container/ModuleFederationPlugin.js, it doesn't work because there's no concept of runtime plugins in that.

I have updated my above mentioned stackblitz example to reflect it.

But interestingly, beforeInit is not even called for the MF 1.0 remote. Even if it is called, because there's no federation runtime, the only way a MF 1.0 remote will not wipe out the share scope is if we forcefully add loaded: true to the packages provided by the host.

Hence my proposal is to modify the SharedBaseArgs to add one additional param to mark any module as loaded. If loaded: true is an internal state, we can rename it to something else like preferred or whatever, but internally it will mark that module as loaded: true after which MF 1.0 container will start using it.

MadaraUchiha-314 avatar Jul 26 '24 16:07 MadaraUchiha-314

@ScriptedAlchemy @zackarychapple https://github.com/module-federation/core/issues/2739 also highlights the same issue. I think we should solve for this as this is a very common issue that enterprises have.

MadaraUchiha-314 avatar Aug 05 '24 19:08 MadaraUchiha-314

This issues happens even if singleton: true is set together import: false. As a result react is fetched and loaded multiple times from the host URL by all remotes.

Also it seems that the same issue happens with exposes, so If my host expose some module and the module has been already loaded by the host, all remotes will re-load it again.

aindlq avatar Aug 05 '24 20:08 aindlq

Can you mutate __webpack_share_scopes__ and manually assign the loaded param

ScriptedAlchemy avatar Aug 06 '24 01:08 ScriptedAlchemy

This issues happens even if singleton: true is set together import: false. As a result react is fetched and loaded multiple times from the host URL by all remotes.

Also it seems that the same issue happens with exposes, so If my host expose some module and the module has been already loaded by the host, all remotes will re-load it again.

It depends on how you are consuming things, do you have repo?

ScriptedAlchemy avatar Aug 06 '24 01:08 ScriptedAlchemy

Can you mutate __webpack_share_scopes__ and manually assign the loaded param

The host is built using rollup. There's no such variable __webpack_share_scopes__ until we load a weback remote.

It would be great if we had a way of doing it directly using the runtime apis instead of hacking it through webpack internal variables.

MadaraUchiha-314 avatar Aug 06 '24 17:08 MadaraUchiha-314

Verified that it's working now with version 0.4.0

Updated the stackblitz example linked above to reflect that.

MadaraUchiha-314 avatar Aug 13 '24 15:08 MadaraUchiha-314