@module-federation/bridge-react 0.0.0-next-20240620052430 → 0.2.0

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,7 +1,12 @@
1
1
  # @module-federation/bridge-react
2
2
 
3
- ## 0.0.0-next-20240620052430
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d2ab821: feat(bridge): Supports exporting and loading of application-level modules (with routing), currently supports react and vue3
4
8
 
5
9
  ### Patch Changes
6
10
 
7
- - @module-federation/bridge-shared@0.0.0-next-20240620052430
11
+ - Updated dependencies [d2ab821]
12
+ - @module-federation/bridge-shared@0.2.0
@@ -51,15 +51,17 @@ describe('bridge', () => {
51
51
  const BridgeComponent = createBridgeComponent({
52
52
  rootComponent: Component,
53
53
  });
54
- const RemoteComponent = createRemoteComponent(async () => {
55
- return {
56
- default: BridgeComponent,
57
- };
54
+ const RemoteComponent = createRemoteComponent({
55
+ loader: async () => {
56
+ return {
57
+ default: BridgeComponent,
58
+ };
59
+ },
60
+ fallback: () => <div></div>,
61
+ loading: <div>loading</div>,
58
62
  });
59
63
 
60
- const { container } = render(
61
- <RemoteComponent fallback={<div>loading</div>} msg={'hello world'} />,
62
- );
64
+ const { container } = render(<RemoteComponent msg={'hello world'} />);
63
65
  expect(getHtml(container)).toMatch('loading');
64
66
 
65
67
  await sleep(200);
@@ -48,8 +48,26 @@ function atLeastReact18(React2) {
48
48
  return false;
49
49
  }
50
50
  }
51
+ function pathJoin(...args) {
52
+ const res = args.reduce((res2, path) => {
53
+ let nPath = path;
54
+ if (!nPath || typeof nPath !== "string") {
55
+ return res2;
56
+ }
57
+ if (nPath[0] !== "/") {
58
+ nPath = `/${nPath}`;
59
+ }
60
+ const lastIndex = nPath.length - 1;
61
+ if (nPath[lastIndex] === "/") {
62
+ nPath = nPath.substring(0, lastIndex);
63
+ }
64
+ return res2 + nPath;
65
+ }, "");
66
+ return res || "/";
67
+ }
51
68
  const RouterContext = React.createContext(null);
52
69
  exports.LoggerInstance = LoggerInstance;
53
70
  exports.RouterContext = RouterContext;
54
71
  exports.atLeastReact18 = atLeastReact18;
55
72
  exports.f = f;
73
+ exports.pathJoin = pathJoin;
@@ -47,10 +47,28 @@ function atLeastReact18(React2) {
47
47
  return false;
48
48
  }
49
49
  }
50
+ function pathJoin(...args) {
51
+ const res = args.reduce((res2, path) => {
52
+ let nPath = path;
53
+ if (!nPath || typeof nPath !== "string") {
54
+ return res2;
55
+ }
56
+ if (nPath[0] !== "/") {
57
+ nPath = `/${nPath}`;
58
+ }
59
+ const lastIndex = nPath.length - 1;
60
+ if (nPath[lastIndex] === "/") {
61
+ nPath = nPath.substring(0, lastIndex);
62
+ }
63
+ return res2 + nPath;
64
+ }, "");
65
+ return res || "/";
66
+ }
50
67
  const RouterContext = React__default.createContext(null);
51
68
  export {
52
69
  LoggerInstance as L,
53
70
  RouterContext as R,
54
71
  atLeastReact18 as a,
55
- f
72
+ f,
73
+ pathJoin as p
56
74
  };
package/dist/index.cjs.js CHANGED
@@ -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-ePt4wynZ.cjs");
5
+ const context = require("./context--mtFt3tp.cjs");
6
6
  const ReactDOM = require("react-dom");
7
7
  function _interopNamespaceDefault(e) {
8
8
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -22,6 +22,101 @@ function _interopNamespaceDefault(e) {
22
22
  }
23
23
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
24
24
  const ReactRouterDOM__namespace = /* @__PURE__ */ _interopNamespaceDefault(ReactRouterDOM);
25
+ const ErrorBoundaryContext = React.createContext(null);
26
+ const initialState = {
27
+ didCatch: false,
28
+ error: null
29
+ };
30
+ class ErrorBoundary extends React.Component {
31
+ constructor(props) {
32
+ super(props);
33
+ this.resetErrorBoundary = this.resetErrorBoundary.bind(this);
34
+ this.state = initialState;
35
+ }
36
+ static getDerivedStateFromError(error) {
37
+ return {
38
+ didCatch: true,
39
+ error
40
+ };
41
+ }
42
+ resetErrorBoundary() {
43
+ const {
44
+ error
45
+ } = this.state;
46
+ if (error !== null) {
47
+ var _this$props$onReset, _this$props;
48
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
49
+ args[_key] = arguments[_key];
50
+ }
51
+ (_this$props$onReset = (_this$props = this.props).onReset) === null || _this$props$onReset === void 0 ? void 0 : _this$props$onReset.call(_this$props, {
52
+ args,
53
+ reason: "imperative-api"
54
+ });
55
+ this.setState(initialState);
56
+ }
57
+ }
58
+ componentDidCatch(error, info) {
59
+ var _this$props$onError, _this$props2;
60
+ (_this$props$onError = (_this$props2 = this.props).onError) === null || _this$props$onError === void 0 ? void 0 : _this$props$onError.call(_this$props2, error, info);
61
+ }
62
+ componentDidUpdate(prevProps, prevState) {
63
+ const {
64
+ didCatch
65
+ } = this.state;
66
+ const {
67
+ resetKeys
68
+ } = this.props;
69
+ if (didCatch && prevState.error !== null && hasArrayChanged(prevProps.resetKeys, resetKeys)) {
70
+ var _this$props$onReset2, _this$props3;
71
+ (_this$props$onReset2 = (_this$props3 = this.props).onReset) === null || _this$props$onReset2 === void 0 ? void 0 : _this$props$onReset2.call(_this$props3, {
72
+ next: resetKeys,
73
+ prev: prevProps.resetKeys,
74
+ reason: "keys"
75
+ });
76
+ this.setState(initialState);
77
+ }
78
+ }
79
+ render() {
80
+ const {
81
+ children,
82
+ fallbackRender,
83
+ FallbackComponent,
84
+ fallback
85
+ } = this.props;
86
+ const {
87
+ didCatch,
88
+ error
89
+ } = this.state;
90
+ let childToRender = children;
91
+ if (didCatch) {
92
+ const props = {
93
+ error,
94
+ resetErrorBoundary: this.resetErrorBoundary
95
+ };
96
+ if (typeof fallbackRender === "function") {
97
+ childToRender = fallbackRender(props);
98
+ } else if (FallbackComponent) {
99
+ childToRender = React.createElement(FallbackComponent, props);
100
+ } else if (fallback === null || React.isValidElement(fallback)) {
101
+ childToRender = fallback;
102
+ } else {
103
+ throw error;
104
+ }
105
+ }
106
+ return React.createElement(ErrorBoundaryContext.Provider, {
107
+ value: {
108
+ didCatch,
109
+ error,
110
+ resetErrorBoundary: this.resetErrorBoundary
111
+ }
112
+ }, childToRender);
113
+ }
114
+ }
115
+ function hasArrayChanged() {
116
+ let a = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : [];
117
+ let b = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [];
118
+ return a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]));
119
+ }
25
120
  const RemoteApp = ({
26
121
  name,
27
122
  memoryRoute,
@@ -83,9 +178,10 @@ const RemoteApp = ({
83
178
  }, []);
84
179
  return /* @__PURE__ */ React.createElement("div", { ref: rootRef });
85
180
  };
86
- RemoteApp["__APP_VERSION__"] = "0.0.1";
87
- function createRemoteComponent(lazyComponent, info) {
181
+ RemoteApp["__APP_VERSION__"] = "0.2.0";
182
+ function createRemoteComponent(info) {
88
183
  return (props) => {
184
+ var _a;
89
185
  const exportName = (info == null ? void 0 : info.export) || "default";
90
186
  let basename = "/";
91
187
  let enableDispathPopstate = false;
@@ -96,41 +192,84 @@ function createRemoteComponent(lazyComponent, info) {
96
192
  } catch {
97
193
  enableDispathPopstate = false;
98
194
  }
195
+ if (props.basename) {
196
+ basename = props.basename;
197
+ } else if (enableDispathPopstate) {
198
+ const ReactRouterDOMAny = ReactRouterDOM__namespace;
199
+ const useRouteMatch = ReactRouterDOMAny["useRouteMatch"];
200
+ const useHistory = ReactRouterDOMAny["useHistory"];
201
+ const useHref = ReactRouterDOMAny["useHref"];
202
+ const UNSAFE_RouteContext = ReactRouterDOMAny["UNSAFE_RouteContext"];
203
+ if (UNSAFE_RouteContext) {
204
+ if (useHref) {
205
+ basename = useHref == null ? void 0 : useHref("/");
206
+ }
207
+ 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
+ );
213
+ }
214
+ } else {
215
+ const match = useRouteMatch == null ? void 0 : useRouteMatch();
216
+ if (useHistory) {
217
+ const history = useHistory == null ? void 0 : useHistory();
218
+ basename = (_a = history == null ? void 0 : history.createHref) == null ? void 0 : _a.call(history, { pathname: "/" });
219
+ }
220
+ if (match) {
221
+ basename = context.pathJoin(basename, (match == null ? void 0 : match.path) || "/");
222
+ }
223
+ }
224
+ }
99
225
  const LazyComponent = React.useMemo(() => {
100
226
  return React.lazy(async () => {
101
227
  context.LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
102
228
  basename,
103
- lazyComponent,
229
+ lazyComponent: info.loader,
104
230
  exportName,
105
231
  props,
106
232
  routerContextVal
107
233
  });
108
- const m2 = await lazyComponent();
109
- const moduleName = m2 && m2[Symbol.for("mf_module_id")];
110
- context.LoggerInstance.log(
111
- `createRemoteComponent LazyComponent loadRemote info >>>`,
112
- { basename, name: moduleName, module: m2, exportName, props }
113
- );
114
- const exportFn = m2[exportName];
115
- if (exportName in m2 && typeof exportFn === "function") {
116
- return {
117
- default: () => /* @__PURE__ */ React.createElement(
118
- RemoteApp,
119
- {
120
- name: moduleName,
121
- dispathPopstate: enableDispathPopstate,
122
- ...info,
123
- ...props,
124
- providerInfo: exportFn,
125
- basename
126
- }
127
- )
128
- };
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;
129
269
  }
130
- throw Error("module not found");
131
270
  });
132
271
  }, [exportName, basename, props.memoryRoute]);
