@module-federation/bridge-react 0.0.0-next-20241101074502 → 0.0.0-next-20241101093646

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.
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const React = require("react");
4
4
  const ReactRouterDom = require("react-router-dom/");
5
- const context = require("./context-BVnJi3LE.cjs");
5
+ const context = require("./context--mtFt3tp.cjs");
6
6
  function _interopNamespaceDefault(e) {
7
7
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
8
8
  if (e) {
@@ -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
@@ -1,7 +1,7 @@
1
1
  import React__default, { useContext } from "react";
2
2
  import * as ReactRouterDom from "react-router-dom/";
3
3
  export * from "react-router-dom/";
4
- import { R as RouterContext, L as LoggerInstance } from "./context-9t7opEwi.js";
4
+ import { R as RouterContext, L as LoggerInstance } from "./context-Bw2PEwa6.js";
5
5
  function WrapperRouter(props) {
6
6
  const { basename, ...propsRes } = props;
7
7
  const routerContextProps = useContext(RouterContext) || {};
@@ -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-20241101074502",
3
+ "version": "0.0.0-next-20241101093646",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,13 +35,13 @@
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-20241101074502",
39
- "@module-federation/sdk": "0.0.0-next-20241101074502"
38
+ "@module-federation/bridge-shared": "0.0.0-next-20241101093646"
40
39
  },
41
40
  "peerDependencies": {
42
41
  "react": ">=16.9.0",
43
42
  "react-dom": ">=16.9.0",
44
- "react-router-dom": ">=4"
43
+ "react-router-dom": ">=4",
44
+ "@module-federation/runtime": "0.0.0-next-20241101093646"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@testing-library/react": "15.0.7",
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,32 @@ 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
+ if (
120
+ bridgeInfo?.hooks &&
121
+ bridgeInfo?.hooks.beforeBridgeDestroy &&
122
+ typeof bridgeInfo?.hooks.beforeBridgeDestroy === 'function'
123
+ ) {
124
+ bridgeInfo.hooks.beforeBridgeDestroy(info);
125
+ }
126
+
127
+ const beforeBridgeDestroy =
128
+ (bridgeInfo?.hooks && bridgeInfo?.hooks.beforeBridgeDestroy) ||
129
+ params?.hooks?.beforeBridgeDestroy;
130
+ beforeBridgeDestroy && beforeBridgeDestroy(info);
131
+
132
+ // call destroy function
85
133
  if (atLeastReact18(React)) {
86
134
  const root = rootMap.get(info.dom);
87
135
  (root as ReactDOMClient.Root)?.unmount();
@@ -89,6 +137,11 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
89
137
  } else {
90
138
  ReactDOM.unmountComponentAtNode(info.dom);
91
139
  }
140
+
141
+ const afterBridgeDestroy =
142
+ (bridgeInfo?.hooks && bridgeInfo?.hooks.afterBridgeDestroy) ||
143
+ params?.hooks?.afterBridgeDestroy;
144
+ afterBridgeDestroy && afterBridgeDestroy(info);
92
145
  },
93
146
  rawComponent: bridgeInfo.rootComponent,
94
147
  __BRIDGE_FN__: (_args: T) => {},
@@ -7,9 +7,26 @@ 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';
14
+ import { getInstance } from '@module-federation/runtime';
15
+
16
+ export const getModuleName = (id: string) => {
17
+ // separate module name without detailed module path
18
+ // @vmok-e2e/edenx-demo-app2/button -> @vmok-e2e/edenx-demo-app2
19
+ const idArray = id.split('/');
20
+ if (idArray.length < 2) {
21
+ return id;
22
+ }
23
+ return idArray[0] + '/' + idArray[1];
24
+ };
25
+
26
+ export const getRootDomDefaultClassName = (moduleName: string) => {
27
+ const name = getModuleName(moduleName).replace(/\@/, '').replace(/\//, '-');
28
+ return `bridge-root-component-${name}`;
29
+ };
13
30
 
14
31
  declare const __APP_VERSION__: string;
15
32
  export interface RenderFnParams extends ProviderParams {
@@ -39,6 +56,8 @@ const RemoteAppWrapper = forwardRef(function (
39
56
  props: RemoteAppParams & RenderFnParams,
40
57
  ref,
41
58
  ) {
59
+ const host = getInstance();
60
+ LoggerInstance.log(`RemoteAppWrapper host >>>`, host);
42
61
  const RemoteApp = () => {
43
62
  LoggerInstance.log(`RemoteAppWrapper RemoteApp props >>>`, { props });
44
63
  const {
@@ -65,7 +84,7 @@ const RemoteAppWrapper = forwardRef(function (
65
84
  const providerReturn = providerInfo();
66
85
  providerInfoRef.current = providerReturn;
67
86
 
68
- const renderProps = {
87
+ let renderProps = {
69
88
  moduleName,
70
89
  dom: rootRef.current,
71
90
  basename,
@@ -78,6 +97,27 @@ const RemoteAppWrapper = forwardRef(function (
78
97
  `createRemoteComponent LazyComponent render >>>`,
79
98
  renderProps,
80
99
  );
100
+
101
+ if (
102
+ host?.bridgeHook &&
103
+ host?.bridgeHook?.lifecycle?.beforeBridgeRender
104
+ ) {
105
+ const beforeBridgeRenderRes =
106
+ host?.bridgeHook?.lifecycle?.beforeBridgeRender.emit({
107
+ ...renderProps,
108
+ });
109
+ const extraProps =
110
+ beforeBridgeRenderRes &&
111
+ typeof beforeBridgeRenderRes === 'object' &&
112
+ beforeBridgeRenderRes?.extraProps
113
+ ? beforeBridgeRenderRes?.extraProps
114
+ : {};
115
+
116
+ renderProps = {
117
+ ...renderProps,
118
+ ...extraProps,
119
+ } as any;
120
+ }
81
121
  providerReturn.render(renderProps);
82
122
  });
83
123
 
@@ -89,6 +129,19 @@ const RemoteAppWrapper = forwardRef(function (
89
129
  `createRemoteComponent LazyComponent destroy >>>`,
90
130
  { moduleName, basename, dom: renderDom.current },
91
131
  );
132
+ if (
133
+ host?.bridgeHook &&
134
+ host?.bridgeHook?.lifecycle?.afterBridgeDestroy
135
+ ) {
136
+ host?.bridgeHook?.lifecycle?.afterBridgeDestroy.emit({
137
+ moduleName,
138
+ dom: renderDom.current,
139
+ basename,
140
+ memoryRoute,
141
+ fallback,
142
+ ...resProps,
143
+ });
144
+ }
92
145
  providerInfoRef.current?.destroy({
93
146
  dom: renderDom.current,
94
147
  });
@@ -97,9 +150,11 @@ const RemoteAppWrapper = forwardRef(function (
97
150
  };
98
151
  }, []);
99
152
 
153
+ // bridge-remote-root
154
+ const rootComponentClassName = `${getRootDomDefaultClassName(moduleName)} ${props?.className}`;
100
155
  return (
101
156
  <div
102
- className={props?.className}
157
+ className={rootComponentClassName}
103
158
  style={props?.style}
104
159
  ref={rootRef}
105
160
  ></div>
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.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
  });
package/src/utils.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import React from 'react';
2
- import { createLogger } from '@module-federation/sdk';
2
+ import { Logger } from '@module-federation/bridge-shared';
3
3
 
4
- export const LoggerInstance = createLogger(
5
- '[ Module Federation Bridge React ]',
6
- );
4
+ export const LoggerInstance = new Logger('bridge-react');
7
5
 
8
6
  type typeReact = typeof React;
9
7