Skip to content

Federate module hot reload not working #3038

Closed

Description

@iMinosGFX
  • Operating System: Windows
  • Node Version: v12.16.1
  • NPM Version: 6.13.4
  • webpack Version: 5.18.0
  • webpack-dev-server Version : 3.11.2
  • Browser: All
  • This is a bug
  • This is a modification request

App Container config

const ReactRefreshPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack").container
  .ModuleFederationPlugin;
const path = require("path");
const deps = require("./package.json").dependencies;

module.exports = {
  entry: "./src/index",
  target:"web",
  mode: "development",
  devServer: {
    contentBase: path.join(__dirname, "public"),
    port: 3001,
    hotOnly:true,
    open:true
  },
  output: {
    publicPath: "auto",
  },
  resolve: {
    extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
  },
  module: {
    rules: [
      {
        test: /bootstrap\.tsx$/,
        loader: "bundle-loader",
        options: {
          lazy: true,
        },
      },
      {
        test: /\.tsx?$/,
        loader: "babel-loader",
        exclude: /node_modules/,
        options: {
          plugins: ['react-refresh/babel'],
          presets: ["@babel/preset-react", "@babel/preset-typescript"],
        },
      },
      {
        enforce: "pre",
        test: /\.js$/,
        loader: "source-map-loader",
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              sourceMap: true,
            },
          },
            {
              loader: 'postcss-loader',
              options: {
                options: {}
              }
            },
        ],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          'resolve-url-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          'file-loader',
        ],
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "appshell",
      filename: "remoteEntry.js",
      remotes: {
        mfsectors: "mfsectors@http://localhost:3002/remoteEntry.js",
      },
      exposes: {
        "./routes": "./src/routes",
        "./src/store/**": "./src/store"
      },
      shared: {
        ...deps,
        react: {
          eager: true,
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          eager: true,
          singleton: true,
          requiredVersion: deps["react-dom"],
        },
      },
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public", "index.html"),
    }),
    new ReactRefreshPlugin({
      exclude: [/node_modules/, /bootstrap\.tsx$/],
    }),
  ],
};

Federate appconfig

const ReactRefreshPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack").container
  .ModuleFederationPlugin;
const path = require("path");
const deps = require("./package.json").dependencies;

module.exports = {
  entry: "./src/index.ts",
  mode: "development",
  devServer: {
    contentBase: path.join(__dirname, "public"),
    port: 3002,
    hotOnly:true
  },
  output: {
    publicPath: "auto",
  },
  resolve: {
    extensions: [".js", ".jsx", ".json", ".ts", ".tsx"],
  },
  module: {
    rules: [
      {
        test: /bootstrap\.tsx$/,
        loader: "bundle-loader",
        options: {
          lazy: true,
        },
      },
      {
        test: /\.tsx?$/,
        loader: "babel-loader",
        exclude: /node_modules/,
        options: {
          plugins: ['react-refresh/babel'],
          presets: ["@babel/preset-react", "@babel/preset-typescript"],
        },
      },
      {
        enforce: "pre",
        test: /\.js$/,
        loader: "source-map-loader",
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              sourceMap: true,
            },
          },
            {
              loader: 'postcss-loader',
              options: {
                options: {}
              }
            },
        ],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          'style-loader',
          'css-loader',
          'resolve-url-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          'file-loader',
        ],
      },
    ],
  },
  plugins: [
    new ModuleFederationPlugin({
      name: "mfsectors",
      filename: "remoteEntry.js",
      remotes: {
        appshell: "appshell@http://localhost:3001/remoteEntry.js",
      },
      exposes: {
        "./routes": "./src/routes",
        "./Pages/SectorsList" : "./src/Pages/SectorsList.tsx"
      },
      shared: {
        ...deps,
        react: {
          eager: true,
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          eager: true,
          singleton: true,
          requiredVersion: deps["react-dom"],
        },
      },
    }),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public", "index.html"),
    }),
    new ReactRefreshPlugin({
      exclude: [/node_modules/, /bootstrap\.tsx$/],
    }),
  ],
};

Expected Behavior

App Container auto reload when federate app change

Actual Behavior

Hello,
I have finally succeeded in setting up my software environment (Dashboard...) in micro-frontend thanks to Module Federation. Only I notice that the live & hot reloading doesn't work when I modify a federate module:

I explain myself, I work on an App Shell which contains all my micro frontends (an app which manages the connection, the authorization in RBAC, the redux provider, etc...) and which will control all the other federated applications, only the automatic refresh will not reload the port 3001(AppShell) if I work on an app of the port 3002.

