├── .editorconfig
├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── angular.json
├── index.html
├── package-lock.json
├── package.json
├── projects
├── mfe1
│ ├── browserslist
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.html
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.routes.ts
│ │ │ ├── flights
│ │ │ │ ├── flights-search
│ │ │ │ │ ├── flights-search.component.html
│ │ │ │ │ └── flights-search.component.ts
│ │ │ │ ├── flights.module.ts
│ │ │ │ ├── flights.routes.ts
│ │ │ │ └── lazy
│ │ │ │ │ ├── lazy.component.html
│ │ │ │ │ └── lazy.component.ts
│ │ │ └── home
│ │ │ │ ├── home.component.html
│ │ │ │ └── home.component.ts
│ │ ├── assets
│ │ │ └── angular.png
│ │ ├── bootstrap.ts
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.css
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ ├── tslint.json
│ ├── webpack.config.js
│ ├── webpack.config.js.bak
│ ├── webpack.prod.config.js
│ └── webpack.prod.config.js.bak
├── mfe2
│ ├── browserslist
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.json
│ ├── karma.conf.js
│ ├── src
│ │ ├── app
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.routes.ts
│ │ │ ├── bookings
│ │ │ │ ├── bookings-search
│ │ │ │ │ ├── bookings-search.component.html
│ │ │ │ │ └── bookings-search.component.ts
│ │ │ │ ├── bookings.module.ts
│ │ │ │ ├── bookings.routes.ts
│ │ │ │ └── lazy
│ │ │ │ │ ├── lazy.component.html
│ │ │ │ │ └── lazy.component.ts
│ │ │ └── home
│ │ │ │ ├── home.component.html
│ │ │ │ └── home.component.ts
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ └── angular.png
│ │ ├── bootstrap.ts
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.css
│ │ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ ├── tslint.json
│ ├── webpack.config.js
│ ├── webpack.config.js.bak
│ ├── webpack.prod.config.js
│ └── webpack.prod.config.js.bak
└── shell
│ ├── browserslist
│ ├── e2e
│ ├── protractor.conf.js
│ ├── src
│ │ ├── app.e2e-spec.ts
│ │ └── app.po.ts
│ └── tsconfig.json
│ ├── karma.conf.js
│ ├── plugin.js
│ ├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── app.routes.ts
│ │ ├── config
│ │ │ ├── config.component.html
│ │ │ └── config.component.ts
│ │ ├── home
│ │ │ ├── home.component.css
│ │ │ ├── home.component.html
│ │ │ ├── home.component.spec.ts
│ │ │ └── home.component.ts
│ │ └── utils
│ │ │ ├── config.ts
│ │ │ └── routes.ts
│ ├── assets
│ │ ├── angular.png
│ │ └── mf.manifest.json
│ ├── bootstrap.ts
│ ├── decl.d.ts
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ └── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ ├── tslint.json
│ ├── webpack.config.js
│ ├── webpack.config.js.bak
│ ├── webpack.prod.config.js
│ └── webpack.prod.config.js.bak
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.ts]
12 | quote_type = single
13 |
14 | [*.md]
15 | max_line_length = off
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.angular/cache
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # System Files
39 | .DS_Store
40 | Thumbs.db
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "workbench.colorCustomizations": {
3 | "activityBar.activeBackground": "#aadbf0",
4 | "activityBar.activeBorder": "#e25cba",
5 | "activityBar.background": "#aadbf0",
6 | "activityBar.foreground": "#15202b",
7 | "activityBar.inactiveForeground": "#15202b99",
8 | "activityBarBadge.background": "#e25cba",
9 | "activityBarBadge.foreground": "#15202b",
10 | "statusBar.background": "#7fc9e8",
11 | "statusBar.foreground": "#15202b",
12 | "statusBarItem.hoverBackground": "#54b7e0",
13 | "titleBar.activeBackground": "#7fc9e8",
14 | "titleBar.activeForeground": "#15202b",
15 | "titleBar.inactiveBackground": "#7fc9e899",
16 | "titleBar.inactiveForeground": "#15202b99"
17 | },
18 | "peacock.color": "#7fc9e8",
19 | "angular.enable-strict-mode-prompt": false
20 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FederationDemo
2 |
3 | Demonstrates webpack 5 Module Federation with Angular and the Angular Router.
4 |
5 | ## Start
6 |
7 | - Install dependencies with yarn (!)
8 | - You need to use yarn until Angular 12 (May 2021) to use the experimental opt-in for webpack 5
9 | - Beginning with Angular 12, webpack 5 will be active by default
10 | - Run Micro Frontend 1
11 | - ng serve mfe1 -o
12 | - Run Micro Frontend 2
13 | - ng serve mfe2 -o
14 | - Run the shell
15 | - ng serve shell -o
16 |
17 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "shell": {
7 | "projectType": "application",
8 | "schematics": {},
9 | "root": "projects/shell",
10 | "sourceRoot": "projects/shell/src",
11 | "prefix": "app",
12 | "architect": {
13 | "build": {
14 | "builder": "ngx-build-plus:browser",
15 | "options": {
16 | "outputPath": "dist/shell",
17 | "index": "projects/shell/src/index.html",
18 | "main": "projects/shell/src/main.ts",
19 | "polyfills": "projects/shell/src/polyfills.ts",
20 | "tsConfig": "projects/shell/tsconfig.app.json",
21 | "aot": true,
22 | "assets": [
23 | "projects/shell/src/favicon.ico",
24 | "projects/shell/src/assets"
25 | ],
26 | "styles": [],
27 | "scripts": [],
28 | "extraWebpackConfig": "projects/shell/webpack.config.js",
29 | "commonChunk": false
30 | },
31 | "configurations": {
32 | "development": {
33 | "optimization": false,
34 | "buildOptimizer": false,
35 | "sourceMap": true
36 | },
37 | "production": {
38 | "fileReplacements": [
39 | {
40 | "replace": "projects/shell/src/environments/environment.ts",
41 | "with": "projects/shell/src/environments/environment.prod.ts"
42 | }
43 | ],
44 | "optimization": true,
45 | "outputHashing": "all",
46 | "sourceMap": false,
47 | "extractCss": true,
48 | "namedChunks": false,
49 | "extractLicenses": true,
50 | "vendorChunk": false,
51 | "buildOptimizer": true,
52 | "extraWebpackConfig": "projects/shell/webpack.prod.config.js"
53 | }
54 | }
55 | },
56 | "serve": {
57 | "builder": "ngx-build-plus:dev-server",
58 | "options": {
59 | "browserTarget": "shell:build",
60 | "publicHost": "http://localhost:4200",
61 | "port": 4200,
62 | "extraWebpackConfig": "projects/shell/webpack.config.js"
63 | },
64 | "configurations": {
65 | "development": {
66 | "browserTarget": "shell:build:development",
67 | "extraWebpackConfig": "projects/shell/webpack.prod.config.js"
68 | },
69 | "production": {
70 | "browserTarget": "shell:build:production",
71 | "extraWebpackConfig": "projects/shell/webpack.prod.config.js"
72 | }
73 | },
74 | "defaultConfiguration": "development"
75 | },
76 | "extract-i18n": {
77 | "builder": "ngx-build-plus:extract-i18n",
78 | "options": {
79 | "browserTarget": "shell:build",
80 | "extraWebpackConfig": "projects/shell/webpack.config.js"
81 | }
82 | },
83 | "test": {
84 | "builder": "ngx-build-plus:karma",
85 | "options": {
86 | "main": "projects/shell/src/test.ts",
87 | "polyfills": "projects/shell/src/polyfills.ts",
88 | "tsConfig": "projects/shell/tsconfig.spec.json",
89 | "karmaConfig": "projects/shell/karma.conf.js",
90 | "assets": [
91 | "projects/shell/src/favicon.ico",
92 | "projects/shell/src/assets"
93 | ],
94 | "styles": [],
95 | "scripts": [],
96 | "extraWebpackConfig": "projects/shell/webpack.config.js"
97 | }
98 | },
99 | "e2e": {
100 | "builder": "@angular-devkit/build-angular:protractor",
101 | "options": {
102 | "protractorConfig": "projects/shell/e2e/protractor.conf.js",
103 | "devServerTarget": "shell:serve"
104 | },
105 | "configurations": {
106 | "production": {
107 | "devServerTarget": "shell:serve:production"
108 | }
109 | }
110 | }
111 | }
112 | },
113 | "mfe1": {
114 | "projectType": "application",
115 | "schematics": {},
116 | "root": "projects/mfe1",
117 | "sourceRoot": "projects/mfe1/src",
118 | "prefix": "app",
119 | "architect": {
120 | "build": {
121 | "builder": "ngx-build-plus:browser",
122 | "options": {
123 | "outputPath": "dist/mfe1",
124 | "index": "projects/mfe1/src/index.html",
125 | "main": "projects/mfe1/src/main.ts",
126 | "polyfills": "projects/mfe1/src/polyfills.ts",
127 | "tsConfig": "projects/mfe1/tsconfig.app.json",
128 | "aot": true,
129 | "assets": [
130 | "projects/mfe1/src/favicon.ico",
131 | "projects/mfe1/src/assets"
132 | ],
133 | "styles": [],
134 | "scripts": [],
135 | "extraWebpackConfig": "projects/mfe1/webpack.config.js",
136 | "commonChunk": false
137 | },
138 | "configurations": {
139 | "development": {
140 | "optimization": false,
141 | "buildOptimizer": false,
142 | "sourceMap": true
143 | },
144 | "production": {
145 | "fileReplacements": [
146 | {
147 | "replace": "projects/mfe1/src/environments/environment.ts",
148 | "with": "projects/mfe1/src/environments/environment.prod.ts"
149 | }
150 | ],
151 | "optimization": true,
152 | "outputHashing": "all",
153 | "sourceMap": false,
154 | "extractCss": true,
155 | "namedChunks": false,
156 | "extractLicenses": true,
157 | "vendorChunk": false,
158 | "buildOptimizer": true,
159 | "extraWebpackConfig": "projects/mfe1/webpack.prod.config.js"
160 | }
161 | }
162 | },
163 | "serve": {
164 | "builder": "ngx-build-plus:dev-server",
165 | "options": {
166 | "browserTarget": "mfe1:build",
167 | "publicHost": "http://localhost:4201",
168 | "port": 4201,
169 | "extraWebpackConfig": "projects/mfe1/webpack.config.js"
170 | },
171 | "configurations": {
172 | "development": {
173 | "browserTarget": "mfe1:build:development",
174 | "extraWebpackConfig": "projects/mfe1/webpack.prod.config.js"
175 | },
176 | "production": {
177 | "browserTarget": "mfe1:build:production",
178 | "extraWebpackConfig": "projects/mfe1/webpack.prod.config.js"
179 | }
180 | },
181 | "defaultConfiguration": "development"
182 | },
183 | "extract-i18n": {
184 | "builder": "ngx-build-plus:extract-i18n",
185 | "options": {
186 | "browserTarget": "mfe1:build",
187 | "extraWebpackConfig": "projects/mfe1/webpack.config.js"
188 | }
189 | },
190 | "test": {
191 | "builder": "ngx-build-plus:karma",
192 | "options": {
193 | "main": "projects/mfe1/src/test.ts",
194 | "polyfills": "projects/mfe1/src/polyfills.ts",
195 | "tsConfig": "projects/mfe1/tsconfig.spec.json",
196 | "karmaConfig": "projects/mfe1/karma.conf.js",
197 | "assets": [
198 | "projects/mfe1/src/favicon.ico",
199 | "projects/mfe1/src/assets"
200 | ],
201 | "styles": [],
202 | "scripts": [],
203 | "extraWebpackConfig": "projects/mfe1/webpack.config.js"
204 | }
205 | },
206 | "e2e": {
207 | "builder": "@angular-devkit/build-angular:protractor",
208 | "options": {
209 | "protractorConfig": "projects/mfe1/e2e/protractor.conf.js",
210 | "devServerTarget": "mfe1:serve"
211 | },
212 | "configurations": {
213 | "production": {
214 | "devServerTarget": "mfe1:serve:production"
215 | }
216 | }
217 | }
218 | }
219 | },
220 | "mfe2": {
221 | "projectType": "application",
222 | "schematics": {},
223 | "root": "projects/mfe2",
224 | "sourceRoot": "projects/mfe2/src",
225 | "prefix": "app",
226 | "architect": {
227 | "build": {
228 | "builder": "ngx-build-plus:browser",
229 | "options": {
230 | "outputPath": "dist/mfe2",
231 | "index": "projects/mfe2/src/index.html",
232 | "main": "projects/mfe2/src/main.ts",
233 | "polyfills": "projects/mfe2/src/polyfills.ts",
234 | "tsConfig": "projects/mfe2/tsconfig.app.json",
235 | "aot": true,
236 | "assets": [
237 | "projects/mfe2/src/favicon.ico",
238 | "projects/mfe2/src/assets"
239 | ],
240 | "styles": [],
241 | "scripts": [],
242 | "extraWebpackConfig": "projects/mfe2/webpack.config.js",
243 | "commonChunk": false
244 | },
245 | "configurations": {
246 | "development": {
247 | "optimization": false,
248 | "buildOptimizer": false,
249 | "sourceMap": true
250 | },
251 | "production": {
252 | "fileReplacements": [
253 | {
254 | "replace": "projects/mfe2/src/environments/environment.ts",
255 | "with": "projects/mfe2/src/environments/environment.prod.ts"
256 | }
257 | ],
258 | "optimization": true,
259 | "outputHashing": "all",
260 | "sourceMap": false,
261 | "extractCss": true,
262 | "namedChunks": false,
263 | "extractLicenses": true,
264 | "vendorChunk": false,
265 | "buildOptimizer": true,
266 | "budgets": [
267 | {
268 | "type": "initial",
269 | "maximumWarning": "2mb",
270 | "maximumError": "5mb"
271 | },
272 | {
273 | "type": "anyComponentStyle",
274 | "maximumWarning": "6kb",
275 | "maximumError": "10kb"
276 | }
277 | ],
278 | "extraWebpackConfig": "projects/mfe2/webpack.prod.config.js"
279 | }
280 | }
281 | },
282 | "serve": {
283 | "builder": "ngx-build-plus:dev-server",
284 | "options": {
285 | "browserTarget": "mfe2:build",
286 | "extraWebpackConfig": "projects/mfe2/webpack.config.js",
287 | "publicHost": "http://localhost:4202",
288 | "port": 4202
289 | },
290 | "configurations": {
291 | "development": {
292 | "browserTarget": "mfe2:build:development",
293 | "extraWebpackConfig": "projects/mfe2/webpack.prod.config.js"
294 | },
295 | "production": {
296 | "browserTarget": "mfe2:build:production",
297 | "extraWebpackConfig": "projects/mfe2/webpack.prod.config.js"
298 | }
299 | },
300 | "defaultConfiguration": "development"
301 | },
302 | "extract-i18n": {
303 | "builder": "ngx-build-plus:extract-i18n",
304 | "options": {
305 | "browserTarget": "mfe2:build",
306 | "extraWebpackConfig": "projects/mfe2/webpack.config.js"
307 | }
308 | },
309 | "test": {
310 | "builder": "ngx-build-plus:karma",
311 | "options": {
312 | "main": "projects/mfe2/src/test.ts",
313 | "polyfills": "projects/mfe2/src/polyfills.ts",
314 | "tsConfig": "projects/mfe2/tsconfig.spec.json",
315 | "karmaConfig": "projects/mfe2/karma.conf.js",
316 | "assets": [
317 | "projects/mfe2/src/favicon.ico",
318 | "projects/mfe2/src/assets"
319 | ],
320 | "styles": [],
321 | "scripts": [],
322 | "extraWebpackConfig": "projects/mfe2/webpack.config.js"
323 | }
324 | },
325 | "e2e": {
326 | "builder": "@angular-devkit/build-angular:protractor",
327 | "options": {
328 | "protractorConfig": "projects/mfe2/e2e/protractor.conf.js",
329 | "devServerTarget": "mfe2:serve"
330 | },
331 | "configurations": {
332 | "production": {
333 | "devServerTarget": "mfe2:serve:production"
334 | }
335 | }
336 | }
337 | }
338 | }
339 | }
340 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Demo
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mf-demo",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start:shell": "ng serve shell -o",
7 | "start:mfe1": "ng serve mfe1 -o",
8 | "start:mfe2": "ng serve mfe2 -o",
9 | "build:shell": "ng build shell --prod",
10 | "build:mfe1": "ng build mfe1 --prod",
11 | "build:mfe2": "ng build mfe2 --prod",
12 | "start": "concurrently \"npm run start:shell\" \"npm run start:mfe1\" \"npm run start:mfe2\" ",
13 | "build": "npm run build:shell && npm run build:mfe1 && npm run build:mfe2",
14 | "test": "ng test",
15 | "lint": "ng lint",
16 | "e2e": "ng e2e",
17 | "run:all": "node node_modules/@angular-architects/module-federation/src/server/mf-dev-server.js"
18 | },
19 | "private": true,
20 | "dependencies": {
21 | "@angular-architects/module-federation": "^14.3.7",
22 | "@angular/animations": "^14.0.0",
23 | "@angular/common": "^14.0.0",
24 | "@angular/compiler": "^14.0.0",
25 | "@angular/core": "^14.0.0",
26 | "@angular/forms": "^14.0.0",
27 | "@angular/platform-browser": "^14.0.0",
28 | "@angular/platform-browser-dynamic": "^14.0.0",
29 | "@angular/router": "^14.0.0",
30 | "@nrwl/workspace": "^10.3.0",
31 | "rxjs": "~6.6.0",
32 | "tslib": "^2.0.0",
33 | "zone.js": "~0.11.4"
34 | },
35 | "devDependencies": {
36 | "@angular-devkit/build-angular": "^14.0.0",
37 | "@angular/cli": "^14.0.0",
38 | "@angular/compiler-cli": "^14.0.0",
39 | "@types/jasmine": "~3.5.0",
40 | "@types/jasminewd2": "~2.0.3",
41 | "@types/node": "^12.11.1",
42 | "codelyzer": "^6.0.0",
43 | "concurrently": "^5.3.0",
44 | "jasmine-core": "~3.6.0",
45 | "jasmine-spec-reporter": "~5.0.0",
46 | "karma": "~6.3.9",
47 | "karma-chrome-launcher": "~3.1.0",
48 | "karma-coverage": "~2.0.3",
49 | "karma-jasmine": "~4.0.0",
50 | "karma-jasmine-html-reporter": "^1.5.0",
51 | "ngx-build-plus": "^14.0.0",
52 | "protractor": "~7.0.0",
53 | "serve": "^11.3.2",
54 | "ts-node": "~8.3.0",
55 | "tslint": "~6.1.0",
56 | "typescript": "~4.7.3",
57 | "webpack-dev-server": "^4.7.1"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/projects/mfe1/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/projects/mfe1/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
--------------------------------------------------------------------------------
/projects/mfe1/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('mfe1 app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/projects/mfe1/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/projects/mfe1/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/mfe1/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage/mfe1'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: 'app.component.html'
6 | })
7 | export class AppComponent {
8 | }
9 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { AppComponent } from './app.component';
4 | import { RouterModule } from '@angular/router';
5 | import { HomeComponent } from './home/home.component';
6 | import { FlightsModule } from './flights/flights.module';
7 | import { APP_ROUTES } from './app.routes';
8 |
9 | @NgModule({
10 | imports: [
11 | BrowserModule,
12 | RouterModule.forRoot(APP_ROUTES)
13 | ],
14 | declarations: [
15 | HomeComponent,
16 | AppComponent,
17 | ],
18 | providers: [],
19 | bootstrap: [
20 | AppComponent
21 | ]
22 | })
23 | export class AppModule { }
24 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 | import { HomeComponent } from './home/home.component';
3 |
4 | export const APP_ROUTES: Routes = [
5 | {
6 | path: '',
7 | component: HomeComponent,
8 | pathMatch: 'full'
9 | },
10 | {
11 | path: 'flights',
12 | loadChildren: () => import('./flights/flights.module')
13 | .then(m => m.FlightsModule)
14 | }
15 | ];
16 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/flights/flights-search/flights-search.component.html:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |

