Search code examples
angularmicro-frontendwebpack-module-federationangular-module-federation

Angular Native Federation "serve" command doesn't live reload the application


I've been trying to create a POC project to work with the Native Federation in a NX Monorepo, following a DDD approach structure.

The mfe1 application in the project has a dependency to its domain and feature-content existing in the repo and defined in the tsconfig.base.json.

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "ES2022",
    "module": "esnext",
    "lib": ["ES2022", "dom"],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "baseUrl": ".",
    "paths": {
      "@native-federation/mfe1/domain": ["libs/mfe1/domain/src/index.ts"],
      "@native-federation/mfe1/feature-content": ["libs/mfe1/feature-content/src/index.ts"]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

I use the component defined in the feature-content in app.component.ts of the mfe1

import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ContentComponent } from '@native-federation/mfe1/feature-content';

@Component({
  standalone: true,
  imports: [RouterModule, ContentComponent],
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
})
export class AppComponent {}

That just show some random content via the tag <mfe1-content /> in the app.component.html.

But when I make changes to files defined in the mfe1 feature-content the build seems to be run but it looks like the changes are not detected and the live reload is not triggered. I have to refresh the page manually to see the changes have effect.

image

I'm not sure if i'm doing something wrong but when I run the application without the Native Federation build process everything works as expected.

I've created a github repo of this project to try this out.

Repro steps

  1. Install dependencies: npm i
  2. Serve mfe1 with Native Federation: npx nx run mfe1:serve
  3. Change something in libs/mfe1/feature-content/src/lib/content.component.html

The live reload is not triggered, but it will if something is not buildable.

On the other hand npx nx run mfe1:serve-original --configuration=development take changes as expected.

Additional infos

package.json

{
  "name": "@native-federation/source",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {},
  "private": true,
  "dependencies": {
    "@angular-architects/native-federation": "^17.1.7",
    "@angular/animations": "17.3.3",
    "@angular/common": "17.3.3",
    "@angular/compiler": "17.3.3",
    "@angular/core": "17.3.3",
    "@angular/forms": "17.3.3",
    "@angular/platform-browser": "17.3.3",
    "@angular/platform-browser-dynamic": "17.3.3",
    "@angular/router": "17.3.3",
    "@ngrx/signals": "^17.1.1",
    "es-module-shims": "^1.9.0",
    "rxjs": "7.8.1",
    "tslib": "^2.3.0",
    "zone.js": "~0.14.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "17.3.3",
    "@angular-devkit/core": "17.3.3",
    "@angular-devkit/schematics": "17.3.3",
    "@angular-eslint/eslint-plugin": "17.3.0",
    "@angular-eslint/eslint-plugin-template": "17.3.0",
    "@angular-eslint/template-parser": "17.3.0",
    "@angular/cli": "~17.3.0",
    "@angular/compiler-cli": "17.3.3",
    "@angular/language-service": "17.3.3",
    "@nx/angular": "18.2.3",
    "@nx/eslint": "18.2.3",
    "@nx/eslint-plugin": "18.2.3",
    "@nx/jest": "18.2.3",
    "@nx/js": "18.2.3",
    "@nx/workspace": "18.2.3",
    "@schematics/angular": "17.3.3",
    "@swc-node/register": "1.8.0",
    "@swc/core": "1.3.101",
    "@swc/helpers": "0.5.3",
    "@types/jest": "29.4.4",
    "@types/node": "^18.16.9",
    "@typescript-eslint/eslint-plugin": "7.3.0",
    "@typescript-eslint/parser": "7.3.0",
    "autoprefixer": "^10.4.0",
    "eslint": "8.57.0",
    "eslint-config-prettier": "9.1.0",
    "jest": "29.5.0",
    "jest-environment-jsdom": "29.5.0",
    "jest-preset-angular": "14.0.3",
    "jsonc-eslint-parser": "^2.1.0",
    "ng-packagr": "17.3.0",
    "nx": "18.2.3",
    "postcss": "^8.4.21",
    "postcss-url": "10.1.3",
    "prettier": "2.8.3",
    "ts-jest": "^29.1.0",
    "ts-node": "10.9.1",
    "typescript": "5.4.4"
  }
}

mfe1 project.json

{
  "name": "mfe1",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "projectType": "application",
  "prefix": "app",
  "sourceRoot": "apps/mfe1/src",
  "tags": [],
  "targets": {
    "build": {
      "executor": "@angular-architects/native-federation:build",
      "options": {},
      "configurations": {
        "production": {
          "target": "mfe1:esbuild:production"
        },
        "development": {
          "target": "mfe1:esbuild:development",
          "dev": true
        }
      },
      "defaultConfiguration": "production"
    },
    "serve": {
      "executor": "@angular-architects/native-federation:build",
      "options": {
        "target": "mfe1:serve-original:development",
        "rebuildDelay": 0,
        "dev": true,
        "port": 0
      }
    },
    "extract-i18n": {
      "executor": "@angular-devkit/build-angular:extract-i18n",
      "options": {
        "buildTarget": "mfe1:build"
      }
    },
    "lint": {
      "executor": "@nx/eslint:lint"
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
      "options": {
        "jestConfig": "apps/mfe1/jest.config.ts"
      }
    },
    "esbuild": {
      "executor": "@angular-devkit/build-angular:application",
      "outputs": ["{options.outputPath}"],
      "options": {
        "outputPath": "dist/apps/mfe1",
        "index": "apps/mfe1/src/index.html",
        "browser": "apps/mfe1/src/main.ts",
        "polyfills": ["zone.js", "es-module-shims"],
        "tsConfig": "apps/mfe1/tsconfig.app.json",
        "inlineStyleLanguage": "scss",
        "assets": ["apps/mfe1/src/favicon.ico", "apps/mfe1/src/assets"],
        "styles": ["apps/mfe1/src/styles.scss"],
        "scripts": []
      },
      "configurations": {
        "production": {
          "budgets": [
            {
              "type": "initial",
              "maximumWarning": "500kb",
              "maximumError": "1mb"
            },
            {
              "type": "anyComponentStyle",
              "maximumWarning": "2kb",
              "maximumError": "4kb"
            }
          ],
          "outputHashing": "all"
        },
        "development": {
          "optimization": false,
          "extractLicenses": false,
          "sourceMap": true,
          "namedChunks": true
        }
      },
      "defaultConfiguration": "production"
    },
    "serve-original": {
      "executor": "@angular-devkit/build-angular:dev-server",
      "options": {
        "port": 4201
      },
      "configurations": {
        "production": {
          "buildTarget": "mfe1:esbuild:production"
        },
        "development": {
          "buildTarget": "mfe1:esbuild:development"
        }
      },
      "defaultConfiguration": "development"
    }
  }
}

Solution

  • I found out the solution, there are 2 possible options

    1. Develop/debug the Microfrontend application (MF1) starting the dev server without Module Federation (nx run project-name:server-original)

    2. In case there's no need to share the library across the microfrontend, add the library name to the skip list in the federation.config.js

    skip: [
        'test-lib',
        'rxjs/ajax',
        'rxjs/fetch',
        'rxjs/testing',
        'rxjs/webSocket',
        '@native-federation/mfe1/domain',
        '@native-federation/mfe1/feature-content',
        // Add further packages you don't need at runtime
    
      ]