import { useLayoutEffect, useRef, useState } from 'react'; import * as React from 'react'; import ReactDOM from 'react-dom'; import ReactDOMClient from 'react-dom/client'; import type { ProviderParams, RenderFnParams, } from '@module-federation/bridge-shared'; import { ErrorBoundary } from 'react-error-boundary'; import { RouterContext } from './context'; import { LoggerInstance, atLeastReact18 } from './utils'; import { federationRuntime } from './plugin'; type RenderParams = RenderFnParams & { [key: string]: unknown; }; type DestroyParams = { moduleName: string; dom: HTMLElement; }; type RootType = HTMLElement | ReactDOMClient.Root; export type ProviderFnParams = { rootComponent: React.ComponentType; render?: ( App: React.ReactElement, id?: HTMLElement | string, ) => RootType | Promise; }; export function createBridgeComponent(bridgeInfo: ProviderFnParams) { return () => { const rootMap = new Map(); const instance = federationRuntime.instance; LoggerInstance.log( `createBridgeComponent instance from props >>>`, instance, ); const RawComponent = (info: { propsInfo: T; appInfo: ProviderParams }) => { const { appInfo, propsInfo, ...restProps } = info; const { moduleName, memoryRoute, basename = '/' } = appInfo; return ( ); }; return { async render(info: RenderParams) { LoggerInstance.log(`createBridgeComponent render Info`, info); const { moduleName, dom, basename, memoryRoute, fallback, ...propsInfo } = info; const beforeBridgeRenderRes = instance?.bridgeHook?.lifecycle?.beforeBridgeRender?.emit(info) || {}; const rootComponentWithErrorBoundary = ( // set ErrorBoundary for RawComponent rendering error, usually caused by user app rendering error ); // call render function if (atLeastReact18(React)) { if (bridgeInfo?.render) { // in case bridgeInfo?.render is an async function, resolve this to promise Promise.resolve( bridgeInfo?.render(rootComponentWithErrorBoundary, dom), ).then((root: RootType) => rootMap.set(info.dom, root)); } else { const root: RootType = ReactDOMClient.createRoot(info.dom); root.render(rootComponentWithErrorBoundary); rootMap.set(info.dom, root); } } else { // react 17 render const renderFn = bridgeInfo?.render || ReactDOM.render; renderFn?.(rootComponentWithErrorBoundary, info.dom); } instance?.bridgeHook?.lifecycle?.afterBridgeRender?.emit(info) || {}; }, async destroy(info: DestroyParams) { LoggerInstance.log(`createBridgeComponent destroy Info`, { dom: info.dom, }); instance?.bridgeHook?.lifecycle?.beforeBridgeDestroy?.emit(info); // call destroy function if (atLeastReact18(React)) { const root = rootMap.get(info.dom); (root as ReactDOMClient.Root)?.unmount(); rootMap.delete(info.dom); } else { ReactDOM.unmountComponentAtNode(info.dom); } instance?.bridgeHook?.lifecycle?.afterBridgeDestroy?.emit(info); }, rawComponent: bridgeInfo.rootComponent, __BRIDGE_FN__: (_args: T) => {}, }; }; } export function ShadowRoot(info: { children: () => JSX.Element }) { const [root] = useState(null); const domRef = useRef(null); useLayoutEffect(() => {}); return
{root && }
; }