@module-federation/bridge-react 0.2.5 → 0.2.7

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @module-federation/bridge-react
2
2
 
3
+ ## 0.2.7
4
+
5
+ ### Patch Changes
6
+
7
+ - @module-federation/bridge-shared@0.2.7
8
+
9
+ ## 0.2.6
10
+
11
+ ### Patch Changes
12
+
13
+ - @module-federation/bridge-shared@0.2.6
14
+
3
15
  ## 0.2.5
4
16
 
5
17
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const React = require("react");
4
- const ReactRouterDOM = require("react-router-dom");
5
4
  const context = require("./context--mtFt3tp.cjs");
5
+ const ReactRouterDOM = require("react-router-dom");
6
6
  const ReactDOM = require("react-dom");
7
7
  function _interopNamespaceDefault(e) {
8
8
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -122,26 +122,11 @@ const RemoteApp = ({
122
122
  memoryRoute,
123
123
  basename,
124
124
  providerInfo,
125
- dispathPopstate,
126
125
  ...resProps
127
126
  }) => {
128
127
  const rootRef = React.useRef(null);
129
128
  const renderDom = React.useRef(null);
130
129
  const providerInfoRef = React.useRef(null);
131
- if (dispathPopstate) {
132
- const location = ReactRouterDOM__namespace.useLocation();
133
- const [pathname, setPathname] = React.useState(location.pathname);
134
- React.useEffect(() => {
135
- if (pathname !== "" && pathname !== location.pathname) {
136
- context.LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
137
- name,
138
- pathname: location.pathname
139
- });
140
- context.f();
141
- }
142
- setPathname(location.pathname);
143
- }, [location]);
144
- }
145
130
  React.useEffect(() => {
146
131
  const renderTimeout = setTimeout(() => {
147
132
  const providerReturn = providerInfo();
@@ -178,12 +163,10 @@ const RemoteApp = ({
178
163
  }, []);
179
164
  return /* @__PURE__ */ React.createElement("div", { ref: rootRef });
180
165
  };
181
- RemoteApp["__APP_VERSION__"] = "0.2.5";
182
- function createRemoteComponent(info) {
166
+ RemoteApp["__APP_VERSION__"] = "0.2.7";
167
+ function withRouterData(WrappedComponent) {
183
168
  return (props) => {
184
169
  var _a;
185
- const exportName = (info == null ? void 0 : info.export) || "default";
186
- let basename = "/";
187
170
  let enableDispathPopstate = false;
188
171
  let routerContextVal;
189
172
  try {
@@ -192,9 +175,8 @@ function createRemoteComponent(info) {
192
175
  } catch {
193
176
  enableDispathPopstate = false;
194
177
  }
195
- if (props.basename) {
196
- basename = props.basename;
197
- } else if (enableDispathPopstate) {
178
+ let basename = "/";
179
+ if (!props.basename && enableDispathPopstate) {
198
180
  const ReactRouterDOMAny = ReactRouterDOM__namespace;
199
181
  const useRouteMatch = ReactRouterDOMAny["useRouteMatch"];
200
182
  const useHistory = ReactRouterDOMAny["useHistory"];
@@ -205,11 +187,10 @@ function createRemoteComponent(info) {
205
187
  basename = useHref == null ? void 0 : useHref("/");
206
188
  }
207
189
  routerContextVal = React.useContext(UNSAFE_RouteContext);
208
- if (routerContextVal && routerContextVal.matches && routerContextVal.matches[0] && routerContextVal.matches[0].pathnameBase) {
209
- basename = context.pathJoin(
210
- basename,
211
- routerContextVal.matches[0].pathnameBase || "/"
212
- );
190
+ if (routerContextVal && routerContextVal.matches && routerContextVal.matches.length > 0) {
191
+ const matchIndex = routerContextVal.matches.length - 1;
192
+ const pathnameBase = routerContextVal.matches[matchIndex].pathnameBase;
193
+ basename = context.pathJoin(basename, pathnameBase || "/");
213
194
  }
214
195
  } else {
215
196
  const match = useRouteMatch == null ? void 0 : useRouteMatch();
@@ -222,54 +203,80 @@ function createRemoteComponent(info) {
222
203
  }
223
204
  }
224
205
  }
225
- const LazyComponent = React.useMemo(() => {
226
- return React.lazy(async () => {
227
- context.LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
228
- basename,
229
- lazyComponent: info.loader,
230
- exportName,
231
- props,
232
- routerContextVal
233
- });
234
- try {
235
- const m2 = await info.loader();
236
- const moduleName = m2 && m2[Symbol.for("mf_module_id")];
237
- context.LoggerInstance.log(
238
- `createRemoteComponent LazyComponent loadRemote info >>>`,
239
- { basename, name: moduleName, module: m2, exportName, props }
240
- );
241
- const exportFn = m2[exportName];
242
- if (exportName in m2 && typeof exportFn === "function") {
243
- return {
244
- default: () => /* @__PURE__ */ React.createElement(
245
- RemoteApp,
246
- {
247
- name: moduleName,
248
- dispathPopstate: enableDispathPopstate,
249
- ...info,
250
- ...props,
251
- providerInfo: exportFn,
252
- basename
253
- }
254
- )
255
- };
256
- } else {
257
- context.LoggerInstance.log(
258
- `createRemoteComponent LazyComponent module not found >>>`,
259
- { basename, name: moduleName, module: m2, exportName, props }
260
- );
261
- throw Error(
262
- `Make sure that ${moduleName} has the correct export when export is ${String(
263
- exportName
264
- )}`
265
- );
266
- }
267
- } catch (error) {
268
- throw error;
206
+ context.LoggerInstance.log(`createRemoteComponent withRouterData >>>`, {
207
+ ...props,
208
+ basename,
209
+ routerContextVal,
210
+ enableDispathPopstate
211
+ });
212
+ if (enableDispathPopstate) {
213
+ const location = ReactRouterDOM__namespace.useLocation();
214
+ const [pathname, setPathname] = React.useState(location.pathname);
215
+ React.useEffect(() => {
216
+ if (pathname !== "" && pathname !== location.pathname) {
217
+ context.LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
218
+ name: props.name,
219
+ pathname: location.pathname
220
+ });
221
+ context.f();
269
222
  }
270
- });
271
- }, [exportName, basename, props.memoryRoute]);
272
- return /* @__PURE__ */ React.createElement(ErrorBoundary, { FallbackComponent: info.fallback }, /* @__PURE__ */ React.createElement(React.Suspense, { fallback: info.loading }, /* @__PURE__ */ React.createElement(LazyComponent, null)));
223
+ setPathname(location.pathname);
224
+ }, [location]);
225
+ }
226
+ return /* @__PURE__ */ React.createElement(WrappedComponent, { ...props, basename });
227
+ };
228
+ }
229
+ const RemoteApp$1 = withRouterData(RemoteApp);
230
+ function createLazyRemoteComponent(info) {
231
+ const exportName = (info == null ? void 0 : info.export) || "default";
232
+ return React.lazy(async () => {
233
+ context.LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
234
+ lazyComponent: info.loader,
235
+ exportName
236
+ });
237
+ try {
238
+ const m2 = await info.loader();
239
+ const moduleName = m2 && m2[Symbol.for("mf_module_id")];
240
+ context.LoggerInstance.log(
241
+ `createRemoteComponent LazyComponent loadRemote info >>>`,
242
+ { name: moduleName, module: m2, exportName }
243
+ );
244
+ const exportFn = m2[exportName];
245
+ if (exportName in m2 && typeof exportFn === "function") {
246
+ const RemoteAppComponent = React.forwardRef((props, _ref) => {
247
+ return /* @__PURE__ */ React.createElement(
248
+ RemoteApp$1,
249
+ {
250
+ name: moduleName,
251
+ providerInfo: exportFn,
252
+ exportName: info.export || "default",
253
+ ...props
254
+ }
255
+ );
256
+ });
257
+ return {
258
+ default: RemoteAppComponent
259
+ };
260
+ } else {
261
+ context.LoggerInstance.log(
262
+ `createRemoteComponent LazyComponent module not found >>>`,
263
+ { name: moduleName, module: m2, exportName }
264
+ );
265
+ throw Error(
266
+ `Make sure that ${moduleName} has the correct export when export is ${String(
267
+ exportName
268
+ )}`
269
+ );
270
+ }
271
+ } catch (error) {
272
+ throw error;
273
+ }
274
+ });
275
+ }
276
+ function createRemoteComponent(info) {
277
+ const LazyComponent = createLazyRemoteComponent(info);
278
+ return (props) => {
279
+ return /* @__PURE__ */ React.createElement(ErrorBoundary, { FallbackComponent: info.fallback }, /* @__PURE__ */ React.createElement(React.Suspense, { fallback: info.loading }, /* @__PURE__ */ React.createElement(LazyComponent, { ...props })));
273
280
  };
274
281
  }
275
282
  var client = {};
package/dist/index.d.ts CHANGED
@@ -19,8 +19,8 @@ export declare function createRemoteComponent<T, E extends keyof T>(info: {
19
19
  fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
20
20
  export?: E;
21
21
  }): (props: {
22
- basename?: ProviderParams["basename"];
23
- memoryRoute?: ProviderParams["memoryRoute"];
22
+ basename?: ProviderParams['basename'];
23
+ memoryRoute?: ProviderParams['memoryRoute'];
24
24
  } & ("__BRIDGE_FN__" extends keyof (T[E] extends (...args: any) => any ? ReturnType<T[E]> : never) ? (T[E] extends (...args: any) => any ? ReturnType<T[E]> : never)["__BRIDGE_FN__"] extends (...args: any) => any ? Parameters<(T[E] extends (...args: any) => any ? ReturnType<T[E]> : never)["__BRIDGE_FN__"]>[0] : {} : {})) => default_2.JSX.Element;
25
25
 
26
26
  declare type ErrorBoundaryPropsWithComponent = ErrorBoundarySharedProps & {
package/dist/index.es.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
- import React__default, { createContext, Component, createElement, isValidElement, useContext, useMemo, useRef, useState, useEffect } from "react";
3
- import * as ReactRouterDOM from "react-router-dom";
2
+ import React__default, { createContext, Component, createElement, isValidElement, useContext, useState, useEffect, useRef, forwardRef } from "react";
4
3
  import { p as pathJoin, L as LoggerInstance, f, a as atLeastReact18, R as RouterContext } from "./context-Bw2PEwa6.js";
4
+ import * as ReactRouterDOM from "react-router-dom";
5
5
  import ReactDOM from "react-dom";
6
6
  const ErrorBoundaryContext = createContext(null);
7
7
  const initialState = {
@@ -103,26 +103,11 @@ const RemoteApp = ({
103
103
  memoryRoute,
104
104
  basename,
105
105
  providerInfo,
106
- dispathPopstate,
107
106
  ...resProps
108
107
  }) => {
109
108
  const rootRef = useRef(null);
110
109
  const renderDom = useRef(null);
111
110
  const providerInfoRef = useRef(null);
112
- if (dispathPopstate) {
113
- const location = ReactRouterDOM.useLocation();
114
- const [pathname, setPathname] = useState(location.pathname);
115
- useEffect(() => {
116
- if (pathname !== "" && pathname !== location.pathname) {
117
- LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
118
- name,
119
- pathname: location.pathname
120
- });
121
- f();
122
- }
123
- setPathname(location.pathname);
124
- }, [location]);
125
- }
126
111
  useEffect(() => {
127
112
  const renderTimeout = setTimeout(() => {
128
113
  const providerReturn = providerInfo();
@@ -159,12 +144,10 @@ const RemoteApp = ({
159
144
  }, []);
160
145
  return /* @__PURE__ */ React__default.createElement("div", { ref: rootRef });
161
146
  };
162
- RemoteApp["__APP_VERSION__"] = "0.2.5";
163
- function createRemoteComponent(info) {
147
+ RemoteApp["__APP_VERSION__"] = "0.2.7";
148
+ function withRouterData(WrappedComponent) {
164
149
  return (props) => {
165
150
  var _a;
166
- const exportName = (info == null ? void 0 : info.export) || "default";
167
- let basename = "/";
168
151
  let enableDispathPopstate = false;
169
152
  let routerContextVal;
170
153
  try {
@@ -173,9 +156,8 @@ function createRemoteComponent(info) {
173
156
  } catch {
174
157
  enableDispathPopstate = false;
175
158
  }
176
- if (props.basename) {
177
- basename = props.basename;
178
- } else if (enableDispathPopstate) {
159
+ let basename = "/";
160
+ if (!props.basename && enableDispathPopstate) {
179
161
  const ReactRouterDOMAny = ReactRouterDOM;
180
162
  const useRouteMatch = ReactRouterDOMAny["useRouteMatch"];
181
163
  const useHistory = ReactRouterDOMAny["useHistory"];
@@ -186,11 +168,10 @@ function createRemoteComponent(info) {
186
168
  basename = useHref == null ? void 0 : useHref("/");
187
169
  }
188
170
  routerContextVal = useContext(UNSAFE_RouteContext);
189
- if (routerContextVal && routerContextVal.matches && routerContextVal.matches[0] && routerContextVal.matches[0].pathnameBase) {
190
- basename = pathJoin(
191
- basename,
192
- routerContextVal.matches[0].pathnameBase || "/"
193
- );
171
+ if (routerContextVal && routerContextVal.matches && routerContextVal.matches.length > 0) {
172
+ const matchIndex = routerContextVal.matches.length - 1;
173
+ const pathnameBase = routerContextVal.matches[matchIndex].pathnameBase;
174
+ basename = pathJoin(basename, pathnameBase || "/");
194
175
  }
195
176
  } else {
196
177
  const match = useRouteMatch == null ? void 0 : useRouteMatch();
@@ -203,54 +184,80 @@ function createRemoteComponent(info) {
203
184
  }
204
185
  }
205
186
  }
206
- const LazyComponent = useMemo(() => {
207
- return React__default.lazy(async () => {
208
- LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
209
- basename,
210
- lazyComponent: info.loader,
211
- exportName,
212
- props,
213
- routerContextVal
214
- });
215
- try {
216
- const m2 = await info.loader();
217
- const moduleName = m2 && m2[Symbol.for("mf_module_id")];
218
- LoggerInstance.log(
219
- `createRemoteComponent LazyComponent loadRemote info >>>`,
220
- { basename, name: moduleName, module: m2, exportName, props }
221
- );
222
- const exportFn = m2[exportName];
223
- if (exportName in m2 && typeof exportFn === "function") {
224
- return {
225
- default: () => /* @__PURE__ */ React__default.createElement(
226
- RemoteApp,
227
- {
228
- name: moduleName,
229
- dispathPopstate: enableDispathPopstate,
230
- ...info,
231
- ...props,
232
- providerInfo: exportFn,
233
- basename
234
- }
235
- )
236
- };
237
- } else {
238
- LoggerInstance.log(
239
- `createRemoteComponent LazyComponent module not found >>>`,
240
- { basename, name: moduleName, module: m2, exportName, props }
241
- );
242
- throw Error(
243
- `Make sure that ${moduleName} has the correct export when export is ${String(
244
- exportName
245
- )}`
246
- );
247
- }
248
- } catch (error) {
249
- throw error;
187
+ LoggerInstance.log(`createRemoteComponent withRouterData >>>`, {
188
+ ...props,
189
+ basename,
190
+ routerContextVal,
191
+ enableDispathPopstate
192
+ });
193
+ if (enableDispathPopstate) {
194
+ const location = ReactRouterDOM.useLocation();
195
+ const [pathname, setPathname] = useState(location.pathname);
196
+ useEffect(() => {
197
+ if (pathname !== "" && pathname !== location.pathname) {
198
+ LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
199
+ name: props.name,
200
+ pathname: location.pathname
201
+ });
202
+ f();
250
203
  }
251
- });
252
- }, [exportName, basename, props.memoryRoute]);
253
- return /* @__PURE__ */ React__default.createElement(ErrorBoundary, { FallbackComponent: info.fallback }, /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: info.loading }, /* @__PURE__ */ React__default.createElement(LazyComponent, null)));
204
+ setPathname(location.pathname);
205
+ }, [location]);
206
+ }
207
+ return /* @__PURE__ */ React__default.createElement(WrappedComponent, { ...props, basename });
208
+ };
209
+ }
210
+ const RemoteApp$1 = withRouterData(RemoteApp);
211
+ function createLazyRemoteComponent(info) {
212
+ const exportName = (info == null ? void 0 : info.export) || "default";
213
+ return React__default.lazy(async () => {
214
+ LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
215
+ lazyComponent: info.loader,
216
+ exportName
217
+ });
218
+ try {
219
+ const m2 = await info.loader();
220
+ const moduleName = m2 && m2[Symbol.for("mf_module_id")];
221
+ LoggerInstance.log(
222
+ `createRemoteComponent LazyComponent loadRemote info >>>`,
223
+ { name: moduleName, module: m2, exportName }
224
+ );
225
+ const exportFn = m2[exportName];
226
+ if (exportName in m2 && typeof exportFn === "function") {
227
+ const RemoteAppComponent = forwardRef((props, _ref) => {
228
+ return /* @__PURE__ */ React__default.createElement(
229
+ RemoteApp$1,
230
+ {
231
+ name: moduleName,
232
+ providerInfo: exportFn,
233
+ exportName: info.export || "default",
234
+ ...props
235
+ }
236
+ );
237
+ });
238
+ return {
239
+ default: RemoteAppComponent
240
+ };
241
+ } else {
242
+ LoggerInstance.log(
243
+ `createRemoteComponent LazyComponent module not found >>>`,
244
+ { name: moduleName, module: m2, exportName }
245
+ );
246
+ throw Error(
247
+ `Make sure that ${moduleName} has the correct export when export is ${String(
248
+ exportName
249
+ )}`
250
+ );
251
+ }
252
+ } catch (error) {
253
+ throw error;
254
+ }
255
+ });
256
+ }
257
+ function createRemoteComponent(info) {
258
+ const LazyComponent = createLazyRemoteComponent(info);
259
+ return (props) => {
260
+ return /* @__PURE__ */ React__default.createElement(ErrorBoundary, { FallbackComponent: info.fallback }, /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: info.loading }, /* @__PURE__ */ React__default.createElement(LazyComponent, { ...props })));
254
261
  };
