@module-federation/bridge-react 0.0.0-next-20240726084328 → 0.0.0-next-20240731105745

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,10 +1,24 @@
1
1
  # @module-federation/bridge-react
2
2
 
3
- ## 0.0.0-next-20240726084328
3
+ ## 0.0.0-next-20240731105745
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - @module-federation/bridge-shared@0.0.0-next-20240726084328
7
+ - 67fa05b: feat(@module-federation/bridge): optimize @module-federation/bridge package
8
+ - Updated dependencies [67fa05b]
9
+ - @module-federation/bridge-shared@0.0.0-next-20240731105745
10
+
11
+ ## 0.3.3
12
+
13
+ ### Patch Changes
14
+
15
+ - @module-federation/bridge-shared@0.3.3
16
+
17
+ ## 0.3.2
18
+
19
+ ### Patch Changes
20
+
21
+ - @module-federation/bridge-shared@0.3.2
8
22
 
9
23
  ## 0.3.1
10
24
 
@@ -8,7 +8,7 @@ import {
8
8
  screen,
9
9
  waitFor,
10
10
  } from '@testing-library/react';
11
- import { createContainer, getHtml, sleep } from './util';
11
+ import { createContainer, createCustomContainer, getHtml, sleep } from './util';
12
12
 
13
13
  describe('bridge', () => {
14
14
  let containerInfo: ReturnType<typeof createContainer>;
@@ -45,8 +45,8 @@ describe('bridge', () => {
45
45
  });
46
46
 
47
47
  it('createRemoteComponent', async () => {
48
- function Component(info: { msg: string }) {
49
- return <div>life cycle render {info.msg}</div>;
48
+ function Component({ props }: { props?: Record<string, any> }) {
49
+ return <div>life cycle render {props?.msg}</div>;
50
50
  }
51
51
  const BridgeComponent = createBridgeComponent({
52
52
  rootComponent: Component,
@@ -61,11 +61,82 @@ describe('bridge', () => {
61
61
  loading: <div>loading</div>,
62
62
  });
63
63
 
64
- const { container } = render(<RemoteComponent msg={'hello world'} />);
64
+ const { container } = render(
65
+ <RemoteComponent props={{ msg: 'hello world' }} />,
66
+ );
67
+ expect(getHtml(container)).toMatch('loading');
68
+
69
+ await sleep(200);
70
+ expect(getHtml(container)).toMatch('life cycle render');
71
+ expect(getHtml(container)).toMatch('hello world');
72
+ });
73
+
74
+ it('createRemoteComponent with dom provided, will render on the provided dom', async () => {
75
+ containerInfo = createCustomContainer();
76
+
77
+ function Component({ props }: { props?: Record<string, any> }) {
78
+ return <div>life cycle render {props?.msg}</div>;
79
+ }
80
+ const BridgeComponent = createBridgeComponent({
81
+ rootComponent: Component,
82
+ });
83
+ const RemoteComponent = createRemoteComponent({
84
+ loader: async () => {
85
+ return {
86
+ default: BridgeComponent,
87
+ };
88
+ },
89
+ fallback: () => <div></div>,
90
+ loading: <div>loading</div>,
91
+ dom: '#container-custom',
92
+ });
93
+
94
+ const { container } = render(
95
+ <RemoteComponent props={{ msg: 'hello there' }} />,
96
+ );
97
+ expect(getHtml(container)).toMatch('loading');
98
+
99
+ await sleep(200);
100
+
101
+ const element = screen.getByTestId('container-custom');
102
+ expect(element.children.length).toBeGreaterThan(0);
103
+ expect(element.innerHTML).toContain('life cycle render');
104
+ expect(element.innerHTML).toContain('hello there');
105
+
106
+ const elementDefault = screen.getByTestId('container');
107
+ expect(elementDefault.children.length).toBe(0);
108
+ expect(elementDefault.innerHTML).toContain('');
109
+ });
110
+
111
+ it('createRemoteComponent and obtain ref property', async () => {
112
+ const ref = {
113
+ current: null,
114
+ };
115
+
116
+ function Component({ props }: { props?: Record<string, any> }) {
117
+ return <div>life cycle render {props?.msg}</div>;
118
+ }
119
+ const BridgeComponent = createBridgeComponent({
120
+ rootComponent: Component,
121
+ });
122
+ const RemoteComponent = createRemoteComponent({
123
+ loader: async () => {
124
+ return {
125
+ default: BridgeComponent,
126
+ };
127
+ },
128
+ fallback: () => <div></div>,
129
+ loading: <div>loading</div>,
130
+ });
131
+
132
+ const { container } = render(
133
+ <RemoteComponent ref={ref} props={{ msg: 'hello world' }} />,
134
+ );
65
135
  expect(getHtml(container)).toMatch('loading');
66
136
 
67
137
  await sleep(200);
68
138
  expect(getHtml(container)).toMatch('life cycle render');
69
139
  expect(getHtml(container)).toMatch('hello world');
140
+ expect(ref.current).not.toBeNull();
70
141
  });
71
142
  });
package/__tests__/util.ts CHANGED
@@ -12,6 +12,24 @@ export async function sleep(time: number) {
12
12
  export function createContainer() {
13
13
  const container = document.createElement('div');
14
14
  container.setAttribute('id', 'container');
15
+ container.setAttribute('data-testid', 'container');
16
+
17
+ container.setAttribute('background', 'rgb(255, 112, 127)');
18
+
19
+ document.body.appendChild(container);
20
+
21
+ return {
22
+ clean: () => {
23
+ document.body.removeChild(container);
24
+ },
25
+ container,
26
+ };
27
+ }
28
+
29
+ export function createCustomContainer() {
30
+ const container = document.createElement('div');
31
+ container.setAttribute('id', 'container-custom');
32
+ container.setAttribute('data-testid', 'container-custom');
15
33
  document.body.appendChild(container);
16
34
 
17
35
  return {
package/dist/index.cjs.js CHANGED
@@ -138,14 +138,16 @@ const RemoteAppWrapper = React.forwardRef(function(props, ref) {
138
138
  providerInfoRef.current = providerReturn;
139
139
  let domElement = null;
140
140
  if (dom) {
141
- domElement = document.getElementById(dom.replace("#", ""));
141
+ domElement = document.querySelector(dom);
142
+ if (!domElement || !(domElement instanceof HTMLElement)) {
143
+ throw new Error(`Invalid dom: ${dom}`);
144
+ }
142
145
  rootRef.current = domElement;
143
146
  } else {
144
147
  domElement = rootRef.current;
145
148
  }
146
149
  const renderProps = {
147
150
  name,
148
- // dom: rootRef.current,
149
151
  dom: domElement,
150
152
  basename,
151
153
  memoryRoute,
@@ -174,9 +176,16 @@ const RemoteAppWrapper = React.forwardRef(function(props, ref) {
174
176
  });
175
177
  };
176
178
  }, []);
177
- return dom ? null : /* @__PURE__ */ React.createElement("div", { className: props == null ? void 0 : props.className, style: props == null ? void 0 : props.style, ref: rootRef });
179
+ return dom ? null : /* @__PURE__ */ React.createElement(
180
+ "div",
181
+ {
182
+ className: props == null ? void 0 : props.className,
183
+ style: props == null ? void 0 : props.style,
184
+ ref: rootRef
185
+ }
186
+ );
178
187
  };
179
- RemoteApp2["__APP_VERSION__"] = "0.3.1";
188
+ RemoteApp2["__APP_VERSION__"] = "0.3.3";
180
189
  return /* @__PURE__ */ React.createElement(RemoteApp2, null);
181
190
  });
182
191
  function withRouterData(WrappedComponent) {
@@ -328,7 +337,14 @@ function createBridgeComponent(bridgeInfo) {
328
337
  const RawComponent = (info) => {
329
338
  const { appInfo, propsInfo, ...restProps } = info;
330
339
  const { name, memoryRoute, basename = "/" } = appInfo;
331
- return /* @__PURE__ */ React__namespace.createElement(context.RouterContext.Provider, { value: { name, basename, memoryRoute } }, /* @__PURE__ */ React__namespace.createElement(bridgeInfo.rootComponent, { ...propsInfo, basename, ...restProps }));
340
+ return /* @__PURE__ */ React__namespace.createElement(context.RouterContext.Provider, { value: { name, basename, memoryRoute } }, /* @__PURE__ */ React__namespace.createElement(
341
+ bridgeInfo.rootComponent,
342
+ {
343
+ ...propsInfo,
344
+ basename,
345
+ ...restProps
346
+ }
347
+ ));
332
348
  };
333
349
  return {
334
350
  async render(info) {
@@ -336,20 +352,22 @@ function createBridgeComponent(bridgeInfo) {
336
352
  const { name, basename, memoryRoute, ...propsInfo } = info;
337
353
  if (context.atLeastReact18(React__namespace)) {
338
354
  if (bridgeInfo == null ? void 0 : bridgeInfo.render) {
339
- Promise.resolve(bridgeInfo == null ? void 0 : bridgeInfo.render(
340
- /* @__PURE__ */ React__namespace.createElement(
341
- RawComponent,
342
- {
343
- propsInfo,
344
- appInfo: {
345
- name,
346
- basename,
347
- memoryRoute
355
+ Promise.resolve(
356
+ bridgeInfo == null ? void 0 : bridgeInfo.render(
357
+ /* @__PURE__ */ React__namespace.createElement(
358
+ RawComponent,
359
+ {
360
+ propsInfo,
361
+ appInfo: {
362
+ name,
363
+ basename,
364
+ memoryRoute
365
+ }
348
366
  }
349
- }
350
- ),
351
- info.dom
352
- )).then((root) => rootMap.set(info.dom, root));
367
+ ),
368
+ info.dom
369
+ )
370
+ ).then((root) => rootMap.set(info.dom, root));
353
371
  } else {
354
372
  const root = client.createRoot(info.dom);
355
373
  root.render(
package/dist/index.es.js CHANGED
@@ -119,14 +119,16 @@ const RemoteAppWrapper = forwardRef(function(props, ref) {
119
119
  providerInfoRef.current = providerReturn;
120
120
  let domElement = null;
121
121
  if (dom) {
122
- domElement = document.getElementById(dom.replace("#", ""));
122
+ domElement = document.querySelector(dom);
123
+ if (!domElement || !(domElement instanceof HTMLElement)) {
124
+ throw new Error(`Invalid dom: ${dom}`);
125
+ }
123
126
  rootRef.current = domElement;
124
127
  } else {
125
128
  domElement = rootRef.current;
126
129
  }
127
130
  const renderProps = {
128
131
  name,
129
- // dom: rootRef.current,
130
132
  dom: domElement,
131
133
  basename,
132
134
  memoryRoute,
@@ -155,9 +157,16 @@ const RemoteAppWrapper = forwardRef(function(props, ref) {
155
157
  });
156
158
  };
157
159
  }, []);
158
- return dom ? null : /* @__PURE__ */ React__default.createElement("div", { className: props == null ? void 0 : props.className, style: props == null ? void 0 : props.style, ref: rootRef });
160
+ return dom ? null : /* @__PURE__ */ React__default.createElement(
161
+ "div",
162
+ {
163
+ className: props == null ? void 0 : props.className,
164
+ style: props == null ? void 0 : props.style,
165
+ ref: rootRef
166
+ }
167
+ );
159
168
  };
160
- RemoteApp2["__APP_VERSION__"] = "0.3.1";
169
+ RemoteApp2["__APP_VERSION__"] = "0.3.3";
161
170
  return /* @__PURE__ */ React__default.createElement(RemoteApp2, null);
162
171
  });
163
172
  function withRouterData(WrappedComponent) {
@@ -309,7 +318,14 @@ function createBridgeComponent(bridgeInfo) {
309
318
  const RawComponent = (info) => {
310
319
  const { appInfo, propsInfo, ...restProps } = info;
311
320
  const { name, memoryRoute, basename = "/" } = appInfo;
312
- return /* @__PURE__ */ React.createElement(RouterContext.Provider, { value: { name, basename, memoryRoute } }, /* @__PURE__ */ React.createElement(bridgeInfo.rootComponent, { ...propsInfo, basename, ...restProps }));
321
+ return /* @__PURE__ */ React.createElement(RouterContext.Provider, { value: { name, basename, memoryRoute } }, /* @__PURE__ */ React.createElement(
322
+ bridgeInfo.rootComponent,
323
+ {
324
+ ...propsInfo,
325
+ basename,
326
+ ...restProps
327
+ }
328
+ ));
313
329
  };
314
330
  return {
315
331
  async render(info) {
@@ -317,20 +333,22 @@ function createBridgeComponent(bridgeInfo) {
317
333
  const { name, basename, memoryRoute, ...propsInfo } = info;
318
334
  if (atLeastReact18(React)) {
319
335
  if (bridgeInfo == null ? void 0 : bridgeInfo.render) {
320
- Promise.resolve(bridgeInfo == null ? void 0 : bridgeInfo.render(
321
- /* @__PURE__ */ React.createElement(
322
- RawComponent,
323
- {
324
- propsInfo,
325
- appInfo: {
326
- name,
327
- basename,
328
- memoryRoute
336
+ Promise.resolve(
337
+ bridgeInfo == null ? void 0 : bridgeInfo.render(
338
+ /* @__PURE__ */ React.createElement(
339
+ RawComponent,
340
+ {
341
+ propsInfo,
342
+ appInfo: {
343
+ name,
344
+ basename,
345
+ memoryRoute
346
+ }
329
347
  }
330
- }
331
- ),
332
- info.dom
333
- )).then((root) => rootMap.set(info.dom, root));
348
+ ),
349
+ info.dom
350
+ )
351
+ ).then((root) => rootMap.set(info.dom, root));
334
352
  } else {
335
353
  const root = client.createRoot(info.dom);
336
354
  root.render(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@module-federation/bridge-react",
3
- "version": "0.0.0-next-20240726084328",
3
+ "version": "0.0.0-next-20240731105745",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,7 +35,7 @@
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-20240726084328"
38
+ "@module-federation/bridge-shared": "0.0.0-next-20240731105745"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "react": ">=16.9.0",
package/src/create.tsx CHANGED
@@ -1,5 +1,9 @@
1
1
  import React, { forwardRef } from 'react';
2
- import type { ForwardRefExoticComponent, PropsWithoutRef, RefAttributes } from 'react';
2
+ import type {
3
+ ForwardRefExoticComponent,
4
+ PropsWithoutRef,
5
+ RefAttributes,
6
+ } from 'react';
3
7
  import type { ProviderParams } from '@module-federation/bridge-shared';
4
8
  import { LoggerInstance } from './utils';
5
9
  import {
@@ -11,11 +15,10 @@ import RemoteApp from './remote';
11
15
  export interface RenderFnParams extends ProviderParams {
12
16
  dom?: any;
13
17
  }
18
+
14
19
  interface RemoteModule {
15
20
  provider: () => {
16
- render: (
17
- info: RenderFnParams,
18
- ) => void;
21
+ render: (info: RenderFnParams) => void;
19
22
  destroy: (info: { dom: any }) => void;
20
23
  };
21
24
  }
@@ -90,7 +93,9 @@ export function createRemoteComponent<T, E extends keyof T>(info: {
90
93
  fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
91
94
  export?: E;
92
95
  dom?: string;
93
- }): ForwardRefExoticComponent<PropsWithoutRef<ProviderParams> & RefAttributes<HTMLElement | HTMLDivElement>> {
96
+ }): ForwardRefExoticComponent<
97
+ PropsWithoutRef<ProviderParams> & RefAttributes<HTMLElement | HTMLDivElement>
98
+ > {
94
99
  // type ExportType = T[E] extends (...args: any) => any
95
100
  // ? ReturnType<T[E]>
96
101
  // : never;
@@ -100,7 +105,7 @@ export function createRemoteComponent<T, E extends keyof T>(info: {
100
105
  // : {}
101
106
  // : {};
102
107
 
103
- return forwardRef(function (props, ref) {
108
+ return forwardRef(function (props, ref) {
104
109
  const LazyComponent = createLazyRemoteComponent(info);
105
110
  return (
106
111
  <ErrorBoundary FallbackComponent={info.fallback}>
package/src/provider.tsx CHANGED
@@ -12,7 +12,10 @@ import { LoggerInstance, atLeastReact18 } from './utils';
12
12
  type RootType = HTMLElement | ReactDOMClient.Root;
13
13
  type ProviderFnParams<T> = {
14
14
  rootComponent: React.ComponentType<T>;
15
- render?: (App: React.ReactElement, id?: HTMLElement | string) => RootType | Promise<RootType>;
15
+ render?: (
16
+ App: React.ReactElement,
17
+ id?: HTMLElement | string,
18
+ ) => RootType | Promise<RootType>;
16
19
  };
17
20
 
18
21
  export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
@@ -24,7 +27,11 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
24
27
 
25
28
  return (
26
29
  <RouterContext.Provider value={{ name, basename, memoryRoute }}>
27
- <bridgeInfo.rootComponent {...propsInfo} basename={basename} {...restProps} />
30
+ <bridgeInfo.rootComponent
31
+ {...propsInfo}
32
+ basename={basename}
33
+ {...restProps}
34
+ />
28
35
  </RouterContext.Provider>
29
36
  );
30
37
  };
@@ -34,19 +41,20 @@ export function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>) {
34
41
  LoggerInstance.log(`createBridgeComponent render Info`, info);
35
42
  const { name, basename, memoryRoute, ...propsInfo } = info;
36
43
  if (atLeastReact18(React)) {
37
- // render is provided by user
38
44
  if (bridgeInfo?.render) {
39
- Promise.resolve(bridgeInfo?.render(
40
- <RawComponent
41
- propsInfo={propsInfo}
42
- appInfo={{
43
- name,
44
- basename,
45
- memoryRoute,
46
- }}
47
- />,
48
- info.dom
49
- )).then((root: RootType) => rootMap.set(info.dom, root));
45
+ Promise.resolve(
46
+ bridgeInfo?.render(
47
+ <RawComponent
48
+ propsInfo={propsInfo}
49
+ appInfo={{
50
+ name,
51
+ basename,
52
+ memoryRoute,
53
+ }}
54
+ />,
55
+ info.dom,
56
+ ),
57
+ ).then((root: RootType) => rootMap.set(info.dom, root));
50
58
  } else {
51
59
  const root: RootType = ReactDOMClient.createRoot(info.dom);
52
60
  root.render(
@@ -1,4 +1,10 @@
1
- import React, { useContext, useEffect, useRef, useState, forwardRef } from 'react';
1
+ import React, {
2
+ useContext,
3
+ useEffect,
4
+ useRef,
5
+ useState,
6
+ forwardRef,
7
+ } from 'react';
2
8
  import * as ReactRouterDOM from 'react-router-dom';
3
9
  import type { ProviderParams } from '@module-federation/bridge-shared';
4
10
  import { LoggerInstance, pathJoin } from '../utils';
@@ -24,10 +30,12 @@ interface RemoteAppParams {
24
30
  name: string;
25
31
  providerInfo: NonNullable<RemoteModule['provider']>;
26
32
  exportName: string | number | symbol;
27
- dom?: string;
28
33
  }
29
34
 
30
- const RemoteAppWrapper = forwardRef(function (props: RemoteAppParams & ProviderParams, ref) {
35
+ const RemoteAppWrapper = forwardRef(function (
36
+ props: RemoteAppParams & RenderFnParams,
37
+ ref,
38
+ ) {
31
39
  const RemoteApp = () => {
32
40
  const {
33
41
  name,
@@ -40,19 +48,24 @@ const RemoteAppWrapper = forwardRef(function (props: RemoteAppParams & ProviderP
40
48
  ...resProps
41
49
  } = props;
42
50
 
43
- const rootRef: React.MutableRefObject<HTMLElement | null> = ref && 'current' in ref ? ref as React.MutableRefObject<HTMLElement | null> : useRef(null);
51
+ const rootRef: React.MutableRefObject<HTMLElement | null> =
52
+ ref && 'current' in ref
53
+ ? (ref as React.MutableRefObject<HTMLElement | null>)
54
+ : useRef(null);
44
55
  const renderDom: React.MutableRefObject<HTMLElement | null> = useRef(null);
45
56
  const providerInfoRef = useRef<any>(null);
46
-
57
+
47
58
  useEffect(() => {
48
59
  const renderTimeout = setTimeout(() => {
49
60
  const providerReturn = providerInfo();
50
61
  providerInfoRef.current = providerReturn;
51
-
62
+
52
63
  let domElement = null;
53
64
  if (dom) {
54
- domElement = document.getElementById(dom.replace('#', ''));
55
- // TODO: if domElement is null, throw Error.
65
+ domElement = document.querySelector(dom);
66
+ if (!domElement || !(domElement instanceof HTMLElement)) {
67
+ throw new Error(`Invalid dom: ${dom}`);
68
+ }
56
69
  rootRef.current = domElement;
57
70
  } else {
58
71
  domElement = rootRef.current;
@@ -60,7 +73,6 @@ const RemoteAppWrapper = forwardRef(function (props: RemoteAppParams & ProviderP
60
73
 
61
74
  const renderProps = {
62
75
  name,
63
- // dom: rootRef.current,
64
76
  dom: domElement,
65
77
  basename,
66
78
  memoryRoute,
@@ -73,7 +85,7 @@ const RemoteAppWrapper = forwardRef(function (props: RemoteAppParams & ProviderP
73
85
  );
74
86
  providerReturn.render(renderProps);
75
87
  });
76
-
88
+
77
89
  return () => {
78
90
  clearTimeout(renderTimeout);
79
91
  setTimeout(() => {
@@ -90,23 +102,30 @@ const RemoteAppWrapper = forwardRef(function (props: RemoteAppParams & ProviderP
90
102
  };
91
103
  }, []);
92
104
 
93
- return dom ? null : <div className={props?.className} style={props?.style} ref={rootRef}></div>;
94
- }
105
+ return dom ? null : (
106
+ <div
107
+ className={props?.className}
108
+ style={props?.style}
109
+ ref={rootRef}
110
+ ></div>
111
+ );
112
+ };
95
113
 
96
114
  (RemoteApp as any)['__APP_VERSION__'] = __APP_VERSION__;
97
- return <RemoteApp />
115
+ return <RemoteApp />;
98
116
  });
99
117
 
100
118
  interface ExtraDataProps {
101
119
  basename?: string;
102
120
  }
103
121
 
104
- export function withRouterData<P extends Parameters<typeof RemoteAppWrapper>[0]>(
122
+ export function withRouterData<
123
+ P extends Parameters<typeof RemoteAppWrapper>[0],
124
+ >(
105
125
  WrappedComponent: React.ComponentType<P & ExtraDataProps>,
106
126
  ): React.FC<Omit<P, keyof ExtraDataProps>> {
107
-
108
127
  const Component = forwardRef(function (props: any, ref) {
109
- let enableDispathPopstate = false
128
+ let enableDispathPopstate = false;
110
129
  let routerContextVal: any;
111
130
  try {
112
131
  ReactRouterDOM.useLocation();
@@ -181,7 +200,7 @@ export function withRouterData<P extends Parameters<typeof RemoteAppWrapper>[0]>
181
200
  });
182
201
 
183
202
  return forwardRef(function (props, ref) {
184
- return <Component {...props} ref={ref} />
203
+ return <Component {...props} ref={ref} />;
185
204
  }) as any;
186
205
  }
187
206