Webpack Module Federation loads chunks from wrong URL - reactjs

I am building a project with webpack module federation with the following setup:
React host (running on localhost:3000)
Angular Remote 1 (running on localhost:4201)
Angular Remote 2 (running on localhost:4202)
The goal is to get both remotes working. If I only run one of the and remove the other it is working perfectly.
The issue I am facing is that when the remotes are loaded, __webpack_require__.p is set by one of the remotes' script and therefore the other remote's chunk is loaded from the wrong url.
Here is the error I get:
My module federation config is the following:
React host:
.
.
.
new ModuleFederationPlugin({
name: "react_host",
filename: "remoteEntry.js",
remotes: {
angular_remote_1: "angular_remote_1#http://localhost:4201/angular_remote_1.js",
angular_remote_2: "angular_remote_2#http://localhost:4202/angular_remote_2.js"
},
exposes: {},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
},
}),
.
.
.
Angular Remote 1
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
output: {
publicPath: "auto",
uniqueName: "angular_remote_1",
scriptType: "text/javascript"
},
optimization: {
runtimeChunk: false
},
experiments: {
outputModule: true
},
plugins: [
new ModuleFederationPlugin({
name: "angular_remote_1",
library: { type: "var", name: "angular_remote_1" },
filename: "angular_remote_1.js",
exposes: {
'./angularRemote1': './src/loadAngularRemote1.ts',
},
shared: ["#angular/core", "#angular/common", "#angular/router"]
})
],
};
Angular Remote 2
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
output: {
publicPath: "auto",
uniqueName: "angular_remote_2",
scriptType: "text/javascript"
},
optimization: {
runtimeChunk: false
},
experiments: {
outputModule: true,
},
plugins: [
new ModuleFederationPlugin({
name: "angular_remote_2",
library: { type: "var", name: "angular_remote_2" },
filename: "angular_remote_2.js",
exposes: {
'./angularRemote2': './src/loadAngularRemote2.ts',
},
shared: ["#angular/core", "#angular/common", "#angular/router"]
})
],
};
Thins I have tried so far:
Playing around with public path (between auto and hardcoded)
Setting a custom chunkLoadingGlobal for both remotes (not the host)
The exact reproduction of this issue can be found here: https://github.com/BarniPro/react-host-angular-remotes-microfrontend
Any help is greatly appreciated.

The issue can be solved by setting the topLevelAwait experiment to true in the remotes's webpack.config.js:
experiments: {
topLevelAwait: true,
},
This results in the two remotes loading in sync which prevents the paths from overriding each other.
Another update I had to make was disabling the splitChunks option in the remotes' optimization settings (see SplitChunksPlugin):
optimization: {
runtimeChunk: false, // This is also needed, but was added in the original question as well
splitChunks: false,
}
The Github repo has been updated with the working solution.

Related

React module federation micro frontend- webpack 5 public path auto is not working