40 |
41 |
Flights
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/flights/flights-search/flights-search.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewChild, ViewContainerRef, Inject, Injector, ComponentFactoryResolver, OnInit} from '@angular/core';
2 |
3 |
4 | @Component({
5 | selector: 'app-flights-search',
6 | templateUrl: './flights-search.component.html'
7 | })
8 | export class FlightsSearchComponent {
9 |
10 | @ViewChild('vc', { read: ViewContainerRef, static: true })
11 | viewContainer: ViewContainerRef;
12 |
13 | constructor(
14 | @Inject(Injector) private injector,
15 | @Inject(ComponentFactoryResolver) private cfr) { }
16 |
17 | search() {
18 | alert('Not implemented for this demo!');
19 | }
20 |
21 | async terms() {
22 | const comp = await import('../lazy/lazy.component').then(m => m.LazyComponent);
23 |
24 | const factory = this.cfr.resolveComponentFactory(comp);
25 | this.viewContainer.createComponent(factory, null, this.injector);
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/flights/flights.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { FlightsSearchComponent } from './flights-search/flights-search.component';
4 | import { RouterModule } from '@angular/router';
5 | import { FLIGHTS_ROUTES } from './flights.routes';
6 |
7 | @NgModule({
8 | imports: [
9 | CommonModule,
10 | RouterModule.forChild(FLIGHTS_ROUTES)
11 | ],
12 | declarations: [
13 | FlightsSearchComponent
14 | ]
15 | })
16 | export class FlightsModule { }
17 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/flights/flights.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 | import { FlightsSearchComponent } from './flights-search/flights-search.component';
3 |
4 | export const FLIGHTS_ROUTES: Routes = [
5 | {
6 | path: '',
7 | redirectTo: 'flights-search',
8 | pathMatch: 'full'
9 | },
10 | {
11 | path: 'flights-search',
12 | component: FlightsSearchComponent
13 | }
14 | ];
15 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/flights/lazy/lazy.component.html:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
--------------------------------------------------------------------------------
/projects/mfe1/src/app/flights/lazy/lazy.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-lazy',
5 | templateUrl: './lazy.component.html'
6 | })
7 | export class LazyComponent implements OnInit {
8 |
9 | constructor() { }
10 |
11 | ngOnInit() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 | Welcome!
5 |
6 |
7 | Search Flights
8 |
--------------------------------------------------------------------------------
/projects/mfe1/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: './home.component.html'
6 | })
7 | export class HomeComponent implements OnInit {
8 |
9 | constructor() { }
10 |
11 | ngOnInit() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/projects/mfe1/src/assets/angular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/mfe1/src/assets/angular.png
--------------------------------------------------------------------------------
/projects/mfe1/src/bootstrap.ts:
--------------------------------------------------------------------------------
1 | import { AppModule } from './app/app.module';
2 | import { environment } from './environments/environment';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 | import { enableProdMode } from '@angular/core';
5 |
6 | if (environment.production) {
7 | enableProdMode();
8 | }
9 |
10 | platformBrowserDynamic().bootstrapModule(AppModule)
11 | .catch(err => console.error(err));
--------------------------------------------------------------------------------
/projects/mfe1/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/mfe1/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/mfe1/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/mfe1/src/favicon.ico
--------------------------------------------------------------------------------
/projects/mfe1/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mfe1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/projects/mfe1/src/main.ts:
--------------------------------------------------------------------------------
1 | import('./bootstrap')
2 | .catch(err => console.error(err));
3 |
--------------------------------------------------------------------------------
/projects/mfe1/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js/dist/zone'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/mfe1/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/projects/mfe1/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(), {
21 | teardown: { destroyAfterEach: false }
22 | }
23 | );
24 | // Then we find all the tests.
25 | const context = require.context('./', true, /\.spec\.ts$/);
26 | // And load the modules.
27 | context.keys().map(context);
28 |
--------------------------------------------------------------------------------
/projects/mfe1/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": [],
6 | "target": "es2020"
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
--------------------------------------------------------------------------------
/projects/mfe1/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/mfe1/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/projects/mfe1/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
2 |
3 | module.exports = withModuleFederationPlugin({
4 |
5 | name: 'mfe1',
6 |
7 | exposes: {
8 | // Adjusted line:
9 | './Module': './projects/mfe1/src/app/flights/flights.module.ts'
10 | },
11 |
12 | shared: {
13 | ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
14 | },
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/projects/mfe1/webpack.config.js.bak:
--------------------------------------------------------------------------------
1 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
2 | const mf = require("@angular-architects/module-federation/webpack");
3 | const path = require("path");
4 | const share = mf.share;
5 |
6 | const sharedMappings = new mf.SharedMappings();
7 | sharedMappings.register(
8 | path.join(__dirname, '../../tsconfig.json'),
9 | [/* mapped paths to share */]);
10 |
11 | module.exports = {
12 | output: {
13 | uniqueName: "mfe1",
14 | publicPath: "auto"
15 | },
16 | optimization: {
17 | runtimeChunk: false
18 | },
19 | resolve: {
20 | alias: {
21 | ...sharedMappings.getAliases(),
22 | }
23 | },
24 | experiments: {
25 | outputModule: true
26 | },
27 | plugins: [
28 | new ModuleFederationPlugin({
29 |
30 | library: { type: "module" },
31 |
32 | name: "mfe1",
33 | filename: "remoteEntry.js",
34 | exposes: {
35 | './Component': './projects/mfe1/src/app/app.component.ts',
36 | './Module': './projects/mfe1/src/app/flights/flights.module.ts'
37 | },
38 |
39 | shared: share({
40 | "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
41 | "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
42 | "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
43 | "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
44 |
45 | ...sharedMappings.getDescriptors()
46 | })
47 |
48 | }),
49 | sharedMappings.getPlugin()
50 | ],
51 | };
52 |
--------------------------------------------------------------------------------
/projects/mfe1/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./webpack.config');
2 |
--------------------------------------------------------------------------------
/projects/mfe1/webpack.prod.config.js.bak:
--------------------------------------------------------------------------------
1 | module.exports = require('./webpack.config');
2 |
--------------------------------------------------------------------------------
/projects/mfe2/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/projects/mfe2/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
--------------------------------------------------------------------------------
/projects/mfe2/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('mfe1 app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/projects/mfe2/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/projects/mfe2/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/mfe2/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage/mfe1'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/mfe2/src/app/app.component.css
--------------------------------------------------------------------------------
/projects/mfe2/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 |
4 | describe('AppComponent', () => {
5 | beforeEach(async(() => {
6 | TestBed.configureTestingModule({
7 | declarations: [
8 | AppComponent
9 | ],
10 | }).compileComponents();
11 | }));
12 |
13 | it('should create the app', () => {
14 | const fixture = TestBed.createComponent(AppComponent);
15 | const app = fixture.componentInstance;
16 | expect(app).toBeTruthy();
17 | });
18 |
19 | it(`should have as title 'mfe2'`, () => {
20 | const fixture = TestBed.createComponent(AppComponent);
21 | const app = fixture.componentInstance;
22 | expect(app.title).toEqual('mfe2');
23 | });
24 |
25 | it('should render title', () => {
26 | const fixture = TestBed.createComponent(AppComponent);
27 | fixture.detectChanges();
28 | const compiled = fixture.nativeElement;
29 | expect(compiled.querySelector('.content span').textContent).toContain('mfe2 app is running!');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-root',
5 | templateUrl: 'app.component.html'
6 | })
7 | export class AppComponent {
8 | }
9 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { AppComponent } from './app.component';
4 | import { RouterModule } from '@angular/router';
5 | import { HomeComponent } from './home/home.component';
6 | // import { BookingsModule } from './bookings/bookings.module';
7 | import { APP_ROUTES } from './app.routes';
8 |
9 | @NgModule({
10 | imports: [
11 | BrowserModule,
12 | // BookingsModule,
13 | RouterModule.forRoot(APP_ROUTES)
14 | ],
15 | declarations: [
16 | HomeComponent,
17 | AppComponent,
18 | ],
19 | providers: [],
20 | bootstrap: [
21 | AppComponent
22 | ]
23 | })
24 | export class AppModule { }
25 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 | import { HomeComponent } from './home/home.component';
3 |
4 | export const APP_ROUTES: Routes = [
5 | {
6 | path: '',
7 | component: HomeComponent,
8 | pathMatch: 'full'
9 | },
10 | {
11 | path: 'bookings',
12 | loadChildren: () => import('./bookings/bookings.module')
13 | .then(m => m.BookingsModule)
14 | }
15 | ];
16 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/bookings/bookings-search/bookings-search.component.html:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |

40 |
41 |
Your Bookings
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/bookings/bookings-search/bookings-search.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ViewChild, ViewContainerRef, Inject, Injector, ComponentFactoryResolver, OnInit} from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-bookings-search',
5 | templateUrl: './bookings-search.component.html'
6 | })
7 | export class BookingsSearchComponent {
8 |
9 | @ViewChild('vc', { read: ViewContainerRef, static: true })
10 | viewContainer: ViewContainerRef;
11 |
12 | constructor(
13 | @Inject(Injector) private injector,
14 | @Inject(ComponentFactoryResolver) private cfr) { }
15 |
16 | search() {
17 | alert('Not implemented for this demo!');
18 | }
19 |
20 | async terms() {
21 | const comp = await import('../lazy/lazy.component').then(m => m.LazyComponent);
22 |
23 | const factory = this.cfr.resolveComponentFactory(comp);
24 | this.viewContainer.createComponent(factory, null, this.injector);
25 | }
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/bookings/bookings.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 | import { BookingsSearchComponent } from './bookings-search/bookings-search.component';
4 | import { RouterModule } from '@angular/router';
5 | import { FLIGHTS_ROUTES } from './bookings.routes';
6 |
7 | @NgModule({
8 | imports: [
9 | CommonModule,
10 | RouterModule.forChild(FLIGHTS_ROUTES)
11 | ],
12 | declarations: [
13 | BookingsSearchComponent
14 | ]
15 | })
16 | export class BookingsModule { }
17 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/bookings/bookings.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 | import { BookingsSearchComponent } from './bookings-search/bookings-search.component';
3 |
4 | export const FLIGHTS_ROUTES: Routes = [
5 | {
6 | path: '',
7 | redirectTo: 'bookings-search',
8 | pathMatch: 'full'
9 | },
10 | {
11 | path: 'bookings-search',
12 | component: BookingsSearchComponent
13 | }
14 | ];
15 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/bookings/lazy/lazy.component.html:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
--------------------------------------------------------------------------------
/projects/mfe2/src/app/bookings/lazy/lazy.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-lazy',
5 | templateUrl: './lazy.component.html'
6 | })
7 | export class LazyComponent implements OnInit {
8 |
9 | constructor() { }
10 |
11 | ngOnInit() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 | Welcome!
5 |
6 |
7 | Search Bookings
8 |
--------------------------------------------------------------------------------
/projects/mfe2/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: './home.component.html'
6 | })
7 | export class HomeComponent implements OnInit {
8 |
9 | constructor() { }
10 |
11 | ngOnInit() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/projects/mfe2/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/mfe2/src/assets/.gitkeep
--------------------------------------------------------------------------------
/projects/mfe2/src/assets/angular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/mfe2/src/assets/angular.png
--------------------------------------------------------------------------------
/projects/mfe2/src/bootstrap.ts:
--------------------------------------------------------------------------------
1 | import { AppModule } from './app/app.module';
2 | import { environment } from './environments/environment';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 | import { enableProdMode } from '@angular/core';
5 |
6 | if (environment.production) {
7 | enableProdMode();
8 | }
9 |
10 | platformBrowserDynamic().bootstrapModule(AppModule)
11 | .catch(err => console.error(err));
--------------------------------------------------------------------------------
/projects/mfe2/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/mfe2/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/mfe2/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/mfe2/src/favicon.ico
--------------------------------------------------------------------------------
/projects/mfe2/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Mfe1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/projects/mfe2/src/main.ts:
--------------------------------------------------------------------------------
1 | import('./bootstrap')
2 | .catch(err => console.error(err));
3 |
--------------------------------------------------------------------------------
/projects/mfe2/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js/dist/zone'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/mfe2/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/projects/mfe2/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(), {
21 | teardown: { destroyAfterEach: false }
22 | }
23 | );
24 | // Then we find all the tests.
25 | const context = require.context('./', true, /\.spec\.ts$/);
26 | // And load the modules.
27 | context.keys().map(context);
28 |
--------------------------------------------------------------------------------
/projects/mfe2/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": [],
6 | "target": "es2020"
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
--------------------------------------------------------------------------------
/projects/mfe2/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/mfe2/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/projects/mfe2/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
2 |
3 | module.exports = withModuleFederationPlugin({
4 |
5 | name: 'mfe1',
6 |
7 | exposes: {
8 | // Adjusted line:
9 | './Module': './projects/mfe2/src/app/bookings/bookings.module.ts'
10 | },
11 |
12 | shared: {
13 | ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
14 | },
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/projects/mfe2/webpack.config.js.bak:
--------------------------------------------------------------------------------
1 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
2 | const mf = require("@angular-architects/module-federation/webpack");
3 | const path = require("path");
4 | const share = mf.share;
5 |
6 | const sharedMappings = new mf.SharedMappings();
7 | sharedMappings.register(
8 | path.join(__dirname, '../../tsconfig.json'),
9 | [/* mapped paths to share */]);
10 |
11 | module.exports = {
12 | output: {
13 | uniqueName: "mfe2",
14 | publicPath: "auto"
15 | },
16 | optimization: {
17 | runtimeChunk: false
18 | },
19 | resolve: {
20 | alias: {
21 | ...sharedMappings.getAliases(),
22 | }
23 | },
24 | experiments: {
25 | outputModule: true
26 | },
27 | plugins: [
28 | new ModuleFederationPlugin({
29 |
30 | library: { type: "module" },
31 |
32 | name: "mfe2",
33 | filename: "remoteEntry.js",
34 | exposes: {
35 | './Component': './projects/mfe2/src/app/app.component.ts',
36 | './Module': './projects/mfe2/src/app/bookings/bookings.module.ts'
37 | },
38 |
39 | shared: share({
40 | "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
41 | "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
42 | "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
43 | "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
44 |
45 | ...sharedMappings.getDescriptors()
46 | })
47 |
48 | }),
49 | sharedMappings.getPlugin()
50 | ],
51 | };
52 |
--------------------------------------------------------------------------------
/projects/mfe2/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./webpack.config');
2 |
--------------------------------------------------------------------------------
/projects/mfe2/webpack.prod.config.js.bak:
--------------------------------------------------------------------------------
1 | module.exports = require('./webpack.config');
2 |
--------------------------------------------------------------------------------
/projects/shell/browserslist:
--------------------------------------------------------------------------------
1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 |
5 | # You can see what browsers were selected by your queries by running:
6 | # npx browserslist
7 |
8 | > 0.5%
9 | last 2 versions
10 | Firefox ESR
11 | not dead
12 | not IE 9-11 # For IE 9-11 support, remove 'not'.
--------------------------------------------------------------------------------
/projects/shell/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Protractor configuration file, see link for more information
3 | // https://github.com/angular/protractor/blob/master/lib/config.ts
4 |
5 | const { SpecReporter } = require('jasmine-spec-reporter');
6 |
7 | /**
8 | * @type { import("protractor").Config }
9 | */
10 | exports.config = {
11 | allScriptsTimeout: 11000,
12 | specs: [
13 | './src/**/*.e2e-spec.ts'
14 | ],
15 | capabilities: {
16 | browserName: 'chrome'
17 | },
18 | directConnect: true,
19 | baseUrl: 'http://localhost:4200/',
20 | framework: 'jasmine',
21 | jasmineNodeOpts: {
22 | showColors: true,
23 | defaultTimeoutInterval: 30000,
24 | print: function() {}
25 | },
26 | onPrepare() {
27 | require('ts-node').register({
28 | project: require('path').join(__dirname, './tsconfig.json')
29 | });
30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
31 | }
32 | };
--------------------------------------------------------------------------------
/projects/shell/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 | import { browser, logging } from 'protractor';
3 |
4 | describe('workspace-project App', () => {
5 | let page: AppPage;
6 |
7 | beforeEach(() => {
8 | page = new AppPage();
9 | });
10 |
11 | it('should display welcome message', () => {
12 | page.navigateTo();
13 | expect(page.getTitleText()).toEqual('shell app is running!');
14 | });
15 |
16 | afterEach(async () => {
17 | // Assert that there are no errors emitted from the browser
18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER);
19 | expect(logs).not.toContain(jasmine.objectContaining({
20 | level: logging.Level.SEVERE,
21 | } as logging.Entry));
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/projects/shell/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo(): Promise {
5 | return browser.get(browser.baseUrl) as Promise;
6 | }
7 |
8 | getTitleText(): Promise {
9 | return element(by.css('app-root .content span')).getText() as Promise;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/projects/shell/e2e/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/projects/shell/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client: {
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage/shell'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | restartOnFileChange: true
31 | });
32 | };
33 |
--------------------------------------------------------------------------------
/projects/shell/plugin.js:
--------------------------------------------------------------------------------
1 |
2 | var merge = require('webpack-merge');
3 | var webpack = require('webpack');
4 | var config = require('./webpack.config');
5 |
6 | exports.default = {
7 | config: function(cfg) {
8 | const strategy = merge.strategy();
9 |
10 | const result = strategy (cfg, config);
11 | delete result.optimization;
12 | return result;
13 | }
14 | }
--------------------------------------------------------------------------------
/projects/shell/src/app/app.component.css:
--------------------------------------------------------------------------------
1 |
2 | /* Hallo */
3 |
4 | .x { }
--------------------------------------------------------------------------------
/projects/shell/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/projects/shell/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { getManifest, Manifest } from '@angular-architects/module-federation';
2 | import { Component, OnInit } from '@angular/core';
3 | import { Router } from '@angular/router';
4 | import { CustomManifest, CustomRemoteConfig } from './utils/config';
5 | import { buildRoutes } from './utils/routes';
6 |
7 | @Component({
8 | selector: 'app-root',
9 | templateUrl: './app.component.html'
10 | })
11 | export class AppComponent implements OnInit {
12 |
13 | remotes: CustomRemoteConfig[] = [];
14 |
15 | constructor(
16 | private router: Router) {
17 | }
18 |
19 | async ngOnInit(): Promise {
20 | const manifest = getManifest();
21 |
22 | // Hint: Move this to an APP_INITIALIZER
23 | // to avoid issues with deep linking
24 | const routes = buildRoutes(manifest);
25 | this.router.resetConfig(routes);
26 |
27 | this.remotes = Object.values(manifest);
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/projects/shell/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule } from '@angular/router';
4 | import { AppComponent } from './app.component';
5 | import { HomeComponent } from './home/home.component';
6 | import { APP_ROUTES } from './app.routes';
7 | import { ConfigComponent } from './config/config.component';
8 |
9 | @NgModule({
10 | imports: [
11 | BrowserModule,
12 | RouterModule.forRoot(APP_ROUTES)
13 | ],
14 | declarations: [
15 | AppComponent,
16 | HomeComponent,
17 | ConfigComponent
18 | ],
19 | providers: [],
20 | bootstrap: [AppComponent]
21 | })
22 | export class AppModule { }
23 |
--------------------------------------------------------------------------------
/projects/shell/src/app/app.routes.ts:
--------------------------------------------------------------------------------
1 | import { Routes } from '@angular/router';
2 | import { HomeComponent } from './home/home.component';
3 | import { ConfigComponent } from './config/config.component';
4 | import { loadRemoteModule } from '@angular-architects/module-federation';
5 |
6 | export const APP_ROUTES: Routes = [
7 | {
8 | path: '',
9 | component: HomeComponent,
10 | pathMatch: 'full'
11 | },
12 | {
13 | path: 'config',
14 | component: ConfigComponent
15 | },
16 | // {
17 | // path: 'flights',
18 | // loadChildren: () => loadRemoteModule({
19 | // type: 'manifest',
20 | // remoteName: 'mfe1',
21 | // exposedModule: './Module'
22 | // })
23 | // .then(m => m.FlightsModule)
24 | // },
25 | // {
26 | // path: 'bookings',
27 | // loadChildren: () => loadRemoteModule({
28 | // type: 'manifest',
29 | // remoteName: 'mfe2',
30 | // exposedModule: './Module'
31 | // })
32 | // .then(m => m.BookingsModule)
33 | // },
34 | ];
35 |
--------------------------------------------------------------------------------
/projects/shell/src/app/config/config.component.html:
--------------------------------------------------------------------------------
1 | Config
2 |
3 | {{ manifest | json }}
--------------------------------------------------------------------------------
/projects/shell/src/app/config/config.component.ts:
--------------------------------------------------------------------------------
1 | import { getManifest } from '@angular-architects/module-federation';
2 | import { Component, OnInit } from '@angular/core';
3 | import { CustomManifest } from '../utils/config';
4 |
5 | @Component({
6 | selector: 'app-config',
7 | templateUrl: './config.component.html'
8 | })
9 | export class ConfigComponent {
10 |
11 | manifest = getManifest();
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/projects/shell/src/app/home/home.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/shell/src/app/home/home.component.css
--------------------------------------------------------------------------------
/projects/shell/src/app/home/home.component.html:
--------------------------------------------------------------------------------
1 | Welcome!
--------------------------------------------------------------------------------
/projects/shell/src/app/home/home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { HomeComponent } from './home.component';
4 |
5 | describe('HomeComponent', () => {
6 | let component: HomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ HomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(HomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/projects/shell/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: './home.component.html'
6 | })
7 | export class HomeComponent implements OnInit {
8 |
9 | constructor() { }
10 |
11 | ngOnInit() {
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/projects/shell/src/app/utils/config.ts:
--------------------------------------------------------------------------------
1 | // projects/shell/src/app/utils/config.ts
2 |
3 | import { Manifest, RemoteConfig } from "@angular-architects/module-federation";
4 |
5 | export type CustomRemoteConfig = RemoteConfig & {
6 | exposedModule: string;
7 | displayName: string;
8 | routePath: string;
9 | ngModuleName: string;
10 | };
11 |
12 | export type CustomManifest = Manifest;
--------------------------------------------------------------------------------
/projects/shell/src/app/utils/routes.ts:
--------------------------------------------------------------------------------
1 | // projects/shell/src/app/utils/routes.ts
2 |
3 | import { loadRemoteModule } from '@angular-architects/module-federation';
4 | import { Routes } from '@angular/router';
5 | import { APP_ROUTES } from '../app.routes';
6 | import { CustomManifest } from './config';
7 |
8 | export function buildRoutes(options: CustomManifest): Routes {
9 |
10 | const lazyRoutes: Routes = Object.keys(options).map(key => {
11 | const entry = options[key];
12 | return {
13 | path: entry.routePath,
14 | loadChildren: () =>
15 | loadRemoteModule({
16 | type: 'manifest',
17 | remoteName: key,
18 | exposedModule: entry.exposedModule
19 | })
20 | .then(m => m[entry.ngModuleName])
21 | }
22 | });
23 |
24 | return [...APP_ROUTES, ...lazyRoutes];
25 | }
26 |
--------------------------------------------------------------------------------
/projects/shell/src/assets/angular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/shell/src/assets/angular.png
--------------------------------------------------------------------------------
/projects/shell/src/assets/mf.manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "mfe1": {
3 | "remoteEntry": "http://localhost:4201/remoteEntry.js",
4 |
5 | "exposedModule": "./Module",
6 | "displayName": "Flights",
7 | "routePath": "flights",
8 | "ngModuleName": "FlightsModule"
9 | },
10 | "mfe2": {
11 | "remoteEntry": "http://localhost:4202/remoteEntry.js",
12 |
13 | "exposedModule": "./Module",
14 | "displayName": "Bookings",
15 | "routePath": "bookings",
16 | "ngModuleName": "BookingsModule"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/projects/shell/src/bootstrap.ts:
--------------------------------------------------------------------------------
1 | import { AppModule } from './app/app.module';
2 | import { environment } from './environments/environment';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 | import { enableProdMode } from '@angular/core';
5 |
6 | if (environment.production) {
7 | enableProdMode();
8 | }
9 |
10 | platformBrowserDynamic().bootstrapModule(AppModule)
11 | .catch(err => console.error(err));
12 |
--------------------------------------------------------------------------------
/projects/shell/src/decl.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'mfe1/Component';
2 | declare module 'mfe1/Module';
--------------------------------------------------------------------------------
/projects/shell/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/projects/shell/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/projects/shell/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manfredsteyer/module-federation-with-angular-dynamic/07d9a2d6c100f0deadfc9828abdcaa425e9e30bd/projects/shell/src/favicon.ico
--------------------------------------------------------------------------------
/projects/shell/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Shell
6 |
7 |
8 |
9 |
10 |
11 |
12 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/projects/shell/src/main.ts:
--------------------------------------------------------------------------------
1 | import { loadManifest } from '@angular-architects/module-federation';
2 |
3 | loadManifest("/assets/mf.manifest.json")
4 | .catch(err => console.error(err))
5 | .then(_ => import('./bootstrap'))
6 | .catch(err => console.error(err));
7 |
--------------------------------------------------------------------------------
/projects/shell/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js/dist/zone'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/projects/shell/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/projects/shell/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting(), {
21 | teardown: { destroyAfterEach: false }
22 | }
23 | );
24 | // Then we find all the tests.
25 | const context = require.context('./', true, /\.spec\.ts$/);
26 | // And load the modules.
27 | context.keys().map(context);
28 |
--------------------------------------------------------------------------------
/projects/shell/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/app",
5 | "types": [],
6 | "target": "es2020"
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
--------------------------------------------------------------------------------
/projects/shell/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts",
12 | "src/polyfills.ts"
13 | ],
14 | "include": [
15 | "src/**/*.spec.ts",
16 | "src/**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/projects/shell/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/projects/shell/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
2 |
3 | module.exports = withModuleFederationPlugin({
4 |
5 | shared: {
6 | ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }),
7 | },
8 |
9 | });
10 |
--------------------------------------------------------------------------------
/projects/shell/webpack.config.js.bak:
--------------------------------------------------------------------------------
1 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
2 | const mf = require("@angular-architects/module-federation/webpack");
3 | const path = require("path");
4 | const share = mf.share;
5 |
6 | const sharedMappings = new mf.SharedMappings();
7 | sharedMappings.register(
8 | path.join(__dirname, '../../tsconfig.json'),
9 | [/* mapped paths to share */]);
10 |
11 | module.exports = {
12 | output: {
13 | uniqueName: "myTestApp",
14 | publicPath: "auto"
15 | },
16 | optimization: {
17 | runtimeChunk: false
18 | },
19 | resolve: {
20 | alias: {
21 | ...sharedMappings.getAliases(),
22 | }
23 | },
24 | experiments: {
25 | outputModule: true
26 | },
27 | plugins: [
28 | new ModuleFederationPlugin({
29 |
30 | library: { type: "module" },
31 |
32 | // No remotes configured upfront anymore!
33 | remotes: { },
34 |
35 | shared: share({
36 | "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
37 | "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
38 | "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
39 | "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
40 |
41 | ...sharedMappings.getDescriptors()
42 | })
43 |
44 | }),
45 | sharedMappings.getPlugin()
46 | ],
47 | };
48 |
49 |
--------------------------------------------------------------------------------
/projects/shell/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./webpack.config');
2 |
--------------------------------------------------------------------------------
/projects/shell/webpack.prod.config.js.bak:
--------------------------------------------------------------------------------
1 | module.exports = require('./webpack.config');
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "downlevelIteration": true,
9 | "experimentalDecorators": true,
10 | "module": "ES2020",
11 | "allowJs": true,
12 | "moduleResolution": "node",
13 | "importHelpers": false,
14 | "target": "ES2020",
15 | "strict": true,
16 | "lib": [
17 | "es2018",
18 | "dom"
19 | ]
20 | }
21 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "tslint:recommended",
3 | "rulesDirectory": [
4 | "codelyzer"
5 | ],
6 | "rules": {
7 | "align": {
8 | "options": [
9 | "parameters",
10 | "statements"
11 | ]
12 | },
13 | "array-type": false,
14 | "arrow-return-shorthand": true,
15 | "curly": true,
16 | "deprecation": {
17 | "severity": "warning"
18 | },
19 | "eofline": true,
20 | "import-blacklist": [
21 | true,
22 | "rxjs/Rx"
23 | ],
24 | "import-spacing": true,
25 | "indent": {
26 | "options": [
27 | "spaces"
28 | ]
29 | },
30 | "max-classes-per-file": false,
31 | "max-line-length": [
32 | true,
33 | 140
34 | ],
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-console": [
47 | true,
48 | "debug",
49 | "info",
50 | "time",
51 | "timeEnd",
52 | "trace"
53 | ],
54 | "no-empty": false,
55 | "no-inferrable-types": [
56 | true,
57 | "ignore-params"
58 | ],
59 | "no-non-null-assertion": true,
60 | "no-redundant-jsdoc": true,
61 | "no-switch-case-fall-through": true,
62 | "no-var-requires": false,
63 | "object-literal-key-quotes": [
64 | true,
65 | "as-needed"
66 | ],
67 | "quotemark": [
68 | true,
69 | "single"
70 | ],
71 | "semicolon": {
72 | "options": [
73 | "always"
74 | ]
75 | },
76 | "space-before-function-paren": {
77 | "options": {
78 | "anonymous": "never",
79 | "asyncArrow": "always",
80 | "constructor": "never",
81 | "method": "never",
82 | "named": "never"
83 | }
84 | },
85 | "typedef-whitespace": {
86 | "options": [
87 | {
88 | "call-signature": "nospace",
89 | "index-signature": "nospace",
90 | "parameter": "nospace",
91 | "property-declaration": "nospace",
92 | "variable-declaration": "nospace"
93 | },
94 | {
95 | "call-signature": "onespace",
96 | "index-signature": "onespace",
97 | "parameter": "onespace",
98 | "property-declaration": "onespace",
99 | "variable-declaration": "onespace"
100 | }
101 | ]
102 | },
103 | "variable-name": {
104 | "options": [
105 | "ban-keywords",
106 | "check-format",
107 | "allow-pascal-case"
108 | ]
109 | },
110 | "whitespace": {
111 | "options": [
112 | "check-branch",
113 | "check-decl",
114 | "check-operator",
115 | "check-separator",
116 | "check-type",
117 | "check-typecast"
118 | ]
119 | },
120 | "component-class-suffix": true,
121 | "contextual-lifecycle": true,
122 | "directive-class-suffix": true,
123 | "no-conflicting-lifecycle": true,
124 | "no-host-metadata-property": true,
125 | "no-input-rename": true,
126 | "no-inputs-metadata-property": true,
127 | "no-output-native": true,
128 | "no-output-on-prefix": true,
129 | "no-output-rename": true,
130 | "no-outputs-metadata-property": true,
131 | "template-banana-in-box": true,
132 | "template-no-negated-async": true,
133 | "use-lifecycle-interface": true,
134 | "use-pipe-transform-interface": true
135 | }
136 | }
137 |
--------------------------------------------------------------------------------