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: injecting scripts for remotes with HtmlWebpackPlugin

See original GitHub issue

Bug report

What is the current behavior?

During runtime when request to remote application is pending, injected script is blocking the page.

If the current behavior is a bug, please provide the steps to reproduce.

In the scenario we have host and remote applications:

  • host application shell (localhost:3000)
  • remote app (localhost:3002) which is not responding, we simulate request pending by setting the timeout

shell webpack configuration:

new ModuleFederationPlugin({
  name: "shell",
  filename: "remoteEntry.js",
  remotes: {
    app: 'app@http://localhost:3002/remoteEntry.js',
  }
})
new HtmlWebpackPlugin({
    template: "./public/index.html",
    inject: "body",
})

Source code of app:

const express = require("express");
const app = express();
const port = 3002;
app.use((req, res, next) => setTimeout(next, 30000));
app.get("*", (req, res) => { res.send("Hello World!"); });
app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); });
Screenshot 2021-08-04 at 13 52 19

As you can see script connecting for the remote entry is located in head tag.

We can use promise based loaders for remotes, pass a promise to this remote and inject into body tag:

market: `promise new Promise(resolve => {
  const remoteUrl = 'http://localhost:3002/remoteEntry.js'
  const script = document.createElement('script')
  script.async = true;
  script.defer = true;
  script.src = remoteUrl
  document.body.appendChild(script);
})
`

and at runtime script is located at the end body but the page is still blocked by loading remote entry.

Screenshot 2021-08-04 at 14 13 31

Alternatively we can dynamically load remotes during runtime using this

What is the expected behavior?

  1. When inject: body is set with HtmlWebpackPlugin put loading script at the end of the body tag.
  2. Do not block page/application when remote entry is not responding.

Other relevant information: webpack version: 5.38.1 Node.js version: 14.17.1 Operating System: macOS/Ubuntu Additional tools: html-webpack-plugin: 5.3.1

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
serrgcommented, Nov 9, 2021

@DevHunvee3 The issue still occurs for us and we are using workaround for non blocking render:

import { ComponentType } from 'react';

declare global {
  interface Window {
    __remotes__: Record<string, string>;
  }

  // eslint-disable-next-line @typescript-eslint/camelcase
  const __webpack_init_sharing__: any;
  // eslint-disable-next-line @typescript-eslint/camelcase
  const __webpack_share_scopes__: any;
}

const getRemoteEntryUrl = (module: string) =>
  `${module}@${getPublicPath(module)}${REMOTE_ENTRY}`;


const dynamicImport = async (path: string): Promise<{ default: ComponentType<any> }> => {
  const remoteName = path.split('/')[0];

  if (!remoteName) {
    throw new Error(`URL not configured for remote '${path}'.`);
  }

  const remoteUrl = getRemoteEntryUrl(remoteName);

  if (remoteUrl.split('@').length !== 2) {
    throw new Error(`URL misconfigured for remote '${path}'`);
  }

  const [moduleName, moduleUrl] = remoteUrl.split('@');

  await __webpack_init_sharing__('default');

  await new Promise<void>((resolve, reject) => {
    const element = document.createElement('script');

    element.src = moduleUrl;
    element.type = 'text/javascript';
    element.async = true;

    element.onload = () => {
      element.parentElement?.removeChild(element);
      resolve();
    };

    element.onerror = (err: Event | string) => {
      element.parentElement?.removeChild(element);
      reject(err);
    };

    document.head.appendChild(element);
  });

  // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
  // @ts-ignore
  const container = window[moduleName] as any;

  // eslint-disable-next-line @typescript-eslint/camelcase
  await container.init(__webpack_share_scopes__.default);

  const component = `.${path.replace(remoteName, '')}`;
  const factory = await container.get(component);

  return factory();
};

and using dynamicImport like:

const Comp = () => {
  const fallback = <></>;
  const RemoteService = lazy(() => dynamicImport('remoteService'));

  return (
    <ErrorBoundary FallbackComponent={() => <></>}>
      <Suspense fallback={fallback}>
        <RemoteService />
      </Suspense>
    </ErrorBoundary>
  );
};

where remoteService is remote path from module federation config

1reaction
alexander-akaitcommented, Aug 4, 2021

Please open an issue in HtmlWebpackPlugin, it should be solved there

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 >
Tutorial - A Guide to Module Federation for Enterprise
Module Federation is an exciting new addition to Webpack 5. ... code example by devonChurch outlines a method for injecting local and remote ......
Read more >
Webpack Module Federation handling when the remote app is ...
Therefore I got error when I run the app1 , it finished loading with displaying the fallback of the React's Suspense , but...
Read more >
Webpack 5 Module Federation — Stitching two simple ...
A short and sweet guide to using Module Federation on two independently deployed web apps, so that they can work like a monolith....
Read more >
How to Use Webpack Module Federation in React
This is a basic webpack example to get our js and jsx code transpiled using babel-loader and injected into an html template. Update...
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