255
262
  }
256
263
  var client = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/bridge-react",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "@loadable/component": "^5.16.4",
27
27
  "react-error-boundary": "^4.0.13",
28
- "@module-federation/bridge-shared": "0.2.5"
28
+ "@module-federation/bridge-shared": "0.2.7"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": ">=16.9.0",
package/src/create.tsx CHANGED
@@ -1,14 +1,11 @@
1
- import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
2
- import * as ReactRouterDOM from 'react-router-dom';
1
+ import React, { forwardRef } from 'react';
3
2
  import type { ProviderParams } from '@module-federation/bridge-shared';
4
- import { LoggerInstance, pathJoin } from './utils';
5
- import { dispatchPopstateEnv } from '@module-federation/bridge-shared';
3
+ import { LoggerInstance } from './utils';
6
4
  import {
7
5
  ErrorBoundary,
8
6
  ErrorBoundaryPropsWithComponent,
9
7
  } from 'react-error-boundary';
10
-
11
- declare const __APP_VERSION__: string;
8
+ import RemoteApp from './remote';
12
9
 
13
10
  export interface RenderFnParams extends ProviderParams {
14
11
  dom?: any;
@@ -25,79 +22,67 @@ interface RemoteModule {
25
22
  };
26
23
  }
27
24
 
