DEV Community

Ruben
Ruben

Posted on

How to load an MFE module from a shell app (Using Angular + Webpack + Module Federation)?

Hi everyone, I have question for you. Do you know how load an MFE (micro front end) remote app, as a module from a shell app? when I try to do so there is an infinite loop. and the module from the MFE does not show. Any tips or ideas on how to accomplish this issue? (Using Angular + Webpack + Module Federation)

// webpack.config.js--SHELL APP 

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const path = require("path");

module.exports = {
    output: {
        uniqueName: "portalWebApp",
        publicPath: "auto",
        scriptType: "text/javascript",
    },
    optimization: {
        runtimeChunk: false,
    },
    resolve: {
        alias: {},
    },
    module: {
        rules: [{
            test: /\.html$/,
            use: ["html-loader"],
        }, ],
    },
    experiments: {
        outputModule: true,
    },
    plugins: [
        new ModuleFederationPlugin({
            // For remotes (please Add this 5 Line)
            name: "portalWebApp",
            filename: "remoteEntry.js",
            remotes: {
                astrPortFolioMasterWebApp: "astrPortFolioMasterWebApp@http://localhost:3002/remoteEntry.js",
                mfe1: "mfe1@http://localhost:4001/remoteEntry.js",
            },

            shared: {
                "@angular/core": {
                    singleton: true,
                    strictVersion: true,
                    eager: true
                },
                "@angular/common/": {
                    singleton: true,
                    strictVersion: true,
                    eager: true,
                },
                "@angular/router": {
                    singleton: true,
                    strictVersion: true,
                    eager: true,
                }
                // react: { singleton: true, eager: true },
                // "react-dom": { singleton: true, eager: true },
            },
        }),
    ],
};

// webpack.config.js--SHELL APP--END

// SHELL APP - Route

import { loadRemoteModule } from '@angular-architects/module-federation';
import { WebComponentWrapper, WebComponentWrapperOptions } from '@angular-architects/module-federation-tools';
import { Routes } from '@angular/router';
import { MsalGuard } from '@azure/msal-angular';
// import { MsalGuard } from '@azure/msal-angular';
// import { UserNotAllowedComponent } from './layout/user-not-allowed/user-not-allowed.component';
// import { ServerErrorComponent } from './layout/server-error/server-error.component';

export const routes: Routes = [{
        path: '',
        pathMatch: 'full',
        loadChildren: () =>
            import('./content-providers/content-provider.module').then(
                (m) => m.ContentProviderModule
            ),
        canActivate: [MsalGuard],
    },
    {
        path: 'mfe1',
        loadChildren: () => {
            return loadRemoteModule({
                    type: 'module',
                    remoteEntry: 'http://localhost:4001/remoteEntry.js',
                    exposedModule: './OrderModule',
                })
                .then((m) => m.OrderModule)
                .catch((e) => console.log(e));
        },
    },
    {
        path: 'astrPortFolioMasterWebApp',
        loadChildren: () => {
            return loadRemoteModule({
                    type: 'module',
                    remoteEntry: 'http://localhost:3002/remoteEntry.js',
                    exposedModule: './astrPortFolioMasterWebAppModule',
                })
                .then((m) => m.astrPortFolioMasterWebAppModule)
                .catch((e) => console.log(e));
        },
    },
    {
        path: 'angular1',
        component: WebComponentWrapper,
        data: {
            remoteEntry: 'https://nice-grass-018f7d910.azurestaticapps.net/remoteEntry.js',
            remoteName: 'angular1',
            exposedModule: './web-components',
            elementName: 'angular1-element',
        }
        as WebComponentWrapperOptions,
    },
    {
        path: 'react1',
        component: WebComponentWrapper,
        data: {
            remoteEntry: 'https://witty-wave-0a695f710.azurestaticapps.net/remoteEntry.js',
            remoteName: 'react',
            exposedModule: './web-components',
            elementName: 'react-element',
        }
        as WebComponentWrapperOptions,
    },
    {
        path: 'vue',
        component: WebComponentWrapper,
        data: {
            remoteEntry: 'https://mango-field-0d0778c10.azurestaticapps.net/remoteEntry.js',
            remoteName: 'vue',
            exposedModule: './web-components',
            elementName: 'vue-element',
        }
        as WebComponentWrapperOptions,
    },
];


// SHELL APP - Route 

// webpack.config.js--MFE / REMOTE APP 

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
const { shareAll, SharedMappings } = require("@angular-architects/module-federation/webpack");
const Path = require("path");
const sharedMappings = new SharedMappings();

sharedMappings.register(
    Path.join(__dirname, 'tsconfig.json'), []
)
module.exports = {
    output: {
        uniqueName: "astrPortFolioMasterWebApp",
        publicPath: "auto",
        scriptType: "text/javascript",
    },
    optimization: {
        runtimeChunk: false
    },
    resolve: {
        alias: {
            ...sharedMappings.getAliases(),
        }
    },
    experiments: {
        outputModule: true
    },
    plugins: [
        new ModuleFederationPlugin({

            // For remotes (please Add this 5 Line)
            name: "astrPortFolioMasterWebApp",
            filename: "remoteEntry.js",
            exposes: {
                './astrPortFolioMasterWebAppModule': './src/app/layout/layout.module.ts',
            },

            shared: {
                "@angular/core": {
                    singleton: true,
                    strictVersion: true,
                    eager: true
                },
                "@angular/common/": {
                    singleton: true,
                    strictVersion: true,
                    eager: true
                },
                "@angular/router": {
                    singleton: true,
                    strictVersion: true,
                    eager: true
                },
            }
        }),
        sharedMappings.getPlugin()
    ]
};

// webpack.config.js--MFE / REMOTE APP - END 

// MFE - Remote - Route 

import { Routes } from '@angular/router';
import { MsalGuard } from '@azure/msal-angular';
import { UserNotAllowedComponent } from './layout/user-not-allowed/user-not-allowed.component';
import { ServerErrorComponent } from './layout/server-error/server-error.component';

export const routes: Routes = [
    // {
    //   path: '',
    //   pathMatch: 'full',
    //   redirectTo: 'portfolio'
    // },
    {
        path: 'portfolio',
        loadChildren: () =>
            import('./layout/layout.module').then((m) => m.MainLayoutModule),
        canActivate: [MsalGuard]
    },

    {
        path: 'usernotallowed',
        component: UserNotAllowedComponent,
    },

    {
        path: '**',
        component: ServerErrorComponent,
    },
];

// MFE - Remote - Route - END 
Enter fullscreen mode Exit fullscreen mode

Billboard image

Monitoring as code

With Checkly, you can use Playwright tests and Javascript to monitor end-to-end scenarios in your NextJS, Astro, Remix, or other application.

Get started now!

Top comments (0)

Heroku

This site is powered by Heroku

Heroku was created by developers, for developers. Get started today and find out why Heroku has been the platform of choice for brands like DEV for over a decade.

Sign Up

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay