vite
vite copied to clipboard
[Bug]: remoteEntryExports is undefined
Describe the bug
Hey folks! I haven't used Module Federation since experimenting with it in its early days, and have a use-case now, so I thought I'd start prototyping. I created 2 apps, both with the same stack (TanStack Query + Router, Vite):
- host (offering an app shell + routing, host application, runs on port 5175)
- hello-world (a simple hello-world component, a remote, runs on port 4173, exposes mf-manifest.json)
I'm not using runtime or dynamic stuff, just a straight up import HelloWorld from 'hello-world/HelloWorld';. When I try to vier my app in browser, it seems as though Module Federation is confused:
It tries to load the assets/remoteEntry-CdYN5o5R.js from 5175 instead of 4173, so it 404s, which then leads to the remoteEntryExports being undefined.
If I change my host to load assets/remoteEntry-CdYN5o5R.js instead of mf-manifest.json instead, it kind of works, but I still get the following errors:
This is also kind of undesirable, as I'd have to keep track of hashes somehow, rather than just pointing to mf-manifest & letting that figure it out for me...
Am I missing something? I've upgraded everything in host & hello-world to latest versions of all dependencies (with npx npm-check-updates).
Version
6.2.0
Reproduction
It's literally the most basic example I can think of...
Relevant log output
Remote Config:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { federation } from '@module-federation/vite';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
federation({
name: 'hello-world',
manifest: true,
exposes: {
'./HelloWorld': './src/components/HelloWorld.tsx',
'./routes': './src/routes/index.tsx',
},
shared: ['react', 'react-dom', '@tanstack/react-router'],
}),
],
build: {
target: 'esnext',
minify: false,
},
});
Host config:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { federation } from '@module-federation/vite';
import path from 'path';
export default defineConfig({
plugins: [
federation({
name: 'host',
remotes: {
'hello-world': {
name: 'hello-world',
type: 'module',
entry: 'http://localhost:4173/mf-manifest.json',
}
},
shared: ['react', 'react-dom', '@tanstack/react-query']
}),
react()
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
build: {
modulePreload: false,
target: 'esnext',
minify: false,
cssCodeSplit: false
}
});
The generated mf-manifest.json:
{
"id": "hello-world",
"name": "hello-world",
"metaData": {
"name": "hello-world",
"type": "app",
"buildInfo": { "buildVersion": "1.0.0", "buildName": "hello-world" },
"remoteEntry": {
"name": "assets/remoteEntry-D0hsAXHw.js",
"path": "",
"type": "module"
},
"ssrRemoteEntry": {
"name": "assets/remoteEntry-D0hsAXHw.js",
"path": "",
"type": "module"
},
"types": { "path": "", "name": "" },
"globalName": "hello-world",
"pluginVersion": "0.2.5",
"publicPath": "/"
},
"shared": [
{
"id": "hello-world:@tanstack/react-router",
"name": "@tanstack/react-router",
"version": "1.111.11",
"requiredVersion": "^1.111.11",
"assets": {
"js": {
"async": [],
"sync": [
"assets/index-Bk4i2veO.js",
"assets/jsx-runtime-DtXR568w.js",
"assets/hello_mf_2_world__loadShare__react__loadShare__-BU5ovZIB.js",
"assets/hello_mf_2_world__mf_v__runtimeInit__mf_v__-Dfj3CfpL.js",
"assets/hello_mf_2_world__loadShare__react_mf_2_dom__loadShare__-dlIcOtSS.js"
]
},
"css": { "async": [], "sync": [] }
}
},
{
"id": "hello-world:react",
"name": "react",
"version": "19.0.0",
"requiredVersion": "^19.0.0",
"assets": {
"js": {
"async": [],
"sync": [
"assets/index-CWTAqxGv.js",
"assets/_commonjsHelpers-B85MJLTf.js"
]
},
"css": { "async": [], "sync": [] }
}
},
{
"id": "hello-world:react-dom",
"name": "react-dom",
"version": "19.0.0",
"requiredVersion": "^19.0.0",
"assets": {
"js": {
"async": [],
"sync": [
"assets/index-D4xJte0W.js",
"assets/_commonjsHelpers-B85MJLTf.js",
"assets/hello_mf_2_world__loadShare__react__loadShare__-BU5ovZIB.js",
"assets/hello_mf_2_world__mf_v__runtimeInit__mf_v__-Dfj3CfpL.js"
]
},
"css": { "async": [], "sync": [] }
}
}
],
"remotes": [],
"exposes": [
{
"id": "hello-world:HelloWorld",
"name": "HelloWorld",
"assets": {
"js": {
"async": [],
"sync": [
"assets/HelloWorld-D5KLP__Z.js",
"assets/jsx-runtime-DtXR568w.js"
]
},
"css": { "sync": [], "async": [] }
},
"path": "./HelloWorld"
},
{
"id": "hello-world:routes",
"name": "routes",
"assets": {
"js": {
"async": [],
"sync": [
"assets/index-BiBtT30k.js",
"assets/hello_mf_2_world__mf_v__runtimeInit__mf_v__-Dfj3CfpL.js",
"assets/jsx-runtime-DtXR568w.js",
"assets/HelloWorld-D5KLP__Z.js"
]
},
"css": { "sync": [], "async": [] }
},
"path": "./routes"
}
]
}
Validations
- [x] Read the docs.
- [x] Read the common issues list.
- [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [x] The provided reproduction is a minimal reproducible example of the bug.
On a sidenote, I tried setting path in mf-manifest.json manually to http://localhost:4173, but that 404'ed as well to http://localhost:5135http://localhost:4173/asset/remoteEntry-[hash].js (so it seemed they were just naively joined, rather than parsed into a proper URL with a single protocol, host & port)
Hi 👋 thanks for looking at this library. can you share your repo please?
Hey @gioboa! Yep sorry, it's here: https://github.com/StevenLangbroek/mf-repro
Run npm run build && npm run preview in remote, npm run dev in host
Not sure it's important, but I'm running node 20.17.0 on Debian Linux.
Thanks, I'll look at it
Hello, you have to set base: 'http://localhost:4173 vite config in remote app. The base config will be used entry prefix like 'http://localhost:4173/assets/remoteEntry-CdYN5o5R.js'.
If you set, you'll see publicPath in manifaset.json
"publicPath": "http://localhost:3000/"
Hey @rich-woowahan! Ehmmm... We won't know the path at build time for our use-case. The host app can add remotes, and ideally, the paths are relative to the manifest... Is that not possible?
Our use-case is that in the host application, there's a configuration screen where admins can add remotes to the application's config, which should expose predetermined entrypoints for navigation, screens & configuration forms, which are dynamically loaded at runtime... We don't always know how our customers deploy stuff, so setting the base at build time isn't possible.
@StevenLangbroek I understand. It seems that you cannot specify a specific domain to deploy at build time for the remote application. I haven’t tried it myself, but from the official documentation, I learned that using the GetPublicPath property allows the remote application to dynamically set the publicPath when it’s loaded. This makes me think that there might be a way for the host application to provide the publicPath for the remote application. I am not an expert yet, so I want to clarify that this is just a suggestion. By the way, the admin dashboard idea you mentioned sounds interesting.
@StevenLangbroek I understand. It seems that you cannot specify a specific domain to deploy at build time for the remote application. I haven’t tried it myself, but from the official documentation, I learned that using the GetPublicPath property allows the remote application to dynamically set the
publicPathwhen it’s loaded. This makes me think that there might be a way for the host application to provide the publicPath for the remote application. I am not an expert yet, so I want to clarify that this is just a suggestion. By the way, the admin dashboard idea you mentioned sounds interesting.
@gioboa i added a getpublicpath and hardcoded the URL for now just to see if it works, and while the tanstack router route works and the component renders when it's navigated to, I still see these 404s in the network tab... I'll push to the repro later in the morning and leave some more debugging info.
@gioboa oh sorry there's some discussion on the PR (#267)
Hello, you have to set
base: 'http://localhost:4173vite config in remote app. Thebaseconfig will be used entry prefix like 'http://localhost:4173/assets/remoteEntry-CdYN5o5R.js'. If you set, you'll seepublicPathin manifaset.json"publicPath": "http://localhost:3000/"
I am running into the same issue, and although I have set the base configuration option, publicPath is "/":
Vite config modified from here:
import { federation } from '@module-federation/vite';
import react from '@vitejs/plugin-react';
import { writeFileSync } from 'fs';
import { defineConfig, loadEnv } from 'vite';
import { dependencies } from './package.json';
export default defineConfig(({ mode }) => {
const selfEnv = loadEnv(mode, process.cwd());
return {
server: {
fs: {
allow: ['.', '../shared'],
},
},
build: {
target: 'chrome89',
},
base: 'http://localhost:4174/',
plugins: [
{
name: 'generate-environment',
options: function () {
console.info('selfEnv', selfEnv);
writeFileSync('./src/environment.ts', `export default ${JSON.stringify(selfEnv, null, 2)};`);
},
},
federation({
name: 'remote',
manifest: true,
exposes: {
'./remote-app': './src/App.tsx',
},
remotes: {},
shared: {
react: {
requiredVersion: dependencies.react,
singleton: true,
},
},
}),
react(),
],
};
});
mf-manifest.json:
{"id":"remote","name":"remote","metaData":{"name":"remote","type":"app","buildInfo":{"buildVersion":"1.0.0","buildName":"remote"},"remoteEntry":{"name":"remoteEntry-[hash]","path":"","type":"module"},"ssrRemoteEntry":{"name":"remoteEntry-[hash]","path":"","type":"module"},"types":{"path":"","name":""},"globalName":"remote","pluginVersion":"0.2.5","publicPath":"/"},"shared":[{"id":"remote:react","name":"react","version":"18.3.1","requiredVersion":"^18.3.1","assets":{"js":{"async":[],"sync":[]},"css":{"async":[],"sync":[]}}}],"remotes":[],"exposes":[{"id":"remote:remote-app","name":"remote-app","assets":{"js":{"async":[],"sync":[]},"css":{"sync":[],"async":[]}},"path":"./remote-app"}]}
Note that the above only applies to dev npm script. If you run the build script publicPath is populated.
I'm experiencing the same issue. The functionality only works when I build and preview at least the Provider. However, it fails when running both, the Consumer and Provider, in development mode (using the vite command with a port specified ).
I'm encountering the same errors mentioned in this post.
I'm using "vite": "^6.2.5", and "react": "^18.3.1"
I am running into similar. the manifest output is remoteEntry":{"name":"remoteEntry-[hash].js","path":"","type":"module"},"ssrRemoteEntry":{"name":"remoteEntry-[hash].js". For some reason in dev mode it doesn't get handled correctly... i think this is in the addEntry plugin.
I just verified this with the vite-vite example https://github.com/module-federation/vite/tree/7114b3896da362488f9d3ffcba0c2091d618c893/examples/vite-vite
in dev mode the hash template var never gets replaced. I also think its an ugly hack to be trying to read a html file as well if im reading things right to get the hash (but i could be mis-reading the code).
overall right now running a dev server is kind of broken if you keep the hash template var, which also means cant use HMR.
same issue here
my vite.config.ts host
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { federation } from '@module-federation/vite';
export default defineConfig({
plugins: [
react(),
federation({
exposes: {},
name: 'host_shell',
remotes: {
remote_products: 'remote_products@http://localhost:5001/remoteEntry.js',
},
shared: {
react: { singleton: true, requiredVersion: '^18.0.0' },
'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
},
})
],
server: { port: 4173 },
build: { target: 'chrome89' },
});
If you prefer, I can send the remote config too, but it is opening normally and providing remoteEntry.js as well.
I am using the
buildandpreviewcommand for both the remote and the host.
@pcfreak30 @StevenLangbroek Did you manage to solve your problems?
same issue here
my vite.config.ts host
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { federation } from '@module-federation/vite';
export default defineConfig({ plugins: [ react(), federation({ exposes: {}, name: 'host_shell', remotes: { remote_products: 'remote_products@http://localhost:5001/remoteEntry.js', }, shared: { react: { singleton: true, requiredVersion: '^18.0.0' }, 'react-dom': { singleton: true, requiredVersion: '^18.0.0' }, }, }) ], server: { port: 4173 }, build: { target: 'chrome89' }, }); If you prefer, I can send the remote config too, but it is opening normally and providing
remoteEntry.jsas well.I am using the
buildandpreviewcommand for both the remote and the host.@pcfreak30 @StevenLangbroek Did you manage to solve your problems?
I have been doing a lot of r&d if you look at the PR's for the plugin. I have an approach now for dev and build mode, though a key finding is dev mode doesn't bundle anything and loads direct as ESM. That also means some CJS don't get processed and have to be fixed.
@pcfreak30 Do you have any examples of this approach?
@pcfreak30 Do you have any examples of this approach?
No. my work is driven by my own needs. The best I can give you is my projects actual vite stuff which is an abstraction https://github.com/LumeWeb/web/blob/647bde87ec985ac7cf204311a94501e422a67178/libs/portal-framework-core/src/vite/plugin.ts.
Same issue, this is how I got around it:
Remote vite.config.js
Remove the hash from remoteEntry.js chunk so the name is predictable
build: {
rollupOptions: {
output: {
chunkFileNames: (chunkInfo) => {
if (/remoteEntry/.test(chunkInfo.name)){
return `js/[name].js`;
}
return `js/[name]_[hash].js`;
}
}
}
}
Host vite.config.js
Use remoteEntry.js instead of mf-manifest.json
I also disabled modulePreload because in my use-case it was trying to preload chunks from the local path instead of the full url
plugins: [
react(),
federation({
name: "host",
remotes: {
helloWorld: `helloworld@http://localhost:3001/js/remoteEntry.js`
}
})
],
build: {
modulePreload: false
}
As there's been no activity for 30 days, this issue has been flagged as stale. If you'd like it to remain open, please add a comment within the next 7 days. Thank you.
This is still an issue.
I am facing this issue as well. Loading a Vite module into an RsBuild host is not currently working
I am facing this issue as well. Loading a Vite module into an RsBuild host is not currently working
Could you share the federation module configuration codes in both vite and rsbuild?
@DevJoaoLopes Have exact same issue as @mattwmarra described.
Host: vite
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { federation } from "@module-federation/vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
federation({
name: "uiAPP",
filename: "remoteEntry.js",
remotes: {
automationMFE: {
name: "automationMFE",
type: "var",
entry: "http://localhost:5001/remoteEntry.js",
},
},
shared: ["react", "react-dom"],
}),
],
build: {
target: "chrome89",
minify: false,
},
});
Remote: rsbuild
import { defineConfig } from "@rsbuild/core";
import { pluginReact } from "@rsbuild/plugin-react";
import { pluginModuleFederation } from "@module-federation/rsbuild-plugin";
// Docs: https://rsbuild.rs/config/
export default defineConfig({
plugins: [
pluginReact(),
pluginModuleFederation({
name: "automationMfe",
filename: "remoteEntry.js",
exposes: {
"./App": "./src/App.tsx",
},
shared: ["react", "react-dom"],
}),
],
server: {
port: 5001,
},
output: {
minify: false,
},
});
Receiving this error in both dev mode and preview
Uncaught Error: [ Federation Runtime ]: remoteEntryExports is undefined
{
"entryGlobalName": "automationMFE",
"name": "automationMFE",
"type": "var",
"entry": "http://localhost:5001/remoteEntry.js",
"shareScope": "default"
}
at error2 (chunk-LTCJ5FP2.js?v=9c972234:954:13)
at assert2 (chunk-LTCJ5FP2.js?v=9c972234:944:9)
at Module.getEntry (chunk-LTCJ5FP2.js?v=9c972234:2273:9)
at async Module.get (chunk-LTCJ5FP2.js?v=9c972234:2283:36)
at async RemoteHandler.loadRemote (chunk-LTCJ5FP2.js?v=9c972234:3403:35)
at async automationMFE_App.js?v=9c972234:26:49
my vite.config.ts host