In my react micro frontend project which uses module federation plugin remoteEntry.js path is incorrectly fetched and application crashes.
With hard-coded publicPath: "http://localhost:3000/" in webpack things work as expected.
But due to existing CI/CD setup in my project, publicPath in the webpack config cannot be hard-coded at the build time.
As per various articles it was recommended to use publicPath : auto in the webpack config of the application.
Yes publicPath : auto solves the issue of hard coding at build time.
But during page refresh, remoteEntry.js is fetched from incorrect url.
To simulate the scenario I created simple react application using webpack and module federation plugin
On the initial load remoteEntry.js is fetched from http://localhost:3000/remoteEntry.js as expected
When under nested URL http://localhost:3000/home/foo/about, On refresh main.js and remoteEntry.js is fetched from http://localhost:3000/home/foo/remoteEntry.js - application crashes
Github link for the project simulating the scenario
https://github.com/jidu0106/mf-page-refresh-issue
webpack.config.js
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const deps = require("./package.json").dependencies;
module.exports = {
output: {
publicPath: "auto",
},
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
},
devServer: {
port: 3000,
historyApiFallback: true,
},
module: {
rules: [
{
test: /\.m?js/,
type: "javascript/auto",
resolve: {
fullySpecified: false,
},
},
{
test: /\.(css|s[ac]ss)$/i,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "mf_page_refresh_issue",
filename: "remoteEntry.js",
remotes: {},
exposes: {
'./Component' : './src/test-component.js'
},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
},
}),
new HtmlWebPackPlugin({
template: "./src/index.html",
}),
],
};
I am using "react-router-dom": "^6.3.0" and also tried with "#reach/router": "^1.3.4", but the issue is same
Tried solutions given in https://github.com/module-federation/module-federation-examples/issues/102 but it didn't work
Last suggestion from ScriptedAlchemy in the above link is to use publicPath : auto in 2022
Any help would much appreciated. Thanks in advance.
In case anyone is facing the same issue, add another publicPath : '/' in HtmlWebpackPlugin().
something like the following
new HtmlWebPackPlugin({
template: './public/index.html',
favicon: './public/favicon.png',
assets: './public/assets',
publicPath: '/',
}),
Referred workaround from the following link and it helped
https://github.com/module-federation/module-federation-examples/pull/2170

Browser tab freezes when running webpack-dev-server in development mode using remote Module Federation module

Dependency versions:
webpack: 5.73.0
webpack-dev-server: 4.9.2
This occurs when running a host application via webpack-dev-server that consumes micro-apps via ModuleFederationPlugin.
Example host config:
module.exports = {
entry: path.resolve(__dirname, "src/index.js"),
mode: "development", // success when changed to "production"
devServer: {
port: 3000,
client: {
overlay: false,
},
},
output: {
path: path.resolve(__dirname, "./build"),
publicPath: "/",
filename: "[name].[contenthash].js",
clean: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
exclude: /node_modules/,
options: {
presets: ["#babel/preset-react"],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "Host",
remotes: {
ExampleModule: `ExampleModule#${
process.env.LOCAL_MODULE
? `http://localhost:3001/`
: "https://example.com/examplemodule/"
}remoteEntry.js`,
},
shared: [
{
react: { version: "16.13.0", singleton: true },
"react-dom": { version: "16.13.0", singleton: true },
},
],
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "public/index.html"),
}),
],
};
Example module config:
module.exports = {
entry: path.resolve(__dirname, "src/index.js"),
mode: "development", // set to "production" for build that is served statically
devServer: {
static: { directory: path.join(__dirname, "public") },
port: PORT,
},
output: {
path: path.resolve(__dirname, "./build"),
publicPath:
process.env.NODE_ENV === "development"
? `http://localhost:${PORT}/`
: "https://example.com/examplemodule/",
filename: "[name].[contenthash].js",
clean: true,
},
resolve: {
extensions: [".js", ".jsx"],
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
exclude: /node_modules/,
options: {
presets: ["#babel/preset-react"],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "ExampleModule",
library: { type: "var", name: "ExampleModule" },
filename: "remoteEntry.js",
exposes: {
"./ExampleModule": "./src/ExampleModule",
},
shared: [
{
react: { version: "16.13.0", singleton: true },
"react-dom": { version: "16.13.0", singleton: true },
},
],
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "public/index.html"),
}),
],
};
When running both the host and ExampleModule via webpack-dev-server, the app works, consuming the module from localhost:3001 without issue.
However, when running the host via webpack-dev-server and consuming the production build of the module from a remote URL (using https://example.com/examplemodule/remoteEntry.js in the example above), the page becomes completely unresponsive immediately after the remote entry file is fetched. There is no output in the JavaScript console due to this, and there is no feedback in the CLI output, so there are no error messages to report. This occurs in any browser, and I am on macOS Monterey.
This only occurs when running webpack-dev-server for the host in the "development" Webpack mode. Switching to production mode will fix this issue. It also will not occur when serving the static build of the host, regardless of the Webpack mode used when creating the build.
Unfortunately, this is difficult to reproduce. I attempted to make an isolated example with a React host and remote module using the same dependencies and very similar webpack configs, but I cannot reproduce the issue this way yet, so it may be specific to application code I cannot share. I'm wondering if anyone else has experienced this.

Webpack Module Federation fails with Next JS when using styled-components

I'm trying to get Webpack Module Federation working with a NextJS app where the federated module is using styled-components. My configuration is working as expected if I'm only using react and react-dom but using styled-components gives me these errors:
It looks like there are several instances of 'styled-components' initialized in this application. This may cause dynamic styles to not render properly, errors during the rehydration process, a missing theme prop, and makes your application bigger without good reason.
and
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I have tried the different approaches to resolve styled-components to a single instance by modifying the Webpack config in both the host and remote apps, but all seem to fail.
Here is my current configuration:
Federated modules repo (webpack.config.js):
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
const path = require("path");
module.exports = {
mode: "development",
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 3001,
},
output: {
publicPath: "auto",
},
resolve: {
extensions: [".js"],
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
exclude: /node_modules/,
options: {
plugins: [
["babel-plugin-styled-components", { pure: true, ssr: true }],
],
presets: ["#babel/preset-react"],
},
},
],
},
plugins: [
new ModuleFederationPlugin({
name: "remoteLib",
filename: "remoteEntry.js",
exposes: {
"./Button": "./src/Button",
},
shared: {
...deps,
react: {
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
"styled-components": {
singleton: true,
requiredVersion: deps["styled-components"],
},
},
}),
],
};
Consuming NextJS app Webpack config (next.config.js):
webpack: (config, options) => {
const { ModuleFederationPlugin } = options.webpack.container;
config.plugins.push(
new ModuleFederationPlugin({
remotes: {
'federated-components': 'remoteLib#http://localhost:3001/remoteEntry.js'
}
})
);
return config;
}

