├── .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 | --------------------------------------------------------------------------------