Skip to content

Commit b3b5777

Browse files
committed
v1.2
1 parent c19a0aa commit b3b5777

File tree

4 files changed

+355
-18
lines changed

4 files changed

+355
-18
lines changed

packages/mf/README.md

Lines changed: 156 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Big thanks to the following people who helped to make this possible:
1515

1616
## Motivation 💥
1717

18-
Module Federation allows to load separately compiled and deployed code (like micro frontends or plugins) into an application. This plugin makes Module Federation work together with Angular and the CLI.
18+
Module Federation allows loading separately compiled and deployed code (like micro frontends or plugins) into an application. This plugin makes Module Federation work together with Angular and the CLI.
1919

2020
## Features 🔥
2121

@@ -27,13 +27,20 @@ Module Federation allows to load separately compiled and deployed code (like mic
2727

2828
The module federation config is a **partial** webpack configuration. It only contains stuff to control module federation. The rest is generated by the CLI as usual.
2929

30+
Since Version 1.2, we also provide some advanced features like:
31+
32+
✅ Dynamic Module Federation support
33+
34+
✅ Sharing Libs of a Monorepo
35+
36+
3037
## Usage 🛠️
3138

3239
1. ``ng add @angular-architects/module-federation``
3340
2. Adjust the generated ``webpack.config.js`` file
3441
3. Repeat this for further projects in your workspace (if needed)
3542

36-
## Opting in into webpack 5 with CLI 11 🧐
43+
## Opt-in into webpack 5 with CLI 11 🧐
3744

3845
- You need to use **yarn** b/c it allows to override dependencies
3946
- Existing Projects: ``ng config -g cli.packageManager yarn``
@@ -49,7 +56,19 @@ The module federation config is a **partial** webpack configuration. It only con
4956

5057
- Run **yarn** to install all packages
5158

52-
Please that the CLI's webpack 5 support is current experimental.
59+
Please that the CLI's webpack 5 support is experimental in CLI 11. Here, you find a list with [unresolved issues](https://github.com/angular/angular-cli/pull/18873) in the current version.
60+
61+
## Getting Started 🧪
62+
63+
Please find here a [tutorial](https://github.com/angular-architects/module-federation-plugin/blob/main/packages/mf/tutorial/tutorial.md) that shows how to use this plugin.
64+
65+
![Microfrontend Loaded into Shell](https://github.com/angular-architects/module-federation-plugin/raw/main/packages/mf/tutorial/result.png)
66+
67+
[>> Start Tutorial](https://github.com/angular-architects/module-federation-plugin/blob/main/packages/mf/tutorial/tutorial.md)
68+
69+
## Documentation 📰
70+
71+
Please have a look at this [article series about Module Federation](https://www.angulararchitects.io/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/).
5372

5473
## Example 📽️
5574

@@ -61,18 +80,146 @@ This [example](https://github.com/manfredsteyer/module-federation-plugin-example
6180
Please have a look into the example's **readme**. It points you to the important aspects of using Module Federation.
6281

6382

64-
## Tutorial 🧪
83+
## Advanced Features
6584

66-
Please find here a [tutorial](https://github.com/angular-architects/module-federation-plugin/blob/main/packages/mf/tutorial/tutorial.md) that shows step by step how to introduce Module Federation into the above mentioned example.
85+
While the above-mentioned tutorial and blog articles guide you through using Module Federation, this section draws your attention to some advanced aspects of this plugin and Module Federation in general.
6786

68-
![Microfrontend Loaded into Shell](https://github.com/angular-architects/module-federation-plugin/raw/main/packages/mf/tutorial/result.png)
87+
### Dynamic Module Federation
6988

70-
[>> Start Tutorial](https://github.com/angular-architects/module-federation-plugin/blob/main/packages/mf/tutorial/tutorial.md)
89+
Since version 1.2, we provide helper functions making dynamic module federation really easy. Just use our ``loadRemoteModule`` function instead of a dynamic ``include``, e. g. together with lazy routes:
90+
91+
```typescript
92+
import { loadRemoteModule } from '@angular-architects/module-federation';
93+
94+
[...]
95+
const routes: Routes = [
96+
[...]
97+
{
98+
path: 'flights',
99+
loadChildren: () =>
100+
loadRemoteModule({
101+
remoteEntry: 'http://localhost:3000/remoteEntry.js',
102+
remoteName: 'mfe1',
103+
exposedModule: './Module'
104+
})
105+
.then(m => m.FlightsModule)
106+
},
107+
[...]
108+
]
109+
```
110+
111+
If somehow possible, load the ``remoteEntry`` upfront. This allows Module Federation to take the remote's metadata in consideration when negotiating the versions of the shared libraries.
112+
113+
For this, you could call ``loadRemoteEntry`` BEFORE bootstrapping Angular:
114+
115+
```typescript
116+
// main.ts
117+
import { loadRemoteEntry } from '@angular-architects/module-federation';
118+
119+
Promise.all([
120+
loadRemoteEntry('http://localhost:3000/remoteEntry.js', 'mfe1')
121+
])
122+
.catch(err => console.error('Error loading remote entries', err))
123+
.then(() => import('./bootstrap'))
124+
.catch(err => console.error(err));
125+
```
126+
127+
The ``bootstrap.ts`` file contains the source code normally found in ``main.ts`` and hence, it calls ``platform.bootstrapModule(AppModule)``. You really need this combination of an upfront file calling loadRemoteEntry and a dynamic import loading another file bootstrapping Angular because Angular itself is already a shared library respected during the version negotiation.
128+
129+
Then, when loading the remote Module, just skip the ``remoteEntry`` property:
130+
131+
```typescript
132+
import { loadRemoteModule } from '@angular-architects/module-federation';
133+
134+
[...]
135+
const routes: Routes = [
136+
[...]
137+
{
138+
path: 'flights',
139+
loadChildren: () =>
140+
loadRemoteModule({
141+
// Skipped - already loaded upfront:
142+
// remoteEntry: 'http://localhost:3000/remoteEntry.js',
143+
remoteName: 'mfe1',
144+
exposedModule: './Module'
145+
})
146+
.then(m => m.FlightsModule)
147+
},
148+
[...]
149+
]
150+
```
151+
152+
### Sharing Libs of a Monorepo
153+
154+
Let's assume, you have an Angular CLI Monorepo or an Nx Monorepo using path mappings in ``tsconfig.json`` for providing libraries:
155+
156+
```json
157+
"shared-lib": [
158+
"projects/shared-lib/src/public-api.ts",
159+
],
160+
```
161+
162+
You can now share such a library across all your micro frontends (apps) in your mono repo. This means, this library will be only loaded once.
163+
164+
To accomplish this, just register this lib name with the ``SharedMappings`` instance in your webpack config:
165+
166+
```javascript
167+
const mf = require("@angular-architects/module-federation/webpack");
168+
const path = require("path");
169+
170+
[...]
171+
172+
const sharedMappings = new mf.SharedMappings();
173+
sharedMappings.register(
174+
path.join(__dirname, '../../tsconfig.json'),
175+
['auth-lib']
176+
);
177+
```
178+
179+
Beginning with version 1.2, the boilerplate for using ``SharedMappings`` is generated for you. You only need to add your lib's name here.
180+
181+
This generated code includes providing the metadata for these libraries for the ``ModuleFederationPlugin`` and adding a plugin making sure that even source code generated by the Angular Compiler uses the shared version of the library.
182+
183+
```javascript
184+
plugins: [
185+
new ModuleFederationPlugin({
186+
[...]
187+
shared: {
188+
[...]
189+
...sharedMappings.getDescriptors()
190+
}
191+
}),
192+
sharedMappings.getPlugin(),
193+
],
194+
```
195+
196+
### Pitfalls when sharing libraries of a Monorepo
197+
198+
#### Bug with styleUrls
199+
200+
Currently, there is, unfortunately, a bug in the experimental CLI/webpack5 integration causing issues when using shared libraries together with components pointing to ``styleUrls``. For the time being, you can work around this issue by removing all ``styleUrls`` in your applications and libraries.
201+
202+
#### Sharing a library that is not even used
203+
204+
If you shared a local library that is not even used, you get the following error:
205+
206+
```
207+
./projects/shared-lib/src/public-api.ts - Error: Module build failed (from ./node_modules/@ngtools/webpack/src/index.js):
208+
Error: C:\Users\Manfred\Documents\projekte\mf-plugin\example\projects\shared-lib\src\public-api.ts is missing from the TypeScript compilation. Please make sure it is in your tsconfig via the 'files' or 'include' property.
209+
at AngularCompilerPlugin.getCompiledFile (C:\Users\Manfred\Documents\projekte\mf-plugin\example\node_modules\@ngtools\webpack\src\angular_compiler_plugin.js:957:23)
210+
at C:\Users\Manfred\Documents\projekte\mf-plugin\example\node_modules\@ngtools\webpack\src\loader.js:43:31
211+
```
212+
213+
#### Not exported Components
71214

215+
If you use a shared component without exporting it via your library's barrel (``index.ts`` or ``public-api.ts``), you get the following error at runtime:
72216

73-
## More Details on Module Federation 📰
217+
```
218+
core.js:4610 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'ɵcmp' of undefined
219+
TypeError: Cannot read property 'ɵcmp' of undefined
220+
at getComponentDef (core.js:1821)
221+
```
74222

75-
Have a look at this [article series about Module Federation](https://www.angulararchitects.io/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/).
76223

77224
## Angular Trainings, Workshops, and Consulting 👨‍🏫
78225

packages/mf/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-architects/module-federation",
3-
"version": "1.2.0-rc.9",
3+
"version": "1.2.0",
44
"license": "MIT",
55
"repository": {
66
"type": "GitHub",

packages/mf/src/schematics/mf/schematic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function makeMainAsync(main: string): Rule {
6161

6262
const mainContent = tree.read(main);
6363
tree.create(bootstrapName, mainContent);
64-
tree.overwrite(main, "import('./bootstrap');\n");
64+
tree.overwrite(main, "import('./bootstrap')\n\t.catch(err => console.error(err));\n");
6565

6666
}
6767
}

0 commit comments

Comments
 (0)