Disable file chunking with CRACO

I am trying to figure out how to use CRACO (https://github.com/gsoft-inc/craco) to disable file chunking in create react app.
I have created the following craco.config.js:
// craco.config.js
module.exports = {
output: {
fileName: 'static/js/bundle.js',
},
}
But it doesn't have any effect. What should the config look like to disable file chunking in CRA with CRACO?
EDIT: To disable chunking completely I believe this might do it.
Source: https://github.com/facebook/create-react-app/issues/5306#issuecomment-650737697
// craco.config.js
module.exports = {
webpack: {
configure: {
optimization: {
runtimeChunk: false,
splitChunks: {
chunks(chunk) {
return false
},
},
},
},
},
}
ORIGNIAL:
Maybe this could help?
module.exports = {
webpack: {
configure: {
output: {
filename: 'static/js/bundle.js',
},
optimization: {
runtimeChunk: false,
splitChunks: {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
// vendor chunk
},
},
},
},
},
plugins: [
{
plugin: require('craco-plugin-scoped-css'),
},
],
}

Basic Auth to a webpack build

I'm using the ant design library for React. I've created a .webpackrc.js file as seen below.
const path = require('path');
export default {
entry: 'src/index.js',
extraBabelPlugins: [['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }]],
env: {
development: {
extraBabelPlugins: ['dva-hmr'],
},
},
externals: {
'#antv/data-set': 'DataSet',
},
alias: {
components: path.resolve(__dirname, 'src/components/'),
},
ignoreMomentLocale: true,
theme: './src/theme.js',
html: {
template: './src/index.ejs',
},
lessLoaderOptions: {
javascriptEnabled: true,
},
disableDynamicImport: true,
publicPath: '/',
hash: true,
};
I'm trying to add http basic auth to the webpack server which prompts users for a username and password. I've seen some examples with webpack.config.js, but not with .webpackrc.js. New to webpack so any examples would be appreciated.

Resources