GithubHelp home page GithubHelp logo

module-federation-plugin-example's Introduction

Example for @angular-architects/module-federation

This examples loads a microfrontend into a shell:

Microfrontend Loaded into Shell

Important Files

Have a particular look at the following files:

  • readme.md: Shows how to install dependencies and how to start the example
  • projects\mfe1\webpack.config.js: Microfrontend config
  • projects\shell\webpack.config.js: Shell config
  • projects\shell\src\app\app.routes.ts: Lazy route for microfrontend
  • projects\shell\src\decl.d.ts: Typing for mapped Url pointing to microfrontend

Installation and Usage

  • Install packages: yarn (!)*
  • Start Micro Frontend (remote): ng serve mfe1 -o
  • Start Shell (host): ng serve shell -o
  • Make sure mfe1 is started before shell is loaded into the browser
  • Use the hyperlink flights in the shell to load mfe1

* Please note, you must use yarn during the beta phase of CLI 11 b/c it allows to override dependencies to force the CLI into webpack 5.

More Details on Module Federation

Have a look at this article series about Module Federation

Angular Trainings, Workshops, and Consulting

module-federation-plugin-example's People

Contributors

frankanneveld avatar manfredsteyer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

module-federation-plugin-example's Issues

Angular CLI proxy for configure several microApps

Hi Manfred,
Thank you for provide this kind of open source plugins that will help to the community.

Based in the example of the implementation of the MicroApp and Host/Shell App, the idea it's great as you have described below.
image

For Host/Shell App, is that possible to use Angular proxy CLI instead?

<some-element-ui microApp="/some-element-ui/main.js">
<some-element-ui/>

Host/Shell
Angular CLI proxy configuration

const PROXY_CONFIG = [
    {
        context: [
            "/some-element-ui"
        ],
        target: "http://localhost:3000",
        pathRewrite: {
            "^/some-element-ui" : ""
        },
        secure: false,
        changeOrigin: true
    }
]

module.exports = PROXY_CONFIG;

Thanks,
Edgar

MFE and PWA

PWA / ServiceWorkers for cache and update management are increasingly common with many advantages.

Micro frontends with separately deployable and isolated modules are equally impactful.

But both together become suddenly quite a mess.
You can easily do the shell/host - and cache/update anything changed there - and even serve it as a PWA. But remotes are essentially uncovered.

Dynamic remotes are unknown during build time - their files are unknown and they do not get a unique hash for change detection.
Remotes may load additional dependencies.

Browser caching of files and assets is extremely unreliable. But you can't use service worker for remote projects either - they are only served through shell.

And the second major issue is lack of update notifications - since the worker is not aware that a new version of a remote is available.

How would you approach this issue?

Update to angular 13

The example works perfectly with angular 12 but with angular 13 it ends up in following error: "Cannot use 'import.meta' outside a module"

Is there a way to fix this issue?

"dependencies": {
"@angular-architects/module-federation": "^13.0.1",
"@angular-architects/module-federation-tools": "^13.0.1",
"@angular/animations": "^13.0.2",
"@angular/common": "^13.0.2",
"@angular/compiler": "^13.0.2",
"@angular/core": "^13.0.2",
"@angular/forms": "^13.0.2",
"@angular/platform-browser": "^13.0.2",
"@angular/platform-browser-dynamic": "^13.0.2",
"@angular/router": "^13.0.2",
"concurrently": "^6.4.0",
"rxjs": "~7.4.0",
"tslib": "^2.3.1",
"word-wrap": "^1.2.3",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "^13.0.3",
"@angular/cli": "^13.0.3",
"@angular/compiler-cli": "^13.0.2",
"@types/jasmine": "~3.10.2",
"@types/jasminewd2": "~2.0.10",
"@types/node": "^16.11.9",
"codelyzer": "^6.0.2",
"jasmine-core": "~3.10.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.2.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"ng-packagr": "^12.0.0-rc.0",
"protractor": "~7.0.0",
"serve": "^11.3.2",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.4.4"
}
Thanks.

(angular 12) missing module @angular-devkit/architect and how to "fix" :)

When i try to serve the apps, i got this message right here:

Require stack:
- C:\ng10-app\node_modules\ngx-build-plus\src\plus-dev-server\index.js
- C:\ng10-app\node_modules\@angular\cli\node_modules\@angular-devkit\architect\node\node-modules-architect-host.js
- C:\ng10-app\node_modules\@angular\cli\node_modules\@angular-devkit\architect\node\index.js
- C:\ng10-app\node_modules\@angular\cli\models\architect-command.js
- C:\ng10-app\node_modules\@angular\cli\commands\serve-impl.js
- C:\ng10-app\node_modules\@angular\cli\node_modules\@angular-devkit\schematics\tools\export-ref.js
- C:\ng10-app\node_modules\@angular\cli\node_modules\@angular-devkit\schematics\tools\index.js
- C:\ng10-app\node_modules\@angular\cli\utilities\json-schema.js
- C:\ng10-app\node_modules\@angular\cli\models\command-runner.js
- C:\ng10-app\node_modules\@angular\cli\lib\cli\index.js
- C:\ng10-app\node_modules\@angular\cli\lib\init.js
- C:\ng10-app\node_modules\@angular\cli\bin\ng
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
    at Function.Module._load (internal/modules/cjs/loader.js:725:27)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (C:\ng10-app\node_modules\ngx-build-plus\src\plus-dev-server\index.js:4:21)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)```

After installing the development package `@angular-devkit/architect@^0.1200.0`, everything works fine :)

Unexpected token "export"

Hello Manfred

I'm trying out this project, but I'm not able to start the shell (host).

yarn ng serve shell -o
yarn run v1.22.4
$ ng serve shell -o
Unknown error: SyntaxError: Unexpected token 'export'
error Command failed with exit code 127.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

The micro frontend is working as expected. Any ideas?

Project fails with error: expected hoisted manifest

Env:
node -v ==> v16.16.0
npm -v ==> 8.11.0
yarn -v ==> 1.22.18

git clone https://github.com/manfredsteyer/module-federation-plugin-example.git --branch starter
cd module-federation-plugin-example.git
yarn

Results in:

error An unexpected error occurred: "expected hoisted manifest for "ngx-build-plus#@angular-devkit/build-angular#webpack-dev-server#webpack-dev-middleware#webpack"".

➜ module-federation-plugin-example git:(starter) cat yarn-error.log
Arguments:
/usr/local/Cellar/node@16/16.16.0/bin/node /usr/local/Cellar/yarn/1.22.18/libexec/bin/yarn.js

PATH:

Yarn version:
1.22.18

Node version:
16.16.0

Platform:
darwin x64

Trace:
Invariant Violation: expected hoisted manifest for "ngx-build-plus#@angular-devkit/build-angular#webpack-dev-server#webpack-dev-middleware#webpack"
at invariant (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:2314:15)
at _loop2 (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:91022:9)
at PackageHoister.init (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:91092:19)
at PackageLinker.getFlatHoistedTree (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:48541:20)
at PackageLinker. (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:48552:27)
at Generator.next ()
at step (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:310:30)
at /usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:328:14
at new Promise ()
at new F (/usr/local/Cellar/yarn/1.22.18/libexec/lib/cli.js:5301:28)

npm manifest:
{
"name": "mf-demo",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "npm run start:shell",
"start:shell": "ng serve shell -o --port 5000",
"start:mfe1": "ng serve mfe1 -o --port 3000",
"start:all": "concurrently "npm run start:shell" "npm run start:mfe1" ",
"build:shell": "ng build shell --prod",
"build:mfe1": "ng build mfe1 --prod",
"serve:dist": "concurrently "serve dist/shell -l 5000 -s" "serve dist/mfe1 -l 3000 -s" ",
"build": "npm run build:shell && npm run build:mfe1",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular-architects/module-federation": "^14.3.0",
"@angular/animations": "14.0.0",
"@angular/common": "14.0.0",
"@angular/compiler": "14.0.0",
"@angular/core": "14.0.0",
"@angular/forms": "14.0.0",
"@angular/platform-browser": "14.0.0",
"@angular/platform-browser-dynamic": "14.0.0",
"@angular/router": "14.0.0",
"@nrwl/workspace": "^10.3.0",
"concurrently": "^5.3.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/architect": "^0.1400.0",
"@angular-devkit/build-angular": "14.0.0",
"@angular/cli": "14.0.0",
"@angular/compiler-cli": "14.0.0",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.9",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"ngx-build-plus": "^14.0.0",
"protractor": "~7.0.0",
"serve": "^11.3.2",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.7.3"
}
}

yarn manifest:
No manifest

Lockfile:
No lockfile

Images used by remotes not found in the shell

Hi Manfred,
in your example I can't load images belonging to the remote app from the shell.
Your specific case works because the angular.png image exist also in the shell app and is that one that is downloaded and used by the app.
I've tried to substitute the image with anoter one that exist in the remote app only and the result is this one below.
It try to reach the image on the host app, but it doesn't exist there. If the remote app it' s run as standalone everything it's ok

image

How can be fixed? (without using absolute path for all the images used in the remotes)
Thank you!

Workers in remote (CORS problems)

After spending a day testing various solutions where workers are in a remote I was left without a solution (not monorepo, can't directly access from shell).

Currently, if remote app sets up a Worker:
new Worker(new URL('./test.worker', import.meta.url), {type: 'module'});
It gets blocked by CORS since the remote runs on a different port (or possibly domain in prod deployments).

And even if you could solve it in prod, running on the same domain on a webserver but a different path - local development will still be impossible with dev servers/ng serve.

I tried a few approaches (corsworker, dynamic scripts/fetch) to "preload" as a Blob.
However, these do not work with import.meta.url. The URL does not get correctly replaced in runtime (you get file:/// which is not available and also blocked by cors).

One option I could think of is if we could expose/share files to shell URL so you could use:
this.worker = new Worker('./remote-shared-path/test.worker', {type: 'module'});

Angular ngzone relations

Hi!

Your article mentions that to avoid problems, one should share NgZone between angular microfrontends. What are those problems that could arise?

Would it be a good alternative to fork the host's zone, and make the remote use that? I imagine that the host's wrapper component could inject ngZone, call run, inside the run callback fork the zone and then append the angular element running in that zone. Looking through the code I see that NgZone of the mfe would just fork the current zone - which is itself a fork of the host's zone.

(Sadly I have a browser API to decorate for remotes only)

module not found error

I am using "@angular-architects/module-federation": "^14.3.14" version in angular application with a library named test and application named host. I used path mapping in angular tsconfig as below.

"paths": {
"test/": [
"projects/test/
",
"dist/test"
]
}
i am trying to access a module like import { TestModule } from 'test/first';
in my app.module and trying to render the library component in app.html.

Error: Module not found: Error: Can't resolve 'test/*' in 'E:\mfe\projects\host\src\app'.

image

I encountered this error only after introducing the webpack file. I suspect the webpack is not resolving the my path alias

Shared monorepo lib duplicated and not shared on new mfe build

Our Setup
We have some Angular 14.x applications in a NX Monorepo using @angular-architects/module-federation": "^14.3.0".
We have a shared library added to the sharedMappings array, as we want to share it across all mfes. This shared library exports some services, e.g. LoginService, where the shell app sets the user data and the different remote mfes read it.

Current Issue
When we deploy all mfes together, everything works fine.
But if we build one mfe individually and deploy only this one to leverage the mfes architecture, we notice that a new bundle is created for the shared library and added to the newly built mfe (this is not the case when we make a build all). This breaks our logic, as a new service instance is created without the user data set by the shell.

In the screenshot below, it is possible to see the duplicate bundle for the shared lib b2b-mfe-shared created for the dashboard mfe:
MicrosoftTeams-image (1)

Expected behavior
We would expect that even for partial builds/deployments the same shared library instance is used. We migrated from Angular 12 and the previous @angular-architects/module-federation version and individual builds/deployments worked fine with that version. We followed the provided tutorial and all seems correctly configured, therefore not sure why we get this behavior.

Build commands used:

# Individual builds
"build:shell": "ng build shell --configuration production && cp apps/shell/scripts/mime.types apps/shell/scripts/.profile apps/shell/scripts/nginx.conf apps/shell/scripts/resolvers.conf dist/apps/shell/",
"build:dashboard": "ng build dashboard --configuration production && cp apps/dashboard/scripts/mime.types apps/dashboard/scripts/nginx.conf dist/apps/dashboard/",

# builds for all mfes
"build:all": "nx run-many --target=build --configuration production --all --parallel --maxParallel=3 && npm run copy:all",
"copy:all": "cp apps/shell/scripts/mime.types apps/shell/scripts/.profile apps/shell/scripts/nginx.conf apps/shell/scripts/resolvers.conf dist/apps/shell/ && cp apps/dashboard/scripts/mime.types apps/dashboard/scripts/nginx.conf dist/apps/dashboard/",

Our shell web.config.js:

const webpackConfiguration = withModuleFederationPlugin({
  name: 'shell',
  filename: 'shell.js',

  sharedMappings: ['@b2b-frontend/b2b-mfe-shared'],  <-- This is our shared monorepo lib

  exposes: {
    './Module': 'apps/shell/src/app/app.module.ts',
  },
  remotes: {},

  shared: share({
    '@angular/core': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' },
    '@angular/common': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' }
    ...
  }),
});

One remote mfe (dashboard) web.config.js:

module.exports = withModuleFederationPlugin({
  name: 'dashboard',
  filename: 'dashboard.js',

  sharedMappings: ['@b2b-frontend/b2b-mfe-shared'],
  exposes: {
    './Module': 'apps/dashboard/src/app/app.module.ts',
  },

  shared: share({
    '@angular/core': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' },
    '@angular/common': { singleton: true, strictVersion: true, requiredVersion: '14.2.12' },
    ...
  }),
});

One of the shared service from lib:

@Injectable({
  providedIn: 'root',   <-- We also tried with 'platform' but without any success
})
export class LoginService implements Resolve<LoginService> {
  private userInfo: UserInfo;

  // When a mfe is newly deployed, this method returns undefined, 
  // as a new instance of this service is created and not the shared one is used.
  getUserInfo(): UserInfo {
    return this.userInfo;
  }

  setUserInfo(userInfo: UserInfo): void {
    this.userInfo = userInfo;
  }

  resolve(): Observable<LoginService> {
    ...
  }
}

Shell routes for remote mfes:

const routes: Routes = [
  {
    path: 'dashboard',
    loadChildren: () => loadRemoteModule('./mfedashboard/dashboard.js').then((m) => m.AppModule),
  ...
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { 
               onSameUrlNavigation: 'reload', 
               relativeLinkResolution: 'legacy', 
               paramsInheritanceStrategy: 'always' }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

tsconfig.base.json

    "paths": {
      "@b2b-frontend/b2b-mfe-shared": ["libs/b2b-mfe-shared/src/index.ts"],
      ...
    }

BrowserAnimationsModule doesn't work with Module Federation

I am working on a module-federation prototype with webpack5 and nrwl12(monorepo).
Basically, I have a host application, that loads a shared module from page b. That shared module should be used in the host then. It sets up a RouterModule.forChild() and is exposed via webpack. That works.
Now I added one of our components, which uses animation inside and things are breaking. With the BrowserAnimationsModule imported in my shared module, the host app will not work anymore. The router will just add a new copy of my shared content underneath one another every time I navigate to it. I assume it creates a new platform everytime it loads the module, but how can I prevent that?
I have also tried to share the @angular/platform-browser/animations across app and host, but without luck.

Internal dependency does not emmited to separated chunk

Hi, thank for working example!

I found unexpected behaviour (with latest update)

In mfe1 application auth-lib does not emmited to separated chunk
image

But, in same time shell emit auth-lib properly:
image

Could you please help to find out what is going wrong here(

Server Side Rendering

I clone the repo and follow Trying it out secion, I disable async module federation as a first step after that once I run the following
ng add @nguniversal/common --project shell
I face the following error
Collection "C:\POC\module-federation-plugin-example\node_modules\@nguniversal\common\schematics\collection.json" cannot be resolved.

I tried to run the command with a project that doesn't have Module Federation and the same error raised.

module federation doesn't work without sharing

in your example, if I remove sharing from remote and host like this

const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({

  shared: {
  },

});
const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({

  name: 'mfe1',

  exposes: {
    './Module': './projects/mfe1/src/app/flights/flights.module.ts',
  },

  shared: {
  },

});

I get this error:

ERROR Error: Uncaught (in promise): Error: NG0203: inject() must be called from an injection context (a constructor, a factory function or a field initializer). Find more at https://angular.io/errors/NG0203
RuntimeError@http://localhost:3000/projects_mfe1_src_app_flights_flights_module_ts.js:11570:5
injectInjectorOnly@http://localhost:3000/projects_mfe1_src_app_flights_flights_module_ts.js:17038:11
ɵɵinject@http://localhost:3000/projects_mfe1_src_app_flights_flights_module_ts.js:17049:59
RouterModule_Factory@http://localhost:3000/projects_mfe1_src_app_flights_flights_module_ts.js:56820:77
factory@http://localhost:5000/projects_shell_src_bootstrap_ts.js:25988:32
hydrate@http://localhost:5000/projects_shell_src_bootstrap_ts.js:25880:29
get@http://localhost:5000/projects_shell_src_bootstrap_ts.js:25762:23
injectInjectorOnly@http://localhost:5000/projects_shell_src_bootstrap_ts.js:20526:29
ɵɵinject@http://localhost:5000/projects_shell_src_bootstrap_ts.js:20531:59
useValue@http://localhost:5000/projects_shell_src_bootstrap_ts.js:25481:33
resolveInjectorInitializers@http://localhost:5000/projects_shell_src_bootstrap_ts.js:25805:9
NgModuleRef@http://localhost:5000/projects_shell_src_bootstrap_ts.js:40872:22
create@http://localhost:5000/projects_shell_src_bootstrap_ts.js:40907:12
loadChildren/loadRunner<@http://localhost:5000/projects_shell_src_bootstrap_ts.js:67153:36
_next@http://localhost:5000/projects_shell_src_bootstrap_ts.js:2819:29
next@http://localhost:5000/projects_shell_src_bootstrap_ts.js:1272:12
notifyNext@http://localhost:5000/projects_shell_src_bootstrap_ts.js:2989:22
_next@http://localhost:5000/projects_shell_src_bootstrap_ts.js:1727:17
next@http://localhost:5000/projects_shell_src_bootstrap_ts.js:1272:12
subscribeToPromise/</<@http://localhost:5000/projects_shell_src_bootstrap_ts.js:4470:18
3484/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invoke@http://localhost:5000/polyfills.js:8113:160
onInvoke@http://localhost:5000/projects_shell_src_bootstrap_ts.js:45573:25
3484/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invoke@http://localhost:5000/polyfills.js:8113:48
3484/</Zone$1</Zone</Zone.prototype.run@http://localhost:5000/polyfills.js:7839:37
scheduleResolveOrReject/<@http://localhost:5000/polyfills.js:9259:28
3484/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invokeTask@http://localhost:5000/polyfills.js:8146:173
onInvokeTask@http://localhost:5000/projects_shell_src_bootstrap_ts.js:45561:25
3484/</Zone$1</ZoneDelegate</ZoneDelegate.prototype.invokeTask@http://localhost:5000/polyfills.js:8146:56
3484/</Zone$1</Zone</Zone.prototype.runTask@http://localhost:5000/polyfills.js:7896:39
drainMicroTaskQueue@http://localhost:5000/polyfills.js:8362:25

how can I make it work without sharing components?

Module Not Found when trying to share a service .

Issue Report .

I am trying to share a service's singleton instance across projects but I am getting Module not found Error .
I followed step by step tutorial .

tsconfig.json app level

        {
          "compileOnSave": false,
          "compilerOptions": {
            "paths": {
              "astrapay-library": [
                "dist/astrapay-library/astrapay-library",
                "dist/astrapay-library"
                // "projects/astrapay-library/src/project.ts"
              ]
            },
            "baseUrl": "./",
            "outDir": "./dist/out-tsc",
            "sourceMap": true,
            "declaration": false,
            "downlevelIteration": true,
            "experimentalDecorators": true,
            "moduleResolution": "node",
            "importHelpers": true,
            "target": "es2015",
            "module": "es2020",
            "lib": [
              "es2018",
              "dom"
            ]
          },
          "angularCompilerOptions": {
            "enableI18nLegacyMessageIdFormat": false,
            // "strictTemplates" : true
          }
        }

Shell app's webpack.config.json

            [...]
            const sharedMappings = new mf.SharedMappings();
            sharedMappings.register(
              path.join(__dirname, '../../tsconfig.json'),
              [
                /* mapped paths to share */
                "astrapay-library"   // <-- This is my shared library's name
              ]);
        
        
        module.exports = {
          output: {
            uniqueName: "shell"
          },
          optimization: {
            // Only needed to bypass a temporary bug
            runtimeChunk: false
          },
          plugins: [
            new ModuleFederationPlugin({
              
                // For remotes (please adjust)
                // name: "shell",
                // filename: "remoteEntry.js",
                // exposes: {
                //     './Component': './projects/shell/src/app/app.component.ts',
                // },        
                
                // For hosts (please adjust)
                remotes: {
                    "payment": "payment@http://localhost:3000/remoteEntry.js",
                },
        
                shared: {
                  "@angular/core": { singleton: true, strictVersion: true }, 
                  "@angular/common": { singleton: true, strictVersion: true }, 
                  "@angular/router": { singleton: true, strictVersion: true },
                  "ngx-toastr" : {singleton: true, strictVersion: true},
                  ...sharedMappings.getDescriptors()
                }
                
            }),
            sharedMappings.getPlugin(),
          ],
        };

payment app's webpack.config.json

        [...] 
        const sharedMappings = new mf.SharedMappings();
        sharedMappings.register(
          path.join(__dirname, '../../tsconfig.json'),
          [
            /* mapped paths to share */
            "astrapay-library"
          ]);
        
        module.exports = {
          output: {
            uniqueName: "payment"
          },
          optimization: {
            // Only needed to bypass a temporary bug
            runtimeChunk: false
          },
          plugins: [
            new ModuleFederationPlugin({
              
                // For remotes (please adjust)
                name: "payment",
                filename: "remoteEntry.js",
                exposes: {
                    './BankManagememt': './projects/payment/src/modules/bank-management/bank-management.module.ts',
                },        
                
                // For hosts (please adjust)
                // remotes: {
                //     "shell": "shell@http://localhost:5000/remoteEntry.js",
        
                // },
        
                shared: {
                  "@angular/core": { singleton: true, strictVersion: true }, 
                  "@angular/common": { singleton: true, strictVersion: true }, 
                  "@angular/router": { singleton: true, strictVersion: true },
          
                  "ngx-toastr" : {singleton: true, strictVersion: true},
                  ...sharedMappings.getDescriptors()
                }
                
            }),
            sharedMappings.getPlugin(),
          ],
        };

When running ng serve payment and ng serve shell I get this error :
image

Meanwhile if I dont specify the library in sharedMappings.register( "dirpath", --here--) and specify the library in shared object like this :

        shared: {
          "@angular/core": { singleton: true, strictVersion: true }, 
          "@angular/common": { singleton: true, strictVersion: true }, 
          "@angular/router": { singleton: true, strictVersion: true },
          "astrapay-library" : {
            singleton: true,
            import : "projects/astrapay-library/src/project"
          },
          "ngx-toastr" : {singleton: true, strictVersion: true},
          ...sharedMappings.getDescriptors()
        }

It works but the service is not singleton .
Why am I getting this issue ?

ng serve not working with module federation and angular 11

ng serve does not work when i use multiple lines of styles or scripts inside angular .json like below. However, if include only single line there, it works fluently.... your help shall be highly appreciated...
.... styles:[ "node_modules/bootstrap/dist/css/bootstrap.min.css", "projects/shell/src/styles.css" ] ....

Deployment to cloud

Hi, how can I deploy those microfrontends to f eg. azure, should i use webservice and run it with npm run run:all?

ng test error: flights.module.ts is missing from the TypeScript compilation

Hi, thanks for providing this sample.

When I run ng test, I get the following error:

PS C:\Code\Repos\Git\module-federation-plugin-example-starter> ng test --browsers ChromeHeadless

  • Generating browser application bundles...(node:71312) [DEP_WEBPACK_WATCH_WITHOUT_CALLBACK] DeprecationWarning: A 'callback' argument need to be provided to the 'webpack(options, callback)' function when the 'watch' option is set. There is no way to handle the 'watch' option without a callback.
    \ Generating browser application bundles (phase: building)...26 02 2021 11:44:10.726:WARN [karma]: No captured browser, open http://localhost:9877/
    26 02 2021 11:44:10.753:INFO [karma-server]: Karma v5.2.3 server started at http://localhost:9877/
    26 02 2021 11:44:10.755:INFO [launcher]: Launching browsers ChromeHeadless with concurrency unlimited
    26 02 2021 11:44:10.766:INFO [launcher]: Starting browser ChromeHeadless
    \ Generating browser application bundles (phase: sealing)...
    ./projects/mfe1/src/app/flights/flights.module.ts - Error: Module build failed (from ./node_modules/@ngtools/webpack/src/index.js):
    Error: C:\Code\Repos\Git\module-federation-plugin-example-starter\projects\mfe1\src\app\flights\flights.module.ts is missing from the TypeScript compilation. Please make sure it is in your tsconfig via the 'files' or 'include' property.
    at AngularCompilerPlugin.getCompiledFile (C:\Code\Repos\Git\module-federation-plugin-example-starter\node_modules@ngtools\webpack\src\angular_compiler_plugin.js:957:23)
    at plugin.done.then (C:\Code\Repos\Git\module-federation-plugin-example-starter\node_modules@ngtools\webpack\src\loader.js:43:31)

I haven't managed to fix the issue so just checking to see if you had any ideas.

How to structure CI/CD pipeline with this approach?

Hey @manfredsteyer,

first of all - thank you for this module federation repository!
I was wondering how you would handle this setup in a pipeline? How can you still build individual microfrontends when working in one repository?
Do you use tags to trigger the pipeline?

Love to hear your thoughts on that!
Kind regards,
Thomas

How can i share a component instead of a module

Hi, this is just a question instead of an issue.

All the examples and documentation that i read about federation in angular are related to modules and all the modules are loaded using router´s lazy load, so they are all linked to an url.

My question is how can i share a component or a module that are not related to an url but to just a shared module/component/service with just a bunch of code?

Question: Reload the module if there is some change without refreshing the window

Hi,
I need to reload the application module without refreshing the page.
For Ex. If I run all the application at first and later when I stopped any one of the application and when I redirect to that path its should show module not found which I am getting only if I refresh the page I need the page to get updated without refreshing the window.

const module = await loadRemoteModule({
         remoteEntry: 'http://localhost:4205/remoteEntry.js',
         remoteName: 'encrypt',
         exposedModule: './Module'
       });

// Trigger initialization of federated module when the route is navigated to

      initFederation('/assets/mf.manifest.json')
        .catch(err => console.error(err))
        .then(_res => {
          console.log(_res);              
          import('../bootstrap')
        })

.catch(err => console.error(err));
I tried with loading remote module and also initFederation when route is navigated but its not updating

Angular Dependency Injection: Singleton Service gets instantiated twice

I have build a minimal setup based on the branch nf-standalone-solutions where a singleton service gets instantiated twice under certain circumstances. The Problem does not occure in a pure angular project.:

Changes:

  • added File "projects/auth/src/lib/test.all.ts" (component+service+module in one file; it is not important if splitted in multiple files)
  • added * library export for test.all.ts
  • imported TestModule in AppModule of the shell app
  • added test component to app component html and injected the test service into the app component

If you start the shell app, the Service "ComponentTestService" gets instantiated twice (console output in the constructor of the service). In a similar workspace generated with angular, the service only gets instantiated once (without the native module federation).

I could also isolate the error:
If the service is not used in the library which defines it, the service gets initialized once even if used in multiple other libraries. The only case i could reproduce this error is if the service is injected in the library itself and outside of the library.

I was not sure where to put this issue. It is a replika of:
angular-architects/module-federation-plugin#433

No support for CSS

In this example (using main branch), if we add a styleUrls attribute in the shell app.component:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})

then ng serve shell does not work anymore.

I imagine there is something missing in the webpack config, but I do not know what.

Note: it only happens when we put 'auth-lib' in the shared mappings. So combination of CSS in app component + shared library breaks the build, but if we remove any of these, it works.

Automatic publicPath is not supported in this browser

i'm using nx workspace to generate my mfe and shell apps.

everyhting works fine on nx serve

but when i run nx build and serve my app i have this error :

Automatic publicPath is not supported in this browser
at main.77eeac4ea562bbf8.js:1:4808
at main.77eeac4ea562bbf8.js:1:4968

does anyone knows how to fix it ?

Dynamic Module Federation with Angular Universal

Thanks for your wonderful approach!!!. Right now I made it work with relative path to consume remote plugins.
Is there any way to load dynamically how its loading for browser module? Currently creating script tag using "document" object but same thing is not working when I do for Server side rendering. Any help would be more for us to proceed further.
Basically remote entry location may vary so the we cannot create static server bundle with relative path

Navigation between micro frontends with web-components does not work

While testing the ng-web-component-mixed branch, I found failures in the navigation between microfrontends, but the navigation from shell to microfrontend works correctly.

Initially to be able to navigate from the host to the microfrontends I had to share the remoteEntry to the WrapperComponent:
Screenshot 2024-03-13 at 4 50 19 PM

If the remoteEntry is not added, the following error is generated:
image

Well, with the remoteEntry added and trying to navigate between microfrontends, the URL changes but the component does not load, it seems that the navigation is trying to resolve to the origin microfrontend (I also made use of the connectRouter function that Mandred Steyer shares with us):

Screen Recording 2024-03-13 at 5 09 33 PM (3)

According to the analysis carried out, navigation from a microfrontend without web-component to the host or to another microfrontend with web-component works well, but navigation from a microfrontend with web-component to an external route (host or another microfrontend) does not work.

Has anyone been able to solve this problem with navigation to external routes using microfrontends with web-component? Thank you

Load Remote Module

Hi,
can I use the (loadRemoteModule) function to load a component from React project?
i try this :

const r = await loadRemoteModule({
type: 'script',
remoteName:'react',
remoteEntry: 'http://localhost:4204/remoteEntry.js',
exposedModule: './web-components'
});

const ref2 = this.reactContainer.createComponent(r.react);

but it not work,

or any way to dynamic load React component and pass parameter to it ?

Use service from remote microfrontend in shell app.

Hey everybody!
Don't judge for my english, please.
I need a help, because cannot find solution on my question.
I have two separate application, host and remote. It isn't monorepo, and I want use Services and Components from remote app in host app not as page in router.
For example in host app was created TestModule, and in template of this module I want use component from remote.
Is it possible? Or I should create monorepo and create some shared libs and declare it in tsconfig?

@angular-architects/module-federation version 14.3.0 unhandled exception occurred with webpack.config.js

When upgrading from @angular-architects/module-federation 14.2.3 to 14.3.0 I am seeing an error (pasted below) when trying to serve the application now.

Generating browser application bundles (phase: setup)...An unhandled exception occurred: The "path" argument must be of type string. Received an instance of Object See "C:\Users\...\AppData\Local\Temp\ng-1b2NOF\angular-errors.log" for further details.

Inside the error log it contains a stack that seems to point to our webpack.config.js as the cause of the error. I pasted what is inside the error log file below:

[error] TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Object
    at new NodeError (node:internal/errors:388:5)
    at validateString (node:internal/validators:114:11)
    at Object.join (node:path:429:7)
    at readConfiguredSecondaries (C:\code\client\node_modules\@angular-architects\module-federation\src\utils\share-utils.js:126:94)
    at getSecondaries (C:\code\client\node_modules\@angular-architects\module-federation\src\utils\share-utils.js:96:24)
    at Object.share (C:\code\client\node_modules\@angular-architects\module-federation\src\utils\share-utils.js:175:33)
    at shareAll (C:\code\client\node_modules\@angular-architects\module-federation\src\utils\share-utils.js:143:27)
    at Object.<anonymous> (C:\code\client\webpack.config.js:50:10)
    at Module._compile (node:internal/modules/cjs/loader:1112:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1166:10)
    at Module.load (node:internal/modules/cjs/loader:988:32)
    at Module._load (node:internal/modules/cjs/loader:834:12)
    at Module.require (node:internal/modules/cjs/loader:1012:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at transforms.<computed> [as webpackConfiguration] (C:\code\client\node_modules\ngx-build-plus\src\utils\index.js:56:38)
    at setup (C:\code\client\node_modules\@angular-devkit\build-angular\src\builders\dev-server\index.js:168:46)

I also pasted what I have in my webpack.config.js below. This error showed up with no changes being made to the webpack.config.js. It just started once we saw the upgrade to the @angular-architects/module-federation 14.3.0 version

const { share,  withModuleFederationPlugin, = require('@angular-architects/module-federation/webpack');

module.exports = withModuleFederationPlugin({
  remotes: { },

  shared: share({
    '@angular/core': { singleton: true, strictVersion: true, requiredVersion: 'auto', },
    '@angular/common': { singleton: true, strictVersion: true, requiredVersion: 'auto', },
    '@angular/common/http': { singleton: true, strictVersion: true, requiredVersion: 'auto', },
    '@angular/router': { singleton: true, strictVersion: true, requiredVersion: 'auto', },
    '@okta/okta-angular': { singleton: true, strictVersion: true, requiredVersion: 'auto', },
    '@okta/okta-auth-js': { singleton: true, strictVersion: true, requiredVersion: 'auto', },
  }),

  sharedMappings: ['auth-lib'],
});

I'm following this article because we want a solution where we don't need to hardcode the URL for the remoteEntry.js file.

To fix this error do we need to adjust how the webpack.config.js file is configured?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.