28
- interface RemoteAppParams {
29
- name: string;
30
- providerInfo: NonNullable<RemoteModule['provider']>;
31
- dispathPopstate: boolean;
32
- }
33
-
34
- const RemoteApp = ({
35
- name,
36
- memoryRoute,
37
- basename,
38
- providerInfo,
39
- dispathPopstate,
40
- ...resProps
41
- }: RemoteAppParams & ProviderParams) => {
42
- const rootRef = useRef(null);
43
- const renderDom = useRef(null);
44
- const providerInfoRef = useRef<any>(null);
45
- if (dispathPopstate) {
46
- const location = ReactRouterDOM.useLocation();
47
- const [pathname, setPathname] = useState(location.pathname);
48
-
49
- useEffect(() => {
50
- if (pathname !== '' && pathname !== location.pathname) {
51
- LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
52
- name,
53
- pathname: location.pathname,
54
- });
55
- dispatchPopstateEnv();
56
- }
57
- setPathname(location.pathname);
58
- }, [location]);
59
- }
60
-
61
- useEffect(() => {
62
- const renderTimeout = setTimeout(() => {
63
- const providerReturn = providerInfo();
64
- providerInfoRef.current = providerReturn;
65
- const renderProps = {
66
- name,
67
- dom: rootRef.current,
68
- basename,
69
- memoryRoute,
70
- ...resProps,
71
- };
72
- renderDom.current = rootRef.current;
25
+ function createLazyRemoteComponent<T, E extends keyof T>(info: {
26
+ loader: () => Promise<T>;
27
+ loading: React.ReactNode;
28
+ fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
29
+ export?: E;
30
+ }) {
31
+ const exportName = info?.export || 'default';
32
+ return React.lazy(async () => {
33
+ LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
34
+ lazyComponent: info.loader,
35
+ exportName,
36
+ });
37
+ try {
38
+ const m = (await info.loader()) as RemoteModule;
39
+ // @ts-ignore
40
+ const moduleName = m && m[Symbol.for('mf_module_id')];
73
41
  LoggerInstance.log(
74
- `createRemoteComponent LazyComponent render >>>`,
75
- renderProps,
42
+ `createRemoteComponent LazyComponent loadRemote info >>>`,
43
+ { name: moduleName, module: m, exportName },
76
44
  );
77
- providerReturn.render(renderProps);
78
- });
79
45
 
80
- return () => {
81
- clearTimeout(renderTimeout);
82
- setTimeout(() => {
83
- if (providerInfoRef.current?.destroy) {
84
- LoggerInstance.log(
85
- `createRemoteComponent LazyComponent destroy >>>`,
86
- { name, basename, dom: renderDom.current },
87
- );
88
- providerInfoRef.current?.destroy({
89
- dom: renderDom.current,
90
- });
91
- }
92
- });
93
- };
94
- }, []);
46
+ // @ts-ignore
47
+ const exportFn = m[exportName] as any;
95
48
 
96
- //@ts-ignore
97
- return <div ref={rootRef}></div>;
98
- };
49
+ if (exportName in m && typeof exportFn === 'function') {
50
+ const RemoteAppComponent = forwardRef<
51
+ HTMLDivElement,
52
+ {
53
+ basename?: ProviderParams['basename'];
54
+ memoryRoute?: ProviderParams['memoryRoute'];
55
+ }
56
+ >((props, _ref) => {
57
+ return (
58
+ <RemoteApp
59
+ name={moduleName}
60
+ providerInfo={exportFn}
61
+ exportName={info.export || 'default'}
62
+ {...props}
63
+ />
64
+ );
65
+ });
99
66
 
100
- (RemoteApp as any)['__APP_VERSION__'] = __APP_VERSION__;
67
+ return {
68
+ default: RemoteAppComponent,
69
+ };
70
+ } else {
71
+ LoggerInstance.log(
72
+ `createRemoteComponent LazyComponent module not found >>>`,
73
+ { name: moduleName, module: m, exportName },
74
+ );
75
+ throw Error(
76
+ `Make sure that ${moduleName} has the correct export when export is ${String(
77
+ exportName,
78
+ )}`,
79
+ );
80
+ }
81
+ } catch (error) {
82
+ throw error;
83
+ }
84
+ });
85
+ }
101
86
 
