@ice/mf-runtime 0.0.11 → 1.0.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.
@@ -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,21 +64,25 @@ 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);
@@ -57,5 +97,9 @@ export const RemoteModule = ({ module, scope, runtime, publicPath, LoadingCompon
57
97
  onError: onError
58
98
  }, /*#__PURE__*/ React.createElement(React.Suspense, {
59
99
  fallback: Loading
60
- }, /*#__PURE__*/ React.createElement(Component, componentProps, children)));
100
+ }, /*#__PURE__*/ React.createElement(Component, _object_spread({
101
+ ref: ref
102
+ }, componentProps), children)));
61
103
  };
104
+ // 使用 forwardRef 包装组件以支持 ref
105
+ export const RemoteModule = /*#__PURE__*/ forwardRef(RemoteModuleInner);
package/es2017/index.js CHANGED
@@ -21,7 +21,7 @@ function generateUniqueId() {
21
21
  export function initByMicroMods(microMods, hostName) {
22
22
  const remotes = microMods.map((microMod)=>{
23
23
  var _microMod_remoteEntry;
24
- const isLegacy = !((_microMod_remoteEntry = microMod.remoteEntry) === null || _microMod_remoteEntry === void 0 ? void 0 : _microMod_remoteEntry.match(/\/mf\/remoteEntry\.js$/));
24
+ const isLegacy = !((_microMod_remoteEntry = microMod.remoteEntry) === null || _microMod_remoteEntry === void 0 ? void 0 : _microMod_remoteEntry.match(/\/mf\/.*\/remoteEntry\.js$/));
25
25
  const remote = {
26
26
  name: microMod.moduleFederatedName,
27
27
  entry: microMod.remoteEntry,
@@ -1,12 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { getExtraInfo, getRemoteInfoFromStore, hasConflict } from './mf-global-store';
3
+ import { FallBack } from './FallBack';
3
4
  const loadRemotePackagedReactAndRender = async (args)=>{
4
5
  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;
5
6
  const hostVersion = ((_args_origin_options = args.origin.options) === null || _args_origin_options === void 0 ? void 0 : (_args_origin_options_shared = _args_origin_options.shared) === null || _args_origin_options_shared === void 0 ? void 0 : (_args_origin_options_shared_reactdom = _args_origin_options_shared['react-dom']) === null || _args_origin_options_shared_reactdom === void 0 ? void 0 : (_args_origin_options_shared_reactdom_ = _args_origin_options_shared_reactdom[0]) === null || _args_origin_options_shared_reactdom_ === void 0 ? void 0 : _args_origin_options_shared_reactdom_.version) || React.version;
6
7
  const remoteInstance = __FEDERATION__.__INSTANCES__.find(// @ts-expect-error wrong type
7
8
  (instance)=>instance.name === args.remote.name);
8
9
  const remoteVersion = remoteInstance ? (_remoteInstance_options = remoteInstance.options) === null || _remoteInstance_options === void 0 ? void 0 : (_remoteInstance_options_shared = _remoteInstance_options.shared) === null || _remoteInstance_options_shared === void 0 ? void 0 : (_remoteInstance_options_shared_reactdom = _remoteInstance_options_shared['react-dom']) === null || _remoteInstance_options_shared_reactdom === void 0 ? void 0 : (_remoteInstance_options_shared_reactdom_ = _remoteInstance_options_shared_reactdom[0]) === null || _remoteInstance_options_shared_reactdom_ === void 0 ? void 0 : _remoteInstance_options_shared_reactdom_.version : false;
9
- if (remoteVersion && hostVersion && remoteVersion !== hostVersion) {
10
+ if (remoteVersion && hostVersion) {
10
11
  var _sharedOptions_find;
11
12
  const remoteReactDOM = await remoteInstance.loadShare('react-dom', {
12
13
  resolver: (sharedOptions)=>(_sharedOptions_find = sharedOptions.find((i)=>i.version === remoteVersion)) !== null && _sharedOptions_find !== void 0 ? _sharedOptions_find : sharedOptions[0]
@@ -18,14 +19,19 @@ const loadRemotePackagedReactAndRender = async (args)=>{
18
19
  if (!remoteReact || !remoteReactDOM) {
19
20
  return null;
20
21
  }
21
- const res = (await import('./FallBack')).FallBack;
22
- return ()=>res({
22
+ // 返回一个接受 mountNode containerClassName 参数的函数,并添加标记
23
+ const wrappedRender = (mountNode, containerClassName)=>FallBack({
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
  };
@@ -59,13 +65,17 @@ export const runtimePlugin = ()=>({
59
65
  console.log('[runtime Plugin onLoad] use same external react');
60
66
  return args;
61
67
  }
62
- // external 版本不一致,走降级渲染
63
- const res = (await import('./FallBack')).FallBack;
64
- return ()=>res({
68
+ // 返回一个接受 mountNode 和 containerClassName 参数的函数,并添加标记
69
+ const wrappedRender = (mountNode, containerClassName)=>FallBack({
65
70
  Original: args.exposeModule.default,
66
71
  remoteReactDOM: remoteReactDOM,
67
- remoteReact: remoteReact
72
+ remoteReact: remoteReact,
73
+ mountNode,
74
+ containerClassName
68
75
  });
76
+ // 添加标记,用于在 RemoteModule 中识别
77
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
78
+ return wrappedRender;
69
79
  }
70
80
  }
71
81
  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) {
@@ -86,5 +130,9 @@ export var RemoteModule = function(param) {
86
130
  onError: onError
87
131
  }, /*#__PURE__*/ React.createElement(React.Suspense, {
88
132
  fallback: Loading
89
- }, /*#__PURE__*/ React.createElement(Component, componentProps, children)));
133
+ }, /*#__PURE__*/ React.createElement(Component, _object_spread({
134
+ ref: ref
135
+ }, componentProps), children)));
90
136
  };
137
+ // 使用 forwardRef 包装组件以支持 ref
138
+ export var RemoteModule = /*#__PURE__*/ forwardRef(RemoteModuleInner);
package/esm/index.js CHANGED
@@ -21,7 +21,7 @@ function generateUniqueId() {
21
21
  export function initByMicroMods(microMods, hostName) {
22
22
  var remotes = microMods.map(function(microMod) {
23
23
  var _microMod_remoteEntry;
24
- var isLegacy = !((_microMod_remoteEntry = microMod.remoteEntry) === null || _microMod_remoteEntry === void 0 ? void 0 : _microMod_remoteEntry.match(/\/mf\/remoteEntry\.js$/));
24
+ var isLegacy = !((_microMod_remoteEntry = microMod.remoteEntry) === null || _microMod_remoteEntry === void 0 ? void 0 : _microMod_remoteEntry.match(/\/mf\/.*\/remoteEntry\.js$/));
25
25
  var remote = {
26
26
  name: microMod.moduleFederatedName,
27
27
  entry: microMod.remoteEntry,
@@ -2,9 +2,10 @@ import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
2
2
  import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
3
3
  import * as React from "react";
4
4
  import { getExtraInfo, getRemoteInfoFromStore, hasConflict } from "./mf-global-store";
5
+ import { FallBack } from "./FallBack";
5
6
  var loadRemotePackagedReactAndRender = function() {
6
7
  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;
8
+ 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, wrappedRender;
8
9
  return _ts_generator(this, function(_state) {
9
10
  switch(_state.label){
10
11
  case 0:
@@ -14,9 +15,9 @@ var loadRemotePackagedReactAndRender = function() {
14
15
  return instance.name === args.remote.name;
15
16
  });
16
17
  remoteVersion = remoteInstance ? (_remoteInstance_options = remoteInstance.options) === null || _remoteInstance_options === void 0 ? void 0 : (_remoteInstance_options_shared = _remoteInstance_options.shared) === null || _remoteInstance_options_shared === void 0 ? void 0 : (_remoteInstance_options_shared_reactdom = _remoteInstance_options_shared["react-dom"]) === null || _remoteInstance_options_shared_reactdom === void 0 ? void 0 : (_remoteInstance_options_shared_reactdom_ = _remoteInstance_options_shared_reactdom[0]) === null || _remoteInstance_options_shared_reactdom_ === void 0 ? void 0 : _remoteInstance_options_shared_reactdom_.version : false;
17
- if (!(remoteVersion && hostVersion && remoteVersion !== hostVersion)) return [
18
+ if (!(remoteVersion && hostVersion)) return [
18
19
  3,
19
- 4
20
+ 3
20
21
  ];
21
22
  return [
22
23
  4,
@@ -48,29 +49,28 @@ var loadRemotePackagedReactAndRender = function() {
48
49
  null
49
50
  ];
50
51
  }
51
- return [
52
- 4,
53
- import("./FallBack")
54
- ];
55
- case 3:
56
- res = _state.sent().FallBack;
52
+ wrappedRender = function(mountNode, containerClassName) {
53
+ return FallBack({
54
+ Original: args.exposeModule.default,
55
+ remoteVersion: function() {
56
+ return remoteVersion;
57
+ },
58
+ hostVersion: function() {
59
+ return hostVersion;
60
+ },
61
+ remoteReactDOM: remoteReactDOM,
62
+ remoteReact: remoteReact,
63
+ mountNode: mountNode,
64
+ containerClassName: containerClassName
65
+ });
66
+ };
67
+ // 添加标记,用于在 RemoteModule 中识别
68
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
57
69
  return [
58
70
  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
- }
71
+ wrappedRender
72
72
  ];
73
- case 4:
73
+ case 3:
74
74
  return [
75
75
  2,
76
76
  null
@@ -99,7 +99,7 @@ export var runtimePlugin = function() {
99
99
  },
100
100
  onLoad: function onLoad(args) {
101
101
  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;
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, wrappedRender, fallBackRender;
103
103
  return _ts_generator(this, function(_state) {
104
104
  switch(_state.label){
105
105
  case 0:
@@ -107,51 +107,45 @@ export var runtimePlugin = function() {
107
107
  remoteName = args.remote.name;
108
108
  hostVersion = ((_args_origin_options = args.origin.options) === null || _args_origin_options === void 0 ? void 0 : (_args_origin_options_shared = _args_origin_options.shared) === null || _args_origin_options_shared === void 0 ? void 0 : (_args_origin_options_shared_reactdom = _args_origin_options_shared["react-dom"]) === null || _args_origin_options_shared_reactdom === void 0 ? void 0 : (_args_origin_options_shared_reactdom_ = _args_origin_options_shared_reactdom[0]) === null || _args_origin_options_shared_reactdom_ === void 0 ? void 0 : _args_origin_options_shared_reactdom_.version) || React.version;
109
109
  extraInfo = getExtraInfo(hostName, remoteName);
110
- if (!((_extraInfo = extraInfo) === null || _extraInfo === void 0 ? void 0 : _extraInfo.external)) return [
111
- 3,
112
- 2
113
- ];
114
- externalReact = extraInfo.external["react"];
115
- externalReactDOM = extraInfo.external["react-dom"];
116
- if (!(externalReact && externalReactDOM)) return [
117
- 3,
118
- 2
119
- ];
120
- remoteReact = function() {
121
- return window[externalReact];
122
- };
123
- remoteReactDOM = function() {
124
- return window[externalReactDOM];
125
- };
126
- if (remoteReactDOM().version === hostVersion) {
127
- console.log("[runtime Plugin onLoad] use same external react");
128
- return [
129
- 2,
130
- args
131
- ];
132
- }
133
- return [
134
- 4,
135
- import("./FallBack")
136
- ];
137
- case 1:
138
- res = _state.sent().FallBack;
139
- return [
140
- 2,
141
- function() {
142
- return res({
143
- Original: args.exposeModule.default,
144
- remoteReactDOM: remoteReactDOM,
145
- remoteReact: remoteReact
146
- });
110
+ if ((_extraInfo = extraInfo) === null || _extraInfo === void 0 ? void 0 : _extraInfo.external) {
111
+ externalReact = extraInfo.external["react"];
112
+ externalReactDOM = extraInfo.external["react-dom"];
113
+ if (externalReact && externalReactDOM) {
114
+ remoteReact = function() {
115
+ return window[externalReact];
116
+ };
117
+ remoteReactDOM = function() {
118
+ return window[externalReactDOM];
119
+ };
120
+ if (remoteReactDOM().version === hostVersion) {
121
+ console.log("[runtime Plugin onLoad] use same external react");
122
+ return [
123
+ 2,
124
+ args
125
+ ];
126
+ }
127
+ wrappedRender = function(mountNode, containerClassName) {
128
+ return FallBack({
129
+ Original: args.exposeModule.default,
130
+ remoteReactDOM: remoteReactDOM,
131
+ remoteReact: remoteReact,
132
+ mountNode: mountNode,
133
+ containerClassName: containerClassName
134
+ });
135
+ };
136
+ // 添加标记,用于在 RemoteModule 中识别
137
+ wrappedRender.__ICE_MF_RUNTIME_WRAPPER__ = true;
138
+ return [
139
+ 2,
140
+ wrappedRender
141
+ ];
147
142
  }
148
- ];
149
- case 2:
143
+ }
150
144
  return [
151
145
  4,
152
146
  loadRemotePackagedReactAndRender(args)
153
147
  ];
154
- case 3:
148
+ case 1:
155
149
  fallBackRender = _state.sent();
156
150
  if (fallBackRender) {
157
151
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ice/mf-runtime",
3
- "version": "0.0.11",
3
+ "version": "1.0.0",
4
4
  "description": "ice mf runtime",
5
5
  "files": [
6
6
  "esm",