Skip to content

Commit 969a178

Browse files
authored
feat: add module federation support (#253)
1 parent 425fb01 commit 969a178

File tree

21 files changed

+1964
-161
lines changed

21 files changed

+1964
-161
lines changed

README.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,141 @@ worker.onmessage = ({data: {result}}) => {
330330

331331
worker.postMessage({a: 1, b: 2});
332332
```
333+
334+
##### Module Federation
335+
336+
Module Federation is a Webpack 5 feature that enables micro-frontend architecture, where JavaScript applications can dynamically load code from each other at runtime.
337+
338+
`app-builder` uses `@module-federation/enhanced` for advanced Module Federation support.
339+
340+
- `moduleFederation` (`object`) — Module Federation configuration
341+
- `name` (`string`) — unique name of the application in the Module Federation ecosystem. Required parameter.
342+
- `version` (`string`) — application version. When specified, the entry file will be named `entry-{version}.js` instead of `entry.js`.
343+
- `publicPath` (`string`) — base URL for loading resources of this micro-frontend. Required parameter.
344+
- `remotes` (`string[]`) — list of remote application names that this application can load. Simplified alternative to `originalRemotes`.
345+
- `originalRemotes` (`RemotesObject`) — full configuration of remote applications in Module Federation Plugin format.
346+
- `remotesRuntimeVersioning` (`boolean`) — enables runtime versioning for remote applications.
347+
- `isolateStyles` (`object`) — CSS style isolation settings to prevent conflicts between micro-frontends.
348+
- `getPrefix` (`(entryName: string) => string`) — function to generate CSS class prefix.
349+
- `prefixSelector` (`(prefix: string, selector: string, prefixedSelector: string, filePath: string) => string`) — function to add prefix to CSS selectors.
350+
- Also supports all standard options from [@module-federation/enhanced](https://module-federation.io/), except `name` and `remotes`, such as:
351+
- `filename` — entry file name (default `remoteEntry.js`)
352+
- `exposes` — modules that this application exports
353+
- `shared` — shared dependencies between applications
354+
- `runtimePlugins` — plugins for Module Federation runtime
355+
356+
**Host Application Configuration Example:**
357+
358+
Host applications consume remote modules from other micro-frontends:
359+
360+
```ts
361+
export default defineConfig({
362+
client: {
363+
moduleFederation: {
364+
name: 'shell',
365+
publicPath: 'https://cdn.example.com/my-app/',
366+
// Simple remotes configuration
367+
remotes: ['header', 'footer', 'sidebar'],
368+
shared: {
369+
react: {singleton: true, requiredVersion: '^18.0.0'},
370+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
371+
lodash: {singleton: true},
372+
},
373+
},
374+
},
375+
});
376+
```
377+
378+
**Advanced Host Configuration:**
379+
380+
```ts
381+
export default defineConfig({
382+
client: {
383+
moduleFederation: {
384+
name: 'main-shell',
385+
version: '2.1.0',
386+
publicPath: 'https://cdn.example.com/my-app/',
387+
// Detailed remotes configuration
388+
originalRemotes: {
389+
header: 'header@https://cdn.example.com/header/remoteEntry.js',
390+
footer: 'footer@https://cdn.example.com/footer/remoteEntry.js',
391+
userProfile: 'userProfile@https://cdn.example.com/user-profile/remoteEntry.js',
392+
},
393+
remotesRuntimeVersioning: true,
394+
isolateStyles: {
395+
getPrefix: (entryName) => `.app-${entryName}`,
396+
prefixSelector: (prefix, selector, prefixedSelector, filePath) => {
397+
if (
398+
[prefix, ':root', 'html', 'body', '.g-root', '.remote-app'].some((item) =>
399+
selector.startsWith(item),
400+
) ||
401+
filePath.includes('@gravity-ui/chartkit')
402+
) {
403+
return selector;
404+
}
405+
return prefixedSelector;
406+
},
407+
},
408+
shared: {
409+
react: {singleton: true, requiredVersion: '^18.0.0'},
410+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
411+
lodash: {singleton: true},
412+
},
413+
},
414+
},
415+
});
416+
```
417+
418+
**Remote Application Configuration Example:**
419+
420+
Remote applications expose their modules for consumption by host applications:
421+
422+
```ts
423+
export default defineConfig({
424+
client: {
425+
moduleFederation: {
426+
name: 'header',
427+
publicPath: 'https://cdn.example.com/my-app/',
428+
// Expose modules for other applications
429+
exposes: {
430+
'./Header': './src/components/Header',
431+
'./Navigation': './src/components/Navigation',
432+
'./UserMenu': './src/components/UserMenu',
433+
},
434+
shared: {
435+
react: {singleton: true, requiredVersion: '^18.0.0'},
436+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
437+
lodash: {singleton: true},
438+
},
439+
},
440+
},
441+
});
442+
```
443+
444+
**Bidirectional Configuration Example:**
445+
446+
Applications can be both host and remote simultaneously:
447+
448+
```ts
449+
export default defineConfig({
450+
client: {
451+
moduleFederation: {
452+
name: 'dashboard',
453+
version: '1.5.0',
454+
publicPath: 'https://cdn.example.com/my-app/',
455+
// Consume remote modules
456+
remotes: ['charts', 'notifications'],
457+
// Expose own modules
458+
exposes: {
459+
'./DashboardLayout': './src/layouts/DashboardLayout',
460+
'./DataTable': './src/components/DataTable',
461+
},
462+
shared: {
463+
react: {singleton: true, requiredVersion: '^18.0.0'},
464+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
465+
lodash: {singleton: true},
466+
},
467+
},
468+
},
469+
});
470+
```

0 commit comments

Comments
 (0)