Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to import a react remote into an angular host #3116

Open
5 tasks done
lfaure75 opened this issue Oct 24, 2024 · 8 comments
Open
5 tasks done

Try to import a react remote into an angular host #3116

lfaure75 opened this issue Oct 24, 2024 · 8 comments

Comments

@lfaure75
Copy link

Describe the bug

Hello,
I am trying to import a react VITE remote into an angular host built with NX.

When I load the host I have a blank page with the following error in the console:

Uncaught SyntaxError: Cannot use 'import.meta' outside a module (at styles.js:8124:29)
Uncaught TypeError: Failed to resolve module specifier "reactNewViteRemote@http://localhost:4173/assets/remoteReactViteManifest.json". Relative references must start with either "/", "./", or "../".

My react remote is built with VITE:
repo: https://github.com/lfaure75/reactViteRemote

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import {federation} from "@module-federation/vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'reactNewViteRemote',
      getPublicPath: 'return "http://localhost:4173/"',
      manifest : {
        filePath : './assets/',
        fileName: 'remoteReactViteManifest.json'
      },
      exposes: {
        './Module': './src/App.jsx',
      },
      shared: ['react', 'react-dom']
    }),
  ],
  base: 'http://localhost:4173/',
  server: {
    origin: 'http://localhost:4173/'
  },
  build: {
    target: 'esnext',
    minify: false,
    cssCodeSplit: false,
  },
})

For my host, I created a angular component in order to wrap my react remote and update the routes:
repo: https://github.com/lfaure75/nx-angular-workspace

my wrapped component:

import { Component, ElementRef, OnDestroy, ViewChild } from '@angular/core';
import * as ReactDOM from 'react-dom/client';
import { createElement } from 'react';
import MyReactComponent from 'reactNewViteRemote/Module';

@Component({
  selector: 'app-react-wrapper',
  standalone: true,
  template: '<div id="react-root"></div>'
})
export class ReactWrapperComponent implements OnDestroy {
  private root: ReactDOM.Root;

  constructor(private elRef: ElementRef) {
    this.root = ReactDOM.createRoot(this.elRef.nativeElement.querySelector('#react-root'));
    this.root.render(createElement(MyReactComponent));
  }

  ngOnDestroy(): void {
    if (this.root) {
      this.root.unmount();
    }
  }
}

the routes:

import { NxWelcomeComponent } from './nx-welcome.component';
import { Route } from '@angular/router';

export const appRoutes: Route[] = [
  {
    path: 'angularRemote',
    loadChildren: () =>
      import('angularRemote/Routes').then((m) => m.remoteRoutes),
  },
  {
    path: 'reactNewViteRemote',
    loadChildren: () =>
      import('./react-wrapper.component').then(m => m.ReactWrapperComponent),
  },
  {
    path: '',
    component: NxWelcomeComponent,
  },
];

module federation:

import { ModuleFederationConfig } from '@nx/webpack';

const config: ModuleFederationConfig = {
  name: 'angularHost',
  remotes: [
    'angularRemote',
    ['reactNewViteRemote', 'reactNewViteRemote@http://localhost:4173/assets/remoteReactViteManifest.json']
  ],
};

export default config;

I have the package "@module-federation/enhanced": "0.6.11"

Many thanks in advance for your help

Reproduction

https://github.com/lfaure75/nx-angular-workspace

Used Package Manager

yarn

System Info

System:
    OS: Linux 6.8 Ubuntu 22.04.5 LTS 22.04.5 LTS (Jammy Jellyfish)
    CPU: (12) x64 Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
    Memory: 19.81 GB / 30.97 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 18.20.4 - ~/.nvm/versions/node/v18.20.4/bin/node
    Yarn: 1.22.22 - ~/.nvm/versions/node/v18.20.4/bin/yarn
    npm: 10.7.0 - ~/.nvm/versions/node/v18.20.4/bin/npm
  Browsers:
    Chrome: 130.0.6723.58

Validations

@ScriptedAlchemy
Copy link
Member

remote configuration looks wrong. esm remotes are usually {key:url} not {key: global@url}

@lfaure75
Copy link
Author

Thank you for your help. I changed the conf but now I have following error:

styles.js:8124 Uncaught SyntaxError: Cannot use 'import.meta' outside a module
remoteReactViteManifest.json:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec.

I thought we could use a manifest instead of js file.

conf in host

import { ModuleFederationConfig } from '@nx/webpack';

const config: ModuleFederationConfig = {
  name: 'angularHost',
  remotes: [
    'angularRemote',
    ['reactNewViteRemote', 'http://localhost:4173/assets/remoteReactViteManifest.json']
  ],
};

export default config;

@ScriptedAlchemy
Copy link
Member

Make sure remote type is esm and output is esm etc?

@lfaure75
Copy link
Author

in the NX host angular project, I forced the resolution of @module-federation/enhanced to version 0.6.11 as "@nx/angular": "20.0.3" uses @module-federation/enhanced 0.6.6 and I was told that esm was took in charge from 0.6.8

For my remote react / vite app, I have the following vite.config.js conf (https://github.com/lfaure75/reactViteRemote/blob/master/vite.config.js)

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import {federation} from "@module-federation/vite";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    federation({
      name: 'reactNewViteRemote',
      getPublicPath: 'return "http://localhost:4173/"',
      manifest : {
        filePath : './assets/',
        fileName: 'remoteReactViteManifest.json'
      },
      exposes: {
        './Module': './src/App.jsx',
      },
      shared: ['react', 'react-dom']
    }),
  ],
  base: 'http://localhost:4173/',
  server: {
    origin: 'http://localhost:4173/'
  },
  build: {
    target: 'esnext',
    minify: false,
    cssCodeSplit: false,
  },
})

And for the host config I have for tsconfig.base.json (https://github.com/lfaure75/nx-angular-workspace/blob/main/tsconfig.base.json)

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "module": "esnext",
    "lib": ["es2020", "dom"],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "baseUrl": ".",
    "paths": {
      "angularRemote/Routes": [
        "apps/angularRemote/src/app/remote-entry/entry.routes.ts"
      ]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

Is there someting I missed ?
Thanks in advance for your help

@lfaure75
Copy link
Author

I changed my way to import my react remote and I am trying to use dynamic module federation.

But do we have a way to indicate to my angular host that my react remote is exposing a manifest file dans not a js ?

When loading my remote, it's adding by default remoteEntry.mjs at the end of my url exposed in module-federation.manifest.json. So having a 404 error: http://localhost:4173/assets/remoteReactViteManifest.json/remoteEntry.mjs

So in my module-federation.manifest.json

{
  "reactNewViteRemote": "http://localhost:4173/assets/remoteReactViteManifest.json"
}

in the main.ts

import { setRemoteDefinitions } from '@nx/angular/mf';

fetch('/module-federation.manifest.json')
  .then((res) => res.json())
  .then((definitions) => setRemoteDefinitions(definitions))
  .then(() => import('./bootstrap').catch((err) => console.error(err)));

in the app.routes.ts

import { NxWelcomeComponent } from './nx-welcome.component';
import { Route } from '@angular/router';
import { loadRemoteModule } from '@nx/angular/mf';

export const appRoutes: Route[] = [
  {
    path: 'angularRemote',
    loadChildren: () =>
      import('angularRemote/Routes').then((m) => m.remoteRoutes),
  },
  {
    path: 'reactNewViteRemote',
    loadChildren: () =>
      loadRemoteModule('reactNewViteRemote', './react-wrapper.component').then((m) => m.ReactWrapperComponent),
  },
  {
    path: '',
    component: NxWelcomeComponent,
  },
];

@ScriptedAlchemy
Copy link
Member

@Coly010 this seems like they are doing it wrong. Anything you can advise on angular?

@Coly010
Copy link
Contributor

Coly010 commented Oct 25, 2024

Yeah it’s being done wrong.
can’t route to a react component.
Need to use Web components and do the loadRemote slightly differently.

see example here: https://github.com/Coly010/advanced-module-federation-examples/tree/main/examples/angular-react-mfes/react-in-angular

@ScriptedAlchemy
Copy link
Member

Web component or something like a framework bridge like we mention on our documentation site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants