@ice/mf-runtime 0.0.11-beta.1 → 0.0.11-beta.3

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.
@@ -5,58 +5,8 @@ interface FallBackOptions {
5
5
  hostVersion?: () => string;
6
6
  remoteReactDOM: () => typeof import('react-dom');
7
7
  remoteReact: () => typeof import('react');
8
+ mountNode?: HTMLElement;
9
+ containerClassName?: string;
8
10
  }
9
- export declare const FallBack: ({ Original, remoteReactDOM, remoteReact, }: FallBackOptions) => {
10
- new (props: {}): {
11
- containerRef: React.RefObject<HTMLDivElement>;
12
- componentDidMount(): void;
13
- componentDidUpdate(): void;
14
- componentWillUnmount(): void;
15
- mountOriginalComponent(shouldRender?: boolean): void;
16
- render(): React.JSX.Element;
17
- context: unknown;
18
- setState<K extends never>(state: {} | ((prevState: Readonly<{}>, props: Readonly<{}>) => {} | Pick<{}, K>) | Pick<{}, K>, callback?: () => void): void;
19
- forceUpdate(callback?: () => void): void;
20
- readonly props: Readonly<{}>;
21
- state: Readonly<{}>;
22
- refs: {
23
- [key: string]: React.ReactInstance;
24
- };
25
- shouldComponentUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): boolean;
26
- componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
27
- getSnapshotBeforeUpdate?(prevProps: Readonly<{}>, prevState: Readonly<{}>): any;
28
- componentWillMount?(): void;
29
- UNSAFE_componentWillMount?(): void;
30
- componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
31
- UNSAFE_componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
32
- componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
33
- UNSAFE_componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
34
- };
35
- new (props: {}, context: any): {
36
- containerRef: React.RefObject<HTMLDivElement>;
37
- componentDidMount(): void;
38
- componentDidUpdate(): void;
39
- componentWillUnmount(): void;
40
- mountOriginalComponent(shouldRender?: boolean): void;
41
- render(): React.JSX.Element;
42
- context: unknown;
43
- setState<K extends never>(state: {} | ((prevState: Readonly<{}>, props: Readonly<{}>) => {} | Pick<{}, K>) | Pick<{}, K>, callback?: () => void): void;
44
- forceUpdate(callback?: () => void): void;
45
- readonly props: Readonly<{}>;
46
- state: Readonly<{}>;
47
- refs: {
48
- [key: string]: React.ReactInstance;
49
- };
50
- shouldComponentUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): boolean;
51
- componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
52
- getSnapshotBeforeUpdate?(prevProps: Readonly<{}>, prevState: Readonly<{}>): any;
53
- componentWillMount?(): void;
54
- UNSAFE_componentWillMount?(): void;
55
- componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
56
- UNSAFE_componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
57
- componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
58
- UNSAFE_componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
59
- };
60
- contextType?: React.Context<any>;
61
- };
11
+ export declare const FallBack: ({ Original, remoteReactDOM, remoteReact, mountNode, containerClassName, }: FallBackOptions) => React.ForwardRefExoticComponent<Omit<any, "ref"> & React.RefAttributes<any>>;
62
12
  export {};
@@ -1,17 +1,27 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
+ import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
3
+ import { _ as _object_spread_props } from "@swc/helpers/_/_object_spread_props";
4
+ import { _ as _object_without_properties } from "@swc/helpers/_/_object_without_properties";
2
5
  import * as React from 'react';
3
6
  class Component extends React.Component {
4
7
  render() {
5
- const { containerRef } = this.props;
8
+ const { containerRef, mountNode, containerClassName } = this.props;
9
+ // 如果提供了 mountNode,则不渲染任何内容(因为会直接挂载到指定节点)
10
+ // 否则渲染一个 div 作为容器
11
+ if (mountNode) {
12
+ return null;
13
+ }
6
14
  return /*#__PURE__*/ React.createElement("div", {
7
- ref: containerRef
15
+ ref: containerRef,
16
+ className: containerClassName
8
17
  });
9
18
  }
10
19
  }
11
- export const FallBack = ({ Original, remoteReactDOM, remoteReact })=>{
20
+ export const FallBack = ({ Original, remoteReactDOM, remoteReact, mountNode, containerClassName })=>{
12
21
  const ReactDOM = remoteReactDOM();
13
- const React1 = remoteReact();
14
- class WrappedComponent extends React1.Component {
22
+ const React = remoteReact();
23
+ // 使用 class 组件来避免 hooks 冲突问题
24
+ class WrappedComponent extends React.Component {
15
25
  componentDidMount() {
16
26
  this.mountOriginalComponent(true);
17
27
  }
@@ -19,24 +29,60 @@ export const FallBack = ({ Original, remoteReactDOM, remoteReact })=>{
19
29
  this.mountOriginalComponent();
20
30
  }
21
31
  componentWillUnmount() {
22
- if (this.containerRef.current) {
23
- ReactDOM.unmountComponentAtNode(this.containerRef.current);
32
+ const targetNode = this.getTargetNode();
33
+ if (targetNode) {
34
+ ReactDOM.unmountComponentAtNode(targetNode);
24
35
  }
25
36
  }
37
+ getTargetNode() {
38
+ // 如果指定了 mountNode,使用它;否则使用 containerRef.current
39
+ return mountNode || this.containerRef.current;
40
+ }
26
41
  mountOriginalComponent(shouldRender = false) {
27
- const element = React1.createElement(Original, this.props);
28
- const renderMethod = shouldRender ? ReactDOM.render : ReactDOM.hydrate;
29
- renderMethod(element, this.containerRef.current);
42
+ const _this_props = this.props, { forwardedRef } = _this_props, otherProps = _object_without_properties(_this_props, [
43
+ "forwardedRef"
44
+ ]);
45
+ const element = React.createElement(Original, _object_spread_props(_object_spread({}, otherProps), {
46
+ ref: this.handleRef
47
+ }));
48
+ const targetNode = this.getTargetNode();
49
+ if (targetNode) {
50
+ const renderMethod = shouldRender ? ReactDOM.render : ReactDOM.hydrate;
51
+ renderMethod(element, targetNode);
52
+ }
30
53
  }
31
54
  render() {
32
- return /*#__PURE__*/ React.createElement(Component, {
33
- containerRef: this.containerRef
55
+ return React.createElement(Component, {
56
+ containerRef: this.containerRef,
57
+ mountNode: mountNode,
58
+ containerClassName: containerClassName
34
59
  });
35
60
  }
36
- constructor(...args){
37
- super(...args);
38
- _define_property(this, "containerRef", React1.createRef());
61
+ constructor(props){
62
+ super(props);
63
+ _define_property(this, "containerRef", void 0);
64
+ _define_property(this, "originalComponentRef", null);
65
+ // 处理 ref 回调
66
+ _define_property(this, "handleRef", (instance)=>{
67
+ this.originalComponentRef = instance;
68
+ // 转发 ref 到外部
69
+ const { forwardedRef } = this.props;
70
+ if (forwardedRef) {
71
+ if (typeof forwardedRef === 'function') {
72
+ forwardedRef(instance);
73
+ } else if (forwardedRef && typeof forwardedRef === 'object') {
74
+ forwardedRef.current = instance;
75
+ }
76
+ }
77
+ });
78
+ this.containerRef = React.createRef();
39
79
  }
40
80
  }
41
- return WrappedComponent;
81
+ // 使用 forwardRef 包装 class 组件来支持 ref 转发
82
+ const ForwardedComponent = React.forwardRef((props, ref)=>{
83
+ return React.createElement(WrappedComponent, _object_spread_props(_object_spread({}, props), {
84
+ forwardedRef: ref
85
+ }));
86
+ });
87
+ return ForwardedComponent;
42
88
  };
@@ -14,6 +14,10 @@ interface RemoteModuleOptions<T = any> {
14
14
  }) => void;
15
15
  componentProps?: T;
16
16
  children?: React.ReactNode;
17
+ mountNode?: HTMLElement | string | (() => HTMLElement | null) | React.RefObject<HTMLElement>;
18
+ fallbackContainerClassName?: string;
17
19
  }
18
- export declare const RemoteModule: <T extends object = any>({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError, componentProps, children, }: RemoteModuleOptions<T>) => string | number | true | Iterable<React.ReactNode> | React.JSX.Element;
20
+ export declare const RemoteModule: <T extends object = any>(props: RemoteModuleOptions<T> & {
21
+ ref?: React.Ref<any>;
22
+ }) => React.ReactElement | null;
19
23
  export {};
@@ -1,13 +1,40 @@
1
+ import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
1
2
  import { loadRemote } from '@module-federation/runtime';
2
3
  import * as React from 'react';
3
- import { useMemo } from 'react';
4
+ import { useMemo, useState, useLayoutEffect, forwardRef } from 'react';
4
5
  import { ErrorBoundary } from 'react-error-boundary';
5
6
  import { FallBack } from './FallBack';
6
7
  import { setFederatedModulePublicPath } from './set-public-path';
7
8
  import { getMicroMod } from './mf-global-store';
8
- export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError, componentProps = {}, children = null })=>{
9
+ const useMountNode = (mountNodeConfig)=>{
10
+ const [resolvedNode, setResolvedNode] = useState(undefined);
11
+ // 解析各种类型的 mountNode
12
+ useLayoutEffect(()=>{
13
+ if (!mountNodeConfig) {
14
+ setResolvedNode(undefined);
15
+ return;
16
+ }
17
+ let node = null;
18
+ if (typeof mountNodeConfig === 'string') {
19
+ node = document.querySelector(mountNodeConfig);
20
+ } else if (typeof mountNodeConfig === 'function') {
21
+ node = mountNodeConfig();
22
+ } else if (mountNodeConfig instanceof HTMLElement) {
23
+ node = mountNodeConfig;
24
+ } else if (mountNodeConfig && 'current' in mountNodeConfig) {
25
+ // 处理 RefObject
26
+ node = mountNodeConfig.current;
27
+ }
28
+ setResolvedNode(node || undefined);
29
+ }, [
30
+ mountNodeConfig
31
+ ]);
32
+ return resolvedNode;
33
+ };
34
+ const RemoteModuleInner = ({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError, componentProps = {}, children = null, mountNode, fallbackContainerClassName }, ref)=>{
9
35
  var _microMod, _runtime, _runtime1;
10
36
  const microMod = getMicroMod(scope);
37
+ const resolvedMountNode = useMountNode(mountNode);
11
38
  if ((_microMod = microMod) === null || _microMod === void 0 ? void 0 : _microMod.publicPath) {
12
39
  setFederatedModulePublicPath(microMod.moduleFederatedName, microMod.publicPath);
13
40
  }
@@ -17,9 +44,18 @@ export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingCompon
17
44
  const Component = useMemo(()=>{
18
45
  if (!module || !scope) return null;
19
46
  return /*#__PURE__*/ React.lazy(async ()=>{
20
- var _remoteModule;
47
+ var _typedRemoteModule;
21
48
  const remoteModule = await loadRemote(`${scope}/${module}`);
22
- if (!((_remoteModule = remoteModule) === null || _remoteModule === void 0 ? void 0 : _remoteModule.default)) {
49
+ // 检查是否是来自 runtime plugin 的包装函数(通过标记识别)
50
+ if (typeof remoteModule === 'function' && remoteModule.__ICE_MF_RUNTIME_WRAPPER__) {
51
+ // 调用包装函数并传入 mountNode 和 containerClassName
52
+ const ComponentFromPlugin = remoteModule(resolvedMountNode, fallbackContainerClassName);
53
+ return {
54
+ default: ComponentFromPlugin
55
+ };
56
+ }
57
+ const typedRemoteModule = remoteModule;
58
+ if (!((_typedRemoteModule = typedRemoteModule) === null || _typedRemoteModule === void 0 ? void 0 : _typedRemoteModule.default)) {
23
59
  return {
24
60
  default: remoteModule
25
61
  };
@@ -28,33 +64,37 @@ export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingCompon
28
64
  const { react, reactDOM } = runtime;
29
65
  return {
30
66
  default: FallBack({
31
- Original: remoteModule.default,
67
+ Original: typedRemoteModule.default,
32
68
  remoteReact: ()=>react,
33
- remoteReactDOM: ()=>reactDOM
69
+ remoteReactDOM: ()=>reactDOM,
70
+ mountNode: resolvedMountNode,
71
+ containerClassName: fallbackContainerClassName
34
72
  })
35
73
  };
36
74
  }
37
75
  return {
38
- default: remoteModule.default
76
+ default: typedRemoteModule.default
39
77
  };
40
78
  });
41
79
  }, [
42
80
  module,
43
81
  scope,
44
82
  (_runtime = runtime) === null || _runtime === void 0 ? void 0 : _runtime.react,
45
- (_runtime1 = runtime) === null || _runtime1 === void 0 ? void 0 : _runtime1.reactDOM
83
+ (_runtime1 = runtime) === null || _runtime1 === void 0 ? void 0 : _runtime1.reactDOM,
84
+ resolvedMountNode,
85
+ fallbackContainerClassName
46
86
  ]);
47
87
  const Loading = LoadingComponent || /*#__PURE__*/ React.createElement("div", null, "Loading...");
48
88
  const ErrorFallback = ({ error })=>ErrorComponent || /*#__PURE__*/ React.createElement("div", null, "远程模块加载失败: ", error.message);
49
89
  if (!Component) return Loading;
50
90
  return /*#__PURE__*/ React.createElement(ErrorBoundary, {
51
- resetKeys: [
52
- module,
53
- scope
54
- ],
55
91
  FallbackComponent: ErrorFallback,
56
92
  onError: onError
57
93
  }, /*#__PURE__*/ React.createElement(React.Suspense, {
58
94
  fallback: Loading
59
- }, /*#__PURE__*/ React.createElement(Component, componentProps, children)));
95
+ }, /*#__PURE__*/ React.createElement(Component, _object_spread({
96
+ ref: ref
97
+ }, componentProps), children)));
60
98
  };
99
+ // 使用 forwardRef 包装组件以支持 ref
100
+ export const RemoteModule = /*#__PURE__*/ forwardRef(RemoteModuleInner);
@@ -19,13 +19,19 @@ const loadRemotePackagedReactAndRender = async (args)=>{
19
19
  return null;
20
20
  }
21
21
  const res = (await import('./FallBack')).FallBack;
22
- return ()=>res({
22
+ // 返回一个接受 mountNode 和 containerClassName 参数的函数,并添加标记
23
+ const wrappedRender = (mountNode, containerClassName)=>res({
23
24
  Original: args.exposeModule.default,
24
25
  remoteVersion: ()=>remoteVersion,
25
26
  hostVersion: ()=>hostVersion,
26
27
  remoteReactDOM: remoteReactDOM,
27
- remoteReact: remoteReact
28
+ remoteReact: remoteReact,
29
+ mountNode,
30
+ containerClassName
28
31
  });
32
+ // 添加标记,用于在 RemoteModule 中识别
33
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
34
+ return wrappedRender;
29
35
  }
30
36
  return null;
31
37
  };
@@ -61,11 +67,17 @@ export const runtimePlugin = ()=>({
61
67
  }
62
68
  // 当 external 版本不一致,走降级渲染
63
69
  const res = (await import('./FallBack')).FallBack;
64
- return ()=>res({
70
+ // 返回一个接受 mountNode 和 containerClassName 参数的函数,并添加标记
71
+ const wrappedRender = (mountNode, containerClassName)=>res({
65
72
  Original: args.exposeModule.default,
66
73
  remoteReactDOM: remoteReactDOM,
67
- remoteReact: remoteReact
74
+ remoteReact: remoteReact,
75
+ mountNode,
76
+ containerClassName
68
77
  });
78
+ // 添加标记,用于在 RemoteModule 中识别
79
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
80
+ return wrappedRender;
69
81
  }
70
82
  }
71
83
  const fallBackRender = await loadRemotePackagedReactAndRender(args);
package/esm/FallBack.d.ts CHANGED
@@ -5,58 +5,8 @@ interface FallBackOptions {
5
5
  hostVersion?: () => string;
6
6
  remoteReactDOM: () => typeof import('react-dom');
7
7
  remoteReact: () => typeof import('react');
8
+ mountNode?: HTMLElement;
9
+ containerClassName?: string;
8
10
  }
9
- export declare const FallBack: ({ Original, remoteReactDOM, remoteReact, }: FallBackOptions) => {
10
- new (props: {}): {
11
- containerRef: React.RefObject<HTMLDivElement>;
12
- componentDidMount(): void;
13
- componentDidUpdate(): void;
14
- componentWillUnmount(): void;
15
- mountOriginalComponent(shouldRender?: boolean): void;
16
- render(): React.JSX.Element;
17
- context: unknown;
18
- setState<K extends never>(state: {} | ((prevState: Readonly<{}>, props: Readonly<{}>) => {} | Pick<{}, K>) | Pick<{}, K>, callback?: () => void): void;
19
- forceUpdate(callback?: () => void): void;
20
- readonly props: Readonly<{}>;
21
- state: Readonly<{}>;
22
- refs: {
23
- [key: string]: React.ReactInstance;
24
- };
25
- shouldComponentUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): boolean;
26
- componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
27
- getSnapshotBeforeUpdate?(prevProps: Readonly<{}>, prevState: Readonly<{}>): any;
28
- componentWillMount?(): void;
29
- UNSAFE_componentWillMount?(): void;
30
- componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
31
- UNSAFE_componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
32
- componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
33
- UNSAFE_componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
34
- };
35
- new (props: {}, context: any): {
36
- containerRef: React.RefObject<HTMLDivElement>;
37
- componentDidMount(): void;
38
- componentDidUpdate(): void;
39
- componentWillUnmount(): void;
40
- mountOriginalComponent(shouldRender?: boolean): void;
41
- render(): React.JSX.Element;
42
- context: unknown;
43
- setState<K extends never>(state: {} | ((prevState: Readonly<{}>, props: Readonly<{}>) => {} | Pick<{}, K>) | Pick<{}, K>, callback?: () => void): void;
44
- forceUpdate(callback?: () => void): void;
45
- readonly props: Readonly<{}>;
46
- state: Readonly<{}>;
47
- refs: {
48
- [key: string]: React.ReactInstance;
49
- };
50
- shouldComponentUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): boolean;
51
- componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
52
- getSnapshotBeforeUpdate?(prevProps: Readonly<{}>, prevState: Readonly<{}>): any;
53
- componentWillMount?(): void;
54
- UNSAFE_componentWillMount?(): void;
55
- componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
56
- UNSAFE_componentWillReceiveProps?(nextProps: Readonly<{}>, nextContext: any): void;
57
- componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
58
- UNSAFE_componentWillUpdate?(nextProps: Readonly<{}>, nextState: Readonly<{}>, nextContext: any): void;
59
- };
60
- contextType?: React.Context<any>;
61
- };
11
+ export declare const FallBack: ({ Original, remoteReactDOM, remoteReact, mountNode, containerClassName, }: FallBackOptions) => React.ForwardRefExoticComponent<Omit<any, "ref"> & React.RefAttributes<any>>;
62
12
  export {};
package/esm/FallBack.js CHANGED
@@ -3,6 +3,9 @@ import { _ as _class_call_check } from "@swc/helpers/_/_class_call_check";
3
3
  import { _ as _create_class } from "@swc/helpers/_/_create_class";
4
4
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
5
5
  import { _ as _inherits } from "@swc/helpers/_/_inherits";
6
+ import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
7
+ import { _ as _object_spread_props } from "@swc/helpers/_/_object_spread_props";
8
+ import { _ as _object_without_properties } from "@swc/helpers/_/_object_without_properties";
6
9
  import { _ as _create_super } from "@swc/helpers/_/_create_super";
7
10
  import * as React from "react";
8
11
  var Component = /*#__PURE__*/ function(_React_Component) {
@@ -17,9 +20,15 @@ var Component = /*#__PURE__*/ function(_React_Component) {
17
20
  {
18
21
  key: "render",
19
22
  value: function render() {
20
- var containerRef = this.props.containerRef;
23
+ var _this_props = this.props, containerRef = _this_props.containerRef, mountNode = _this_props.mountNode, containerClassName = _this_props.containerClassName;
24
+ // 如果提供了 mountNode,则不渲染任何内容(因为会直接挂载到指定节点)
25
+ // 否则渲染一个 div 作为容器
26
+ if (mountNode) {
27
+ return null;
28
+ }
21
29
  return /*#__PURE__*/ React.createElement("div", {
22
- ref: containerRef
30
+ ref: containerRef,
31
+ className: containerClassName
23
32
  });
24
33
  }
25
34
  }
@@ -27,18 +36,34 @@ var Component = /*#__PURE__*/ function(_React_Component) {
27
36
  return Component;
28
37
  }(React.Component);
29
38
  export var FallBack = function(param) {
30
- var Original = param.Original, remoteReactDOM = param.remoteReactDOM, remoteReact = param.remoteReact;
39
+ var Original = param.Original, remoteReactDOM = param.remoteReactDOM, remoteReact = param.remoteReact, mountNode = param.mountNode, containerClassName = param.containerClassName;
31
40
  var ReactDOM = remoteReactDOM();
32
41
  var _$React = remoteReact();
42
+ // 使用 class 组件来避免 hooks 冲突问题
33
43
  var WrappedComponent = /*#__PURE__*/ function(_React_Component) {
34
44
  "use strict";
35
45
  _inherits(WrappedComponent, _React_Component);
36
46
  var _super = _create_super(WrappedComponent);
37
- function WrappedComponent() {
47
+ function WrappedComponent(props) {
38
48
  _class_call_check(this, WrappedComponent);
39
49
  var _this;
40
- _this = _super.apply(this, arguments);
41
- _define_property(_assert_this_initialized(_this), "containerRef", _$React.createRef());
50
+ _this = _super.call(this, props);
51
+ _define_property(_assert_this_initialized(_this), "containerRef", void 0);
52
+ _define_property(_assert_this_initialized(_this), "originalComponentRef", null);
53
+ // 处理 ref 回调
54
+ _define_property(_assert_this_initialized(_this), "handleRef", function(instance) {
55
+ _this.originalComponentRef = instance;
56
+ // 转发 ref 到外部
57
+ var forwardedRef = _this.props.forwardedRef;
58
+ if (forwardedRef) {
59
+ if (typeof forwardedRef === "function") {
60
+ forwardedRef(instance);
61
+ } else if (forwardedRef && typeof forwardedRef === "object") {
62
+ forwardedRef.current = instance;
63
+ }
64
+ }
65
+ });
66
+ _this.containerRef = _$React.createRef();
42
67
  return _this;
43
68
  }
44
69
  _create_class(WrappedComponent, [
@@ -57,30 +82,54 @@ export var FallBack = function(param) {
57
82
  {
58
83
  key: "componentWillUnmount",
59
84
  value: function componentWillUnmount() {
60
- if (this.containerRef.current) {
61
- ReactDOM.unmountComponentAtNode(this.containerRef.current);
85
+ var targetNode = this.getTargetNode();
86
+ if (targetNode) {
87
+ ReactDOM.unmountComponentAtNode(targetNode);
62
88
  }
63
89
  }
64
90
  },
91
+ {
92
+ key: "getTargetNode",
93
+ value: function getTargetNode() {
94
+ // 如果指定了 mountNode,使用它;否则使用 containerRef.current
95
+ return mountNode || this.containerRef.current;
96
+ }
97
+ },
65
98
  {
66
99
  key: "mountOriginalComponent",
67
100
  value: function mountOriginalComponent() {
68
101
  var shouldRender = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
69
- var element = _$React.createElement(Original, this.props);
70
- var renderMethod = shouldRender ? ReactDOM.render : ReactDOM.hydrate;
71
- renderMethod(element, this.containerRef.current);
102
+ var _this_props = this.props, forwardedRef = _this_props.forwardedRef, otherProps = _object_without_properties(_this_props, [
103
+ "forwardedRef"
104
+ ]);
105
+ var element = _$React.createElement(Original, _object_spread_props(_object_spread({}, otherProps), {
106
+ ref: this.handleRef
107
+ }));
108
+ var targetNode = this.getTargetNode();
109
+ if (targetNode) {
110
+ var renderMethod = shouldRender ? ReactDOM.render : ReactDOM.hydrate;
111
+ renderMethod(element, targetNode);
112
+ }
72
113
  }
73
114
  },
74
115
  {
75
116
  key: "render",
76
117
  value: function render() {
77
- return /*#__PURE__*/ React.createElement(Component, {
78
- containerRef: this.containerRef
118
+ return _$React.createElement(Component, {
119
+ containerRef: this.containerRef,
120
+ mountNode: mountNode,
121
+ containerClassName: containerClassName
79
122
  });
80
123
  }
81
124
  }
82
125
  ]);
83
126
  return WrappedComponent;
84
127
  }(_$React.Component);
85
- return WrappedComponent;
128
+ // 使用 forwardRef 包装 class 组件来支持 ref 转发
129
+ var ForwardedComponent = _$React.forwardRef(function(props, ref) {
130
+ return _$React.createElement(WrappedComponent, _object_spread_props(_object_spread({}, props), {
131
+ forwardedRef: ref
132
+ }));
133
+ });
134
+ return ForwardedComponent;
86
135
  };
@@ -14,6 +14,10 @@ interface RemoteModuleOptions<T = any> {
14
14
  }) => void;
15
15
  componentProps?: T;
16
16
  children?: React.ReactNode;
17
+ mountNode?: HTMLElement | string | (() => HTMLElement | null) | React.RefObject<HTMLElement>;
18
+ fallbackContainerClassName?: string;
17
19
  }
18
- export declare const RemoteModule: <T extends object = any>({ module, scope, runtime, publicPath, LoadingComponent, ErrorComponent, onError, componentProps, children, }: RemoteModuleOptions<T>) => string | number | true | Iterable<React.ReactNode> | React.JSX.Element;
20
+ export declare const RemoteModule: <T extends object = any>(props: RemoteModuleOptions<T> & {
21
+ ref?: React.Ref<any>;
22
+ }) => React.ReactElement | null;
19
23
  export {};
@@ -1,16 +1,45 @@
1
1
  import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
2
+ import { _ as _instanceof } from "@swc/helpers/_/_instanceof";
3
+ import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
4
+ import { _ as _sliced_to_array } from "@swc/helpers/_/_sliced_to_array";
2
5
  import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
3
6
  import { loadRemote } from "@module-federation/runtime";
4
7
  import * as React from "react";
5
- import { useMemo } from "react";
8
+ import { useMemo, useState, useLayoutEffect, forwardRef } from "react";
6
9
  import { ErrorBoundary } from "react-error-boundary";
7
10
  import { FallBack } from "./FallBack";
8
11
  import { setFederatedModulePublicPath } from "./set-public-path";
9
12
  import { getMicroMod } from "./mf-global-store";
10
- export var RemoteModule = function(param) {
11
- var module = param.module, scope = param.scope, runtime = param.runtime, publicPath = param.publicPath, LoadingComponent = param.LoadingComponent, ErrorComponent = param.ErrorComponent, onError = param.onError, _param_componentProps = param.componentProps, componentProps = _param_componentProps === void 0 ? {} : _param_componentProps, _param_children = param.children, children = _param_children === void 0 ? null : _param_children;
13
+ var useMountNode = function(mountNodeConfig) {
14
+ var _useState = _sliced_to_array(useState(undefined), 2), resolvedNode = _useState[0], setResolvedNode = _useState[1];
15
+ // 解析各种类型的 mountNode
16
+ useLayoutEffect(function() {
17
+ if (!mountNodeConfig) {
18
+ setResolvedNode(undefined);
19
+ return;
20
+ }
21
+ var node = null;
22
+ if (typeof mountNodeConfig === "string") {
23
+ node = document.querySelector(mountNodeConfig);
24
+ } else if (typeof mountNodeConfig === "function") {
25
+ node = mountNodeConfig();
26
+ } else if (_instanceof(mountNodeConfig, HTMLElement)) {
27
+ node = mountNodeConfig;
28
+ } else if (mountNodeConfig && "current" in mountNodeConfig) {
29
+ // 处理 RefObject
30
+ node = mountNodeConfig.current;
31
+ }
32
+ setResolvedNode(node || undefined);
33
+ }, [
34
+ mountNodeConfig
35
+ ]);
36
+ return resolvedNode;
37
+ };
38
+ var RemoteModuleInner = function(param, ref) {
39
+ var module = param.module, scope = param.scope, runtime = param.runtime, publicPath = param.publicPath, LoadingComponent = param.LoadingComponent, ErrorComponent = param.ErrorComponent, onError = param.onError, _param_componentProps = param.componentProps, componentProps = _param_componentProps === void 0 ? {} : _param_componentProps, _param_children = param.children, children = _param_children === void 0 ? null : _param_children, mountNode = param.mountNode, fallbackContainerClassName = param.fallbackContainerClassName;
12
40
  var _microMod, _runtime, _runtime1;
13
41
  var microMod = getMicroMod(scope);
42
+ var resolvedMountNode = useMountNode(mountNode);
14
43
  if ((_microMod = microMod) === null || _microMod === void 0 ? void 0 : _microMod.publicPath) {
15
44
  setFederatedModulePublicPath(microMod.moduleFederatedName, microMod.publicPath);
16
45
  }
@@ -20,7 +49,7 @@ export var RemoteModule = function(param) {
20
49
  var Component = useMemo(function() {
21
50
  if (!module || !scope) return null;
22
51
  return /*#__PURE__*/ React.lazy(/*#__PURE__*/ _async_to_generator(function() {
23
- var _remoteModule, remoteModule, react, reactDOM;
52
+ var _typedRemoteModule, remoteModule, ComponentFromPlugin, typedRemoteModule, react, reactDOM;
24
53
  return _ts_generator(this, function(_state) {
25
54
  switch(_state.label){
26
55
  case 0:
@@ -30,7 +59,18 @@ export var RemoteModule = function(param) {
30
59
  ];
31
60
  case 1:
32
61
  remoteModule = _state.sent();
33
- if (!((_remoteModule = remoteModule) === null || _remoteModule === void 0 ? void 0 : _remoteModule.default)) {
62
+ // 检查是否是来自 runtime plugin 的包装函数(通过标记识别)
63
+ if (typeof remoteModule === "function" && remoteModule.__ICE_MF_RUNTIME_WRAPPER__) {
64
+ ComponentFromPlugin = remoteModule(resolvedMountNode, fallbackContainerClassName);
65
+ return [
66
+ 2,
67
+ {
68
+ default: ComponentFromPlugin
69
+ }
70
+ ];
71
+ }
72
+ typedRemoteModule = remoteModule;
73
+ if (!((_typedRemoteModule = typedRemoteModule) === null || _typedRemoteModule === void 0 ? void 0 : _typedRemoteModule.default)) {
34
74
  return [
35
75
  2,
36
76
  {
@@ -44,13 +84,15 @@ export var RemoteModule = function(param) {
44
84
  2,
45
85
  {
46
86
  default: FallBack({
47
- Original: remoteModule.default,
87
+ Original: typedRemoteModule.default,
48
88
  remoteReact: function() {
49
89
  return react;
50
90
  },
51
91
  remoteReactDOM: function() {
52
92
  return reactDOM;
53
- }
93
+ },
94
+ mountNode: resolvedMountNode,
95
+ containerClassName: fallbackContainerClassName
54
96
  })
55
97
  }
56
98
  ];
@@ -58,7 +100,7 @@ export var RemoteModule = function(param) {
58
100
  return [
59
101
  2,
60
102
  {
61
- default: remoteModule.default
103
+ default: typedRemoteModule.default
62
104
  }
63
105
  ];
64
106
  }
@@ -68,7 +110,9 @@ export var RemoteModule = function(param) {
68
110
  module,
69
111
  scope,
70
112
  (_runtime = runtime) === null || _runtime === void 0 ? void 0 : _runtime.react,
71
- (_runtime1 = runtime) === null || _runtime1 === void 0 ? void 0 : _runtime1.reactDOM
113
+ (_runtime1 = runtime) === null || _runtime1 === void 0 ? void 0 : _runtime1.reactDOM,
114
+ resolvedMountNode,
115
+ fallbackContainerClassName
72
116
  ]);
73
117
  var Loading = LoadingComponent || /*#__PURE__*/ React.createElement("div", null, "Loading...");
74
118
  var ErrorFallback = function(param) {
@@ -77,13 +121,13 @@ export var RemoteModule = function(param) {
77
121
  };
78
122
  if (!Component) return Loading;
79
123
  return /*#__PURE__*/ React.createElement(ErrorBoundary, {
80
- resetKeys: [
81
- module,
82
- scope
83
- ],
84
124
  FallbackComponent: ErrorFallback,
85
125
  onError: onError
86
126
  }, /*#__PURE__*/ React.createElement(React.Suspense, {
87
127
  fallback: Loading
88
- }, /*#__PURE__*/ React.createElement(Component, componentProps, children)));
128
+ }, /*#__PURE__*/ React.createElement(Component, _object_spread({
129
+ ref: ref
130
+ }, componentProps), children)));
89
131
  };
132
+ // 使用 forwardRef 包装组件以支持 ref
133
+ export var RemoteModule = /*#__PURE__*/ forwardRef(RemoteModuleInner);
@@ -4,7 +4,7 @@ import * as React from "react";
4
4
  import { getExtraInfo, getRemoteInfoFromStore, hasConflict } from "./mf-global-store";
5
5
  var loadRemotePackagedReactAndRender = function() {
6
6
  var _ref = _async_to_generator(function(args) {
7
- var _args_origin_options_shared_reactdom_, _args_origin_options_shared_reactdom, _args_origin_options_shared, _args_origin_options, _remoteInstance_options_shared_reactdom_, _remoteInstance_options_shared_reactdom, _remoteInstance_options_shared, _remoteInstance_options, hostVersion, remoteInstance, remoteVersion, _sharedOptions_find, remoteReactDOM, _sharedOptions_find1, remoteReact, res;
7
+ var _args_origin_options_shared_reactdom_, _args_origin_options_shared_reactdom, _args_origin_options_shared, _args_origin_options, _remoteInstance_options_shared_reactdom_, _remoteInstance_options_shared_reactdom, _remoteInstance_options_shared, _remoteInstance_options, hostVersion, remoteInstance, remoteVersion, _sharedOptions_find, remoteReactDOM, _sharedOptions_find1, remoteReact, res, wrappedRender;
8
8
  return _ts_generator(this, function(_state) {
9
9
  switch(_state.label){
10
10
  case 0:
@@ -54,21 +54,26 @@ var loadRemotePackagedReactAndRender = function() {
54
54
  ];
55
55
  case 3:
56
56
  res = _state.sent().FallBack;
57
+ wrappedRender = function(mountNode, containerClassName) {
58
+ return res({
59
+ Original: args.exposeModule.default,
60
+ remoteVersion: function() {
61
+ return remoteVersion;
62
+ },
63
+ hostVersion: function() {
64
+ return hostVersion;
65
+ },
66
+ remoteReactDOM: remoteReactDOM,
67
+ remoteReact: remoteReact,
68
+ mountNode: mountNode,
69
+ containerClassName: containerClassName
70
+ });
71
+ };
72
+ // 添加标记,用于在 RemoteModule 中识别
73
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
57
74
  return [
58
75
  2,
59
- function() {
60
- return res({
61
- Original: args.exposeModule.default,
62
- remoteVersion: function() {
63
- return remoteVersion;
64
- },
65
- hostVersion: function() {
66
- return hostVersion;
67
- },
68
- remoteReactDOM: remoteReactDOM,
69
- remoteReact: remoteReact
70
- });
71
- }
76
+ wrappedRender
72
77
  ];
73
78
  case 4:
74
79
  return [
@@ -99,7 +104,7 @@ export var runtimePlugin = function() {
99
104
  },
100
105
  onLoad: function onLoad(args) {
101
106
  return _async_to_generator(function() {
102
- var _args_origin_options_shared_reactdom_, _args_origin_options_shared_reactdom, _args_origin_options_shared, _args_origin_options, _extraInfo, hostName, remoteName, hostVersion, extraInfo, externalReact, externalReactDOM, remoteReact, remoteReactDOM, res, fallBackRender;
107
+ var _args_origin_options_shared_reactdom_, _args_origin_options_shared_reactdom, _args_origin_options_shared, _args_origin_options, _extraInfo, hostName, remoteName, hostVersion, extraInfo, externalReact, externalReactDOM, remoteReact, remoteReactDOM, res, wrappedRender, fallBackRender;
103
108
  return _ts_generator(this, function(_state) {
104
109
  switch(_state.label){
105
110
  case 0:
@@ -136,15 +141,20 @@ export var runtimePlugin = function() {
136
141
  ];
137
142
  case 1:
138
143
  res = _state.sent().FallBack;
144
+ wrappedRender = function(mountNode, containerClassName) {
145
+ return res({
146
+ Original: args.exposeModule.default,
147
+ remoteReactDOM: remoteReactDOM,
148
+ remoteReact: remoteReact,
149
+ mountNode: mountNode,
150
+ containerClassName: containerClassName
151
+ });
152
+ };
153
+ // 添加标记,用于在 RemoteModule 中识别
154
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
139
155
  return [
140
156
  2,
141
- function() {
142
- return res({
143
- Original: args.exposeModule.default,
144
- remoteReactDOM: remoteReactDOM,
145
- remoteReact: remoteReact
146
- });
147
- }
157
+ wrappedRender
148
158
  ];
149
159
  case 2:
150
160
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ice/mf-runtime",
3
- "version": "0.0.11-beta.1",
3
+ "version": "0.0.11-beta.3",
4
4
  "description": "ice mf runtime",
5
5
  "files": [
6
6
  "esm",