question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Module Federation Support

See original GitHub issue

Feature request

@Timer @rauchg @timneutkens In order to stop my “Twitter Driven Development” (still love that quote) I have updated the description of this issue to hopefully help move this challenge forwards.

Is your feature request related to a problem? Please describe.

Support Module Federation ability to share vendor code, like react

Describe the solution you’d like

  • We should not use entrypoint based routing, each entrypoint would require react and react-dom to be async imported, or each entrypoint would need to use the import(./bootstrap) method in order to allow webpack to figure out if react exists already or if it needs to go and download the chunk. Ideally, there should be only one entrypoint, then whatever else needs to happen is done so via code splitting and async imports.
  • An alternative nice to have would be to make shared: {import:'react',eager:true} work - this would force react to be loaded upfront - however it seems that react is included multiple times in each entrypoint - causing various multiple copies of react issues. If eager worked, I could at least share react and force the remote to always use the hosts copy of react (I think) - this would / should be okay since next mostly requires us to be on a somewhat consistent version of react. Like next 11 will use v17, since all my apps use the latest nextjs, they will be compliment with a v17 version of react.

The bare minimum to get this working client or server-side would be to have some flag that changes how the client entrypoint gets started. If the client.js looked like this https://github.com/module-federation/module-federation-examples/blob/master/bi-directional/app1/src/index.js then there’s a high likelihood we could hold up the application until whatever shared modules with static imports were ready before hydration begins.

Current workaround

My workaround no longer works for SSR (since 10.x), but works for CSR - is to alias react as some external global https://github.com/module-federation/nextjs-mf/blob/main/react.js

~I then patchSharing which is an ultra hack that literally inlines react into the head of _document as a script https://github.com/module-federation/nextjs-mf/blob/main/patchSharing.js~ I use resolve.alias against react$ to point react imports to my custom react file https://github.com/module-federation/nextjs-mf/blob/main/withModuleFederation.js#L27

~This is not optimal for production-grade use but the limits of next give us little choice~ Seems okay for production grade apps

Even with Webpack 5, what we have done to get our federated AB testing, Tag manager, and AB testing engine to work with next is apply a shim that creates a fake sharing API that attaches react to share scope manually. This mechanism is the same one that I used a year ago when next was still webpack 4 based and I was using webpack 5 remotes against v4 hosts.

import logger from "../logger";
/*  This logic is needed because we're not using webpack 5 yet */

const sharedExports = {};

const shareExports = (exports, remote = "") => {
  /*
  exports is expected to be an object
  {
    "react": ()=> Promise.resolve(()=>require("react"))
  }

  The value should be:
  - a function that returns a promise
  - that promise should resolve to a function
  - that function should return the module
  */

  // if no remote is given "" is used and that will apply to all remote entries
  // This method is additive, there is no "unshare"
  sharedExports[remote] = Object.assign(sharedExports[remote] || {}, exports);
};

const applySharedExports = (remote) => {
  if (typeof window !== "undefined") {
    // Given a remote entry, we gather all the modules that the host application has already
    const mergedExports = {
      ...sharedExports?.[""], // apply globally shared modules
      ...sharedExports?.[remote] // modules shared only for this remote entry
    };

    // Logic for applying @ScriptedAlchemy  
    // check if using new webpack API
    const override = window?.[remote]?.init
      ? window?.[remote]?.init
      : window?.[remote]?.override;
    if (window?.[remote]?.init) {
      Object.assign(
        mergedExports,
        Object.entries(mergedExports).reduce((acc, [key, value]) => {
          if (typeof value === "function") {
            let version = "16.13.1";
            try {
              // eslint-disable-next-line
              version = value.version;
            } catch (error) {
              logger.error("Error retrieving shared version", error);
            }

            Object.assign(acc, {
              [key]: {
                [version]: {
                  get: value,
                  loaded: true,
                  from: "dar"
                }
              }
            });
          }
          return acc;
        }, {})
      );
    }
    if (override) {
      try {
        override(Object.assign(mergedExports, __webpack_require__.O));
      } catch (error) {
        logger.warn(`Unable to apply webpack 5 shim of shared exports`, error);
      }
    }
  }
};

export { shareExports, applySharedExports };

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:216
  • Comments:54 (20 by maintainers)

github_iconTop GitHub Comments

32reactions
ScriptedAlchemycommented, Jun 11, 2021

As of today, I was able to use just the Module Federation plugin with next js.

It appears that sharing works now, at least when it’s in eager mode.

~As of my testing today - nextjs supports federation without any workarounds~. 🎉

It’s almost too good to be true.

I’ll perform some deeper tests tomorrow to confirm.

22reactions
ScriptedAlchemycommented, May 19, 2021

Update on this.

~I’ve found a way to move the async boundary into the webpack runtime. This circumvents limitations of next.js by changing how webpack startup runtime module works.~

~Replacing the startup module will effectively prevent next from initializing till webpack container negotiations take place.~

~I only uncovered this today, so it’ll take some time to modify and PR webpack.~

Too hard

Read more comments on GitHub >

github_iconTop Results From Across the Web

Module Federation - webpack
webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable...
Read more >
Module Federation
Webpack 5 Module Federation aims to solve the sharing of modules in a distributed system, by shipping those critical shared pieces as macro...
Read more >
What Is Webpack Module Federation and Why Does It Matter?
Module Federation is one of the most exciting features in Webpack 5 and is considered a game-changer in JavaScript architecture. It supports ......
Read more >
Building micro-frontends with webpack's Module Federation
Module federation is a JavaScript architecture invented by Zack Jackson. This architecture allows the sharing of code and dependencies between ...
Read more >
Implementation examples of module federation , by ... - GitHub
Module federation will work with any type of file that youre able to import, that Webpack understands how to process. It is not...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found