133
- return /* @__PURE__ */ React.createElement(React.Suspense, { fallback: props.fallback }, /* @__PURE__ */ React.createElement(LazyComponent, null));
272
+ return /* @__PURE__ */ React.createElement(ErrorBoundary, { FallbackComponent: info.fallback }, /* @__PURE__ */ React.createElement(React.Suspense, { fallback: info.loading }, /* @__PURE__ */ React.createElement(LazyComponent, null)));
134
273
  };
135
274
  }
136
275
  var client = {};
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import { ComponentType } from 'react';
1
2
  import { default as default_2 } from 'react';
3
+ import { ErrorInfo } from 'react';
4
+ import { PropsWithChildren } from 'react';
2
5
  import * as React_2 from 'react';
3
- import { ReactNode } from 'react';
4
6
 
5
7
  export declare function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>): () => {
6
8
  render(info: RenderFnParams & any): void;
@@ -11,14 +13,40 @@ export declare function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>
11
13
  __BRIDGE_FN__: (_args: T) => void;
12
14
  };
13
15
 
14
- export declare function createRemoteComponent<T, E extends keyof T>(lazyComponent: () => Promise<T>, info?: {
16
+ export declare function createRemoteComponent<T, E extends keyof T>(info: {
17
+ loader: () => Promise<T>;
18
+ loading: default_2.ReactNode;
19
+ fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
15
20
  export?: E;
16
21
  }): (props: {
17
- basename?: ProviderParams['basename'];
18
- memoryRoute?: ProviderParams['memoryRoute'];
19
- fallback: ReactNode;
22
+ basename?: ProviderParams["basename"];
23
+ memoryRoute?: ProviderParams["memoryRoute"];
20
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;
21
25
 
26
+ declare type ErrorBoundaryPropsWithComponent = ErrorBoundarySharedProps & {
27
+ fallback?: never;
28
+ FallbackComponent: ComponentType<FallbackProps>;
29
+ fallbackRender?: never;
30
+ };
31
+
32
+ declare type ErrorBoundarySharedProps = PropsWithChildren<{
33
+ onError?: (error: Error, info: ErrorInfo) => void;
34
+ onReset?: (details: {
35
+ reason: "imperative-api";
36
+ args: any[];
37
+ } | {
38
+ reason: "keys";
39
+ prev: any[] | undefined;
40
+ next: any[] | undefined;
41
+ }) => void;
42
+ resetKeys?: any[];
43
+ }>;
44
+
45
+ declare type FallbackProps = {
46
+ error: any;
47
+ resetErrorBoundary: (...args: any[]) => void;
48
+ };
49
+
22
50
  declare type ProviderFnParams<T> = {
23
51
  rootComponent: React_2.ComponentType<T>;
24
52
  };
package/dist/index.es.js CHANGED
@@ -1,8 +1,103 @@
1
1
  import * as React from "react";
2
- import React__default, { useMemo, useRef, useState, useEffect } from "react";
2
+ import React__default, { createContext, Component, createElement, isValidElement, useContext, useMemo, useRef, useState, useEffect } from "react";
3
3
  import * as ReactRouterDOM from "react-router-dom";
4
- import { L as LoggerInstance, f, a as atLeastReact18, R as RouterContext } from "./context-CPtN38Ur.js";
4
+ import { p as pathJoin, L as LoggerInstance, f, a as atLeastReact18, R as RouterContext } from "./context-Bw2PEwa6.js";
5
5
  import ReactDOM from "react-dom";
6
+ const ErrorBoundaryContext = createContext(null);
7
+ const initialState = {
8
+ didCatch: false,
9
+ error: null
10
+ };
11
+ class ErrorBoundary extends Component {
12
+ constructor(props) {
13
+ super(props);
14
+ this.resetErrorBoundary = this.resetErrorBoundary.bind(this);
15
+ this.state = initialState;
16
+ }
17
+ static getDerivedStateFromError(error) {
18
+ return {
19
+ didCatch: true,
20
+ error
21
+ };
22
+ }
23
+ resetErrorBoundary() {
24
+ const {
25
+ error
26
+ } = this.state;
27
+ if (error !== null) {
28
+ var _this$props$onReset, _this$props;
29
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
30
+ args[_key] = arguments[_key];
31
+ }
32
+ (_this$props$onReset = (_this$props = this.props).onReset) === null || _this$props$onReset === void 0 ? void 0 : _this$props$onReset.call(_this$props, {
33
+ args,
34
+ reason: "imperative-api"
35
+ });
36
+ this.setState(initialState);
37
+ }
38
+ }
39
+ componentDidCatch(error, info) {
40
+ var _this$props$onError, _this$props2;
41
+ (_this$props$onError = (_this$props2 = this.props).onError) === null || _this$props$onError === void 0 ? void 0 : _this$props$onError.call(_this$props2, error, info);
42
+ }
43
+ componentDidUpdate(prevProps, prevState) {
44
+ const {
45
+ didCatch
46
+ } = this.state;
47
+ const {
48
+ resetKeys
49
+ } = this.props;
50
+ if (didCatch && prevState.error !== null && hasArrayChanged(prevProps.resetKeys, resetKeys)) {
51
+ var _this$props$onReset2, _this$props3;
52
+ (_this$props$onReset2 = (_this$props3 = this.props).onReset) === null || _this$props$onReset2 === void 0 ? void 0 : _this$props$onReset2.call(_this$props3, {
53
+ next: resetKeys,
54
+ prev: prevProps.resetKeys,
55
+ reason: "keys"
56
+ });
57
+ this.setState(initialState);
58
+ }
59
+ }
60
+ render() {
61
+ const {
62
+ children,
63
+ fallbackRender,
64
+ FallbackComponent,
65
+ fallback
66
+ } = this.props;
67
+ const {
68
+ didCatch,
69
+ error
70
+ } = this.state;
71
+ let childToRender = children;
72
+ if (didCatch) {
73
+ const props = {
74
+ error,
75
+ resetErrorBoundary: this.resetErrorBoundary
76
+ };
77
+ if (typeof fallbackRender === "function") {
78
+ childToRender = fallbackRender(props);
79
+ } else if (FallbackComponent) {
80
+ childToRender = createElement(FallbackComponent, props);
81
+ } else if (fallback === null || isValidElement(fallback)) {
82
+ childToRender = fallback;
83
+ } else {
84
+ throw error;
85
+ }
86
+ }
87
+ return createElement(ErrorBoundaryContext.Provider, {
88
+ value: {
89
+ didCatch,
90
+ error,
91
+ resetErrorBoundary: this.resetErrorBoundary
92
+ }
93
+ }, childToRender);
94
+ }
95
+ }
96
+ function hasArrayChanged() {
97
+ let a = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : [];
98
+ let b = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [];
99
+ return a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]));
100
+ }
6
101
  const RemoteApp = ({
7
102
  name,
8
103
  memoryRoute,
@@ -64,9 +159,10 @@ const RemoteApp = ({
64
159
  }, []);
65
160
  return /* @__PURE__ */ React__default.createElement("div", { ref: rootRef });
66
161
  };
67
- RemoteApp["__APP_VERSION__"] = "0.0.1";
68
- function createRemoteComponent(lazyComponent, info) {
162
+ RemoteApp["__APP_VERSION__"] = "0.2.0";
163
+ function createRemoteComponent(info) {
69
164
  return (props) => {
165
+ var _a;
70
166
  const exportName = (info == null ? void 0 : info.export) || "default";
71
167
  let basename = "/";
72
168
  let enableDispathPopstate = false;
@@ -77,41 +173,84 @@ function createRemoteComponent(lazyComponent, info) {
77
173
  } catch {
78
174
  enableDispathPopstate = false;
79
175
  }
176
+ if (props.basename) {
177
+ basename = props.basename;
178
+ } else if (enableDispathPopstate) {
179
+ const ReactRouterDOMAny = ReactRouterDOM;
180
+ const useRouteMatch = ReactRouterDOMAny["useRouteMatch"];
181
+ const useHistory = ReactRouterDOMAny["useHistory"];
182
+ const useHref = ReactRouterDOMAny["useHref"];
183
+ const UNSAFE_RouteContext = ReactRouterDOMAny["UNSAFE_RouteContext"];
184
+ if (UNSAFE_RouteContext) {
185
+ if (useHref) {
186
+ basename = useHref == null ? void 0 : useHref("/");
187
+ }
188
+ 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
+ );
194
+ }
195
+ } else {
196
+ const match = useRouteMatch == null ? void 0 : useRouteMatch();
197
+ if (useHistory) {
198
+ const history = useHistory == null ? void 0 : useHistory();
199
+ basename = (_a = history == null ? void 0 : history.createHref) == null ? void 0 : _a.call(history, { pathname: "/" });
200
+ }
201
+ if (match) {
202
+ basename = pathJoin(basename, (match == null ? void 0 : match.path) || "/");
203
+ }
204
+ }
205
+ }
80
206
  const LazyComponent = useMemo(() => {
81
207
  return React__default.lazy(async () => {
82
208
  LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
83
209
  basename,
84
- lazyComponent,
210
+ lazyComponent: info.loader,
85
211
  exportName,
86
212
  props,
87
213
  routerContextVal
88
214
  });
89
- const m2 = await lazyComponent();
90
- const moduleName = m2 && m2[Symbol.for("mf_module_id")];
91
- LoggerInstance.log(
92
- `createRemoteComponent LazyComponent loadRemote info >>>`,
93
- { basename, name: moduleName, module: m2, exportName, props }
94
- );
95
- const exportFn = m2[exportName];
96
- if (exportName in m2 && typeof exportFn === "function") {
97
- return {
98
- default: () => /* @__PURE__ */ React__default.createElement(
99
- RemoteApp,
100
- {
101
- name: moduleName,
102
- dispathPopstate: enableDispathPopstate,
103
- ...info,
104
- ...props,
105
- providerInfo: exportFn,
106
- basename
107
- }
108
- )
109
- };
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;
110
250
  }
111
- throw Error("module not found");
112
251
  });
113
252
  }, [exportName, basename, props.memoryRoute]);
