diff --git a/build/ModuleFederation.d.ts b/build/ModuleFederation.d.ts index f409ca6..0502158 100644 --- a/build/ModuleFederation.d.ts +++ b/build/ModuleFederation.d.ts @@ -38,4 +38,36 @@ export declare const ComponentWithFederatedImports: ({ renderO module: string; }[]; }) => React.JSX.Element; +type ShellHooks = T["shellHooks"]; +type ShellAlerts = T["shellAlerts"]; +type Listener = () => void; +export declare const shellHooksStore: { + getShellHooks: () => any; + subscribe: (listener: Listener) => () => void; + setShellHooks: (newHooks: any) => void; +}; +export declare const shellAlertsStore: { + getShellAlerts: () => any; + subscribe: (listener: Listener) => () => void; + setShellAlerts: (newAlerts: any) => void; +}; +export declare const useShellHooks: () => ShellHooks; +export declare const useShellAlerts: () => ShellAlerts; +export declare const ShellHooksProvider: ({ shellHooks, shellAlerts, children, }: { + shellHooks: ShellHooks; + shellAlerts: ShellAlerts; + children: ReactNode; +}) => React.JSX.Element; export {}; diff --git a/build/ModuleFederation.js b/build/ModuleFederation.js index bb7e84e..670d8e0 100644 --- a/build/ModuleFederation.js +++ b/build/ModuleFederation.js @@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.ComponentWithFederatedImports = exports.lazyWithModules = exports.FederatedComponent = exports.useCurrentApp = exports.loadModule = exports.registerAndLoadModule = void 0; +exports.ShellHooksProvider = exports.useShellAlerts = exports.useShellHooks = exports.shellAlertsStore = exports.shellHooksStore = exports.ComponentWithFederatedImports = exports.lazyWithModules = exports.FederatedComponent = exports.useCurrentApp = exports.loadModule = exports.registerAndLoadModule = void 0; const runtime_1 = require("@module-federation/enhanced/runtime"); const react_1 = __importStar(require("react")); const registeredApps = []; @@ -66,8 +66,9 @@ function FederatedComponent({ url, scope, module, app, renderOnLoading, props, } throw new Error("Can't federate a component without url, scope and module"); } return (react_1.default.createElement(react_1.Suspense, { fallback: renderOnLoading !== null && renderOnLoading !== void 0 ? renderOnLoading : react_1.default.createElement(react_1.default.Fragment, null, "Loading...") }, - react_1.default.createElement(CurrentAppContext.Provider, { value: app }, - react_1.default.createElement(Component, { ...props })))); + react_1.default.createElement(exports.ShellHooksProvider, { shellHooks: props.shellHooks, shellAlerts: props.shellAlerts }, + react_1.default.createElement(CurrentAppContext.Provider, { value: app }, + react_1.default.createElement(Component, { ...props }))))); } exports.FederatedComponent = FederatedComponent; const lazyWithModules = (functionComponent, ...modules) => { @@ -96,3 +97,67 @@ const ComponentWithFederatedImports = ({ renderOnError, renderOnLoading, compone react_1.default.createElement(Component, { ...componentProps }))); }; exports.ComponentWithFederatedImports = ComponentWithFederatedImports; +const createShellHooksStore = () => { + let shellHooks = null; + const listeners = new Set(); + return { + getShellHooks: () => shellHooks, + subscribe: (listener) => { + listeners.add(listener); + return () => { + listeners.delete(listener); + }; + }, + setShellHooks: (newHooks) => { + if (shellHooks !== newHooks) { + shellHooks = newHooks; + listeners.forEach((listener) => listener()); + } + }, + }; +}; +const createShellAlertsStore = () => { + let shellAlerts = null; + const listeners = new Set(); + return { + getShellAlerts: () => shellAlerts, + subscribe: (listener) => { + listeners.add(listener); + return () => { + listeners.delete(listener); + }; + }, + setShellAlerts: (newAlerts) => { + if (shellAlerts !== newAlerts) { + shellAlerts = newAlerts; + listeners.forEach((listener) => listener()); + } + }, + }; +}; +exports.shellHooksStore = createShellHooksStore(); +exports.shellAlertsStore = createShellAlertsStore(); +const useShellHooks = () => { + const hooks = (0, react_1.useSyncExternalStore)(exports.shellHooksStore.subscribe, exports.shellHooksStore.getShellHooks); + if (!hooks) { + throw new Error("useShellHooks must be used within a ShellHooksProvider and initialized with valid hooks."); + } + return hooks; +}; +exports.useShellHooks = useShellHooks; +const useShellAlerts = () => { + const alerts = (0, react_1.useSyncExternalStore)(exports.shellAlertsStore.subscribe, exports.shellAlertsStore.getShellAlerts); + if (!alerts) { + throw new Error("useShellAlerts must be used within a ShellHooksProvider and initialized with valid alerts."); + } + return alerts; +}; +exports.useShellAlerts = useShellAlerts; +const ShellHooksProvider = ({ shellHooks, shellAlerts, children, }) => { + (0, react_1.useMemo)(() => { + exports.shellHooksStore.setShellHooks(shellHooks); + exports.shellAlertsStore.setShellAlerts(shellAlerts); + }, [shellHooks, shellAlerts]); + return react_1.default.createElement(react_1.default.Fragment, null, children); +}; +exports.ShellHooksProvider = ShellHooksProvider; diff --git a/src/ModuleFederation.tsx b/src/ModuleFederation.tsx index ddbd166..7253db9 100644 --- a/src/ModuleFederation.tsx +++ b/src/ModuleFederation.tsx @@ -176,13 +176,13 @@ export const ComponentWithFederatedImports = ({ ); }; -type ShellHooks = ReturnType; -type ShellAlerts = ReturnType; +type ShellHooks = T["shellHooks"]; +type ShellAlerts = T["shellAlerts"]; type Listener = () => void; -const createShellHooksStore = () => { - let shellHooks: ShellHooks | null = null; +const createShellHooksStore = () => { + let shellHooks: ShellHooks | null = null; const listeners: Set = new Set(); @@ -196,7 +196,7 @@ const createShellHooksStore = () => { }; }, - setShellHooks: (newHooks: ShellHooks) => { + setShellHooks: (newHooks: ShellHooks) => { if (shellHooks !== newHooks) { shellHooks = newHooks; listeners.forEach((listener) => listener()); @@ -205,8 +205,8 @@ const createShellHooksStore = () => { }; }; -const createShellAlertsStore = () => { - let shellAlerts: ShellAlerts | null = null; +const createShellAlertsStore = () => { + let shellAlerts: ShellAlerts | null = null; const listeners: Set = new Set(); return { @@ -219,7 +219,7 @@ const createShellAlertsStore = () => { }; }, - setShellAlerts: (newAlerts: ShellAlerts) => { + setShellAlerts: (newAlerts: ShellAlerts) => { if (shellAlerts !== newAlerts) { shellAlerts = newAlerts; listeners.forEach((listener) => listener()); @@ -231,7 +231,9 @@ const createShellAlertsStore = () => { export const shellHooksStore = createShellHooksStore(); export const shellAlertsStore = createShellAlertsStore(); -export const useShellHooks = (): ShellHooks => { +export const useShellHooks = < + T extends { shellHooks: any } +>(): ShellHooks => { const hooks = useSyncExternalStore( shellHooksStore.subscribe, shellHooksStore.getShellHooks @@ -246,7 +248,9 @@ export const useShellHooks = (): ShellHooks => { return hooks; }; -export const useShellAlerts = (): ShellAlerts => { +export const useShellAlerts = < + T extends { shellAlerts: any } +>(): ShellAlerts => { const alerts = useSyncExternalStore( shellAlertsStore.subscribe, shellAlertsStore.getShellAlerts @@ -261,11 +265,18 @@ export const useShellAlerts = (): ShellAlerts => { return alerts; }; -export const ShellHooksProvider: FC<{ - shellHooks: ShellHooks; - shellAlerts: ShellAlerts; +export const ShellHooksProvider = < + T extends { shellHooks: any }, + K extends { shellAlerts: any } +>({ + shellHooks, + shellAlerts, + children, +}: { + shellHooks: ShellHooks; + shellAlerts: ShellAlerts; children: ReactNode; -}> = ({ shellHooks, shellAlerts, children }) => { +}) => { useMemo(() => { shellHooksStore.setShellHooks(shellHooks); shellAlertsStore.setShellAlerts(shellAlerts);