Skip to content

Commit b0a9ec5

Browse files
committed
v14.2
1 parent cc6fee9 commit b0a9ec5

File tree

5 files changed

+115
-90
lines changed

5 files changed

+115
-90
lines changed

libs/mf-runtime/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@angular-architects/module-federation-runtime",
33
"license": "MIT",
4-
"version": "14.1.1",
4+
"version": "14.2.0",
55
"peerDependencies": {
66
"@angular/common": ">=12.0.0",
77
"@angular/core": ">=12.0.0"

libs/mf-tools/README.md

Lines changed: 72 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This can help to **balance the trade-off** between bundle size and isolation of
2323
- [Source Code for Micro Frontend](https://github.com/manfredsteyer/angular-app1)
2424
- [Source Code for Micro Frontend with Routing](https://github.com/manfredsteyer/angular3-app)
2525
- [Source Code for Micro Frontend with Vue](https://github.com/manfredsteyer/vue-js)
26+
- [Source Code for Micro Frontend with React](https://github.com/manfredsteyer/react-app)
2627
- [Source Code for Micro Frontend with AngularJS](https://github.com/manfredsteyer/angularjs-app)
2728

2829
## Tutorial
@@ -31,7 +32,17 @@ Please find our [tutorial here](https://github.com/angular-architects/module-fed
3132

3233
## Providing a Web Component with Module Federation
3334

34-
Expose your Angular components via Angular Elements:
35+
This helper packages assumes that Micro Frontends are exposed as Web Components.
36+
37+
### Exposing Micro Frontend as Web Component in Angular
38+
39+
To do this in Angular, install `@angular/elements`:
40+
41+
```bash
42+
npm i @angular/elements
43+
```
44+
45+
Then you can directly convert your AppComponent to a Web Component:
3546

3647
```typescript
3748
import { createCustomElement } from '@angular/elements';
@@ -56,6 +67,12 @@ export class AppModule {
5667
}
5768
```
5869

70+
### Exposing Web Component with other Frameworks like Ract
71+
72+
If you framework doesn't directly support exposing your application as a web component, you can easily write a simple Wrapper around it. Basically, a web component -- to be more precise: a custom element -- is just an EcmaScript class extending ``HtmlElement`` and registered via ``customElements.register``. Please find an [example for React here](https://github.com/manfredsteyer/react-app/blob/main/app.js).
73+
74+
### Exposting Web Component-based Micro Frontend via Module Federation
75+
5976
Add ``@angular-architects/module-federation`` to your micro frontend:
6077

6178
```
@@ -74,9 +91,12 @@ exposes: {
7491
},
7592
```
7693

77-
## Bootstrapping
94+
If the file that bootstraps your applications is called differently, adjust these settings accordingly.
95+
96+
## Helper for Angular
97+
98+
For enabling Angular for a multi version/ multi framework scenario, we need some helper functions. The easiest way to use them, is to bootstrap your Angular app with our bootstrap helper:
7899

79-
Our ``bootstrap`` helper function bootstraps your shell and your micro frontend and takes care of some details needed in a multi-framework/ multi-version scenario (like sharing the ``platform`` used).
80100

81101
```typescript
82102
// main.ts
@@ -85,71 +105,15 @@ import { environment } from './environments/environment';
85105
import { bootstrap } from '@angular-architects/module-federation-tools';
86106

87107
bootstrap(AppModule, {
88-
production: environment.production
108+
production: environment.production,
109+
appType: 'shell',
110+
// appType: 'microfrontend'
89111
});
90112
```
91113

92114
> Use this bootstrap helper for **both**, your shell and your micro frontends!
93115
94-
## Sharing Zone.js
95-
96-
In order to share zone.js, call our ``shareNgZone`` helper when starting the shell.
97-
98-
```typescript
99-
import { Component, NgZone } from '@angular/core';
100-
import { shareNgZone } from '@angular-architects/module-federation-tools';
101-
102-
@Component({
103-
selector: 'app-root',
104-
templateUrl: './app.component.html',
105-
})
106-
export class AppComponent {
107-
title = 'shell';
108-
109-
constructor(private ngZone: NgZone) {
110-
shareNgZone(ngZone);
111-
}
112-
113-
}
114-
```
115-
116-
The micro frontends will pick it up, if they are bootstrapped with the ``bootstrap`` helper (see above).
117-
118-
## Details on ngZone and Platform sharing
119-
120-
> In a multi version micro frontend strategy, it is important to load the zone.js bundle to the window object only once. Also, one need to make sure that only one instance of the ngZone is used by all the micro frontends.
121-
122-
If you share `@angular/core` and therefore also have one technical reference to the BrowserPlatform, that is used by more than one micro frondend, Angular's default setup is, to support only one platform instance per shared version. Be aware that you **need** to create multi platform instances in case of different versions, but also in case the version is the same, but `@angular/core` is not shared, but packed into the micro frontend's bundles directly (like in Angular's default way w/o module federation).
123-
124-
Naturally, such technical details are hard to get into. Therefore the `bootstrap()` function of this package helps to implement your multi version strategy w/o the need of implementing those low-level aspects on your own.
125-
126-
Some optional flags are offered to provide options for custom behavior of the `bootstrap()` function:
127-
128-
- `ngZoneSharing: false`: Deactivate ngZone sharing in the window object (not recommended):
129-
```typescript
130-
bootstrap(AppModule, {
131-
production: environment.production,
132-
ngZoneSharing: false // defaults to true
133-
});
134-
```
135-
- `platformSharing: false`: Deactivate Platform sharing in the window object (not recommended):
136-
```typescript
137-
bootstrap(AppModule, {
138-
production: environment.production,
139-
platformSharing: false // defaults to true
140-
});
141-
```
142-
- Possible, if dependencies are not shared or each bootstrapped remote app uses a different version.
143-
- `activeLegacyMode: false`: Deactivates the legacy mode that provides backwards compatibility for Platform sharing:
144-
```typescript
145-
bootstrap(AppModule, {
146-
production: environment.production,
147-
activeLegacyMode: false // defaults to true
148-
});
149-
```
150-
- If all your micro frontends use `@angular-architects/module-federation-tools` in version `^12.6.0`, `^13.1.0` or any newer major version you can switch off the legacy mode manually.
151-
- Those versions introduced new features on how to share the Platform in the window object.
152-
- This allows to use the `bootstrap()` function even in such cases, where the same version is packed into different micro frontend bundles.
116+
Please make sure to set the ``appType`` to ``shell`` for your shell application and to ``microfrontend`` for your Micro Frontends.
153117

154118

155119
## Routing to Web Components
@@ -218,28 +182,15 @@ export const APP_ROUTES: Routes = [
218182
219183
```typescript
220184
// Micro Frontend
221-
export const APP_ROUTES: Routes = [
222-
[...]
223-
{ matcher: endsWith('a'), component: AComponent},
224-
{ matcher: endsWith('b'), component: BComponent},
225-
[...]
226-
}
227-
```
228-
229-
In order to prevent issues with the "inner" router, use our helper function ``connectRouter``.
230-
231-
```typescript
232-
// AppComponent in Micro Frontend
233-
@Component({ ... })
234-
export class AppComponent {
235-
236-
constructor(private router: Router) { }
185+
RouterModule.forRoot([
186+
{ path: 'angular3/a', component: AComponent },
187+
{ path: 'angular3/b', component: BComponent },
237188

238-
ngOnInit(): void {
239-
connectRouter(this.router);
240-
}
189+
// To prevent issues when routing to other micro frontends
190+
// a catch-all route should be defined
191+
{ path: '**', component: EmptyComponent },
241192

242-
}
193+
]);
243194
```
244195
245196
## Directly Loading a Web Component via Module Federation
@@ -277,6 +228,44 @@ events = {
277228
<mft-wc-wrapper [options]="item" [props]="props" [events]="events"></mft-wc-wrapper>
278229
```
279230
231+
## Some Additional Details
232+
233+
> In a multi version micro frontend strategy, it is important to load the zone.js bundle to the window object only once. Also, one need to make sure that only one instance of the ngZone is used by all the micro frontends.
234+
235+
If you share `@angular/core` and therefore also have one technical reference to the BrowserPlatform, that is used by more than one micro frondend, Angular's default setup is, to support only one platform instance per shared version. Be aware that you **need** to create multi platform instances in case of different versions, but also in case the version is the same, but `@angular/core` is not shared, but packed into the micro frontend's bundles directly (like in Angular's default way w/o module federation).
236+
237+
Naturally, such technical details are hard to get into. Therefore the `bootstrap()` function of this package helps to implement your multi version strategy w/o the need of implementing those low-level aspects on your own.
238+
239+
Some optional flags are offered to provide options for custom behavior of the `bootstrap()` function:
240+
241+
- `ngZoneSharing: false`: Deactivate ngZone sharing in the window object (not recommended):
242+
```typescript
243+
bootstrap(AppModule, {
244+
production: environment.production,
245+
ngZoneSharing: false // defaults to true
246+
});
247+
```
248+
- `platformSharing: false`: Deactivate Platform sharing in the window object (not recommended):
249+
```typescript
250+
bootstrap(AppModule, {
251+
production: environment.production,
252+
platformSharing: false // defaults to true
253+
});
254+
```
255+
- Possible, if dependencies are not shared or each bootstrapped remote app uses a different version.
256+
- `activeLegacyMode: false`: Deactivates the legacy mode that provides backwards compatibility for Platform sharing:
257+
```typescript
258+
bootstrap(AppModule, {
259+
production: environment.production,
260+
activeLegacyMode: false // defaults to true
261+
});
262+
```
263+
- If all your micro frontends use `@angular-architects/module-federation-tools` in version `^12.6.0`, `^13.1.0` or any newer major version you can switch off the legacy mode manually.
264+
- Those versions introduced new features on how to share the Platform in the window object.
265+
- This allows to use the `bootstrap()` function even in such cases, where the same version is packed into different micro frontend bundles.
266+
267+
268+
280269
## More about the underlying ideas
281270
282271
Please find more information on the underlying ideas in this [blog article](https://www.angulararchitects.io/aktuelles/multi-framework-and-version-micro-frontends-with-module-federation-the-good-the-bad-the-ugly).

libs/mf-tools/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "@angular-architects/module-federation-tools",
3-
"version": "14.1.1",
3+
"version": "14.2.0",
44
"license": "MIT",
55
"peerDependencies": {
66
"@angular/common": ">=11.0.0",
77
"@angular/core": ">=11.0.0",
88
"@angular/router": ">=11.0.0",
9-
"@angular-architects/module-federation": "^14.1.1",
9+
"@angular-architects/module-federation": "^14.2.0",
1010
"@angular/platform-browser": ">=11.0.0",
1111
"rxjs": ">= 6.0.0"
1212
},

libs/mf-tools/src/lib/web-components/bootstrap-utils.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import { CompilerOptions, enableProdMode, NgModuleRef, NgZone, PlatformRef, Type, Version } from "@angular/core";
1+
import { CompilerOptions, enableProdMode, Injector, NgModuleRef, NgZone, PlatformRef, Type, Version } from "@angular/core";
22
import { platformBrowser } from "@angular/platform-browser";
33
import { VERSION } from '@angular/core';
44
import { getGlobalStateSlice, setGlobalStateSlice } from "../utils/global-state";
5+
import { Router } from "@angular/router";
6+
import { connectRouter } from "./router-utils";
7+
8+
export type AppType = 'shell' | 'microfrontend';
59

610
export type Options = {
711
production: boolean,
812
platformFactory?: () => PlatformRef,
913
compilerOptions?: CompilerOptions & BootstrapOptions,
1014
version?: () => string | Version,
15+
appType?: AppType;
1116
/**
1217
* Opt-out of ngZone sharing.
1318
* Not recommanded.
@@ -161,5 +166,36 @@ export function bootstrap<M>(module: Type<M>, options: Options): Promise<NgModul
161166
options.compilerOptions.ngZone = getNgZone();
162167
}
163168

164-
return getPlatform(options).bootstrapModule(module, options.compilerOptions);
169+
return getPlatform(options).bootstrapModule(module, options.compilerOptions).then(ref => {
170+
171+
if (options.appType === 'shell') {
172+
shareShellZone(ref.injector);
173+
}
174+
else if (options.appType === 'microfrontend') {
175+
connectMicroFrontendRouter(ref.injector);
176+
}
177+
178+
return ref;
179+
});
180+
}
181+
182+
function shareShellZone(injector: Injector) {
183+
const ngZone = injector.get(NgZone, null);
184+
if (!ngZone) {
185+
console.warn('No NgZone to share found');
186+
return;
187+
}
188+
shareNgZone(ngZone);
189+
}
190+
191+
function connectMicroFrontendRouter(injector: Injector) {
192+
const router = injector.get(Router);
193+
194+
if (!router) {
195+
console.warn('No router to connect found');
196+
return;
197+
}
198+
199+
connectRouter(router);
165200
}
201+

libs/mf/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-architects/module-federation",
3-
"version": "14.1.1",
3+
"version": "14.2.0",
44
"license": "MIT",
55
"repository": {
66
"type": "GitHub",
@@ -17,7 +17,7 @@
1717
"schematics": "./collection.json",
1818
"builders": "./builders.json",
1919
"dependencies": {
20-
"@angular-architects/module-federation-runtime": "14.1.1",
20+
"@angular-architects/module-federation-runtime": "14.2.0",
2121
"word-wrap": "^1.2.3",
2222
"callsite": "^1.0.0",
2323
"node-fetch": "^2.6.7",

0 commit comments

Comments
 (0)