102
87
  export function createRemoteComponent<T, E extends keyof T>(info: {
103
88
  loader: () => Promise<T>;
@@ -114,121 +99,18 @@ export function createRemoteComponent<T, E extends keyof T>(info: {
114
99
  : {}
115
100
  : {};
116
101
 
102
+ const LazyComponent = createLazyRemoteComponent(info);
103
+
117
104
  return (
118
105
  props: {
119
106
  basename?: ProviderParams['basename'];
120
107
  memoryRoute?: ProviderParams['memoryRoute'];
121
108
  } & RawComponentType,
122
109
  ) => {
123
- const exportName = info?.export || 'default';
124
- let basename = '/';
125
- let enableDispathPopstate = false;
126
- let routerContextVal: any;
127
- try {
128
- ReactRouterDOM.useLocation();
129
- enableDispathPopstate = true;
130
- } catch {
131
- enableDispathPopstate = false;
132
- }
133
-
134
- if (props.basename) {
135
- basename = props.basename;
136
- } else if (enableDispathPopstate) {
137
- const ReactRouterDOMAny: any = ReactRouterDOM;
138
- // Avoid building tools checking references
139
- const useRouteMatch = ReactRouterDOMAny['use' + 'RouteMatch']; //v5
140
- const useHistory = ReactRouterDOMAny['use' + 'History']; //v5
141
- const useHref = ReactRouterDOMAny['use' + 'Href'];
142
- const UNSAFE_RouteContext = ReactRouterDOMAny['UNSAFE_' + 'RouteContext'];
143
-
144
- if (UNSAFE_RouteContext /* react-router@6 */) {
145
- if (useHref) {
146
- basename = useHref?.('/');
147
- }
148
- routerContextVal = useContext(UNSAFE_RouteContext);
149
- if (
150
- routerContextVal &&
151
- routerContextVal.matches &&
152
- routerContextVal.matches[0] &&
153
- routerContextVal.matches[0].pathnameBase
154
- ) {
155
- basename = pathJoin(
156
- basename,
157
- routerContextVal.matches[0].pathnameBase || '/',
158
- );
159
- }
160
- } /* react-router@5 */ else {
161
- const match = useRouteMatch?.(); // v5
162
- if (useHistory /* react-router@5 */) {
163
- // there is no dynamic switching of the router version in the project
164
- // so hooks can be used in conditional judgment
165
- const history = useHistory?.();
166
- // To be compatible to history@4.10.1 and @5.3.0 we cannot write like this `history.createHref(pathname)`
167
- basename = history?.createHref?.({ pathname: '/' });
168
- }
169
- if (match /* react-router@5 */) {
170
- basename = pathJoin(basename, match?.path || '/');
171
- }
172
- }
173
- }
174
-
175
- const LazyComponent = useMemo(() => {
176
- //@ts-ignore
177
- return React.lazy(async () => {
178
- LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
179
- basename,
180
- lazyComponent: info.loader,
181
- exportName,
182
- props,
183
- routerContextVal,
184
- });
185
- try {
186
- const m = (await info.loader()) as RemoteModule;
187
- // @ts-ignore
188
- const moduleName = m && m[Symbol.for('mf_module_id')];
189
- LoggerInstance.log(
190
- `createRemoteComponent LazyComponent loadRemote info >>>`,
191
- { basename, name: moduleName, module: m, exportName, props },
192
- );
193
-
194
- // @ts-ignore
195
- const exportFn = m[exportName] as any;
196
-
197
- if (exportName in m && typeof exportFn === 'function') {
198
- return {
199
- default: () => (
200
- <RemoteApp
201
- name={moduleName}
202
- dispathPopstate={enableDispathPopstate}
203
- {...info}
204
- {...props}
205
- providerInfo={exportFn}
206
- basename={basename}
207
- />
208
- ),
209
- };
210
- } else {
211
- LoggerInstance.log(
212
- `createRemoteComponent LazyComponent module not found >>>`,
213
- { basename, name: moduleName, module: m, exportName, props },
214
- );
215
- throw Error(
216
- `Make sure that ${moduleName} has the correct export when export is ${String(
217
- exportName,
218
- )}`,
219
- );
220
- }
221
- } catch (error) {
222
- throw error;
223
- }
224
- });
225
- }, [exportName, basename, props.memoryRoute]);
226
-
227
- //@ts-ignore
228
110
  return (
229
111
  <ErrorBoundary FallbackComponent={info.fallback}>
230
112
  <React.Suspense fallback={info.loading}>
231
- <LazyComponent />
113
+ <LazyComponent {...props} />
232
114
  </React.Suspense>
233
115
  </ErrorBoundary>
234
116
  );
@@ -0,0 +1,165 @@
1
+ import React, { useContext, useEffect, useRef, useState } from 'react';
2
+ import * as ReactRouterDOM from 'react-router-dom';
3
+ import type { ProviderParams } from '@module-federation/bridge-shared';
4
+ import { LoggerInstance, pathJoin } from '../utils';
5
+ import { dispatchPopstateEnv } from '@module-federation/bridge-shared';
6
+
7
+ declare const __APP_VERSION__: string;
8
+
9
+ export interface RenderFnParams extends ProviderParams {
10
+ dom?: any;
11
+ }
12
+
13
+ interface RemoteModule {
14
+ provider: () => {
15
+ render: (
16
+ info: ProviderParams & {
17
+ dom: any;
18
+ },
19
+ ) => void;
20
+ destroy: (info: { dom: any }) => void;
21
+ };
22
+ }
23
+
24
+ interface RemoteAppParams {
25
+ name: string;
26
+ providerInfo: NonNullable<RemoteModule['provider']>;
27
+ exportName: string | number | symbol;
28
+ }
29
+
30
+ const RemoteApp = ({
31
+ name,
32
+ memoryRoute,
33
+ basename,
34
+ providerInfo,
35
+ ...resProps
36
+ }: RemoteAppParams & ProviderParams) => {
37
+ const rootRef = useRef(null);
38
+ const renderDom = useRef(null);
39
+ const providerInfoRef = useRef<any>(null);
40
+
41
+ useEffect(() => {
42
+ const renderTimeout = setTimeout(() => {
43
+ const providerReturn = providerInfo();
44
+ providerInfoRef.current = providerReturn;
45
+ const renderProps = {
46
+ name,
47
+ dom: rootRef.current,
48
+ basename,
49
+ memoryRoute,
50
+ ...resProps,
51
+ };
52
+ renderDom.current = rootRef.current;
53
+ LoggerInstance.log(
54
+ `createRemoteComponent LazyComponent render >>>`,
55
+ renderProps,
56
+ );
57
+ providerReturn.render(renderProps);
58
+ });
59
+
60
+ return () => {
61
+ clearTimeout(renderTimeout);
62
+ setTimeout(() => {
63
+ if (providerInfoRef.current?.destroy) {
64
+ LoggerInstance.log(
65
+ `createRemoteComponent LazyComponent destroy >>>`,
66
+ { name, basename, dom: renderDom.current },
67
+ );
68
+ providerInfoRef.current?.destroy({
69
+ dom: renderDom.current,
70
+ });
71
+ }
72
+ });
73
+ };
74
+ }, []);
75
+
76
+ //@ts-ignore
77
+ return <div ref={rootRef}></div>;
78
+ };
79
+
80
+ (RemoteApp as any)['__APP_VERSION__'] = __APP_VERSION__;
81
+
82
+ interface ExtraDataProps {
83
+ basename?: string;
84
+ }
85
+
86
+ export function withRouterData<P extends Parameters<typeof RemoteApp>[0]>(
87
+ WrappedComponent: React.ComponentType<P & ExtraDataProps>,
88
+ ): React.FC<Omit<P, keyof ExtraDataProps>> {
89
+ return (props: any) => {
90
+ let enableDispathPopstate = false;
91
+ let routerContextVal: any;
92
+ try {
93
+ ReactRouterDOM.useLocation();
94
+ enableDispathPopstate = true;
95
+ } catch {
96
+ enableDispathPopstate = false;
97
+ }
98
+ let basename = '/';
99
+
100
+ if (!props.basename && enableDispathPopstate) {
101
+ const ReactRouterDOMAny: any = ReactRouterDOM;
102
+ // Avoid building tools checking references
103
+ const useRouteMatch = ReactRouterDOMAny['use' + 'RouteMatch']; //v5
104
+ const useHistory = ReactRouterDOMAny['use' + 'History']; //v5
105
+ const useHref = ReactRouterDOMAny['use' + 'Href'];
106
+ const UNSAFE_RouteContext = ReactRouterDOMAny['UNSAFE_' + 'RouteContext'];
107
+
108
+ if (UNSAFE_RouteContext /* react-router@6 */) {
109
+ if (useHref) {
110
+ basename = useHref?.('/');
111
+ }
112
+ routerContextVal = useContext(UNSAFE_RouteContext);
113
+ if (
114
+ routerContextVal &&
115
+ routerContextVal.matches &&
116
+ routerContextVal.matches.length > 0
117
+ ) {
118
+ const matchIndex = routerContextVal.matches.length - 1;
119
+ const pathnameBase =
120
+ routerContextVal.matches[matchIndex].pathnameBase;
121
+ basename = pathJoin(basename, pathnameBase || '/');
122
+ }
123
+ } /* react-router@5 */ else {
124
+ const match = useRouteMatch?.(); // v5
125
+ if (useHistory /* react-router@5 */) {
126
+ // there is no dynamic switching of the router version in the project
127
+ // so hooks can be used in conditional judgment
128
+ const history = useHistory?.();
129
+ // To be compatible to history@4.10.1 and @5.3.0 we cannot write like this `history.createHref(pathname)`
130
+ basename = history?.createHref?.({ pathname: '/' });
131
+ }
132
+ if (match /* react-router@5 */) {
133
+ basename = pathJoin(basename, match?.path || '/');
134
+ }
135
+ }
136
+ }
137
+
138
+ LoggerInstance.log(`createRemoteComponent withRouterData >>>`, {
139
+ ...props,
140
+ basename,
141
+ routerContextVal,
142
+ enableDispathPopstate,
143
+ });
144
+
145
+ if (enableDispathPopstate) {
146
+ const location = ReactRouterDOM.useLocation();
147
+ const [pathname, setPathname] = useState(location.pathname);
148
+
149
+ useEffect(() => {
150
+ if (pathname !== '' && pathname !== location.pathname) {
151
+ LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
152
+ name: props.name,
153
+ pathname: location.pathname,
154
+ });
155
+ dispatchPopstateEnv();
156
+ }
157
+ setPathname(location.pathname);
158
+ }, [location]);
159
+ }
160
+
161
+ return <WrappedComponent {...(props as P)} basename={basename} />;
162
+ };
163
+ }
164
+
165
+ export default withRouterData(RemoteApp);