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 returns blank in dev when connecting to remote repo, works in prod

See original GitHub issue

I have 2 repos, both microfrontends, created with Module Federation and custom webpack config.

The shared repo is deployed. In the other repo I want to connect to the remote shared version, but if I do that in the webpack.dev.config.ts, where the mode is set to “development”, it shows me a blank page with no error.

When I change the mode to “production” it works - I have copied the prod config to the dev, commented out the differences, turned them on one by one and narrowed it down to the mode being the problem.

Based on some tutorials I saw, it looks like this wasn’t an issue before. I’m using

"webpack": "5.72.1",
"webpack-cli": "4.9.2",
"webpack-dev-server": "4.8.0"

here’s the webpack.dev.config.ts setup that works fine with the mode: “production” and the remote repo given in module federation setup.

const port = 3502;

interface Configuration extends WebpackConfiguration {
  devServer?: WebpackDevServerConfiguration;
}

const config: Configuration = {
  mode: "production", // TODO works with remote repo in module federation setup
  // mode: "development", // TODO doesn't work

  output: {},
  optimization: {
    runtimeChunk: false,
  },
  devServer: {
    port,
    host: "0.0.0.0",
    hot: true,
    historyApiFallback: true,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
  module: {
    rules: [
      {
        test: /\.module\.(css|scss)$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[name]_[local]__[hash:base64:5]",
              },
              sourceMap: true,
            },
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
      {
        test: /\.(css|scss)$/,
        exclude: /\.module\.(css|scss)$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
            },
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
      {
        test: /\.(ts|js)x?$/i,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
          },
        },
      },
      {
        test: /\.(jpg|jpeg|png|gif|svg)$/,
        use: ["file-loader"],
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
    alias: {
      "@": path.resolve(__dirname, "src"),
      "@App": path.resolve(__dirname, "src/App"),
      "@Components": path.resolve(__dirname, "src/App/components"),
      "@Remote": path.resolve(__dirname, "src/App/components/@remote"),
    },
  },
  plugins: [
    new container.ModuleFederationPlugin({
      name: "sof",
      filename: "remoteEntry.js",
      exposes: {
        "./Sof": "./src/App/App.tsx",
      },
      remotes: {
        shared: "shared@https://shared-dev.foo/remoteEntry.js", //this doesn't work with mode: "development"
        // shared: "shared@http://localhost:6002/remoteEntry.js", //this works in both cases
      },
      shared: [
        {
          react: {
            singleton: true,
          },
        },
      ],
    }),
    new HtmlWebpackPlugin({
      template: "public/index.html",
    }),
    new MiniCssExtractPlugin({
      filename: "[name].[hash].css",
      chunkFilename: "[id].[hash].css",
    }),
    new ForkTsCheckerWebpackPlugin({
      async: false,
    }),
    new ESLintPlugin({
      extensions: ["js", "jsx", "ts", "tsx"],
    }),
    new Dotenv(),
  ],
  // devtool: "inline-source-map", // TODO
};

export default config;

here’s the prod version. e.g. the output is different, but when I changed the output to an empty object in dev config, it works fine.

const config: Configuration = {
  mode: "production",
  output: {
    path: path.resolve("build"),
    filename: "[name].bundle.js?v=[contenthash]",
  },
  module: {
    rules: [
      {
        test: /\.module\.(css|scss)$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              modules: {
                localIdentName: "[name]_[local]__[hash:base64:5]",
              },
              sourceMap: true,
            },
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
      {
        test: /\.(css|scss)$/,
        exclude: /\.module\.(css|scss)$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
            },
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
      {
        test: /\.(ts|js)x?$/i,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],
          },
        },
      },
      {
        test: /\.(jpg|jpeg|png|gif|svg)$/,
        use: ["file-loader"],
      },
    ],
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
    alias: {
      "@": path.resolve(__dirname, "src"),
      "@App": path.resolve(__dirname, "src/App"),
      "@Components": path.resolve(__dirname, "src/App/components"),
      "@Remote": path.resolve(__dirname, "src/App/components/@remote"),
    },
  },
  plugins: [
    new container.ModuleFederationPlugin({
      name: "sof",
      filename: "remoteEntry.js",
      exposes: {
        "./Sof": "./src/App/App.tsx",
      },
      remotes: {
        shared: "shared@https://shared-dev.foo/remoteEntry.js",
      },
      shared: [
        {
          react: {
            singleton: true,
          },
        },
      ],
    }),
    new HtmlWebpackPlugin({
      template: "public/index.html",
    }),
    new MiniCssExtractPlugin({
      filename: "[name].[hash].css",
      chunkFilename: "[id].[hash].css",
    }),
    new ForkTsCheckerWebpackPlugin({
      async: false,
    }),
    new ESLintPlugin({
      extensions: ["js", "jsx", "ts", "tsx"],
    }),
    new Dotenv(),
    new CleanWebpackPlugin(),
  ],
};

export default config;

I want to be able to connect to the remote shared repo without having to make manual changes in webpack. Any idea how to?

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:10 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
dominikabiedercommented, Jun 16, 2022

hi, so we found a problem with my team. we’ve upgraded all packages to the newest and realised we were missing eager: true in webpack module federation config.

first we had no error, but after tweaking some packages we’ve ended up with the following error, that eager:true resolved

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'current')
    at isConcurrentActEnvironment (react-dom.development.js:25143:62)
    at warnIfUpdatesNotWrappedWithActDEV (react-dom.development.js:27508:12)
    at scheduleUpdateOnFiber (react-dom.development.js:25399:5)
    at updateContainer (react-dom.development.js:28799:14)
    at ReactDOMHydrationRoot.render.ReactDOMRoot.render (react-dom.development.js:29211:3)

here’s what fixed the problem for us

new container.ModuleFederationPlugin({
      name: "sof",
      filename: "remoteEntry.js",
      exposes: {
        "./Sof": "./src/App/App.tsx",
      },
      remotes: {
        shared: "shared@https://foo.com/remoteEntry.js",
      },
      shared: [
        {
          react: { singleton: true, eager: true }, // <-------
        },
      ],
    }),
1reaction
vankopcommented, Jun 9, 2022

please create reproducible repo… it is hard to guess a problem…

Read more comments on GitHub >

github_iconTop Results From Across the Web

Webpack 5 module federation error handling - Stack Overflow
I've error boundary which works perfect on dev / staging / production environment and displays 404 page when the remoteEntry is not fetched...
Read more >
Tutorial - A Guide to Module Federation for Enterprise
Module Federation is an exciting new addition to Webpack 5. ... Next, we're getting all remote chunks and returning the code.
Read more >
Micro-frontends: Module Federation with WebPack 5
It allows a JavaScript application to dynamically load code from another application (a different Webpack build). This is how you normally use ...
Read more >
Revolutionizing Micro Frontends with Webpack 5, Module ...
In short, Module Federation allows JavaScript application to dynamically import code from another application at runtime. The module will build ...
Read more >
Webpack 5 Module Federation — Stitching two simple ...
Both app1 and app2 are pretty minimal and standard react applications. Nothing fancy. TLDR; Heres a full screencast of this medium article.
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