module-federation / module-federation-examples

Implementation examples of module federation , by the creators of module federation
https://module-federation.io/
MIT License
5.62k stars 1.74k forks source link

Dynamic publicPath #102

Closed adamhaeger closed 4 years ago

adamhaeger commented 4 years ago

Hi!

Im amazed and grateful by all this work, truly a gamechanger! Very smooth to implement, but the one thing Im struggling with is publicPath. I know there is a pr up to solve this, but there seems to be debate about whether it should be merged or not.

In our case, we have a host application that has dev, test and prod environments, and then the remote apps also have dev, test and prod environments. So the host app in dev, needs to load the remote code from dev etc. It seems to me that the only way to achieve this currently is to create a separate build for each environment. Is this correct or is there a workaround?

Thanks again!

zhangwilling commented 4 years ago

that's what your outer config should do.

you can write all env config in a Map, and get corresponding publicPath by juding env.

env variable can be supplied by your package scripts or npm scripts.

'create a separate build for each environment' is right, all remotes should be served by local server, such as webpack-dev-server.

zhangwilling commented 4 years ago

if you want more flexiable, you can download config from server and use advanced api in MF at runtime.

adamhaeger commented 4 years ago

if you want more flexiable, you can download config from server and use advanced api in MF at runtime.

Thanks for the super swift answer! Sounds like Im on the right track then. I am interested in a more flexible way to set the path though, would be nice to not have to run the build once for each environment.

Is there any examples on how to use the advanced API that you mention? I haven't found this in the docs anywhere.

ScriptedAlchemy commented 4 years ago

public path is on the docket for a future release. Allowing hosts to execute initialization code in a remote, like changing public path. For right now. you're stuck with defining it in a hard-coded webpack config. The only solution beyond waiting for us to do it is for webpack

ScriptedAlchemy commented 4 years ago

You can reverse engineer this -> https://gist.github.com/ScriptedAlchemy/60d0c49ce049184f6ce3e86ca351fdca

devonChurch commented 4 years ago

I can confirm that the intermediary approach @ScriptedAlchemy outlined above ☝️ works well in the context of assigning publicPath's to individual remotes at run time.

We have a Micro Front-end setup using Federated Modules. We can build our artifact once and move it along our environment flow (Dev, Test, Stage, UAT, Prod) without re-building our bundle 🎉

deployment-flow


I will describe a simplified version of our system below and hope that it can help others get set up and working with the Federated Module paradigm 🙏

Configuration

To achieve this environment agnostic setup, we use two configuration files:

  1. location.config.json A global reference of each Micro Front-end's endpoints on a per-environment basis.

  2. environment.config.json A file scoped to the root of each environment bucket that gives the application its context at run time.

Webpack

  1. When we build our artifact, we exchange the static public path for a function that will execute at run time.

  2. The function expects global variables to be present in the windows scope, so we need to set them up at run time before calling the function (see below) 👇 .

Browser

  1. First, we fetch our configuration files (location.config.json and environment.config.json), then assign them to global variables.

  2. From there, we fetch all of the required Javascript (generic bundle and remoteEntry.js data) and add it to the page.

  3. When the Javascript added above ☝️ initialises, it will generate a publicPath based on the global variables that hold our configuration files 🎊

run-time-flow


I hope that this helps someone 🙏

This has proven effective in our team so far. The Webpack and HTML (shell) orchestration is generic and can be used for each Micro Front-end as part of a boilerplate 👍

This would be a great topic to cover in Practical Module Federation (which I enjoyed reading BTW) 😃 .

ScriptedAlchemy commented 4 years ago

This has been resolved. https://link.medium.com/odRa0yWMdab

waldronmatt commented 3 years ago

For anyone needing a working solution, I put together a repository using the code and methods outlined by devonChurch and ScriptedAlchemy above; thank you both for sharing your insights!

https://github.com/waldronmatt/dynamic-host-module-federation

https://dev.to/waldronmatt/tutorial-a-guide-to-module-federation-for-enterprise-n5

maitrungduc1410 commented 2 years ago

at the moment, 2022, set publicPath: 'auto' works perfectly.

Anyway, thanks @devonChurch for his solution with DynamicPublicPathPlugin, I have followed that solution, it has been working fine for a while.

ScriptedAlchemy commented 2 years ago

yep publicPath: auto is the built in way, it uses document.currentScript.src to get the base path based on where the remote came from to begin with

cyberfury10 commented 2 years ago

publicPath : auto is not working as expected . In my react micro frontend project which uses module federation plugin remoteEntry.js path is incorrectly fetched and application crashes.

With hard-coded publicPath in webpack things work as expected. Due existing CI/CD setup in my project, I couldn't hard-code publicPath at the build time.

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 a 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

Screenshot 2022-09-08 at 15 14 54

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

Screenshot 2022-09-08 at 15 18 59

Github link for the project simulating the scenario https://github.com/jidu0106/mf-page-refresh-issue

I am using "react-router-dom": "^6.3.0" and also tried with "@reach/router": "^1.3.4", but the issue is same

ScriptedAlchemy commented 2 years ago

probably a config issue with how youre serving the spa. it always is

cyberfury10 commented 2 years ago

probably a config issue with how youre serving the spa. it always is

Thanks for the response. With reference to https://github.com/module-federation/module-federation-examples/pull/2170 adding another publicPath : '/' in HtmlWebpackPlugin() solved the issue

 new HtmlWebPackPlugin({
    template: './public/index.html',
    favicon: './public/favicon.png',
    assets: './public/assets',
    publicPath: '/',
  }),
taylous commented 1 year ago

@cyberfury10

assigned to HtmlWebpackPlugin as above code, but still request bundle file in wrong place when refreshed. 😥

the publicPath of the host app is set to'auto' and the publicPath of the remote app is set to domain(ex: http://localhost:4000).

is there anything I set wrong? I'd appreciate your advice.

darktasevski commented 1 year ago

@taylous I had the same issue and setting the base field in the HtmlWebPackPlugin config, seems to help:

new HtmlWebPackPlugin({
   template: './public/index.html',
   // ...
   base: { href: '/' }
 }),
robinsout commented 1 year ago

Thanks for the response. With reference to #2170 adding another publicPath : '/' in HtmlWebpackPlugin() solved the issue

Man! That is really helpfull! I've spent so much time trying to configure my project to work as standalone and via module federation at once and this fixed the issue with page refresh in standalone mode.

Now I have in my webpack config:

output: {
  publicPath: 'auto', // this is for Module Federation exposition
},
...
plugins[
  new HtmlWebpackPlugin({
    publicPath: '/', // this is for standalone mode
    ...
],
Likhitha4131 commented 7 months ago

Having this error I am unable to resolve this @robinsout [ Module Federation Manifest Plugin ]: Manifest will not generate, because publicPath can only be absolute path, but got 'auto' (node:20292) [DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID] import { NextFederationPlugin } from '@module-federation/nextjs-mf';

const nextConfig = { reactStrictMode: true, webpack: (config, options) => { const { isServer } = options; config.plugins.push( new NextFederationPlugin({ name: 'shop', remotes: { main: main@http://localhost:3000/_next/static/${isServer ? 'ssr' : 'chunks'}/remoteEntry.js, }, filename: 'static/chunks/remoteEntry.js', exposes:{ './catalog': "./src/components/MISCard.tsx" }, extraOptions: { exposePages: true }, // publicPath:'/' }) ); config.output.publicPath='auto'; console.log(config.output); return config; }

}; export default nextConfig; I have tried to set the both you said either of them haven't solved this error I am also unable to run my application My OS-Windows 10 I am using "webpack": "5.1.3", "next": "14.1.4"

ScriptedAlchemy commented 7 months ago

look into runtime plugins from module-federation/enhanced and /runtime - this would let you control more aspects

kalraDikshit commented 2 months ago

look into runtime plugins from module-federation/enhanced and /runtime - this would let you control more aspects

https://module-federation.io/configure/getpublicpath.html

This worked for me.