Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serve with multi-server doesn't work with Module Federation #3390

Closed
Christian24 opened this issue Aug 26, 2022 · 15 comments
Closed

serve with multi-server doesn't work with Module Federation #3390

Christian24 opened this issue Aug 26, 2022 · 15 comments
Labels

Comments

@Christian24
Copy link

Christian24 commented Aug 26, 2022

Describe the bug

Hello,

I don't know if this is the correct, project and a few problems that all seem to be related to either webpack-cli or webpack-dev-server, so please excuse if this is not the correct place.

General overview:

We are building multiple SPAs and we use multi-server for our development environment. This works, but now we want to introduce module federation. I added configurations for two federated modules and this is where problems began. To emulate our production environment I need to set publicPath on all configurations, so that they can run on the same port.

What is the current behavior?
Adding two module federation configurations and setting their publicPath option, I get a ScriptLoadError. Upon further investigation it seems that the remoteEntry.js files both located under /app2/remoteEntry.js and /app3/remoteEntry.jsare identical.

To Reproduce

Steps to reproduce the behavior:

I created a reproduction here: https://stackblitz.com/edit/github-f36ynq?file=app1/webpack.config.js

Just look at the /app2/remoteEntry.js and /app3/remoteEntry.js. They should be identical, even though one wouldn't expect that.

Expected behavior

The correct remoteEntry.js files should be served.

Additional context

I can avoid the error by renaming the second remoteEntry.js to something else it seems. Then both files are correctly served. However, then webpack has problems loading additional chunks. I can solve this by setting the absolute path for publicPath like https://localhost:8080/app2/. However, in this scenario dependency sharing doesn't work. The urls webpack tries to load from somewhere contain undefined within the url. So, for now I had to disable dependency sharing in development.

I know publicPath: 'auto' should be used with module federation. However, setting that I am not able to setup my remotes the way I need to simulate a single server that is serving our assets.

Not sure, but maybe this is somewhat related to #2408?

Any advice would be greatly appreciated.

@alexander-akait
Copy link
Member

alexander-akait commented Sep 27, 2022

Sorry for delay, I will look at this problem soon, did you find any solutions?

@Christian24
Copy link
Author

Thanks. Nothing more than I already described sadly.

@Christian24
Copy link
Author

We are also experiencing an issue with shared: When using shared the url with which webpack tries to resolve the remote entry suddenly contains undefined. So I get a path like /app1/undefined/app2/remoteEntry.js which obviously doesn't work.

@Christian24
Copy link
Author

I would love to help get this fixed @alexander-akait, but I have no idea where to even start looking.

@Christian24
Copy link
Author

We are also experiencing an issue with shared: When using shared the url with which webpack tries to resolve the remote entry suddenly contains undefined. So I get a path like /app1/undefined/app2/remoteEntry.js which obviously doesn't work.

This might be an issue on our end. It works fine when not using external remotes plugin.

Additional context

I can avoid the error by renaming the second remoteEntry.js to something else it seems. Then both files are correctly served. However, then webpack has problems loading additional chunks. I can solve this by setting the absolute path for publicPath like https://localhost:8080/app2/. However, in this scenario dependency sharing doesn't work.

I think dependency sharing doesn't work because the chunks still are the same just like the remoteEntries. The chunks contain the wrong webpack ids, that is how I figured out they were identical. And I think webpack just throws an error when it tries to load a chunk for a federated module if it detects the chunk actually belongs to a different module.

As I see it, the files served are identical. No matter the public path. I just tried debugging it, but didn't get far. This is probably a dev server issue, isn't it? Should I open it there or can you move it?

@alexander-akait
Copy link
Member

Sounds like the problem with invalid publicPath or URLs

@Christian24
Copy link
Author

The URLs are correct. The publicPaths are set up exactly the way they are for all our applications. Interestingly, I get the correct file when I rename it - as mentioned above. However, the chunks generated by module federation plugin I cannot rename (plus it is error prone), so this should be fixed.

@alexander-akait
Copy link
Member

Please double check this, you can ahndle the same URL for one server, I will look at this soon, but the problem is somewhere here

@Christian24
Copy link
Author

Just simplified the example a bit:

The relevant configs:
App2:

{
  entry: './src/index',
  mode: 'development',
  output: {
    publicPath: '/app2/',
  },
  context: __dirname,
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: ['@babel/preset-react'],
        },
      },
    ],
  },
  plugins: [
    // To learn more about the usage of this plugin, please visit https://webpack.js.org/plugins/module-federation-plugin/
    new ModuleFederationPlugin({
      name: 'app2',
      filename: 'remoteEntry.js',
      exposes: {
        './App': './src/App',
      },
      shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
};

App3:

{
  entry: './src/index',
  mode: 'development',
  output: {
    publicPath: '/app3/',
  },
  context: __dirname,
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        options: {
          presets: ['@babel/preset-react'],
        },
      },
    ],
  },
  plugins: [
    // To learn more about the usage of this plugin, please visit https://webpack.js.org/plugins/module-federation-plugin/
    new ModuleFederationPlugin({
      name: 'app3',
      filename: 'remoteEntry.js',
      exposes: {
        './App': './src/App',
      },
      shared: { react: { singleton: true }, 'react-dom': { singleton: true } },
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
  ],
};

Notice that names of the federated module and the the publicPaths are different. I can also access the related URLs: Both http://localhost:3001/app2/remoteEntry.js and http://localhost:3001/app3/remoteEntry.js work.

However, both files seem to be identical which is wrong. It seems the file served at http://localhost:3001/app3/remoteEntry.js is identical to app2/remoteEntry.js. From quick investigation it seems that the publicPaths are also correctly passed to webpack-dev-server, so the issue might not be in CLI.

@alexander-akait
Copy link
Member

What is your URL for testing?

@Christian24
Copy link
Author

https://stackblitz.com/edit/github-f36ynq?file=app1/webpack.config.js

yarn install and yarn start (both should run automatically)
Then in the browser on the right you can open: https://github-f36ynq--3001.local.webcontainer.io/app2/remoteEntry.jsand https://github-f36ynq--3001.local.webcontainer.io/app3/remoteEntry.js. Contents will be identical even though they shouldn't be.

If you want to test it locally, clone https://github.com/Christian24/module-federation-reproduction, yarn install and yarn start

The urls are then http://localhost:3001/app2/remoteEntry.js and http://localhost:3001/app3/remoteEntry.js.

Hope this helps.

@alexander-akait
Copy link
Member

alexander-akait commented Oct 7, 2022

Oh, hard to undestand what do you want, let's start:

  • Do you want to run dev server for each compilation? (if yes you need to add devServer to each configuration and setup valid URLs, also you can setup proxy if you need), it is first solution, so you can multiple dev server
  • Use multi compiler mode as in your case, you get this strange behaviour because you have the same output.path, and your files will be stored at the same place, that is why you can get app2 or app3 randomly and they are the same.

So here two solutions:

  • set unique output.path
  • prefix chunks

Here solution with prefixed chunks (note there is possible ability to override more if you will use copy-webpack-plugin or if you have the same asset, like image, with the same name, ideally you need to prefix all output.*Filename options to avoid it ):

bug-module-federation.zip

Also I see you want to use app2 and app3 inside app1, so you need to set dependencies https://webpack.js.org/configuration/other-options/#dependencies (don't forget to add the name) to build them before using app1 (Note - required only if you want to use multi compiler mode, i.e. as you use now)

I think you don't need window.app2Url at all

@Christian24
Copy link
Author

  • Use multi compiler mode as in your case, you get this strange behaviour because you have the same output.path, and your files will be stored at the same place, that is why you can get app2 or app3 randomly and they are the same.

Exactly this.

So here two solutions:

  • set unique output.path

I just tried this in my demo and it works. Thank you so much. What confuses me though, is the fact that we have this setup quite often, setting public path is always enough and I thought compilation would happen in memory anyway. So, it is odd that we only have this problem with module federation.

Also I see you want to use app2 and app3 inside app1, so you need to set dependencies https://webpack.js.org/configuration/other-options/#dependencies (don't forget to add the name) to build them before using app1 (Note - required only if you want to use multi compiler mode, i.e. as you use now)

Interesting. I didn't know about this. I will have to play around with that a bit.

I think you don't need window.app2Url at all

That's true, this is mostly me putting the demo together without changing much from the official examples.

I will close this issue as soon as I have verified the fix works in our codebase. Thanks again for your help @alexander-akait!

@alexander-akait
Copy link
Member

Yeah, feel free to feedback

@Christian24
Copy link
Author

Fixed. Thanks @alexander-akait

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants