@module-federation/bridge-react 0.0.0-next-20240731082143 → 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,12 @@
1
1
  # @module-federation/bridge-react
2
2
 
3
- ## 0.0.0-next-20240731082143
3
+ ## 0.0.0-next-20240731105745
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - @module-federation/bridge-shared@0.0.0-next-20240731082143
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
8
10
 
9
11
  ## 0.3.3
10
12
 
@@ -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
@@ -117,55 +117,79 @@ function hasArrayChanged() {
117
117
  let b = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [];
118
118
  return a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]));
119
119
  }
120
- const RemoteApp = ({
121
- name,
122
- memoryRoute,
123
- basename,
124
- providerInfo,
125
- ...resProps
126
- }) => {
127
- const rootRef = React.useRef(null);
128
- const renderDom = React.useRef(null);
129
- const providerInfoRef = React.useRef(null);
130
- React.useEffect(() => {
131
- const renderTimeout = setTimeout(() => {
132
- const providerReturn = providerInfo();
133
- providerInfoRef.current = providerReturn;
134
- const renderProps = {
135
- name,
136
- dom: rootRef.current,
137
- basename,
138
- memoryRoute,
139
- ...resProps
140
- };
141
- renderDom.current = rootRef.current;
142
- context.LoggerInstance.log(
143
- `createRemoteComponent LazyComponent render >>>`,
144
- renderProps
145
- );
146
- providerReturn.render(renderProps);
147
- });
148
- return () => {
149
- clearTimeout(renderTimeout);
150
- setTimeout(() => {
151
- var _a, _b;
152
- if ((_a = providerInfoRef.current) == null ? void 0 : _a.destroy) {
153
- context.LoggerInstance.log(
154
- `createRemoteComponent LazyComponent destroy >>>`,
155
- { name, basename, dom: renderDom.current }
156
- );
157
- (_b = providerInfoRef.current) == null ? void 0 : _b.destroy({
158
- dom: renderDom.current
159
- });
120
+ const RemoteAppWrapper = React.forwardRef(function(props, ref) {
121
+ const RemoteApp2 = () => {
122
+ const {
123
+ name,
124
+ memoryRoute,
125
+ basename,
126
+ providerInfo,
127
+ dom,
128
+ className,
129
+ style,
130
+ ...resProps
131
+ } = props;
132
+ const rootRef = ref && "current" in ref ? ref : React.useRef(null);
133
+ const renderDom = React.useRef(null);
134
+ const providerInfoRef = React.useRef(null);
135
+ React.useEffect(() => {
136
+ const renderTimeout = setTimeout(() => {
137
+ const providerReturn = providerInfo();
138
+ providerInfoRef.current = providerReturn;
139
+ let domElement = null;
140
+ if (dom) {
141
+ domElement = document.querySelector(dom);
142
+ if (!domElement || !(domElement instanceof HTMLElement)) {
143
+ throw new Error(`Invalid dom: ${dom}`);
144
+ }
145
+ rootRef.current = domElement;
146
+ } else {
147
+ domElement = rootRef.current;
160
148
  }
149
+ const renderProps = {
150
+ name,
151
+ dom: domElement,
152
+ basename,
153
+ memoryRoute,
154
+ ...resProps
155
+ };
156
+ renderDom.current = rootRef.current;
157
+ context.LoggerInstance.log(
158
+ `createRemoteComponent LazyComponent render >>>`,
159
+ renderProps
160
+ );
161
+ providerReturn.render(renderProps);
161
162
  });
162
- };
163
- }, []);
164
- return /* @__PURE__ */ React.createElement("div", { ref: rootRef });
165
- };
166
- RemoteApp["__APP_VERSION__"] = "0.3.3";
163
+ return () => {
164
+ clearTimeout(renderTimeout);
165
+ setTimeout(() => {
166
+ var _a, _b;
167
+ if ((_a = providerInfoRef.current) == null ? void 0 : _a.destroy) {
168
+ context.LoggerInstance.log(
169
+ `createRemoteComponent LazyComponent destroy >>>`,
170
+ { name, basename, dom: renderDom.current }
171
+ );
172
+ (_b = providerInfoRef.current) == null ? void 0 : _b.destroy({
173
+ dom: renderDom.current
174
+ });
175
+ }
176
+ });
177
+ };
178
+ }, []);
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
+ );
187
+ };
188
+ RemoteApp2["__APP_VERSION__"] = "0.3.3";
189
+ return /* @__PURE__ */ React.createElement(RemoteApp2, null);
190
+ });
167
191
  function withRouterData(WrappedComponent) {
168
- return (props) => {
192
+ const Component = React.forwardRef(function(props, ref) {
169
193
  var _a;
170
194
  let enableDispathPopstate = false;
171
195
  let routerContextVal;
@@ -223,10 +247,13 @@ function withRouterData(WrappedComponent) {
223
247
  setPathname(location.pathname);
224
248
  }, [location]);
225
249
  }
226
- return /* @__PURE__ */ React.createElement(WrappedComponent, { ...props, basename });
227
- };
250
+ return /* @__PURE__ */ React.createElement(WrappedComponent, { ...props, basename, ref });
251
+ });
252
+ return React.forwardRef(function(props, ref) {
253
+ return /* @__PURE__ */ React.createElement(Component, { ...props, ref });
254
+ });
228
255
  }
229
- const RemoteApp$1 = withRouterData(RemoteApp);
256
+ const RemoteApp = withRouterData(RemoteAppWrapper);
230
257
  function createLazyRemoteComponent(info) {
231
258
  const exportName = (info == null ? void 0 : info.export) || "default";
232
259
  return React.lazy(async () => {
@@ -243,13 +270,14 @@ function createLazyRemoteComponent(info) {
243
270
  );
244
271
  const exportFn = m2[exportName];
245
272
  if (exportName in m2 && typeof exportFn === "function") {
246
- const RemoteAppComponent = React.forwardRef((props, _ref) => {
273
+ const RemoteAppComponent = React.forwardRef((props, ref) => {
247
274
  return /* @__PURE__ */ React.createElement(
248
- RemoteApp$1,
275
+ RemoteApp,
249
276
  {
250
277
  name: moduleName,
251
278
  providerInfo: exportFn,
252
279
  exportName: info.export || "default",
280
+ ref,
253
281
  ...props
254
282
  }
255
283
  );
@@ -274,10 +302,10 @@ function createLazyRemoteComponent(info) {
274
302
  });
275
303
  }
276
304
  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 })));
280
- };
305
+ return React.forwardRef(function(props, ref) {
306
+ const LazyComponent = createLazyRemoteComponent(info);
307
+ return /* @__PURE__ */ React.createElement(ErrorBoundary, { FallbackComponent: info.fallback }, /* @__PURE__ */ React.createElement(React.Suspense, { fallback: info.loading }, /* @__PURE__ */ React.createElement(LazyComponent, { ...props, dom: info == null ? void 0 : info.dom, ref })));
308
+ });
281
309
  }
282
310
  var client = {};
283
311
  var m = ReactDOM;
@@ -307,32 +335,59 @@ function createBridgeComponent(bridgeInfo) {
307
335
  return () => {
308
336
  const rootMap = /* @__PURE__ */ new Map();
309
337
  const RawComponent = (info) => {
310
- const { appInfo, propsInfo } = info;
338
+ const { appInfo, propsInfo, ...restProps } = info;
311
339
  const { name, memoryRoute, basename = "/" } = appInfo;
312
- return /* @__PURE__ */ React__namespace.createElement(context.RouterContext.Provider, { value: { name, basename, memoryRoute } }, /* @__PURE__ */ React__namespace.createElement(bridgeInfo.rootComponent, { ...propsInfo, basename }));
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
+ ));
313
348
  };
314
349
  return {
315
- render(info) {
350
+ async render(info) {
316
351
  context.LoggerInstance.log(`createBridgeComponent render Info`, info);
317
352
  const { name, basename, memoryRoute, ...propsInfo } = info;
318
353
  if (context.atLeastReact18(React__namespace)) {
319
- const root = client.createRoot(info.dom);
320
- rootMap.set(info.dom, root);
321
- root.render(
322
- /* @__PURE__ */ React__namespace.createElement(
323
- RawComponent,
324
- {
325
- propsInfo,
326
- appInfo: {
327
- name,
328
- basename,
329
- memoryRoute
354
+ if (bridgeInfo == null ? void 0 : bridgeInfo.render) {
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
+ }
366
+ }
367
+ ),
368
+ info.dom
369
+ )
370
+ ).then((root) => rootMap.set(info.dom, root));
371
+ } else {
372
+ const root = client.createRoot(info.dom);
373
+ root.render(
374
+ /* @__PURE__ */ React__namespace.createElement(
375
+ RawComponent,
376
+ {
377
+ propsInfo,
378
+ appInfo: {
379
+ name,
380
+ basename,
381
+ memoryRoute
382
+ }
330
383
  }
331
- }
332
- )
333
- );
384
+ )
385
+ );
386
+ rootMap.set(info.dom, root);
387
+ }
334
388
  } else {
335
- ReactDOM.render(
389
+ const renderFunc = (bridgeInfo == null ? void 0 : bridgeInfo.render) || ReactDOM.render;
390
+ renderFunc(
336
391
  /* @__PURE__ */ React__namespace.createElement(
337
392
  RawComponent,
338
393
  {
@@ -348,7 +403,7 @@ function createBridgeComponent(bridgeInfo) {
348
403
  );
349
404
  }
350
405
  },
351
- destroy(info) {
406
+ async destroy(info) {
352
407
  context.LoggerInstance.log(`createBridgeComponent destroy Info`, {
353
408
  dom: info.dom
354
409
  });
package/dist/index.d.ts CHANGED
@@ -1,14 +1,18 @@
1
1
  import { ComponentType } from 'react';
2
2
  import { default as default_2 } from 'react';
3
+ import { default as default_3 } from 'react-dom/client';
3
4
  import { ErrorInfo } from 'react';
5
+ import { ForwardRefExoticComponent } from 'react';
4
6
  import { PropsWithChildren } from 'react';
7
+ import { PropsWithoutRef } from 'react';
5
8
  import * as React_2 from 'react';
9
+ import { RefAttributes } from 'react';
6
10
 
7
11
  export declare function createBridgeComponent<T>(bridgeInfo: ProviderFnParams<T>): () => {
8
- render(info: RenderFnParams & any): void;
12
+ render(info: RenderFnParams & any): Promise<void>;
9
13
  destroy(info: {
10
14
  dom: HTMLElement;
11
- }): void;
15
+ }): Promise<void>;
12
16
  rawComponent: React_2.ComponentType<T>;
13
17
  __BRIDGE_FN__: (_args: T) => void;
14
18
  };
@@ -18,10 +22,8 @@ export declare function createRemoteComponent<T, E extends keyof T>(info: {
18
22
  loading: default_2.ReactNode;
19
23
  fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
20
24
  export?: E;
21
- }): (props: {
22
- basename?: ProviderParams['basename'];
23
- memoryRoute?: ProviderParams['memoryRoute'];
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
+ dom?: string;
26
+ }): ForwardRefExoticComponent<PropsWithoutRef<ProviderParams> & RefAttributes<HTMLElement | HTMLDivElement>>;
25
27
 
26
28
  declare type ErrorBoundaryPropsWithComponent = ErrorBoundarySharedProps & {
27
29
  fallback?: never;
@@ -49,18 +51,24 @@ declare type FallbackProps = {
49
51
 
50
52
  declare type ProviderFnParams<T> = {
51
53
  rootComponent: React_2.ComponentType<T>;
54
+ render?: (App: React_2.ReactElement, id?: HTMLElement | string) => RootType | Promise<RootType>;
52
55
  };
53
56
 
54
- export declare interface ProviderParams {
57
+ export declare interface ProviderParams extends default_2.HTMLAttributes<HTMLDivElement> {
55
58
  name?: string;
56
59
  basename?: string;
57
60
  memoryRoute?: {
58
61
  entryPath: string;
59
62
  };
63
+ props?: {
64
+ [key: string]: any;
65
+ };
60
66
  }
61
67
 
62
68
  export declare interface RenderFnParams extends ProviderParams {
63
69
  dom: HTMLElement;
64
70
  }
65
71
 
72
+ declare type RootType = HTMLElement | default_3.Root;
73
+
66
74
  export { }