While searching on the internet I could find some unanswered questions about this, I would like to understand how is it possible to reload the app container automatically?

For Bugs; How can we reproduce the behavior?

For Features; What is the motivation and/or use-case for the feature?

Activity

  1. bedrich-schindler commented on Feb 22, 2021

    @bedrich-schindler

    I met the same issue last week. Hot reload module is not working when using webpack-dev-server v3 with webpack v5. You need to upgrade webpack-dev-server to latest beta version which includes bug fix (unfortunately, it will not be fixed in v3)

    See: #2758 and #2961

    Hope this helps.

  2. alexander-akait commented on Mar 23, 2021

    @alexander-akait
    Member

    Yep, only v4 (beta right now supports module federation), we are working on the next beta release with more fixes, stable release will be in the end of month

  3. wdfinch commented on Apr 12, 2021

    @wdfinch

    Hi, I'm trying to use HMR in a module federation POC app. I've been following a bunch of related issues on this and I'm not sure where this stands. While I can get my app to start via web-pack-dev server, I get an error whenever I edit a file:

    Cannot set property './src/App.tsx' of undefined

    I'm currently using:
    webpack-dev-server 4.0.0-beta.2
    webpack: 5.31.0

    My module federation config is as follows:

    new ModuleFederationPlugin({
      name: 'child',
      filename: 'remoteEntry.js',
      exposes: {
        './ChildIndex': './src/bootstrap'
      },
    }),
    

    Is HMR replacement possible with module federation right now or is this still under development?

  4. clyfish commented on Aug 19, 2022

    @clyfish

    @wdfinch I have a workaround for your problem by excluding the module federation entry in html-webpack-plugin, because it's included in the output HTML by mistake.

    You can use the chunks or excludeChunks options of html-webpack-plugin.

    {
      plugins: [
        new HtmlWebpackPlugin({
          excludeChunks: ['child'],
        }),
      ],
    }
    
  5. ItayElgazar commented on Jan 10, 2023

    @ItayElgazar

    @clyfish

    @wdfinch I have a workaround for your problem by excluding the module federation entry in html-webpack-plugin, because it's included in the output HTML by mistake.

    You can use the chunks or excludeChunks options of html-webpack-plugin.

    {
      plugins: [
        new HtmlWebpackPlugin({
          excludeChunks: ['child'],
        }),
      ],
    }
    

    Based on your example, could you tell me exactly what these chunks are? Can you paste here a real-world example?

    Thanks!

  6. clyfish commented on Jan 16, 2023

    @clyfish

    @clyfish

    @wdfinch I have a workaround for your problem by excluding the module federation entry in html-webpack-plugin, because it's included in the output HTML by mistake.
    You can use the chunks or excludeChunks options of html-webpack-plugin.

    {
      plugins: [
        new HtmlWebpackPlugin({
          excludeChunks: ['child'],
        }),
      ],
    }
    

    Based on your example, could you tell me exactly what these chunks are? Can you paste here a real-world example?

    Thanks!

    @ItayElgazar You can check the HTML outputted by webpack yourself. Right-click and choose View Page Source.

    ModuleFederationPlugin creates an entry to be used by other apps, but html-webpack-plugin includes that entry into HTML by mistake.

  7. ESoch commented on Mar 9, 2023

    @ESoch

    For clarity's sake, as someone who went through this thread and was also confused with understanding what chunk names should be and was looking for a real-world example of the HtmlWebpack excludeChunks fix, here it is.

    Let's say you have a simple Webpack configuration that looks like this:

    module.exports = {
      entry: './src/index.ts',
      output: {
        clean: true,
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[contenthash].bundle.js',
        chunkFilename: '[name].[contenthash].bundle.js',
      },
      plugins: [
        new ModuleFederationPlugin({
            name: 'mfe-chunk',
            filename: 'remoteEntry.js',
            exposes: {
              './ChildIndex': './src/bootstrap'
            },
        }),
        new HtmlWebpackPlugin({
          excludeChunks: ['mfe-chunk'],
          template: 'src/index.html',
          publicPath: '/',
        }),
      ],
    }
    

    The above is an example where we're excluding the remoteEntry.js (i.e module federation entry) from the host's own index.html.

    An alternative implementation where the chunk: configuration is used is as follows:

        new HtmlWebpackPlugin({
          chunks: ['main'],
          template: 'src/index.html',
          publicPath: '/',
        }),
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions