Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
47cadbb
feat(esbuild): completely redesign and rebuild Module Federation plugin
cursoragent Feb 8, 2026
33ad656
feat(esbuild): add subpath handling for shared modules, update README
cursoragent Feb 8, 2026
52cd0ea
fix(esbuild): clean up remote proxy code generation, update example app
cursoragent Feb 8, 2026
1f007ee
fix(esbuild): address review issues in plugin implementation
cursoragent Feb 8, 2026
41bb05c
feat(esbuild): add missing features, comprehensive test suite (62 tests)
cursoragent Feb 8, 2026
34ed493
refactor(esbuild): remove 12 dead files and 3 unused dependencies
cursoragent Feb 8, 2026
6e3d7fe
feat(esbuild): implement full webpack MF parity features (83 tests)
cursoragent Feb 8, 2026
d2ea75a
test(esbuild): expand test suite to 117 tests with webpack-style cove…
cursoragent Feb 8, 2026
ecae579
fix(esbuild): address all code review comments and annotations
cursoragent Feb 8, 2026
f2c5e9d
refactor(esbuild): remove remaining dead code - 3 files, 1 dep
cursoragent Feb 8, 2026
2ed1312
fix(esbuild): address PR review comments - P1 fallback path, P2 manif…
cursoragent Feb 8, 2026
0954cf7
fix(esbuild): use default export pattern for remote components
cursoragent Feb 8, 2026
60fd2fa
feat(esbuild): transform named imports from remotes to work like webpack
cursoragent Feb 8, 2026
a9b3b36
fix(esbuild): address security scanner warnings and remaining code is…
cursoragent Feb 8, 2026
42aa318
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 8, 2026
1070735
fix(esbuild): harden codegen sanitization and runtime init entry matc…
ScriptedAlchemy Feb 8, 2026
46fcb9a
fix(esbuild): address review findings in plugin and manifest
ScriptedAlchemy Feb 8, 2026
4513fac
refactor(esbuild): remove dead native federation references and unuse…
ScriptedAlchemy Feb 8, 2026
6d040a1
chore: apply queued repository updates
ScriptedAlchemy Feb 8, 2026
dd9e34f
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 9, 2026
c7adba9
fix(router-host-2000): stabilize router e2e on CI
ScriptedAlchemy Feb 9, 2026
6cab664
fix(router-host-2000): stabilize router e2e locally
ScriptedAlchemy Feb 9, 2026
0363b1a
Merge remote-tracking branch 'origin/main' into cursor/esbuild-module…
ScriptedAlchemy Feb 9, 2026
464fa57
Revert "chore: apply queued repository updates"
ScriptedAlchemy Feb 9, 2026
1b2230a
Merge remote-tracking branch 'origin/main' into cursor/esbuild-module…
ScriptedAlchemy Feb 9, 2026
c583ab5
fix(enhanced): reduce fallback build races
ScriptedAlchemy Feb 9, 2026
5bebce7
fix(router-host-2000): bind router demo servers to 127.0.0.1
ScriptedAlchemy Feb 9, 2026
351daf1
fix(bridge-react): stabilize router demo e2e in CI
ScriptedAlchemy Feb 9, 2026
dadf174
fix(esbuild): reduce measure job nx invocation crashes
ScriptedAlchemy Feb 9, 2026
e39e080
Merge remote-tracking branch 'origin/main' into cursor/esbuild-module…
ScriptedAlchemy Feb 12, 2026
77cec83
chore(core): add changeset coverage for pr #4389
ScriptedAlchemy Feb 12, 2026
5ed2555
chore(core): add contextual bridge-react/enhanced changeset
ScriptedAlchemy Feb 12, 2026
0a19ed9
chore(esbuild): merge origin/main
ScriptedAlchemy Feb 14, 2026
5f488ae
test(enhanced): harden reshake fixture bootstrap
ScriptedAlchemy Feb 14, 2026
e623e08
test(enhanced): stabilize treeshake fixtures under load
ScriptedAlchemy Feb 14, 2026
edce619
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 14, 2026
b0d8f0e
Merge remote-tracking branch 'origin/main' into cursor/esbuild-module…
ScriptedAlchemy Feb 16, 2026
71ebcd7
Merge origin/main into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 16, 2026
5b977ec
Merge origin/main into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 19, 2026
f9b8722
Merge origin/main into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 19, 2026
9ddfb29
Merge remote-tracking branch 'origin/main' into cursor/esbuild-module…
ScriptedAlchemy Feb 24, 2026
8daf370
fix(dts-plugin): align workspace entrypoints and RawSource typing
ScriptedAlchemy Feb 24, 2026
a62f024
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 25, 2026
5543638
fix(core): sync lockfile for frozen install checks
ScriptedAlchemy Feb 25, 2026
61ff018
fix(sdk): align package entrypoints with emitted artifacts
ScriptedAlchemy Feb 25, 2026
82c4261
Merge remote-tracking branch 'origin/main' into cursor/esbuild-module…
ScriptedAlchemy Feb 25, 2026
d8bfde3
chore(esbuild): revert non-esbuild scope creep changes
ScriptedAlchemy Feb 26, 2026
e99f967
chore(esbuild): minimize lockfile update for runtime dependency swap
ScriptedAlchemy Feb 26, 2026
b11510f
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 26, 2026
53e87b1
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 26, 2026
ec4929b
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 27, 2026
9a7b322
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 27, 2026
68ecb7d
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 28, 2026
5e82aa6
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Feb 28, 2026
a349431
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Mar 2, 2026
31ca80f
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Mar 3, 2026
28a4035
Merge branch 'main' into cursor/esbuild-module-federation-plugin-2869
ScriptedAlchemy Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion apps/esbuild/shell/app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
//@ts-nocheck

import React from 'react';
import { App as RemoteApp } from 'mfe1/component';
// Remote modules are loaded via Module Federation runtime.
// Since remote exports are unknown at build time, use default import
// and destructure the named exports from it.
import RemoteModule from 'mfe1/component';
const RemoteApp = RemoteModule.App || RemoteModule;

export function App() {
return (
Expand Down
306 changes: 231 additions & 75 deletions packages/esbuild/README.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,269 @@
# @module-federation/esbuild

This package provides an esbuild plugin for Module Federation, enabling you to easily share code between independently built and deployed applications.
Module Federation plugin for esbuild. Enables sharing code between independently built and deployed applications using the Module Federation protocol.

## Installation

Install the package using npm:

```bash
npm install @module-federation/esbuild
npm install @module-federation/esbuild @module-federation/runtime
# or
pnpm add @module-federation/esbuild @module-federation/runtime
```

## Usage
## Requirements

- **esbuild** `^0.25.0`
- **format**: `'esm'` (ESM output is required for dynamic imports and top-level await)
- **splitting**: `true` (code splitting is required for shared/exposed module chunks)
- **@module-federation/runtime** must be installed and resolvable

The plugin will automatically set `format: 'esm'` and `splitting: true` if not already configured.

To use the Module Federation plugin with esbuild, add it to your esbuild configuration:
## Quick Start

### 1. Create a Federation Config

```js
// federation.config.js
const { withFederation } = require('@module-federation/esbuild/build');

module.exports = withFederation({
name: 'myApp',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
},
remotes: {
remoteApp: 'http://localhost:3001/remoteEntry.js',
},
shared: {
react: { singleton: true, version: '^18.2.0' },
'react-dom': { singleton: true, version: '^18.2.0' },
},
});
```

### 2. Use the Plugin in Your Build

```js
const esbuild = require('esbuild');
const path = require('path');
const { moduleFederationPlugin } = require('@module-federation/esbuild/plugin');
const federationConfig = require('./federation.config.js');

async function buildApp() {
const tsConfig = 'tsconfig.json';
const outputPath = path.join('dist', 'host');

try {
await esbuild.build({
entryPoints: [path.join('host', 'main.ts')],
outdir: outputPath,
bundle: true,
platform: 'browser',
format: 'esm',
mainFields: ['es2020', 'browser', 'module', 'main'],
conditions: ['es2020', 'es2015', 'module'],
resolveExtensions: ['.ts', '.tsx', '.mjs', '.js'],
tsconfig: tsConfig,
splitting: true,
plugins: [moduleFederationPlugin(federationConfig)],
});
} catch (err) {
console.error(err);
process.exit(1);
}
}
esbuild.build({
entryPoints: ['./src/main.tsx'],
outdir: './dist',
bundle: true,
format: 'esm',
splitting: true,
plugins: [moduleFederationPlugin(federationConfig)],
});
```

## How It Works

### Architecture

The plugin uses `@module-federation/runtime` directly for all Module Federation functionality. It works by intercepting module imports via esbuild's plugin hooks and replacing them with virtual modules that use the MF runtime:

1. **Shared Modules**: Imports of shared dependencies (e.g., `react`) are replaced with virtual proxy modules that call `loadShare()` from the MF runtime for version negotiation between containers.

2. **Remote Modules**: Imports matching remote names (e.g., `remoteApp/Button`) are replaced with virtual proxy modules that call `loadRemote()` to fetch modules from remote containers at runtime.

3. **Container Entry**: When `exposes` is configured, a `remoteEntry.js` is generated with standard `get()`/`init()` exports that follow the Module Federation protocol.

4. **Runtime Initialization**: Entry points are augmented with runtime initialization code that sets up the MF instance before any app code runs, using ESM top-level await.

5. **Manifest**: An `mf-manifest.json` is generated for runtime discovery.

### Shared Module Flow

```
┌─────────────────────────────────────────────────┐
│ import React from 'react' │
│ │ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ Shared Proxy (virtual) │ │
│ │ loadShare('react') │ │
│ │ ├─ Share Scope found? │ │
│ │ │ ├─ YES: use shared │ │
│ │ │ └─ NO: use fallback │───► Bundled react │
│ │ └─ return module │ (separate │
│ └──────────────────────────┘ chunk) │
└─────────────────────────────────────────────────┘
```

### Remote Module Flow

```
┌─────────────────────────────────────────────────┐
│ import Button from 'remoteApp/Button' │
│ │ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ Remote Proxy (virtual) │ │
│ │ loadRemote('remoteApp/ │ │
│ │ Button') │ │
│ │ ├─ Load remoteEntry.js │ │
│ │ ├─ Call init(shareScope)│ │
│ │ ├─ Call get('./Button') │ │
│ │ └─ return module │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────┘
```

## Configuration

### `withFederation(config)`

Normalizes a federation configuration object. Use this to prepare your config before passing it to `moduleFederationPlugin()`.

```js
const { withFederation } = require('@module-federation/esbuild/build');
```

#### Config Properties

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `name` | `string` | Yes | Unique name for this federation container |
| `filename` | `string` | No | Remote entry filename (default: `'remoteEntry.js'`) |
| `exposes` | `Record<string, string>` | No | Modules to expose to other containers |
| `remotes` | `Record<string, string>` | No | Remote containers to consume |
| `shared` | `Record<string, SharedConfig>` | No | Dependencies to share between containers |

#### SharedConfig

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `singleton` | `boolean` | `false` | Only allow a single version of this package |
| `strictVersion` | `boolean` | `false` | Throw error on version mismatch |
| `requiredVersion` | `string` | `'*'` | Required semver version range |
| `version` | `string` | auto | The version of the shared package |
| `eager` | `boolean` | `false` | Load shared module eagerly |

buildApp();
### `moduleFederationPlugin(config)`

Creates the esbuild plugin instance.

```js
const { moduleFederationPlugin } = require('@module-federation/esbuild/plugin');
```

// Example of federation.config.js
## Examples

const { withFederation, shareAll } = require('@module-federation/esbuild/build');
### Host Application (Consumer)

```js
// federation.config.js
const { withFederation } = require('@module-federation/esbuild/build');

module.exports = withFederation({
name: 'host',
remotes: {
mfe1: 'http://localhost:3001/remoteEntry.js',
},
shared: {
react: { singleton: true, version: '^18.2.0' },
'react-dom': { singleton: true, version: '^18.2.0' },
},
});
```

```tsx
// App.tsx - Using remote modules
import RemoteComponent from 'mfe1/component';

export function App() {
return (
<div>
<h1>Host App</h1>
<RemoteComponent />
</div>
);
}
```

### Remote Application (Provider)

```js
// federation.config.js
const { withFederation } = require('@module-federation/esbuild/build');

module.exports = withFederation({
name: 'mfe1',
filename: 'remoteEntry.js',
exposes: {
'./Component': './src/Component',
'./component': './src/MyComponent',
},
shared: {
react: {
singleton: true,
version: '^18.2.0',
},
'react-dom': {
singleton: true,
version: '^18.2.0',
},
rxjs: {
singleton: true,
version: '^7.8.1',
},
...shareAll({
singleton: true,
strictVersion: true,
requiredVersion: 'auto',
includeSecondaries: false,
}),
react: { singleton: true, version: '^18.2.0' },
'react-dom': { singleton: true, version: '^18.2.0' },
},
});
```

The `moduleFederationPlugin` accepts a configuration object with the following properties:

- `name` (string): The name of the host application.
- `filename` (string, optional): The name of the remote entry file. Defaults to `'remoteEntry.js'`.
- `remotes` (object, optional): An object specifying the remote applications and their entry points.
- `exposes` (object, optional): An object specifying the modules to be exposed by the host application.
- `shared` (array, optional): An array of package names to be shared between the host and remote applications.
### Both Host and Remote

## Plugin Features
An application can be both a host and a remote simultaneously:

The `moduleFederationPlugin` includes the following features:
```js
const { withFederation } = require('@module-federation/esbuild/build');

- **Virtual Share Module**: Creates a virtual module for sharing dependencies between the host and remote applications.
- **Virtual Remote Module**: Creates a virtual module for importing exposed modules from remote applications.
- **CommonJS to ESM Transformation**: Transforms CommonJS modules to ESM format for compatibility with Module Federation.
- **Shared Dependencies Linking**: Links shared dependencies between the host and remote applications.
- **Manifest Generation**: Generates a manifest file containing information about the exposed modules and their exports.
module.exports = withFederation({
name: 'shell',
filename: 'remoteEntry.js',
exposes: {
'./Header': './src/Header',
},
remotes: {
sidebar: 'http://localhost:3002/remoteEntry.js',
},
shared: {
react: { singleton: true, version: '^18.2.0' },
'react-dom': { singleton: true, version: '^18.2.0' },
},
});
```

## API

### `moduleFederationPlugin(config)`
### Exports from `@module-federation/esbuild/plugin`

- `moduleFederationPlugin(config)` - Creates the esbuild plugin

Creates an esbuild plugin for Module Federation.
### Exports from `@module-federation/esbuild/build`

- `withFederation(config)` - Normalizes federation configuration
- `share(shareObjects)` - Processes shared dependency configurations
- `shareAll(config)` - Shares all dependencies from package.json
- `findPackageJson(folder)` - Finds nearest package.json
- `lookupVersion(key, workspaceRoot)` - Looks up dependency version
- `setInferVersion(infer)` - Enable/disable version inference

### Exports from `@module-federation/esbuild`

Re-exports everything from both `plugin` and `build` entry points.

## Notes

### Remote Module Named Exports

Since remote module exports are unknown at build time, only the default export is statically re-exported. For named exports from remote modules, use one of these patterns:

```js
// Pattern 1: Default import (recommended for React components)
import RemoteComponent from 'remote/component';

// Pattern 2: Destructure from default
import Remote from 'remote/utils';
const { helper, formatter } = Remote;

// Pattern 3: Dynamic import
const { helper } = await import('remote/utils');
```

- `config` (object): The Module Federation configuration.
- `name` (string): The name of the host application.
- `filename` (string, optional): The name of the remote entry file. Defaults to `'remoteEntry.js'`.
- `remotes` (object, optional): An object specifying the remote applications and their entry points.
- `exposes` (object, optional): An object specifying the modules to be exposed by the host application.
- `shared` (array, optional): An array of package names to be shared between the host and remote applications.
### Shared Module Subpaths

Returns an esbuild plugin instance.
When you share a package like `react`, subpath imports like `react/jsx-runtime` are also handled through the share scope. The plugin automatically detects subpath imports and routes them appropriately.
29 changes: 29 additions & 0 deletions packages/esbuild/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { readFileSync } from 'fs';

const { exclude: _, ...swcJestConfig } = JSON.parse(
readFileSync(`${__dirname}/.swcrc`, 'utf-8'),
);

swcJestConfig.swcrc ??= false;

export default {
clearMocks: true,
cache: false,
testEnvironment: 'node',
coveragePathIgnorePatterns: ['__tests__', '/node_modules/', '/dist/'],
globals: {
__VERSION__: '"0.0.0-test"',
FEDERATION_DEBUG: '""',
},
preset: 'ts-jest',
transformIgnorePatterns: ['/node_modules/', '/dist/'],
transform: {
'^.+\\.(t|j)sx?$': ['@swc/jest', swcJestConfig],
},
rootDir: __dirname,
testMatch: [
'<rootDir>/src/**/*.spec.[jt]s?(x)',
'<rootDir>/src/**/*.test.[jt]s?(x)',
],
testPathIgnorePatterns: ['/node_modules/'],
};
Loading
Loading