diff --git a/modulefederation/apps/react/react-remote/src/assets/manifest.json b/modulefederation/apps/react/react-remote/src/assets/manifest.json index 265dc4ba..3a8106c2 100644 --- a/modulefederation/apps/react/react-remote/src/assets/manifest.json +++ b/modulefederation/apps/react/react-remote/src/assets/manifest.json @@ -3,13 +3,14 @@ "type": "microfrontend", "version": "0.0.0", "commitHash": "7e9a2e916fd1da6786caac41b0fe6b838fbbb12d", - "module": "???", - "stack": "web-component", + "module": "", + "remoteEntryName": "remoteEntry.js", + "stack": "react", "features": [ { "id": "", "label": "React", - "component": "???", + "component": "", "tags": ["navigation"], "visibility": ["public", "private"] } diff --git a/modulefederation/libs/portal/src/lib/deployment/deployment-model.ts b/modulefederation/libs/portal/src/lib/deployment/deployment-model.ts index 49c2b09f..7550e3e5 100644 --- a/modulefederation/libs/portal/src/lib/deployment/deployment-model.ts +++ b/modulefederation/libs/portal/src/lib/deployment/deployment-model.ts @@ -5,12 +5,13 @@ import { FolderData } from "../folder.decorator"; export interface Manifest extends ModuleMetadata { name : string, type: "shell" | "microfrontend", - stack?: "angular" | "react" | " vue" | "web-component", + stack?: string, version : string, enabled? : boolean, health? : string, commitHash : string, remoteEntry? : string, + remoteEntryName? : string, healthCheck?: string, module : string, features : FeatureConfig[], diff --git a/modulefederation/libs/portal/src/lib/federation/react/react-route-builder.ts b/modulefederation/libs/portal/src/lib/federation/react/react-route-builder.ts index 565d896a..b776032c 100644 --- a/modulefederation/libs/portal/src/lib/federation/react/react-route-builder.ts +++ b/modulefederation/libs/portal/src/lib/federation/react/react-route-builder.ts @@ -19,7 +19,6 @@ export class ReactRouteBuilder implements RouteBuilder { build(manifest: Manifest, route: Route) : void { route.component = ReactComponentWrapper - route.data!["module"] = manifest.module - route.data!["elementName"] = manifest.module // ?? + route.data!["module"] = manifest.name } } diff --git a/modulefederation/libs/portal/src/lib/federation/react/react-wrapper.ts b/modulefederation/libs/portal/src/lib/federation/react/react-wrapper.ts index 349a091e..828cd03b 100644 --- a/modulefederation/libs/portal/src/lib/federation/react/react-wrapper.ts +++ b/modulefederation/libs/portal/src/lib/federation/react/react-wrapper.ts @@ -1,15 +1,49 @@ -import { AfterContentInit, Component } from "@angular/core"; +import { Component, AfterContentInit, ViewChild, ElementRef, OnDestroy } from '@angular/core'; +import { loadRemoteModule } from "@nx/angular/mf"; + +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { ActivatedRoute } from "@angular/router"; +import { Root } from 'react-dom/client'; + +export type ReactWrapperOptions = { + module: string +}; @Component({ - selector: 'react-component-wrapper', - template: '
', - }) - // eslint-disable-next-line @angular-eslint/component-class-suffix - export class ReactComponentWrapper implements AfterContentInit { - // implement AfterContentInit - - ngAfterContentInit(): void { - // TODO - console.log() - } - } \ No newline at end of file + selector: 'react-component-wrapper', + template: '' +}) +export class ReactComponentWrapper implements AfterContentInit, OnDestroy { + // instance data + + @ViewChild('container', { static: true }) container!: ElementRef; + + options! : ReactWrapperOptions + root!: Root + + // constructor + + constructor(private route: ActivatedRoute) {} + + // implement AfterContentInit + + async ngAfterContentInit() { + this.options = this.route.snapshot.data as ReactWrapperOptions + + const module = await loadRemoteModule(this.options.module, "./Module"); + + const component = module.default; // Assuming default export + const reactElement = React.createElement(component); // args? + + this.root = ReactDOM.createRoot(this.container.nativeElement); + + this.root.render(reactElement); // Render using 'createRoot' + } + + // implement OnDestroy + + ngOnDestroy(): void { + this.root.unmount() + } +} diff --git a/modulefederation/libs/portal/src/lib/federation/route-builder.ts b/modulefederation/libs/portal/src/lib/federation/route-builder.ts index 82e7caec..e63066b0 100644 --- a/modulefederation/libs/portal/src/lib/federation/route-builder.ts +++ b/modulefederation/libs/portal/src/lib/federation/route-builder.ts @@ -28,7 +28,12 @@ export class RouteBuilderManager { // public build(manifest: Manifest, route: Route) : void { - this.find(manifest.stack || "angular").build(manifest, route) + let stack = manifest.stack || "angular" + const colon = stack.indexOf(":") + if ( colon > 0) + stack = stack.substring(0, colon) + + this.find(stack).build(manifest, route) } register(type: string, builder: RouteBuilder) { diff --git a/modulefederation/libs/portal/src/lib/federation/web-component/web-component-route-builder.ts b/modulefederation/libs/portal/src/lib/federation/web-component/web-component-route-builder.ts index 06f00abd..1a6ccd9a 100644 --- a/modulefederation/libs/portal/src/lib/federation/web-component/web-component-route-builder.ts +++ b/modulefederation/libs/portal/src/lib/federation/web-component/web-component-route-builder.ts @@ -19,9 +19,12 @@ export class WebComponentRouteBuilder implements RouteBuilder { build(manifest: Manifest, route: Route) : void { route.component = WebComponentWrapper + const colon = manifest.stack!.indexOf(":") + const elementName = manifest.stack?.substring(colon + 1) + // remember data route.data!["module"] = manifest.name - route.data!["elementName"] = manifest.name + "-element" // ?? + route.data!["elementName"] = elementName } } diff --git a/modulefederation/libs/portal/src/lib/federation/web-component/web-component-wrapper.ts b/modulefederation/libs/portal/src/lib/federation/web-component/web-component-wrapper.ts index 46a7dc4d..a73da04f 100644 --- a/modulefederation/libs/portal/src/lib/federation/web-component/web-component-wrapper.ts +++ b/modulefederation/libs/portal/src/lib/federation/web-component/web-component-wrapper.ts @@ -39,8 +39,6 @@ export class WebComponentWrapper implements AfterContentInit { async ngAfterContentInit() { this.options = this.route.snapshot.data as WebComponentWrapperOptions - console.log(this.options) - try { await loadRemoteModule(this.options.module, "./Module"); diff --git a/modulefederation/libs/portal/src/lib/portal-manager.ts b/modulefederation/libs/portal/src/lib/portal-manager.ts index 2f2970f2..25fd5efe 100644 --- a/modulefederation/libs/portal/src/lib/portal-manager.ts +++ b/modulefederation/libs/portal/src/lib/portal-manager.ts @@ -378,13 +378,14 @@ export class PortalManager { if (module.remoteEntry) { module.type = 'microfrontend'; - remotes[moduleName] = module.remoteEntry; + let remoteEntry = module.remoteEntry; + if ( module.remoteEntryName) + remoteEntry = remoteEntry + "/" + module.remoteEntryName + + remotes[moduleName] = remoteEntry; } } - if ( remotes.react) - remotes.react = remotes.react + "/remoteEntry.js" // TODO - setRemoteDefinitions(remotes); // fill feature registry diff --git a/portal/core/src/main/java/com/serious/portal/model/Manifest.kt b/portal/core/src/main/java/com/serious/portal/model/Manifest.kt index 4261166b..041692b7 100644 --- a/portal/core/src/main/java/com/serious/portal/model/Manifest.kt +++ b/portal/core/src/main/java/com/serious/portal/model/Manifest.kt @@ -19,6 +19,7 @@ data class Manifest( var stack: String?, var version: String, var commitHash: String, + var remoteEntryName: String?, var remoteEntry: String?, var healthCheck: String?,