# Angular CLI Setup This guide explains how to integrate Module Federation with Angular CLI. The `@angular-architects/module-federation` plugin is used to assist with this integration. ## Prerequisites - **Angular CLI**: Version 10 or higher. - **Plugin Installation**: Install the `@angular-architects/module-federation` plugin. ## Installation To start, configure the Angular CLI to use Module Federation during the build phase. A custom builder is needed to unlock Module Federation's potential. The `@angular-architects/module-federation` package provides this custom builder. Use the ng add command to incorporate it into your projects: **Angular CLI** ```bash ng add @angular-architects/module-federation --project shell --port 4200 --type host ng add @angular-architects/module-federation --project mfe1 --port 4201 --type remote ``` **Nx Cli** For Nx users, the procedure is slightly different. ```bash npm i @angular-architects/module-federation -D ng g @angular-architects/module-federation:init --project shell --port 4200 --type host ng g @angular-architects/module-federation:init --project mfe1 --port 4201 --type remote ```
The `--type` argument, introduced in version 14.3, ensures that only the necessary configuration is generated.
## Shell (Host) Configuration The Shell (Host) is crucial for Module Federation integration. This section configures the Shell to support lazy-loading of a `FlightModule` through routing.
### Routing Configuration Start by defining the application routes, specifying a lazy-loaded `FlightModule` using a virtual path: ```javascript export const APP_ROUTES: Routes = [ { path: '', component: HomeComponent, pathMatch: 'full' }, { path: 'flights', loadChildren: () => import('mfe1/Module').then(m => m.FlightsModule) }, ]; ``` In this configuration, the path `'mfe1/Module'` is a virtual representation, indicating it doesn't physically exist within the Shell application. Instead, it's a reference to a module in a separate project. ### TypeScript Typing Create a type definition for the virtual path: ```typescript // decl.d.ts declare module 'mfe1/Module'; ``` This helps the TypeScript compiler understand the virtual path. ### Webpack Configuration Instruct Webpack to resolve all paths prefixed with `mfe1` to a remote project. This is done in the `webpack.config.js` file: ```javascript const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ remotes: { "mfe1": "http://localhost:4201/remoteEntry.js", }, shared: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }), }, }); ``` In the `remotes` section, the path `mfe1` is mapped to the remote micro-frontend's entry point. This entry point, generated by Webpack, contains essential information for interacting with the micro-frontend.
For development, hardcoding the remote entry's URL is enough. However, a dynamic approach is necessary for production. The concept of dynamic remotes is further explored in a dedicated documentation page on Dynamic Remotes. - The `shared` property specifies the npm packages to be shared between the Shell and the micro-frontend(s). Using the `shareAll` helper method, all dependencies listed in your `package.json` are shared. While this facilitates a quick setup, it may lead to an excessive number of shared dependencies, which could be a concern for optimization. - The combination of `singleton: true` and `strictVersion: true` settings instructs Webpack to throw a runtime error if there is a version mismatch between the Shell and the micro-frontend(s). Changing `strictVersion` to `false` would instead result in a runtime warning. - The `requiredVersion: 'auto'` option, provided by the `@angular-architects/module-federation` plugin, automatically determines the version from your `package.json`, helping to prevent version-related issues.
## Configuring the Remote The Micro-frontend, also known as the Remote in Module Federation, has a structure similar to a standard Angular app. It has specific routes in the `AppModule` and a `FlightsModule` for flight-related tasks. This section explains how to smoothly load the `FlightsModule` into the Shell (Host).
### Route Definition Establish the basic routes within the `AppModule`: ```typescript export const APP_ROUTES: Routes = [ { path: '', component: HomeComponent, pathMatch: 'full'} ]; ``` This simple routing setup navigates to a `HomeComponent` when the application is accessed. ### Module Creation Create a `FlightsModule` to handle flight-related operations: ```typescript @NgModule({ imports: [ CommonModule, RouterModule.forChild(FLIGHTS_ROUTES) ], declarations: [ FlightsSearchComponent ] }) export class FlightsModule { } ``` This module contains a route to a `FlightsSearchComponent` defined as follows: ```typescript export const FLIGHTS_ROUTES: Routes = [ { path: 'flights-search', component: FlightsSearchComponent } ]; ``` ### Exposing Modules via Webpack Configuration To enable the loading of `FlightsModule` into the Shell, expose it through the Remote's Webpack configuration: ```javascript 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: { ...shareAll({ singleton: true, strictVersion: true, requiredVersion: 'auto' }), }, }); ```
In this configuration: - The `name` property identifies the micro-frontend as `mfe1`. - The `exposes` property signifies the exposure of `FlightsModule` under the public name `Module`, allowing its consumption by the Shell. - The `shared` property lists the libraries to be shared with the Shell, using the `shareAll` method to share all dependencies found in your `package.json`. The `singleton: true` and `strictVersion: true` properties ensure that a single version of shared libraries is used, and a runtime error is triggered in case of version incompatibility, respectively.
## Starting the Applications Having set up the Shell (Host) and Micro-frontend (Remote), it's time to test the configuration to ensure the seamless integration of Module Federation. To start the Shell and Micro-frontend, use the following commands: ```bash ng serve shell -o ng serve mfe1 -o ``` Navigate to the Flights section in the Shell to see the Micro-frontend being dynamically loaded. :::tip The plugin installs an npm script `run:all` during the `ng-add` and `init` schematics, allowing for simultaneous serving of all applications: ```bash npm run run:all # or npm run run:all shell mfe1 ``` ::: ## Optimizing Dependency Sharing The initial setup with `shareAll` is simple and functional, but it can result in the creation of unnecessarily large shared bundles. To manage shared dependencies more effectively, consider switching from `shareAll` to using the `share` helper. This provides finer control over which dependencies are shared: ```javascript // Replace shareAll with share: const { share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack'); module.exports = withModuleFederationPlugin({ // Specify the packages to share: 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' }, }) }); ``` In this configuration, the `share` helper allows for explicit sharing of selected packages, enabling a more optimized bundle sharing and potentially reducing load times.