Skip to content
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

Feature: MFE import functions #2832

Closed
2 tasks done
bh3605 opened this issue Aug 6, 2024 · 8 comments
Closed
2 tasks done

Feature: MFE import functions #2832

bh3605 opened this issue Aug 6, 2024 · 8 comments

Comments

@bh3605
Copy link

bh3605 commented Aug 6, 2024

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

  • Read the Contributing Guidelines.
  • Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
@ScriptedAlchemy
Copy link
Member

Just use module-federation/runtime. LoadRemote and init

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

@bh3605
Copy link
Author

bh3605 commented Aug 7, 2024

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

@ScriptedAlchemy
Copy link
Member

ScriptedAlchemy commented Aug 7, 2024

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

@ScriptedAlchemy
Copy link
Member

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

Copy link
Contributor

github-actions bot commented Oct 7, 2024

Stale issue message

@bh3605
Copy link
Author

bh3605 commented Oct 8, 2024

That's what I've done.

@bh3605 bh3605 closed this as completed Oct 8, 2024
@ScriptedAlchemy
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants