I have created a monorepo with Nx, featuring an Angular host and an Angular remote, and this works perfectly. I also have another monorepo with a React host and a React remote, which also work.
The issue arises when I try to load the remote React microfrontend into the Angular host.
I believe the problem lies within the React microfrontend because if I use the demo URL from Angular Architects URL, it works fine, but it doesn't work with my own URL that is running on a Live Server.
My app.routes.ts
import { Route } from '@angular/router';
import { loadRemoteModule } from '@nx/angular/mf';
import {
WebComponentWrapper,
WebComponentWrapperOptions,
} from '@angular-architects/module-federation-tools';
import { NotFoundError } from 'rxjs';
import { HomeComponent } from './home/home.component';
export const appRoutes: Route[] = [
{
path: '',
component: HomeComponent,
},
{
path: 'microfront-angular',
loadChildren: () =>
loadRemoteModule('microfront-angular', './Module').then(
(m) => m.RemoteEntryMfNg
),
},
{
path: 'microfront-react',
component: WebComponentWrapper,
data: {
// type: 'module',
remoteEntry:
'http://localhost:4301/remoteEntry.js',
remoteName: 'microfront-react',
elementName: 'microfront-react',
exposedModule: './Module',
} as WebComponentWrapperOptions,
},
{
path: 'react',
component: WebComponentWrapper,
data: {
remoteEntry:
'https://witty-wave-0a695f710.azurestaticapps.net/remoteEntry.js',
remoteName: 'react',
elementName: 'react-element',
exposedModule: './web-components',
} as WebComponentWrapperOptions,
},
{
path: 'vue',
component: WebComponentWrapper,
data: {
remoteEntry:
'https://mango-field-0d0778c10.azurestaticapps.net/remoteEntry.js',
remoteName: 'vue',
exposedModule: './web-components',
elementName: 'vue-element',
} as WebComponentWrapperOptions,
},
{
path: '**',
component: NotFoundError,
},
];
microfront-react dont'works but react works
This happens when try to access to microfront-react
But I still see remoteEntry.js from my build on Network...
The command to create my React microfront was:
nx g @nx/react:host host-react --remotes=microfront-react --style=scss
My apps/microfront-react/src/bootstrap.tsx
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './app/app';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
class MfeReact extends HTMLElement {
connectedCallback() {
console.log('web-component: bootstrap.tsx');
root.render(
<StrictMode>
<App />
</StrictMode>
);
}
}
customElements.define('microfront-react', MfeReact);
I think that maybe the fail is in the webpack.config.js and module-federation.config.js from my microfront-react
webpack.config.js
const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const { withModuleFederation } = require('@nx/react/module-federation');
const baseConfig = require('./module-federation.config');
const config = {
...baseConfig,
};
// Nx plugins for webpack to build config object from Nx options and context.
module.exports = composePlugins(
withNx(),
withReact(),
withModuleFederation(config)
);
module-federation.config.js
module.exports = {
name: 'microfront-react',
filename: 'remoteEntry.js',
exposes: {
'./web-components': './src/remote-entry.ts',
}
};
With that lines is enought to package correctly the remoteEntry.js?
Hope this helps to undertand my problem and thanks a lot to all people!!!
Source and thx:
I solved that!
Need to create a customElements on our bootstrap.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/app';
class Mfe4Element extends HTMLElement {
connectedCallback() {
console.log('http-mfe-react-element connectedCallback from DOM');
window.React = React;
ReactDOM.render(<App />, this);
}
disconnectedCallback() {
console.log('http-mfe-react-element disconnectedCallback from DOM');
}
}
customElements.define('http-mfe-react-element', Mfe4Element);
And create a proper config on webpack.config.js
const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const path = require('path');
const webpackEntry = [
path.resolve(__dirname, './src/index.html'),
path.resolve(__dirname, './src/main.tsx'),
];
const webpackOutput = {
publicPath: 'auto',
path: path.resolve(__dirname, '../../dist/apps/http-mfe-react'),
};
const webpackModuleFederationPlugin = new ModuleFederationPlugin({
name: 'http_mfe_react',
library: { type: 'var', name: 'http_mfe_react' },
filename: 'remoteEntry.js',
exposes: {
'./web-components': path.resolve(__dirname, './src/bootstrap.tsx'),
},
shared: ['react', 'react-dom'],
});
const ruleForTsx = {
test: /\.tsx$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: ['@babel/react', '@babel/env'],
},
},
],
};
const ruleForMisc = {
test: /\.(png|jpe?g|gif|woff|svg|eot|ttf)$/i,
use: ['file-loader'],
};
const ruleForHtml = {
test: /\.html$/,
use: ['file-loader?name=[name].[ext]'],
};
const ruleForStyles = {
test: /\.(s[ac]ss|\.css)$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
};
const webpackRules = [ruleForTsx, ruleForMisc, ruleForHtml, ruleForStyles];
const webpackExtensions = ['.tsx', '.ts', '.js'];
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
config.entry = webpackEntry;
config.output = webpackOutput;
config.plugins.push(webpackModuleFederationPlugin);
config.optimization.runtimeChunk = false; // Only needed to bypass a temporary bug
config.module.rules = webpackRules;
config.resolve.extensions = webpackExtensions;
return config;
});
Hope it helps someone.