@module-federation/bridge-react 0.0.0-next-20241014092946 → 0.0.0-next-20241015065721

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,8 +65,7 @@ function WraperRouterProvider(props) {
65
65
  return /* @__PURE__ */ React.createElement(RouterProvider, { router: MemeoryRouterInstance });
66
66
  } else {
67
67
  const BrowserRouterInstance = createBrowserRouter(routers, {
68
- // In host app, the routerContextProps is {}, so we should use router.basename as fallback
69
- basename: routerContextProps.basename || router.basename,
68
+ basename: routerContextProps.basename || (router == null ? void 0 : router.basename),
70
69
  future: router.future,
71
70
  window: router.window
72
71
  });
@@ -47,8 +47,7 @@ function WraperRouterProvider(props) {
47
47
  return /* @__PURE__ */ React__default.createElement(RouterProvider, { router: MemeoryRouterInstance });
48
48
  } else {
49
49
  const BrowserRouterInstance = createBrowserRouter(routers, {
50
- // In host app, the routerContextProps is {}, so we should use router.basename as fallback
51
- basename: routerContextProps.basename || router.basename,
50
+ basename: routerContextProps.basename || (router == null ? void 0 : router.basename),
52
51
  future: router.future,
53
52
  window: router.window
54
53
  });
@@ -65,7 +65,7 @@ function WrapperRouterProvider(props) {
65
65
  return /* @__PURE__ */ React.createElement(RouterProvider, { router: MemeoryRouterInstance });
66
66
  } else {
67
67
  const BrowserRouterInstance = createBrowserRouter(routers, {
68
- basename: routerContextProps.basename,
68
+ basename: routerContextProps.basename || (router == null ? void 0 : router.basename),
69
69
  future: router.future,
70
70
  window: router.window
71
71
  });
package/dist/router.es.js CHANGED
@@ -47,7 +47,7 @@ function WrapperRouterProvider(props) {
47
47
  return /* @__PURE__ */ React__default.createElement(RouterProvider, { router: MemeoryRouterInstance });
48
48
  } else {
49
49
  const BrowserRouterInstance = createBrowserRouter(routers, {
50
- basename: routerContextProps.basename,
50
+ basename: routerContextProps.basename || (router == null ? void 0 : router.basename),
51
51
  future: router.future,
52
52
  window: router.window
53
53
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/bridge-react",
3
- "version": "0.0.0-next-20241014092946",
3
+ "version": "0.0.0-next-20241015065721",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,23 +35,24 @@
35
35
  "dependencies": {
36
36
  "@loadable/component": "^5.16.4",
37
37
  "react-error-boundary": "^4.0.13",
38
- "@module-federation/bridge-shared": "0.0.0-next-20241014092946"
38
+ "@module-federation/bridge-shared": "0.0.0-next-20241015065721"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "react": ">=16.9.0",
42
42
  "react-dom": ">=16.9.0",
43
- "react-router-dom": ">=4"
43
+ "react-router-dom": ">=4",
44
+ "@module-federation/runtime": "0.0.0-next-20241015065721"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@testing-library/react": "15.0.7",
47
48
  "@types/react": "18.2.79",
48
- "@types/react-dom": "18.3.0",
49
+ "@types/react-dom": "18.2.25",
49
50
  "@vitejs/plugin-react": "^4.3.0",
50
51
  "@vitejs/plugin-vue": "^5.0.4",
51
52
  "@vitejs/plugin-vue-jsx": "^4.0.0",
52
53
  "jsdom": "^24.1.0",
53
- "react": "18.3.1",
54
- "react-dom": "18.3.1",
54
+ "react": "18.1.0",
55
+ "react-dom": "18.1.0",
55
56
  "react-router-dom": "6.22.3",
56
57
  "typescript": "^5.2.2",
57
58
  "vite": "^5.2.14",
package/project.json CHANGED
@@ -3,7 +3,6 @@
3
3
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
4
  "sourceRoot": "packages/bridge/bridge-react/src",
5
5
  "projectType": "library",
6
- "tags": ["type:pkg"],
7
6
  "targets": {
8
7
  "build": {
9
8
  "executor": "nx:run-commands",
@@ -23,5 +22,6 @@
23
22
  ]
24
23
  }
25
24
  }
26
- }
25
+ },
26
+ "tags": ["type:pkg"]
27
27
  }
package/src/create.tsx CHANGED
@@ -1,11 +1,11 @@
1
1
  import React, { forwardRef } from 'react';
2
- import type { ProviderParams } from '@module-federation/bridge-shared';
3
- import { LoggerInstance } from './utils';
4
2
  import {
5
3
  ErrorBoundary,
6
4
  ErrorBoundaryPropsWithComponent,
7
5
  } from 'react-error-boundary';
6
+ import { LoggerInstance } from './utils';
8
7
  import RemoteApp from './remote';
8
+ import type { ProviderParams } from '@module-federation/bridge-shared';
9
9
 
10
10
  export interface RenderFnParams extends ProviderParams {
11
11
  dom?: any;
@@ -0,0 +1,28 @@
1
+ import { getInstance } from '@module-federation/runtime';
2
+ import helper from '@module-federation/runtime/helpers';
3
+
4
+ function registerBridgeLifeCycle() {
5
+ const { registerPlugins, pluginHelper } = helper.global;
6
+ const host = getInstance();
7
+ const pluginSystem = new pluginHelper.PluginSystem({
8
+ beforeBridgeRender: new pluginHelper.SyncHook<[Record<string, any>], any>(),
9
+ afterBridgeRender: new pluginHelper.SyncHook<[Record<string, any>], any>(),
10
+ beforeBridgeDestroy: new pluginHelper.SyncHook<
11
+ [Record<string, any>],
12
+ any
13
+ >(),
14
+ afterBridgeDestroy: new pluginHelper.SyncHook<[Record<string, any>], any>(),
15
+ });
16
+
17
+ if (host) {
18
+ registerPlugins<typeof pluginSystem.lifecycle, typeof pluginSystem>(
19
+ host?.options?.plugins,
20
+ [pluginSystem],
21
+ );
22
+ return pluginSystem;
23
+ }
24
+
25
+ return null;
26
+ }
27
+
28
+ export { registerBridgeLifeCycle };
package/src/provider.tsx CHANGED
@@ -2,25 +2,37 @@ import { useLayoutEffect, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import ReactDOM from 'react-dom';
4
4
  import ReactDOMClient from 'react-dom/client';
5
- import { RouterContext } from './context';
6
5
  import type {
7
6
  ProviderParams,
8
7
  RenderFnParams,
9
8
  } from '@module-federation/bridge-shared';
10
- import { LoggerInstance, atLeastReact18 } from './utils';
11
9
  import { ErrorBoundary } from 'react-error-boundary';
10
+ import { RouterContext } from './context';
11
+ import { LoggerInstance, atLeastReact18 } from './utils';
12
12
 
13
+ type RenderParams = RenderFnParams & any;
14
+ type DestroyParams = {
15
+ dom: HTMLElement;
16
+ };
13
17
  type RootType = HTMLElement | ReactDOMClient.Root;
18
+
19
+ type BridgeHooks = {
20
+ beforeBridgeRender?: (params: RenderFnParams) => any;
21
+ afterBridgeRender?: (params: RenderFnParams) => any;
22
+ beforeBridgeDestroy?: (params: DestroyParams) => any;
23
+ afterBridgeDestroy?: (params: DestroyParams) => any;
24
+ };
25
+
14
26
  type ProviderFnParams<T> = {
15
27
  rootComponent: React.ComponentType<T>;
16
28
  render?: (
17
29
  App: React.ReactElement,
18
30
  id?: HTMLElement | string,
19
31
  ) => RootType | Promise<RootType>;
32
+ hooks?: BridgeHooks;
20
33
  };
21
-
22
34
  export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
23
- return () => {
35
+ return (params: { hooks?: BridgeHooks }) => {
24
36
  const rootMap = new Map<any, RootType>();
25
37
  const RawComponent = (info: { propsInfo: T; appInfo: ProviderParams }) => {
26
38
  const { appInfo, propsInfo, ...restProps } = info;
@@ -37,7 +49,7 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
37
49
  };
38
50
 
39
51
  return {
40
- async render(info: RenderFnParams & any) {
52
+ async render(info: RenderParams) {
41
53
  LoggerInstance.log(`createBridgeComponent render Info`, info);
42
54
  const {
43
55
  moduleName,
@@ -47,6 +59,21 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
47
59
  fallback,
48
60
  ...propsInfo
49
61
  } = info;
62
+
63
+ const beforeBridgeRender =
64
+ (bridgeInfo?.hooks && bridgeInfo?.hooks.beforeBridgeRender) ||
65
+ params?.hooks?.beforeBridgeRender;
66
+
67
+ // 可通过beforeBridgeRender返回一个props对象,用于传递额外的 props 参数
68
+ const beforeBridgeRenderRes =
69
+ beforeBridgeRender && beforeBridgeRender(info);
70
+ const extraProps =
71
+ beforeBridgeRenderRes &&
72
+ typeof beforeBridgeRenderRes === 'object' &&
73
+ beforeBridgeRenderRes?.extraProps
74
+ ? beforeBridgeRenderRes?.extraProps
75
+ : {};
76
+
50
77
  const rootComponentWithErrorBoundary = (
51
78
  // set ErrorBoundary for RawComponent rendering error, usually caused by user app rendering error
52
79
  <ErrorBoundary FallbackComponent={fallback}>
@@ -56,11 +83,11 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
56
83
  basename,
57
84
  memoryRoute,
58
85
  }}
59
- propsInfo={propsInfo}
86
+ propsInfo={{ ...propsInfo, ...extraProps } as T}
60
87
  />
61
88
  </ErrorBoundary>
62
89
  );
63
-
90
+ // call render function
64
91
  if (atLeastReact18(React)) {
65
92
  if (bridgeInfo?.render) {
66
93
  // in case bridgeInfo?.render is an async function, resolve this to promise
@@ -77,11 +104,33 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
77
104
  const renderFn = bridgeInfo?.render || ReactDOM.render;
78
105
  renderFn?.(rootComponentWithErrorBoundary, info.dom);
79
106
  }
107
+
108
+ const afterBridgeRender =
109
+ (bridgeInfo?.hooks && bridgeInfo?.hooks.afterBridgeDestroy) ||
110
+ params?.hooks?.afterBridgeRender;
111
+ afterBridgeRender && afterBridgeRender(info);
80
112
  },
81
- async destroy(info: { dom: HTMLElement }) {
113
+
114
+ async destroy(info: DestroyParams) {
82
115
  LoggerInstance.log(`createBridgeComponent destroy Info`, {
83
116
  dom: info.dom,
84
117
  });
118
+
119
+ // call beforeBridgeDestroy hook
120
+ if (
121
+ bridgeInfo?.hooks &&
122
+ bridgeInfo?.hooks.beforeBridgeDestroy &&
123
+ typeof bridgeInfo?.hooks.beforeBridgeDestroy === 'function'
124
+ ) {
125
+ bridgeInfo.hooks.beforeBridgeDestroy(info);
126
+ }
127
+
128
+ const beforeBridgeDestroy =
129
+ (bridgeInfo?.hooks && bridgeInfo?.hooks.beforeBridgeDestroy) ||
130
+ params?.hooks?.beforeBridgeDestroy;
131
+ beforeBridgeDestroy && beforeBridgeDestroy(info);
132
+
133
+ // call destroy function
85
134
  if (atLeastReact18(React)) {
86
135
  const root = rootMap.get(info.dom);
87
136
  (root as ReactDOMClient.Root)?.unmount();
@@ -89,6 +138,11 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
89
138
  } else {
90
139
  ReactDOM.unmountComponentAtNode(info.dom);
91
140
  }
141
+
142
+ const afterBridgeDestroy =
143
+ (bridgeInfo?.hooks && bridgeInfo?.hooks.afterBridgeDestroy) ||
144
+ params?.hooks?.afterBridgeDestroy;
145
+ afterBridgeDestroy && afterBridgeDestroy(info);
92
146
  },
93
147
  rawComponent: bridgeInfo.rootComponent,
94
148
  __BRIDGE_FN__: (_args: T) => {},
@@ -7,9 +7,10 @@ import React, {
7
7
  } from 'react';
8
8
  import * as ReactRouterDOM from 'react-router-dom';
9
9
  import type { ProviderParams } from '@module-federation/bridge-shared';
10
- import { LoggerInstance, pathJoin } from '../utils';
11
10
  import { dispatchPopstateEnv } from '@module-federation/bridge-shared';
12
11
  import { ErrorBoundaryPropsWithComponent } from 'react-error-boundary';
12
+ import { registerBridgeLifeCycle } from '../lifecycle';
13
+ import { LoggerInstance, pathJoin } from '../utils';
13
14
 
14
15
  declare const __APP_VERSION__: string;
15
16
  export interface RenderFnParams extends ProviderParams {
@@ -39,6 +40,7 @@ const RemoteAppWrapper = forwardRef(function (
39
40
  props: RemoteAppParams & RenderFnParams,
40
41
  ref,
41
42
  ) {
43
+ const bridgeHook = registerBridgeLifeCycle();
42
44
  const RemoteApp = () => {
43
45
  LoggerInstance.log(`RemoteAppWrapper RemoteApp props >>>`, { props });
44
46
  const {
@@ -65,7 +67,7 @@ const RemoteAppWrapper = forwardRef(function (
65
67
  const providerReturn = providerInfo();
66
68
  providerInfoRef.current = providerReturn;
67
69
 
68
- const renderProps = {
70
+ let renderProps = {
69
71
  moduleName,
70
72
  dom: rootRef.current,
71
73
  basename,
@@ -78,6 +80,24 @@ const RemoteAppWrapper = forwardRef(function (
78
80
  `createRemoteComponent LazyComponent render >>>`,
79
81
  renderProps,
80
82
  );
83
+
84
+ if (bridgeHook && bridgeHook?.lifecycle?.beforeBridgeRender) {
85
+ const beforeBridgeRenderRes =
86
+ bridgeHook?.lifecycle?.beforeBridgeRender.emit({
87
+ ...renderProps,
88
+ });
89
+ const extraProps =
90
+ beforeBridgeRenderRes &&
91
+ typeof beforeBridgeRenderRes === 'object' &&
92
+ beforeBridgeRenderRes?.extraProps
93
+ ? beforeBridgeRenderRes?.extraProps
94
+ : {};
95
+
96
+ renderProps = {
97
+ ...renderProps,
98
+ ...extraProps,
99
+ } as any;
100
+ }
81
101
  providerReturn.render(renderProps);
82
102
  });
83
103
 
@@ -89,6 +109,16 @@ const RemoteAppWrapper = forwardRef(function (
89
109
  `createRemoteComponent LazyComponent destroy >>>`,
90
110
  { moduleName, basename, dom: renderDom.current },
91
111
  );
112
+ if (bridgeHook && bridgeHook?.lifecycle?.afterBridgeDestroy) {
113
+ bridgeHook?.lifecycle?.afterBridgeDestroy.emit({
114
+ moduleName,
115
+ dom: renderDom.current,
116
+ basename,
117
+ memoryRoute,
118
+ fallback,
119
+ ...resProps,
120
+ });
121
+ }
92
122
  providerInfoRef.current?.destroy({
93
123
  dom: renderDom.current,
94
124
  });
package/src/router-v5.tsx CHANGED
@@ -2,7 +2,6 @@ import React, { useContext } from 'react';
2
2
  // The upper alias react-router-dom$ into this file avoids the loop
3
3
  // @ts-ignore
4
4
  import * as ReactRouterDom from 'react-router-dom/index.js';
5
-
6
5
  import { RouterContext } from './context';
7
6
  import { LoggerInstance } from './utils';
8
7
 
package/src/router-v6.tsx CHANGED
@@ -60,8 +60,7 @@ function WraperRouterProvider(
60
60
  return <RouterProvider router={MemeoryRouterInstance} />;
61
61
  } else {
62
62
  const BrowserRouterInstance = createBrowserRouter(routers, {
63
- // In host app, the routerContextProps is {}, so we should use router.basename as fallback
64
- basename: routerContextProps.basename || router.basename,
63
+ basename: routerContextProps.basename || router?.basename,
65
64
  future: router.future,
66
65
  window: router.window,
67
66
  });
package/src/router.tsx CHANGED
@@ -59,7 +59,7 @@ function WrapperRouterProvider(
59
59
  return <RouterProvider router={MemeoryRouterInstance} />;
60
60
  } else {
61
61
  const BrowserRouterInstance = createBrowserRouter(routers, {
62
- basename: routerContextProps.basename,
62
+ basename: routerContextProps.basename || router?.basename,
63
63
  future: router.future,
64
64
  window: router.window,
65
65
  });