Microfrontend Configuration with Webpack

Overview

This document aims to explain the details related to Microfrontend’s configuration. It is required to utilize Webpack technology to expose Microfrontend’s content via a remote entry file, so the Shell and other Microfrontends (e.g. via Slot Components) are able to load and use it. Webpack is a powerful module bundler for JavaScript, which takes modules with dependencies and generates static assets representing those modules. Since Webpack version five, Module Federation technology, integrated with Webpack, is used to achieve these goals.

For Angular based Microfrontends, consider using @angular-architects/module-federation package. This package is used internally by OneCX to load remote modules and is recommended for Angular based applications.

Microfrontend Configuration with Webpack Explained

Each Microfrontend integrated with OneCX needs to have a configuration file for Webpack (usually called webpack.config.js), utilizing Module Federation to expose remote modules. For both module and Remote Components, a remote module needs to be exposed.

Important Elements of Webpack Configuration

Webpack configuration is going to contain important configuration describing the Microfrontend. This configuration is used by Webpack to create and expose a remote entry file containing the whole code required to load remote modules in other Applications using Module Federation.

Below, a Webpack configuration example of an Angular based Microfrontend is presented.

const { ModifyEntryPlugin } = require('@angular-architects/module-federation/src/utils/modify-entry-plugin')
const { share, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack')
const config = withModuleFederationPlugin({
  name: 'example-ui',
  filename: 'remoteEntry.js',
  exposes: {
    './ExampleModule': './src/main.ts',
    './ExampleComponent': './src/remotes/example/example.component.main.ts'
  },
  shared: share({
    '@angular/core': { requiredVersion: 'auto', includeSecondaries: true },
    '@angular/forms': { requiredVersion: 'auto', includeSecondaries: true },
    '@angular/common': { requiredVersion: 'auto', includeSecondaries: { skip: ['@angular/common/http/testing'] } },
    '@angular/common/http': { requiredVersion: 'auto', includeSecondaries: true },
    '@angular/platform-browser': { requiredVersion: 'auto', includeSecondaries: true },
    '@angular/router': { requiredVersion: 'auto', includeSecondaries: true },
    '@ngx-translate/core': { requiredVersion: 'auto' },
    primeng: { requiredVersion: 'auto', includeSecondaries: true },
    rxjs: { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/accelerator': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/angular-accelerator': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/angular-auth': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/angular-integration-interface': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/angular-remote-components': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/angular-testing': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/angular-webcomponents': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/integration-interface': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/keycloak-auth': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/ngrx-accelerator': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/portal-integration-angular': { requiredVersion: 'auto', includeSecondaries: true },
    '@onecx/portal-layout-styles': { requiredVersion: 'auto', includeSecondaries: true }
  }),

  sharedMappings: []
})

module.exports = config

const plugins = config.plugins.filter((plugin) => !(plugin instanceof ModifyEntryPlugin))

module.exports = {
  ...config,
  plugins,
  output: { uniqueName: 'example-ui', publicPath: 'auto' },
  experiments: { ...config.experiments, topLevelAwait: true },
  optimization: { runtimeChunk: false, splitChunks: false }
}

The most important elements of Webpack configurations are:

Exposed content

Each Microfrontend can expose multiple modules and Remote Components. Every separate module and Remote Component needs to be defined as a separate, unique entry representing a remote module. Each entry is a Key-Value Pair:

Key

Name of the exposed module.

Value

File to be used for module bootstrap.

In the Angular example, there are two remote modules defined:

  • ExampleModule - Microfrontend’s remote module pointing to Microfrontend’s main.ts file.

  • ExampleComponent - Remote Component’s remote module pointing to component’s main.ts file.

Module Federation considers any exposed content as a remote module. In the OneCX context, Microfrontends expose two types of content, being modules and Remote Components.
Shared resources

Each Microfrontend can define packages that can be shared with others. Every Microfrontend can specify what packages and with what versions are allowed to be shared. In the OneCX context, it is important to ensure the correct package sharing configuration dependent on the used packages used functionalities within the Microfrontend’s exposed content.

In the Angular example, there are multiple packages shared. All of them are related to OneCX. For the detailed explanation on package sharing with OneCX, please refer here. For even more information on how package sharing works, please refer here.

Remote name

The unique name of the Microfrontend.

Remote entry file name

The name of the remote entry file (usually remoteEntry.js).

  • Content bootstrapping - based on the content type and expose method bootstrap mechanism needs to be adjusted.

  • Package sharing - based on the content of the Microfrontend different packages are required or should be considered for sharing.