From 57b0f91fa3f1735b2a1c2e401903d2998202d04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9stor?= Date: Mon, 8 Dec 2025 16:51:43 +0100 Subject: [PATCH 1/3] fix(esbuild): fix esbuild 0.25.0 compatibility for watch mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use esbuild context() API for watch mode (0.25.0 removed watch option) - Enables proper build and watch functionality in apps/esbuild demo 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/esbuild/build/build-common.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/esbuild/build/build-common.js b/apps/esbuild/build/build-common.js index dffd6cbd3b6..ae684292baf 100644 --- a/apps/esbuild/build/build-common.js +++ b/apps/esbuild/build/build-common.js @@ -11,7 +11,7 @@ async function buildProject(projectName, watch) { fs.rmSync(outputPath, { force: true, recursive: true }); - await esbuild.build({ + const buildOptions = { entryPoints: [path.join(projectName, 'main.ts')], outdir: outputPath, bundle: true, @@ -28,8 +28,15 @@ async function buildProject(projectName, watch) { require(path.join('../', projectName, 'federation.config.js')), ), ], - watch, - }); + }; + + if (watch) { + const ctx = await esbuild.context(buildOptions); + await ctx.watch(); + console.log(`Watching ${projectName} for changes...`); + } else { + await esbuild.build(buildOptions); + } ['index.html', 'favicon.ico', 'styles.css'].forEach((file) => { fs.copyFileSync(path.join(projectName, file), path.join(outputPath, file)); From f5593f3313ac7b322f3047a9c70d6b9fd5cb86cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9stor?= Date: Mon, 8 Dec 2025 17:05:30 +0100 Subject: [PATCH 2/3] fix(esbuild): fix import map registration for ESM remote modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The import map registration was failing because: 1. The runtimePlugin was looking for moduleMap in the host scope instead of fetching it from the remote module exports 2. The import map key was using the host name instead of the remote name 3. The async registration in runtimePlugin.init() ran after bootstrap imports 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. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../src/adapters/lib/containerReference.ts | 77 ++++++++++++++----- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/packages/esbuild/src/adapters/lib/containerReference.ts b/packages/esbuild/src/adapters/lib/containerReference.ts index 653f91575d4..484b2b0acb4 100644 --- a/packages/esbuild/src/adapters/lib/containerReference.ts +++ b/packages/esbuild/src/adapters/lib/containerReference.ts @@ -59,28 +59,38 @@ export const buildFederationHost = (config: NormalizedFederationConfig) => { const runtimePlugin = () => ({ name: 'import-maps-plugin', async init(args) { - const remotePrefetch = args.options.remotes.map(async (remote) => { - console.log('remote', remote); - if (remote.type === 'esm') { - await import(remote.entry); - } - }); - - await Promise.all(remotePrefetch); - if (typeof moduleMap !== 'undefined') { - const map = Object.keys(moduleMap).reduce((acc, expose) => { - const importMap = importShim.getImportMap().imports; - const key = args.origin.name + expose.replace('.', ''); - if (!importMap[key]) { - const encodedModule = encodeInlineESM( - createVirtualRemoteModule(args.origin.name, key, moduleMap[expose].exports) - ); - acc[key] = encodedModule; + // Load all remotes and collect their moduleMaps + const remotesWithModuleMaps = await Promise.all( + args.options.remotes.map(async (remote) => { + console.log('remote', remote); + if (remote.type === 'esm') { + const remoteModule = await import(remote.entry); + return { remote, moduleMap: remoteModule.moduleMap }; + } + return { remote, moduleMap: null }; + }) + ); + + // Build import map entries for all remote modules + const allImports = {}; + for (const { remote, moduleMap } of remotesWithModuleMaps) { + if (moduleMap && typeof moduleMap === 'object') { + for (const expose of Object.keys(moduleMap)) { + const currentImportMap = importShim.getImportMap().imports; + // Use remote name + expose path (e.g., "mfe1/component") + const key = remote.name + expose.replace('.', ''); + if (!currentImportMap[key] && !allImports[key]) { + const encodedModule = encodeInlineESM( + createVirtualRemoteModule(remote.name, key, moduleMap[expose].exports) + ); + allImports[key] = encodedModule; + } } - return acc; - }, {}); + } + } - await importShim.addImportMap({ imports: map }); + if (Object.keys(allImports).length > 0) { + await importShim.addImportMap({ imports: allImports }); } return args; @@ -96,6 +106,33 @@ export const buildFederationHost = (config: NormalizedFederationConfig) => { await Promise.all(mfHoZJ92.initializeSharing('default', 'version-first')); + // Ensure import maps are registered before any code that uses remotes runs + const remotesList = ${remoteConfigs}; + const allImports = {}; + for (const remote of remotesList) { + if (remote.type === 'esm') { + try { + const remoteModule = await import(remote.entry); + const moduleMap = remoteModule.moduleMap; + if (moduleMap && typeof moduleMap === 'object') { + for (const expose of Object.keys(moduleMap)) { + const key = remote.name + expose.replace('.', ''); + if (!allImports[key]) { + const encodedModule = encodeInlineESM( + createVirtualRemoteModule(remote.name, key, moduleMap[expose].exports) + ); + allImports[key] = encodedModule; + } + } + } + } catch (e) { + console.error('Failed to load remote:', remote.name, e); + } + } + } + if (Object.keys(allImports).length > 0) { + await importShim.addImportMap({ imports: allImports }); + } `; }; From 3793678cababc24ed84fcd7045228b0246d54412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9stor?= Date: Mon, 8 Dec 2025 17:10:05 +0100 Subject: [PATCH 3/3] chore: add changeset for esbuild import map fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .changeset/fix-esbuild-import-maps.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/fix-esbuild-import-maps.md diff --git a/.changeset/fix-esbuild-import-maps.md b/.changeset/fix-esbuild-import-maps.md new file mode 100644 index 00000000000..67cfe50a9e7 --- /dev/null +++ b/.changeset/fix-esbuild-import-maps.md @@ -0,0 +1,7 @@ +--- +"@module-federation/esbuild": patch +--- + +fix: fix import map registration for ESM remote modules + +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.