114
- return /* @__PURE__ */ React__default.createElement(React__default.Suspense, { fallback: props.fallback }, /* @__PURE__ */ React__default.createElement(LazyComponent, null));
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)));
115
254
  };
116
255
  }
117
256
  var client = {};
@@ -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-ePt4wynZ.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) {
@@ -57,26 +57,23 @@ function WraperRouterProvider(props) {
57
57
  WraperRouterProviderProps: props,
58
58
  router
59
59
  });
60
+ const RouterProvider = ReactRouterDom__namespace["RouterProvider"];
61
+ const createMemoryRouter = ReactRouterDom__namespace["createMemoryRouter"];
62
+ const createBrowserRouter = ReactRouterDom__namespace["createBrowserRouter"];
60
63
  if (!routerContextProps)
61
- return /* @__PURE__ */ React.createElement(ReactRouterDom__namespace.RouterProvider, { ...props });
64
+ return /* @__PURE__ */ React.createElement(RouterProvider, { ...props });
62
65
  if (routerContextProps.memoryRoute) {
63
- const MemeoryRouterInstance = ReactRouterDom__namespace.createMemoryRouter(routers, {
66
+ const MemeoryRouterInstance = createMemoryRouter(routers, {
64
67
  initialEntries: [routerContextProps == null ? void 0 : routerContextProps.memoryRoute.entryPath]
65
68
  });
66
- return /* @__PURE__ */ React.createElement(ReactRouterDom__namespace.RouterProvider, { router: MemeoryRouterInstance });
69
+ return /* @__PURE__ */ React.createElement(RouterProvider, { router: MemeoryRouterInstance });
67
70
  } else {
68
- const BrowserRouterInstance = ReactRouterDom__namespace.createBrowserRouter(routers, {
71
+ const BrowserRouterInstance = createBrowserRouter(routers, {
69
72
  basename: routerContextProps.basename,
70
73
  future: router.future,
71
74
  window: router.window
72
75
  });
73
- return /* @__PURE__ */ React.createElement(
74
- ReactRouterDom__namespace.RouterProvider,
75
- {
76
- ...propsRes,
77
- router: BrowserRouterInstance
78
- }
79
- );
76
+ return /* @__PURE__ */ React.createElement(RouterProvider, { ...propsRes, router: BrowserRouterInstance });
80
77
  }
81
78
  }
82
79
  exports.BrowserRouter = WraperRouter;
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-CPtN38Ur.js";
4
+ import { R as RouterContext, L as LoggerInstance } from "./context-Bw2PEwa6.js";
5
5
  function WraperRouter(props) {
6
6
  const { basename, ...propsRes } = props;
7
7
  const routerContextProps = useContext(RouterContext) || {};
@@ -39,26 +39,23 @@ function WraperRouterProvider(props) {
39
39
  WraperRouterProviderProps: props,
40
40
  router
41
41
  });
42
+ const RouterProvider = ReactRouterDom["RouterProvider"];
43
+ const createMemoryRouter = ReactRouterDom["createMemoryRouter"];
44
+ const createBrowserRouter = ReactRouterDom["createBrowserRouter"];
42
45
  if (!routerContextProps)
43
- return /* @__PURE__ */ React__default.createElement(ReactRouterDom.RouterProvider, { ...props });
46
+ return /* @__PURE__ */ React__default.createElement(RouterProvider, { ...props });
44
47
  if (routerContextProps.memoryRoute) {
45
- const MemeoryRouterInstance = ReactRouterDom.createMemoryRouter(routers, {
48
+ const MemeoryRouterInstance = createMemoryRouter(routers, {
46
49
  initialEntries: [routerContextProps == null ? void 0 : routerContextProps.memoryRoute.entryPath]
47
50
  });
48
- return /* @__PURE__ */ React__default.createElement(ReactRouterDom.RouterProvider, { router: MemeoryRouterInstance });
51
+ return /* @__PURE__ */ React__default.createElement(RouterProvider, { router: MemeoryRouterInstance });
49
52
  } else {
50
- const BrowserRouterInstance = ReactRouterDom.createBrowserRouter(routers, {
53
+ const BrowserRouterInstance = createBrowserRouter(routers, {
51
54
  basename: routerContextProps.basename,
52
55
  future: router.future,
53
56
  window: router.window
54
57
  });
55
- return /* @__PURE__ */ React__default.createElement(
56
- ReactRouterDom.RouterProvider,
57
- {
58
- ...propsRes,
59
- router: BrowserRouterInstance
60
- }
61
- );
58
+ return /* @__PURE__ */ React__default.createElement(RouterProvider, { ...propsRes, router: BrowserRouterInstance });
62
59
  }
63
60
  }
64
61
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/bridge-react",
3
- "version": "0.0.0-next-20240620052430",
3
+ "version": "0.2.0",
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.0.0-next-20240620052430"
28
+ "@module-federation/bridge-shared": "0.2.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": ">=16.9.0",
package/src/create.tsx CHANGED
@@ -1,8 +1,12 @@
1
- import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import * as ReactRouterDOM from 'react-router-dom';
3
3
  import type { ProviderParams } from '@module-federation/bridge-shared';
4
- import { LoggerInstance } from './utils';
4
+ import { LoggerInstance, pathJoin } from './utils';
5
5
  import { dispatchPopstateEnv } from '@module-federation/bridge-shared';
6
+ import {
7
+ ErrorBoundary,
8
+ ErrorBoundaryPropsWithComponent,
9
+ } from 'react-error-boundary';
6
10
 
7
11
  declare const __APP_VERSION__: string;
8
12
 
@@ -95,12 +99,12 @@ const RemoteApp = ({
95
99
 
96
100
  (RemoteApp as any)['__APP_VERSION__'] = __APP_VERSION__;
97
101
 
98
- export function createRemoteComponent<T, E extends keyof T>(
99
- lazyComponent: () => Promise<T>,
100
- info?: {
101
- export?: E;
102
- },
103
- ) {
102
+ export function createRemoteComponent<T, E extends keyof T>(info: {
103
+ loader: () => Promise<T>;
104
+ loading: React.ReactNode;
105
+ fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
106
+ export?: E;
107
+ }) {
104
108
  type ExportType = T[E] extends (...args: any) => any
105
109
  ? ReturnType<T[E]>
106
110
  : never;
@@ -114,7 +118,6 @@ export function createRemoteComponent<T, E extends keyof T>(
114
118
  props: {
115
119
  basename?: ProviderParams['basename'];
116
120
  memoryRoute?: ProviderParams['memoryRoute'];
117
- fallback: ReactNode;
118
121
  } & RawComponentType,
119
122
  ) => {
120
123
  const exportName = info?.export || 'default';
@@ -128,90 +131,106 @@ export function createRemoteComponent<T, E extends keyof T>(
128
131
  enableDispathPopstate = false;
129
132
  }
130
133
 
131
- // if (props.basename) {
132
- // basename = props.basename
133
- // } else if (enableDispathPopstate) {
134
- // const routerV5Api = 'useRoute' + 'Match';
135
- // const routerV6Api = 'useMatches';
136
- // const routerV5History = 'use' + 'History';
137
- // const useHref = 'useHref';
138
-
139
- // const match = (ReactRouterDOM as any)[routerV5Api]?.();
140
- // const matchs = (ReactRouterDOM as any)[routerV6Api]?.();
141
- // const location = ReactRouterDOM.useLocation();
142
-
143
- // if ((ReactRouterDOM as any)[routerV5History] /* react-router@5 */) {
144
- // // there is no dynamic switching of the router version in the project
145
- // // so hooks can be used in conditional judgment
146
- // // eslint-disable-next-line react-hooks/rules-of-hooks
147
- // const history = (ReactRouterDOM as any)[routerV5History]?.();
148
- // // To be compatible to history@4.10.1 and @5.3.0 we cannot write like this `history.createHref(pathname)`
149
- // basename = history?.createHref?.({ pathname: '/' });
150
- // } else if (useHref /* react-router@6 */) {
151
- // // eslint-disable-next-line react-hooks/rules-of-hooks
152
- // basename = useHref?.('/');
153
- // }
154
- // }
155
-
156
- // if (ReactRouterDOM.UNSAFE_RouteContext && enableDispathPopstate) {
157
- // routerContextVal = eval('useContext(ReactRouterDOM.UNSAFE_RouteContext)');
158
- // if (
159
- // routerContextVal &&
160
- // routerContextVal.matches &&
161
- // routerContextVal.matches[0] &&
162
- // routerContextVal.matches[0].pathnameBase
163
- // ) {
164
- // basename = routerContextVal.matches[0].pathnameBase;
165
- // }
166
- // enableDispathPopstate =
167
- // routerContextVal?.matches && routerContextVal?.matches.length > 0;
168
- // }
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
+ }
169
174
 
170
175
  const LazyComponent = useMemo(() => {
171
176
  //@ts-ignore
172
177
  return React.lazy(async () => {
173
178
  LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
174
179
  basename,
175
- lazyComponent,
180
+ lazyComponent: info.loader,
176
181
  exportName,
177
182
  props,
178
183
  routerContextVal,
179
184
  });
180
- const m = (await lazyComponent()) as RemoteModule;
181
- // @ts-ignore
182
- const moduleName = m && m[Symbol.for('mf_module_id')];
183
- LoggerInstance.log(
184
- `createRemoteComponent LazyComponent loadRemote info >>>`,
185
- { basename, name: moduleName, module: m, exportName, props },
186
- );
187
-
188
- // @ts-ignore
189
- const exportFn = m[exportName] as any;
190
-
191
- if (exportName in m && typeof exportFn === 'function') {
192
- return {
193
- default: () => (
194
- <RemoteApp
195
- name={moduleName}
196
- dispathPopstate={enableDispathPopstate}
197
- {...info}
198
- {...props}
199
- providerInfo={exportFn}
200
- basename={basename}
201
- />
202
- ),
203
- };
204
- }
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
+ );
205
193
 
206
- throw Error('module not found');
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
+ }
207
224
  });
208
225
  }, [exportName, basename, props.memoryRoute]);
209
226
 
210
227
  //@ts-ignore
211
228
  return (
212
- <React.Suspense fallback={props.fallback}>
213
- <LazyComponent />
214
- </React.Suspense>
229
+ <ErrorBoundary FallbackComponent={info.fallback}>
230
+ <React.Suspense fallback={info.loading}>
231
+ <LazyComponent />
232
+ </React.Suspense>
233
+ </ErrorBoundary>
215
234
  );
216
235
  };
217
236
  }
package/src/router.tsx CHANGED
@@ -47,24 +47,25 @@ function WraperRouterProvider(
47
47
  WraperRouterProviderProps: props,
48
48
  router,
49
49
  });
50
- if (!routerContextProps) return <ReactRouterDom.RouterProvider {...props} />;
50
+ const RouterProvider = (ReactRouterDom as any)['Router' + 'Provider'];
51
+ const createMemoryRouter = (ReactRouterDom as any)['create' + 'MemoryRouter'];
52
+ const createBrowserRouter = (ReactRouterDom as any)[
53
+ 'create' + 'BrowserRouter'
54
+ ];
55
+ if (!routerContextProps) return <RouterProvider {...props} />;
56
+
51
57
  if (routerContextProps.memoryRoute) {
52
- const MemeoryRouterInstance = ReactRouterDom.createMemoryRouter(routers, {
58
+ const MemeoryRouterInstance = createMemoryRouter(routers, {
53
59
  initialEntries: [routerContextProps?.memoryRoute.entryPath],
54
60
  });
55
- return <ReactRouterDom.RouterProvider router={MemeoryRouterInstance} />;
61
+ return <RouterProvider router={MemeoryRouterInstance} />;
56
62
  } else {
57
- const BrowserRouterInstance = ReactRouterDom.createBrowserRouter(routers, {
63
+ const BrowserRouterInstance = createBrowserRouter(routers, {
58
64
  basename: routerContextProps.basename,
59
65
  future: router.future,
60
66
  window: router.window,
61
67
  });
62
- return (
63
- <ReactRouterDom.RouterProvider
64
- {...propsRes}
65
- router={BrowserRouterInstance}
66
- />
67
- );
68
+ return <RouterProvider {...propsRes} router={BrowserRouterInstance} />;
68
69
  }
69
70
  }
70
71
 
package/src/utils.ts CHANGED
@@ -21,3 +21,21 @@ export function atLeastReact18(React: typeReact) {
21
21
  return false;
22
22
  }
23
23
  }
24
+
25
+ export function pathJoin(...args: string[]) {
26
+ const res = args.reduce((res, path: string) => {
27
+ let nPath = path;
28
+ if (!nPath || typeof nPath !== 'string') {
29
+ return res;
30
+ }
31
+ if (nPath[0] !== '/') {
32
+ nPath = `/${nPath}`;
33
+ }
34
+ const lastIndex = nPath.length - 1;
35
+ if (nPath[lastIndex] === '/') {
36
+ nPath = nPath.substring(0, lastIndex);
37
+ }
38
+ return res + nPath;
39
+ }, '');
40
+ return res || '/';
41
+ }