angular-architects / module-federation-plugin

MIT License
683 stars 184 forks source link

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

Closed nicdenny closed 2 weeks ago

nicdenny commented 1 month ago

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

Install dependencies: npm i

Serve mfe1 with Native Federation: npx nx run mfe1:serve

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"
    }
  }
}
manfredsteyer commented 1 month ago

Yes, this is how it currently works. If you change MFE1, MFE1 is live-reloaded, but not the shell. There, you need to press F5

nicdenny commented 1 month ago

Yes, this is how it currently works. If you change MFE1, MFE1 is live-reloaded, but not the shell. There, you need to press F5

At the moment I'm stucked in a point where I don't even serve the shell yet, I'm just serving the MFE1 and I try to change some files which the MFE1 depends on, defined in the tsconfig.json, but no reaload is happening.

With module federation I could serve mfe applications and work on them by in "standalone". It looks like the serve doesn't detect changes happening in files defined as dependency in the tsconfig.

manfredsteyer commented 2 weeks ago

Hi,

Ah, now I got you. It's the combination of an app and local, repo-internal libs. If you change the lib you need to press F5 by hand.

Yes, that's currently the case because, by default, all libs are shared and can hence be loaded from a different remote.

There are several options for bypassing this behavior:

a) when developing/ debugging in standalone mode, start the dev server without federation, e.g. nx run project-name:server-original b) If you don't need to share your lib, e.g. because its just used in your app, add the lib name to the skip list in your federation.config.js

skip: [
    'test-lib',
    'rxjs/ajax',
    'rxjs/fetch',
    'rxjs/testing',
    'rxjs/webSocket',
    // Add further packages you don't need at runtime
  ]`
nicdenny commented 2 weeks ago

Ok option B is what i was looking for and works fine! I couldn't find this information and this behaviour unluckily was not specified anywhere.

Thanks!