module-federation-examples: cannot find remote module if publicPath includes a path as part of the URL... http://[host]:[port]/[path]/

HI,

I have an issue where my remote application has it’s web root set as /shell/. So it needs its publicPath to be /shell/ in order to serve content.

When I create a host application that tries to use an exposed component from the remote it needs the host publicPath to be:

http://localhost:8080/

without the /shell/

I can not reconcile the two. If I set the publicPath to /shell/ in remote and build, with webpack, to dist/ directory then the remote will work correctly.

If I then change the publicPath in the remote to http://localhost:8080/ and run webpack serve; without building to disc again, then my host imports the remote component correctly.

Obviously this is not going to work for the apps when they are deployed. I have tried to dynamically change the publicPath at runtime but it seems that the host can not find the remote in order to change the path.

I do see remoteEntry.js under localhsot:8080 in chrome console on the host and I do see the StandAlone component and set-public-path referenced in there.

So my questions are:

  1. most obvious , why is this not working?
  2. What does the key in the ‘exposes’ property reference, in other words what is the ‘./’ prefixing the component name. Is this a path ?
  3. Can federation reference publicPath as more than just ‘http://[host]:[port]/’, can it reference a URL with /path. If so is my issue something to do with my path set up rather than federation itself?

Any pointers/help on this would be much appreciated, I have been going round in circles for longer than I care too mention; so I know its going to be something obvious that I just can’t see.

Thanks!

This is the error I get:

ScriptExternalLoadError Loading script failed. (missing: http://localhost:8080/shell/remoteEntry.js) while loading “./set-public-path” from webpack/container/reference/remote

Below is a bare bones config that I am using:

//remote webpack.config.js

...
entry: {
	    remote: './set-public-path',
            app: [
                '~/common
                `~/shell`
            ]
        },
        mode: 'development',
        output: {
            path: path.resolve(__dirname, 'dist/shell'),
            filename: '[name].js',
            publicPath: '/shell/'
        },
plugins: [
        new ModuleFederationPlugin({
            name: 'remote',
            library: { type: "var", name: 'remote' },
            filename: 'remoteEntry.js',
            exposes: [
                 `./set-public-path`,
                {'./StandAlone': path.resolve(contextPath, `src/stand-alone/StandAlone.tsx`)}
            ],
            shared: {            
                react: {
                    eager: true
                }
            },
        }),
    ]
...

//host webpack.config.js

...
    plugins: [
        new ModuleFederationPlugin({
            name: host,
            'remote': `remote@http://localhost:8080/shell/remoteEntry.js`
        }),
    ],
...

//remote set-public-path.js

export function set(value) {
    __webpack_public_path__ = value;
}

//host index.js

const publicPath = await import('remote/set-public-path');
publicPath.set('http://localhost:8080/');

//remote index.js

// @ts-ignore
import('./bootstrap');

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16 (4 by maintainers)

Most upvoted comments

Set publicPath:auto

@digitalhank would you consider opening a PR to my readme about this.

Full transparency, you taught me something i didnt know how to resolve when “auto” wouldnt work for users 😛

For anyone coming here with this issue. I see what I was doing wrong now.

If either of the following attributes are specified, the base element must come before other elements with attribute values of URLs, such as link’s href attribute.

Also, make sure the base element does not have a closing tag. You want <base href="/"> not <base href="/" />

For example, in your index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <base href="/">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <link rel="apple-touch-icon" sizes="120x120" href="/icon-120x120.png">
    <link rel="apple-touch-icon" sizes="152x152" href="/icon-152x152.png">
    <link rel="apple-touch-icon" sizes="167x167" href="/icon-167x167.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/icon-180x180.png">
    <link rel="icon" href="/favicon.ico" />
    <title>HSC widget</title>
  </head>
  <body>
    <noscript>If you're seeing this message, that means <strong>JavaScript has been disabled on your browser</strong>, please <strong>enable JS</strong> to make this app work.</noscript>
    <div id="app"></div>
  </body>
</html>

@digitalhank Did you ever find a solution to this?

Yes, setting “base” element on the remote fixed it. https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

I have set publicPath to ‘auto’. It works great on the host loading the remote. but on the remote, which is a full fledged react app exposing an inject function that the host uses to inject the remote app into the host as described in one of your examples. But on twice nested nested routes on the remote, it tries to append the current route when fetching its own static asset. i.e.

remote is on /hello/howareyou and it tries to fetch assets from /hello/static/js instead of /static/js. @ScriptedAlchemy