Skip to content

Commit a944c2d

Browse files
NstttclaudeScriptedAlchemyzhoushaw
authored
fix(esbuild): fix esbuild 0.25.0 compatibility and remote module loading (#4255)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: ScriptedAlchemy <zackaryjackson@bytedance.com> Co-authored-by: Zack Jackson <25274700+ScriptedAlchemy@users.noreply.github.com> Co-authored-by: ScriptedAlchemy <zackary.l.jackson@gmail.com> Co-authored-by: zhouxiao.shaw <zhouxiao.shaw@bytedance.com>
1 parent 5fe1ceb commit a944c2d

3 files changed

Lines changed: 74 additions & 23 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@module-federation/esbuild": patch
3+
---
4+
5+
fix: fix import map registration for ESM remote modules
6+
7+
The import map registration was failing because the runtimePlugin was looking for moduleMap in the host scope instead of fetching it from the remote module exports. This fix adds explicit synchronous import map registration in the host initialization code that runs after initializeSharing but before any code that imports from remotes.

apps/esbuild/build/build-common.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ async function buildProject(projectName, watch) {
1111

1212
fs.rmSync(outputPath, { force: true, recursive: true });
1313

14-
await esbuild.build({
14+
const buildOptions = {
1515
entryPoints: [path.join(projectName, 'main.ts')],
1616
outdir: outputPath,
1717
bundle: true,
@@ -28,8 +28,15 @@ async function buildProject(projectName, watch) {
2828
require(path.join('../', projectName, 'federation.config.js')),
2929
),
3030
],
31-
watch,
32-
});
31+
};
32+
33+
if (watch) {
34+
const ctx = await esbuild.context(buildOptions);
35+
await ctx.watch();
36+
console.log(`Watching ${projectName} for changes...`);
37+
} else {
38+
await esbuild.build(buildOptions);
39+
}
3340

3441
['index.html', 'favicon.ico', 'styles.css'].forEach((file) => {
3542
fs.copyFileSync(path.join(projectName, file), path.join(outputPath, file));

packages/esbuild/src/adapters/lib/containerReference.ts

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,28 +59,38 @@ export const buildFederationHost = (config: NormalizedFederationConfig) => {
5959
const runtimePlugin = () => ({
6060
name: 'import-maps-plugin',
6161
async init(args) {
62-
const remotePrefetch = args.options.remotes.map(async (remote) => {
63-
console.log('remote', remote);
64-
if (remote.type === 'esm') {
65-
await import(remote.entry);
66-
}
67-
});
68-
69-
await Promise.all(remotePrefetch);
70-
if (typeof moduleMap !== 'undefined') {
71-
const map = Object.keys(moduleMap).reduce((acc, expose) => {
72-
const importMap = importShim.getImportMap().imports;
73-
const key = args.origin.name + expose.replace('.', '');
74-
if (!importMap[key]) {
75-
const encodedModule = encodeInlineESM(
76-
createVirtualRemoteModule(args.origin.name, key, moduleMap[expose].exports)
77-
);
78-
acc[key] = encodedModule;
62+
// Load all remotes and collect their moduleMaps
63+
const remotesWithModuleMaps = await Promise.all(
64+
args.options.remotes.map(async (remote) => {
65+
console.log('remote', remote);
66+
if (remote.type === 'esm') {
67+
const remoteModule = await import(remote.entry);
68+
return { remote, moduleMap: remoteModule.moduleMap };
69+
}
70+
return { remote, moduleMap: null };
71+
})
72+
);
73+
74+
// Build import map entries for all remote modules
75+
const allImports = {};
76+
for (const { remote, moduleMap } of remotesWithModuleMaps) {
77+
if (moduleMap && typeof moduleMap === 'object') {
78+
for (const expose of Object.keys(moduleMap)) {
79+
const currentImportMap = importShim.getImportMap().imports;
80+
// Use remote name + expose path (e.g., "mfe1/component")
81+
const key = remote.name + expose.replace('.', '');
82+
if (!currentImportMap[key] && !allImports[key]) {
83+
const encodedModule = encodeInlineESM(
84+
createVirtualRemoteModule(remote.name, key, moduleMap[expose].exports)
85+
);
86+
allImports[key] = encodedModule;
87+
}
7988
}
80-
return acc;
81-
}, {});
89+
}
90+
}
8291
83-
await importShim.addImportMap({ imports: map });
92+
if (Object.keys(allImports).length > 0) {
93+
await importShim.addImportMap({ imports: allImports });
8494
}
8595
8696
return args;
@@ -96,6 +106,33 @@ export const buildFederationHost = (config: NormalizedFederationConfig) => {
96106
97107
await Promise.all(mfHoZJ92.initializeSharing('default', 'version-first'));
98108
109+
// Ensure import maps are registered before any code that uses remotes runs
110+
const remotesList = ${remoteConfigs};
111+
const allImports = {};
112+
for (const remote of remotesList) {
113+
if (remote.type === 'esm') {
114+
try {
115+
const remoteModule = await import(remote.entry);
116+
const moduleMap = remoteModule.moduleMap;
117+
if (moduleMap && typeof moduleMap === 'object') {
118+
for (const expose of Object.keys(moduleMap)) {
119+
const key = remote.name + expose.replace('.', '');
120+
if (!allImports[key]) {
121+
const encodedModule = encodeInlineESM(
122+
createVirtualRemoteModule(remote.name, key, moduleMap[expose].exports)
123+
);
124+
allImports[key] = encodedModule;
125+
}
126+
}
127+
}
128+
} catch (e) {
129+
console.error('Failed to load remote:', remote.name, e);
130+
}
131+
}
132+
}
133+
if (Object.keys(allImports).length > 0) {
134+
await importShim.addImportMap({ imports: allImports });
135+
}
99136
100137
`;
101138
};

0 commit comments

Comments
 (0)