core icon indicating copy to clipboard operation
core copied to clipboard

Feature: MFE import functions

Open bh3605 opened this issue 1 year ago • 5 comments

Clear and concise description of the problem

Angular architects has their own code to import modules using import(). https://github.com/angular-architects/module-federation-plugin/blob/main/libs/mf-runtime/src/lib/loader/dynamic-federation.ts

You have an example doing something similar for React. https://github.com/module-federation/module-federation-examples/blob/master/advanced-api/dynamic-remotes-runtime-environment-variables/host/src/hooks/useFederatedComponent.js

Would it be possible to generalize what your code uses like how angular architects has? It would be amazing if there was a single library that could leverage the high customization level the advanced mod fed plugin has and provides functions that can create a shared scope, import MFEs and even import/initialize web components. This library has everything I want to customize the remoteEntry.js file, but now it needs some helper functions to import these MFEs and share a scope object between them.

Suggested solution

There are a bunch of different ways to achieve this.

prototype code of mine using systemjs

// https://medium.com/@rkhasawneh/single-spa-with-module-federation-in-systemjs-446f0de4832b
// https://github.com/rami8k/spa-complete-example
export async function importApp(app, shareScope, importedApps) {
  let appParts = getAppParts(app)
  // if no module property then this MFE isn't using module federation
  if(!appParts.module) {
    return System.import(appParts.app);
  }
  return System.import(appParts.app).then(app => {
    if(!importedApps.includes(appParts.app)) {
      // https://webpack.js.org/concepts/module-federation/#dynamic-remote-containers
      app.init(shareScope)
    }
    if(appParts.module) {
      return app.get(appParts.module).then(module => {
        return module();
      });
    } else {
      return app;
    }
  });
}

function getAppParts(app) {
  let appParts = app.split('/')

  if(app.startsWith('@') && appParts.length > 2) {
    return {
      app: `${appParts[0]}/${appParts[1]}`,
      module: appParts.length > 2 ? appParts[2] : null
    }
  }

  return {
    app: appParts[0],
    module: appParts[1]
  }
}

You could take inspiration from how @angular-architects does it https://github.com/angular-architects/module-federation-plugin/blob/main/libs/mf-runtime/src/lib/loader/dynamic-federation.ts

This prototype of mine tries to be able to import both MFEs and module federated MFEs using @angular-architects/mod-fed and systemjs. No fear posting this code here.

let importedApps = [];
let shareScope = [];

export async function importApp(app, shareScope, importedApps) {
  let appParts = getAppParts(app);
  // if(!appParts.module) {
    // return System.import(appParts.app);
  // }
  // if(app == "consumer-services") {
  //   const s = {
  //     type: "manifest",
  //     remoteName: appParts.app,
  //     exposedModule: `./${appParts.module ? appParts.module : ''}`
  //   } as LoadRemoteModuleOptions;
  //   return loadRemoteModule(s);
    // const url = System.resolve(appParts.app);
    // const s = {
    //   type: "manifest",
    //   remoteName: appParts.app,
    //   exposedModule: `./${appParts.module ? appParts.module : ''}`
    // } as LoadRemoteModuleOptions;
    // return loadRemoteModule(s).then(
    //   (module) => {console.log(module); System.set(appParts.app, module); return module;}
    // );
    // return await import(/* webpackIgnore:true */ url).then(
    //   (see) => {
    //     System.set(appParts.app, see);
    //     return see;
    //   }
    // );
  // } else {
    return System.import(appParts.app).then(app => {
      if(!importedApps.includes(appParts.app) && app.init !== undefined) {
        // const s = {
        //   type: "manifest",
        //   remoteName: appParts.app,
        //   exposedModule: `./${appParts.module ? appParts.module : ''}`
        // } as LoadRemoteModuleOptions;
        // return loadRemoteModule(s);
        app.init(shareScope);
      }
      if(app.get !== undefined) {
        return app.get(`./${appParts.module ? appParts.module : ''}`).then(module => {
          return module();
        })
      } else {
        return app;
      }
    });
  // }
}

function getAppParts(app) {
  let appParts = app.split('/')

  if(app.startsWith('@')) {
    if(appParts.length > 2) {
      return {
        app: `${appParts[0]}/${appParts[1]}`,
        module: appParts.length > 2 ? appParts[2] : null
      }
    }
  }

  return {
    app: appParts[0],
    module: appParts.length > 2 ? appParts[1] : null
  };
}

Alternative

No response

Additional context

No response

Validations

  • [X] Read the Contributing Guidelines.
  • [X] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.

bh3605 avatar Aug 06 '24 14:08 bh3605

Just use module-federation/runtime. LoadRemote and init

https://module-federation.io/guide/basic/runtime.html

ScriptedAlchemy avatar Aug 06 '24 23:08 ScriptedAlchemy

https://github.com/module-federation/module-federation-examples/tree/master/dynamic-system-host

ScriptedAlchemy avatar Aug 06 '24 23:08 ScriptedAlchemy

Is there a function I can call to pass my manifest json to? I prefer to using that than hardcoding. I do have it calling init(), but there's a weird build error. Also you should be exporting the object type the init method references to make typing easier. Makes it hard to type stuff.

image image

bh3605 avatar Aug 07 '24 15:08 bh3605

Provide a repo or send a PR for the TS stuff.

ScriptedAlchemy avatar Aug 07 '24 23:08 ScriptedAlchemy

If you use a runtime plugin you can change the remotes on the fly.

ScriptedAlchemy avatar Aug 07 '24 23:08 ScriptedAlchemy

Stale issue message

github-actions[bot] avatar Oct 07 '24 15:10 github-actions[bot]

That's what I've done.

bh3605 avatar Oct 08 '24 15:10 bh3605

Is there a function I can call to pass my manifest json to? I prefer to using that than hardcoding. I do have it calling init(), but there's a weird build error. Also you should be exporting the object type the init method references to make typing easier. Makes it hard to type stuff.

image image

Send a PR improving our types if you wish.

ScriptedAlchemy avatar Oct 08 '24 19:10 ScriptedAlchemy