remote-reload-utils 0.0.10 → 0.0.12
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 +18 -0
- package/README.md +62 -0
- package/dist/components/ErrorBoundary.d.ts +34 -0
- package/dist/components/RemoteModuleProvider.d.ts +95 -0
- package/dist/components/SuspenseLoader.d.ts +131 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/main.cjs +336 -8
- package/dist/main.js +292 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
|
|
8
8
|
## [未发布]
|
|
9
9
|
|
|
10
|
+
## [0.0.12] - 2026-03-16
|
|
11
|
+
|
|
12
|
+
### Release
|
|
13
|
+
|
|
14
|
+
- Published version 0.0.12 with patch bump
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.0.11] - 2026-03-16
|
|
18
|
+
|
|
19
|
+
### Release
|
|
20
|
+
|
|
21
|
+
- Published version 0.0.11 with patch bump
|
|
22
|
+
|
|
23
|
+
|
|
10
24
|
## [0.0.10] - 2026-03-15
|
|
11
25
|
|
|
12
26
|
### Release
|
|
@@ -130,6 +144,10 @@
|
|
|
130
144
|
|
|
131
145
|
| 版本 | 日期 | 主要变更 |
|
|
132
146
|
|------|------|----------|
|
|
147
|
+
| 0.0.12 | 2026-03-16 | patch 版本发布 |
|
|
148
|
+
|------|------|----------|
|
|
149
|
+
| 0.0.11 | 2026-03-16 | patch 版本发布 |
|
|
150
|
+
|------|------|----------|
|
|
133
151
|
| 0.0.10 | 2026-03-15 | patch 版本发布 |
|
|
134
152
|
|------|------|----------|
|
|
135
153
|
| 0.0.9 | 2026-03-15 | patch 版本发布 |
|
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ pnpm dev
|
|
|
24
24
|
|
|
25
25
|
### 使用方式
|
|
26
26
|
|
|
27
|
+
#### 1. 基础加载远程模块
|
|
28
|
+
|
|
27
29
|
```ts
|
|
28
30
|
import { loadRemoteMultiVersion } from 'remote-reload-utils';
|
|
29
31
|
const [comp, setComp] = useState(null);
|
|
@@ -52,3 +54,63 @@ useEffect(() => {
|
|
|
52
54
|
init();
|
|
53
55
|
}, []);
|
|
54
56
|
```
|
|
57
|
+
|
|
58
|
+
#### 2. 使用 React 组件加载远程模块
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { RemoteModuleProvider, ErrorBoundary, lazyRemote } from 'remote-reload-utils';
|
|
62
|
+
import React, { Suspense } from 'react';
|
|
63
|
+
|
|
64
|
+
// 方式一:使用 RemoteModuleProvider 组件(推荐)
|
|
65
|
+
function App() {
|
|
66
|
+
return (
|
|
67
|
+
<RemoteModuleProvider
|
|
68
|
+
pkg="@myorg/remote-app"
|
|
69
|
+
version="^1.0.0"
|
|
70
|
+
moduleName="Dashboard"
|
|
71
|
+
scopeName="myorg"
|
|
72
|
+
loadingFallback={<Spinner />}
|
|
73
|
+
errorFallback={(error, reset) => (
|
|
74
|
+
<div>
|
|
75
|
+
<p>加载失败:{error.message}</p>
|
|
76
|
+
<button onClick={reset}>重试</button>
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
componentProps={{ userId: 123 }}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 方式二:使用 lazy + Suspense
|
|
85
|
+
const LazyDashboard = lazyRemote({
|
|
86
|
+
pkg: "@myorg/remote-app",
|
|
87
|
+
version: "^1.0.0",
|
|
88
|
+
moduleName: "Dashboard",
|
|
89
|
+
scopeName: "myorg"
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
function App() {
|
|
93
|
+
return (
|
|
94
|
+
<ErrorBoundary fallback={(error) => <div>错误:{error.message}</div>}>
|
|
95
|
+
<Suspense fallback={<Spinner />}>
|
|
96
|
+
<LazyDashboard userId={123} />
|
|
97
|
+
</Suspense>
|
|
98
|
+
</ErrorBoundary>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 方式三:使用 SuspenseRemoteLoader(一体化方案)
|
|
103
|
+
function App() {
|
|
104
|
+
return (
|
|
105
|
+
<SuspenseRemoteLoader
|
|
106
|
+
pkg="@myorg/remote-app"
|
|
107
|
+
version="^1.0.0"
|
|
108
|
+
moduleName="Dashboard"
|
|
109
|
+
scopeName="myorg"
|
|
110
|
+
fallback={<Spinner />}
|
|
111
|
+
errorFallback={(error) => <div>错误:{error.message}</div>}
|
|
112
|
+
componentProps={{ userId: 123 }}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ErrorBoundaryProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
fallback?: React.ReactNode | ((error: Error, resetError: () => void) => React.ReactNode);
|
|
5
|
+
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
6
|
+
onReset?: () => void;
|
|
7
|
+
}
|
|
8
|
+
interface ErrorBoundaryState {
|
|
9
|
+
hasError: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
errorInfo?: React.ErrorInfo | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 错误边界组件
|
|
15
|
+
* 用于捕获子组件树中的错误,并显示降级 UI
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <ErrorBoundary
|
|
20
|
+
* fallback={(error, reset) => <div onClick={reset}>Error: {error.message}</div>}
|
|
21
|
+
* onError={(error, info) => console.error(error, info)}
|
|
22
|
+
* >
|
|
23
|
+
* <MyComponent />
|
|
24
|
+
* </ErrorBoundary>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
28
|
+
constructor(props: ErrorBoundaryProps);
|
|
29
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
30
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
|
|
31
|
+
handleReset: () => void;
|
|
32
|
+
render(): string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ErrorBoundaryProps } from './ErrorBoundary';
|
|
3
|
+
export interface RemoteModuleCardProps {
|
|
4
|
+
/** 包名称 */
|
|
5
|
+
pkg: string;
|
|
6
|
+
/** 版本号,支持 semver 范围 */
|
|
7
|
+
version: string;
|
|
8
|
+
/** 远程模块名称(导出名) */
|
|
9
|
+
moduleName: string;
|
|
10
|
+
/** 作用域名称 */
|
|
11
|
+
scopeName: string;
|
|
12
|
+
/** 加载中的占位内容 */
|
|
13
|
+
loadingFallback?: React.ReactNode;
|
|
14
|
+
/** 错误状态的占位内容 */
|
|
15
|
+
errorFallback?: React.ReactNode | ((error: Error, resetError: () => void) => React.ReactNode);
|
|
16
|
+
/** 传递给远程组件的 props */
|
|
17
|
+
componentProps?: Record<string, any>;
|
|
18
|
+
/** 容器类名 */
|
|
19
|
+
className?: string;
|
|
20
|
+
/** 容器样式 */
|
|
21
|
+
style?: React.CSSProperties;
|
|
22
|
+
/** 加载成功回调 */
|
|
23
|
+
onLoad?: (component: React.ComponentType<any>) => void;
|
|
24
|
+
/** 加载失败回调 */
|
|
25
|
+
onError?: (error: Error) => void;
|
|
26
|
+
/** 是否禁用错误边界 */
|
|
27
|
+
disableErrorBoundary?: boolean;
|
|
28
|
+
/** 错误边界配置 */
|
|
29
|
+
errorBoundaryOptions?: Omit<ErrorBoundaryProps, 'children' | 'fallback'>;
|
|
30
|
+
}
|
|
31
|
+
interface ModuleState {
|
|
32
|
+
loading: boolean;
|
|
33
|
+
error: Error | null;
|
|
34
|
+
component: React.ComponentType<any> | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Hook 选项:加载远程模块
|
|
38
|
+
*/
|
|
39
|
+
export interface UseRemoteModuleOptions {
|
|
40
|
+
pkg: string;
|
|
41
|
+
version: string;
|
|
42
|
+
moduleName: string;
|
|
43
|
+
scopeName: string;
|
|
44
|
+
onError?: (error: Error) => void;
|
|
45
|
+
onLoad?: (component: React.ComponentType<any>) => void;
|
|
46
|
+
retryKey?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Hook:加载远程模块
|
|
50
|
+
*/
|
|
51
|
+
export declare function useRemoteModule({ pkg, version, moduleName, scopeName, onError, onLoad, retryKey, }: UseRemoteModuleOptions): ModuleState;
|
|
52
|
+
/**
|
|
53
|
+
* 远程模块渲染器:纯内容渲染(不含 Suspense/ErrorBoundary)
|
|
54
|
+
*/
|
|
55
|
+
export declare function RemoteModuleRenderer({ pkg, version, moduleName, scopeName, loadingFallback, errorFallback, componentProps, className, style, onError, onLoad, }: RemoteModuleCardProps): import("react/jsx-runtime").JSX.Element | null;
|
|
56
|
+
/**
|
|
57
|
+
* 远程模块提供者:带 Suspense 和 ErrorBoundary 的完整组件
|
|
58
|
+
*
|
|
59
|
+
* @features
|
|
60
|
+
* - 自动加载远程模块
|
|
61
|
+
* - 内置加载状态和错误处理
|
|
62
|
+
* - 支持 Suspense 惰性加载
|
|
63
|
+
* - 可配置的错误边界
|
|
64
|
+
* - 支持重试机制
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* <RemoteModuleProvider
|
|
69
|
+
* pkg="@myorg/remote-app"
|
|
70
|
+
* version="^1.0.0"
|
|
71
|
+
* moduleName="Dashboard"
|
|
72
|
+
* scopeName="myorg"
|
|
73
|
+
* loadingFallback={<Spinner />}
|
|
74
|
+
* errorFallback={(error, reset) => <Button onClick={reset}>Retry</Button>}
|
|
75
|
+
* componentProps={{ userId: 123 }}
|
|
76
|
+
* />
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* // 使用 lazy + Suspense 模式
|
|
82
|
+
* const LazyRemote = lazyRemote({
|
|
83
|
+
* pkg: "@myorg/remote-app",
|
|
84
|
+
* version: "^1.0.0",
|
|
85
|
+
* moduleName: "Dashboard",
|
|
86
|
+
* scopeName: "myorg"
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* <Suspense fallback={<Spinner />}>
|
|
90
|
+
* <LazyRemote userId={123} />
|
|
91
|
+
* </Suspense>
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function RemoteModuleProvider(props: RemoteModuleCardProps): import("react/jsx-runtime").JSX.Element;
|
|
95
|
+
export {};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
export interface LazyRemoteOptions {
|
|
3
|
+
/** 包名称 */
|
|
4
|
+
pkg: string;
|
|
5
|
+
/** 版本号 */
|
|
6
|
+
version?: string;
|
|
7
|
+
/** 模块名称 */
|
|
8
|
+
moduleName: string;
|
|
9
|
+
/** 作用域名称 */
|
|
10
|
+
scopeName: string;
|
|
11
|
+
/** 加载失败时的最大重试次数 */
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
/** 重试延迟(毫秒) */
|
|
14
|
+
retryDelay?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SuspenseRemoteProps {
|
|
17
|
+
/** 加载中的占位内容 */
|
|
18
|
+
fallback?: ReactNode;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
export interface SuspenseRemoteWithPropsProps extends LazyRemoteOptions {
|
|
22
|
+
/** 加载中的占位内容 */
|
|
23
|
+
fallback?: ReactNode;
|
|
24
|
+
/** 错误状态的占位内容 */
|
|
25
|
+
errorFallback?: ReactNode | ((error: Error) => ReactNode);
|
|
26
|
+
/** 传递给远程组件的 props */
|
|
27
|
+
componentProps?: Record<string, any>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 创建一个惰性加载的远程组件
|
|
31
|
+
* 返回一个可用于 React.lazy() 的 Promise
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* const LazyDashboard = lazyRemote({
|
|
36
|
+
* pkg: "@myorg/remote-app",
|
|
37
|
+
* version: "^1.0.0",
|
|
38
|
+
* moduleName: "Dashboard",
|
|
39
|
+
* scopeName: "myorg"
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* function App() {
|
|
43
|
+
* return (
|
|
44
|
+
* <Suspense fallback={<Spinner />}>
|
|
45
|
+
* <LazyDashboard userId={123} />
|
|
46
|
+
* </Suspense>
|
|
47
|
+
* );
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function lazyRemote(options: LazyRemoteOptions): React.LazyExoticComponent<React.ComponentType<any>>;
|
|
52
|
+
/**
|
|
53
|
+
* 带 Suspense 和错误处理的远程组件包装器
|
|
54
|
+
* 注意:错误处理需要与 ErrorBoundary 配合使用
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* // 与 ErrorBoundary 配合使用
|
|
59
|
+
* <ErrorBoundary fallback={(error) => <div>Error: {error.message}</div>}>
|
|
60
|
+
* <SuspenseRemote fallback={<Spinner />}>
|
|
61
|
+
* <RemoteModuleProvider pkg="@myorg/remote-app" version="^1.0.0" moduleName="Dashboard" scopeName="myorg" />
|
|
62
|
+
* </SuspenseRemote>
|
|
63
|
+
* </ErrorBoundary>
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function SuspenseRemote({ fallback, children, }: {
|
|
67
|
+
fallback?: ReactNode;
|
|
68
|
+
children: ReactNode;
|
|
69
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
70
|
+
/**
|
|
71
|
+
* 一体化的远程组件加载器(包含 Suspense 和 ErrorBoundary)
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* <SuspenseRemoteLoader
|
|
76
|
+
* pkg="@myorg/remote-app"
|
|
77
|
+
* version="^1.0.0"
|
|
78
|
+
* moduleName="Dashboard"
|
|
79
|
+
* scopeName="myorg"
|
|
80
|
+
* fallback={<Spinner />}
|
|
81
|
+
* errorFallback={(error) => <div>Error: {error.message}</div>}
|
|
82
|
+
* componentProps={{ userId: 123 }}
|
|
83
|
+
* />
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function SuspenseRemoteLoader({ pkg, version, moduleName, scopeName, fallback, errorFallback, componentProps, }: SuspenseRemoteWithPropsProps): import("react/jsx-runtime").JSX.Element;
|
|
87
|
+
/**
|
|
88
|
+
* HOC:为组件添加远程加载能力
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```tsx
|
|
92
|
+
* const EnhancedComponent = withRemote(
|
|
93
|
+
* (props) => <div>{props.message}</div>,
|
|
94
|
+
* {
|
|
95
|
+
* pkg: "@myorg/remote-app",
|
|
96
|
+
* version: "^1.0.0",
|
|
97
|
+
* moduleName: "RemoteComponent",
|
|
98
|
+
* scopeName: "myorg"
|
|
99
|
+
* }
|
|
100
|
+
* );
|
|
101
|
+
*
|
|
102
|
+
* <EnhancedComponent message="Hello" />
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare function withRemote<P extends object>(WrappedComponent: ComponentType<P>, options: LazyRemoteOptions): (props: P) => import("react/jsx-runtime").JSX.Element;
|
|
106
|
+
/**
|
|
107
|
+
* Hook:在函数组件中加载远程模块
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```tsx
|
|
111
|
+
* function MyComponent() {
|
|
112
|
+
* const { component: RemoteComp, loading, error } = useRemoteModule({
|
|
113
|
+
* pkg: "@myorg/remote-app",
|
|
114
|
+
* version: "^1.0.0",
|
|
115
|
+
* moduleName: "Dashboard",
|
|
116
|
+
* scopeName: "myorg"
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* if (loading) return <Spinner />;
|
|
120
|
+
* if (error) return <div>Error: {error.message}</div>;
|
|
121
|
+
* if (!RemoteComp) return null;
|
|
122
|
+
*
|
|
123
|
+
* return <RemoteComp userId={123} />;
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function useRemoteModuleHook(options: LazyRemoteOptions): {
|
|
128
|
+
component: ComponentType<any> | null;
|
|
129
|
+
loading: boolean;
|
|
130
|
+
error: Error | null;
|
|
131
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ErrorBoundary } from './ErrorBoundary';
|
|
2
|
+
export type { ErrorBoundaryProps } from './ErrorBoundary';
|
|
3
|
+
export { RemoteModuleProvider, RemoteModuleRenderer, useRemoteModule } from './RemoteModuleProvider';
|
|
4
|
+
export type { RemoteModuleCardProps, UseRemoteModuleOptions } from './RemoteModuleProvider';
|
|
5
|
+
export { lazyRemote, SuspenseRemote, SuspenseRemoteLoader, withRemote, useRemoteModuleHook, } from './SuspenseLoader';
|
|
6
|
+
export type { LazyRemoteOptions, SuspenseRemoteProps, SuspenseRemoteWithPropsProps } from './SuspenseLoader';
|
package/dist/index.d.ts
CHANGED
|
@@ -8,3 +8,5 @@ export { checkRemoteHealth, checkModuleLoadable, getRemoteHealthReport, formatHe
|
|
|
8
8
|
export { eventBus, createEventBus, } from './event-bus';
|
|
9
9
|
export type { LoadRemoteOptions, VersionCache, PreloadOptions, PreloadCacheItem, PreloadStatus, } from './types';
|
|
10
10
|
export { fallbackPlugin } from './plugins/fallback';
|
|
11
|
+
export { ErrorBoundary, RemoteModuleProvider, RemoteModuleRenderer, useRemoteModule, lazyRemote, SuspenseRemote, SuspenseRemoteLoader, withRemote, useRemoteModuleHook, } from './components';
|
|
12
|
+
export type { ErrorBoundaryProps, RemoteModuleCardProps, UseRemoteModuleOptions, LazyRemoteOptions, SuspenseRemoteProps, SuspenseRemoteWithPropsProps, } from './components';
|
package/dist/main.cjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
3
12
|
(()=>{
|
|
4
13
|
__webpack_require__.d = (exports1, definition)=>{
|
|
5
14
|
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
@@ -27,31 +36,39 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
27
36
|
compareVersions: ()=>compareVersions,
|
|
28
37
|
fetchAvailableVersions: ()=>fetchAvailableVersions,
|
|
29
38
|
sortVersions: ()=>sortVersions,
|
|
39
|
+
useRemoteModuleHook: ()=>useRemoteModuleHook,
|
|
30
40
|
registerRemoteInstance: ()=>registerRemoteInstance,
|
|
31
41
|
fallbackPlugin: ()=>fallbackPlugin,
|
|
32
42
|
createEventBus: ()=>createEventBus,
|
|
43
|
+
withRemote: ()=>withRemote,
|
|
33
44
|
getPreloadStatus: ()=>getPreloadStatus,
|
|
34
45
|
parseVersion: ()=>parseVersion,
|
|
35
46
|
resolveFinalVersion: ()=>resolveFinalVersion,
|
|
36
47
|
buildFinalUrls: ()=>buildFinalUrls,
|
|
37
48
|
fetchLatestVersion: ()=>fetchLatestVersion,
|
|
38
|
-
|
|
49
|
+
ErrorBoundary: ()=>ErrorBoundary,
|
|
39
50
|
getFinalSharedConfig: ()=>getFinalSharedConfig,
|
|
51
|
+
isRemoteLoaded: ()=>isRemoteLoaded,
|
|
40
52
|
tryLoadRemote: ()=>tryLoadRemote,
|
|
41
53
|
getVersionCache: ()=>getVersionCache,
|
|
42
54
|
eventBus: ()=>eventBus,
|
|
43
|
-
|
|
55
|
+
RemoteModuleRenderer: ()=>RemoteModuleRenderer,
|
|
44
56
|
clearPreloadCache: ()=>clearPreloadCache,
|
|
45
|
-
|
|
57
|
+
loadRemoteMultiVersion: ()=>loadRemoteMultiVersion,
|
|
46
58
|
getRemoteHealthReport: ()=>getRemoteHealthReport,
|
|
47
|
-
getStableVersions: ()=>getStableVersions,
|
|
48
59
|
checkModuleLoadable: ()=>checkModuleLoadable,
|
|
49
|
-
|
|
60
|
+
getStableVersions: ()=>getStableVersions,
|
|
50
61
|
extractMajorVersion: ()=>extractMajorVersion,
|
|
62
|
+
buildCdnUrls: ()=>buildCdnUrls,
|
|
63
|
+
loadReactVersion: ()=>loadReactVersion,
|
|
51
64
|
preloadRemoteList: ()=>preloadRemoteList,
|
|
65
|
+
SuspenseRemoteLoader: ()=>SuspenseRemoteLoader,
|
|
52
66
|
registerLoadedModule: ()=>registerLoadedModule,
|
|
53
|
-
unloadRemote: ()=>unloadRemote,
|
|
54
67
|
getCompatibleReactVersions: ()=>getCompatibleReactVersions,
|
|
68
|
+
SuspenseRemote: ()=>SuspenseRemote,
|
|
69
|
+
unloadRemote: ()=>unloadRemote,
|
|
70
|
+
useRemoteModule: ()=>useRemoteModule,
|
|
71
|
+
RemoteModuleProvider: ()=>RemoteModuleProvider,
|
|
55
72
|
cancelPreload: ()=>cancelPreload,
|
|
56
73
|
findCompatibleVersion: ()=>findCompatibleVersion,
|
|
57
74
|
getLoadedRemotes: ()=>getLoadedRemotes,
|
|
@@ -61,8 +78,9 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
61
78
|
formatHealthStatus: ()=>formatHealthStatus,
|
|
62
79
|
unloadAll: ()=>unloadAll,
|
|
63
80
|
setVersionCache: ()=>setVersionCache,
|
|
64
|
-
|
|
81
|
+
lazyRemote: ()=>lazyRemote,
|
|
65
82
|
checkRemoteHealth: ()=>checkRemoteHealth,
|
|
83
|
+
isPrerelease: ()=>isPrerelease,
|
|
66
84
|
checkVersionCompatibility: ()=>checkVersionCompatibility
|
|
67
85
|
});
|
|
68
86
|
const runtime_namespaceObject = require("@module-federation/enhanced/runtime");
|
|
@@ -751,6 +769,303 @@ const eventBus = EventBusClass.create();
|
|
|
751
769
|
function createEventBus() {
|
|
752
770
|
return EventBusClass.create();
|
|
753
771
|
}
|
|
772
|
+
const external_react_namespaceObject = require("react");
|
|
773
|
+
var external_react_default = /*#__PURE__*/ __webpack_require__.n(external_react_namespaceObject);
|
|
774
|
+
class ErrorBoundary extends external_react_default().Component {
|
|
775
|
+
constructor(props){
|
|
776
|
+
super(props);
|
|
777
|
+
this.state = {
|
|
778
|
+
hasError: false,
|
|
779
|
+
error: null,
|
|
780
|
+
errorInfo: null
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
static getDerivedStateFromError(error) {
|
|
784
|
+
return {
|
|
785
|
+
hasError: true,
|
|
786
|
+
error,
|
|
787
|
+
errorInfo: null
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
componentDidCatch(error, errorInfo) {
|
|
791
|
+
this.setState({
|
|
792
|
+
errorInfo
|
|
793
|
+
});
|
|
794
|
+
this.props.onError?.(error, errorInfo);
|
|
795
|
+
console.error('[RemoteReloadUtils] ErrorBoundary caught error:', error, errorInfo);
|
|
796
|
+
}
|
|
797
|
+
handleReset = ()=>{
|
|
798
|
+
this.setState({
|
|
799
|
+
hasError: false,
|
|
800
|
+
error: null,
|
|
801
|
+
errorInfo: null
|
|
802
|
+
});
|
|
803
|
+
this.props.onReset?.();
|
|
804
|
+
};
|
|
805
|
+
render() {
|
|
806
|
+
if (this.state.hasError && this.state.error) {
|
|
807
|
+
if ('function' == typeof this.props.fallback) return this.props.fallback(this.state.error, this.handleReset);
|
|
808
|
+
if (void 0 !== this.props.fallback) return this.props.fallback;
|
|
809
|
+
return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
810
|
+
role: "alert",
|
|
811
|
+
style: {
|
|
812
|
+
padding: '16px',
|
|
813
|
+
border: '1px solid #ffcccc',
|
|
814
|
+
backgroundColor: '#fff5f5',
|
|
815
|
+
borderRadius: '4px'
|
|
816
|
+
}
|
|
817
|
+
}, /*#__PURE__*/ external_react_default().createElement("h3", null, "Something went wrong"), /*#__PURE__*/ external_react_default().createElement("p", null, this.state.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
|
|
818
|
+
onClick: this.handleReset,
|
|
819
|
+
style: {
|
|
820
|
+
marginTop: '8px',
|
|
821
|
+
padding: '8px 16px',
|
|
822
|
+
cursor: 'pointer'
|
|
823
|
+
}
|
|
824
|
+
}, "Try again"));
|
|
825
|
+
}
|
|
826
|
+
return this.props.children;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
function useRemoteModule({ pkg, version, moduleName, scopeName, onError, onLoad, retryKey = 0 }) {
|
|
830
|
+
const [moduleState, setModuleState] = (0, external_react_namespaceObject.useState)({
|
|
831
|
+
loading: true,
|
|
832
|
+
error: null,
|
|
833
|
+
component: null
|
|
834
|
+
});
|
|
835
|
+
(0, external_react_namespaceObject.useEffect)(()=>{
|
|
836
|
+
let mounted = true;
|
|
837
|
+
async function loadModule() {
|
|
838
|
+
try {
|
|
839
|
+
setModuleState((prev)=>({
|
|
840
|
+
...prev,
|
|
841
|
+
loading: true,
|
|
842
|
+
error: null
|
|
843
|
+
}));
|
|
844
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
845
|
+
name: scopeName,
|
|
846
|
+
pkg,
|
|
847
|
+
version
|
|
848
|
+
}, []);
|
|
849
|
+
if (!mf || !mounted) return;
|
|
850
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
851
|
+
if (!mounted) return;
|
|
852
|
+
if (mod && 'object' == typeof mod && 'default' in mod) {
|
|
853
|
+
const Component = mod.default;
|
|
854
|
+
setModuleState({
|
|
855
|
+
loading: false,
|
|
856
|
+
error: null,
|
|
857
|
+
component: Component
|
|
858
|
+
});
|
|
859
|
+
onLoad?.(Component);
|
|
860
|
+
} else throw new Error(`Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
861
|
+
} catch (err) {
|
|
862
|
+
if (mounted) {
|
|
863
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
864
|
+
setModuleState({
|
|
865
|
+
loading: false,
|
|
866
|
+
error,
|
|
867
|
+
component: null
|
|
868
|
+
});
|
|
869
|
+
onError?.(error);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
loadModule();
|
|
874
|
+
return ()=>{
|
|
875
|
+
mounted = false;
|
|
876
|
+
};
|
|
877
|
+
}, [
|
|
878
|
+
pkg,
|
|
879
|
+
version,
|
|
880
|
+
moduleName,
|
|
881
|
+
scopeName,
|
|
882
|
+
onError,
|
|
883
|
+
onLoad,
|
|
884
|
+
retryKey
|
|
885
|
+
]);
|
|
886
|
+
return moduleState;
|
|
887
|
+
}
|
|
888
|
+
function RemoteModuleRenderer({ pkg, version, moduleName, scopeName, loadingFallback, errorFallback, componentProps, className, style, onError, onLoad }) {
|
|
889
|
+
const moduleState = useRemoteModule({
|
|
890
|
+
pkg,
|
|
891
|
+
version,
|
|
892
|
+
moduleName,
|
|
893
|
+
scopeName,
|
|
894
|
+
onError,
|
|
895
|
+
onLoad
|
|
896
|
+
});
|
|
897
|
+
const [retryKey, setRetryKey] = (0, external_react_namespaceObject.useState)(0);
|
|
898
|
+
const handleRetry = (0, external_react_namespaceObject.useCallback)(()=>{
|
|
899
|
+
setRetryKey((prev)=>prev + 1);
|
|
900
|
+
}, []);
|
|
901
|
+
(0, external_react_namespaceObject.useEffect)(()=>{}, [
|
|
902
|
+
retryKey
|
|
903
|
+
]);
|
|
904
|
+
if (moduleState.loading) return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
905
|
+
className: className,
|
|
906
|
+
style: style,
|
|
907
|
+
role: "status",
|
|
908
|
+
"aria-live": "polite"
|
|
909
|
+
}, loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", {
|
|
910
|
+
className: "module-card module-card--loading"
|
|
911
|
+
}, /*#__PURE__*/ external_react_default().createElement("div", {
|
|
912
|
+
className: "loading-spinner",
|
|
913
|
+
"aria-hidden": "true"
|
|
914
|
+
}), /*#__PURE__*/ external_react_default().createElement("span", null, "Loading ", moduleName, "...")));
|
|
915
|
+
if (moduleState.error) {
|
|
916
|
+
if ('function' == typeof errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback(moduleState.error, handleRetry));
|
|
917
|
+
if (void 0 !== errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback);
|
|
918
|
+
return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
919
|
+
className: className,
|
|
920
|
+
style: style,
|
|
921
|
+
role: "alert"
|
|
922
|
+
}, /*#__PURE__*/ external_react_default().createElement("div", {
|
|
923
|
+
className: "module-card module-card--error"
|
|
924
|
+
}, /*#__PURE__*/ external_react_default().createElement("span", {
|
|
925
|
+
className: "error-icon",
|
|
926
|
+
"aria-hidden": "true"
|
|
927
|
+
}, "!"), /*#__PURE__*/ external_react_default().createElement("span", null, "Failed to load ", moduleName), /*#__PURE__*/ external_react_default().createElement("p", {
|
|
928
|
+
className: "error-message"
|
|
929
|
+
}, moduleState.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
|
|
930
|
+
onClick: handleRetry,
|
|
931
|
+
className: "retry-button",
|
|
932
|
+
type: "button"
|
|
933
|
+
}, "Retry")));
|
|
934
|
+
}
|
|
935
|
+
if (!moduleState.component) return null;
|
|
936
|
+
const Component = moduleState.component;
|
|
937
|
+
return /*#__PURE__*/ external_react_default().createElement("div", {
|
|
938
|
+
className: className,
|
|
939
|
+
style: style
|
|
940
|
+
}, /*#__PURE__*/ external_react_default().createElement(Component, componentProps));
|
|
941
|
+
}
|
|
942
|
+
function RemoteModuleProvider(props) {
|
|
943
|
+
const { disableErrorBoundary, errorFallback, loadingFallback, errorBoundaryOptions } = props;
|
|
944
|
+
if (disableErrorBoundary) return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
945
|
+
fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
946
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteModuleRenderer, props));
|
|
947
|
+
return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
|
|
948
|
+
fallback: errorFallback,
|
|
949
|
+
onError: errorBoundaryOptions?.onError,
|
|
950
|
+
onReset: errorBoundaryOptions?.onReset
|
|
951
|
+
}, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
952
|
+
fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
953
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteModuleRenderer, props)));
|
|
954
|
+
}
|
|
955
|
+
function lazyRemote(options) {
|
|
956
|
+
const { pkg, version = 'latest', moduleName, scopeName, maxRetries = 3, retryDelay = 1000 } = options;
|
|
957
|
+
let retryCount = 0;
|
|
958
|
+
const loadComponent = async ()=>{
|
|
959
|
+
try {
|
|
960
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
961
|
+
name: scopeName,
|
|
962
|
+
pkg,
|
|
963
|
+
version
|
|
964
|
+
}, []);
|
|
965
|
+
if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
|
|
966
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
967
|
+
if (!mod || 'object' != typeof mod || !('default' in mod)) throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
968
|
+
return {
|
|
969
|
+
default: mod.default
|
|
970
|
+
};
|
|
971
|
+
} catch (error) {
|
|
972
|
+
if (retryCount < maxRetries) {
|
|
973
|
+
retryCount++;
|
|
974
|
+
await new Promise((resolve)=>setTimeout(resolve, retryDelay * retryCount));
|
|
975
|
+
return loadComponent();
|
|
976
|
+
}
|
|
977
|
+
throw error;
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
return /*#__PURE__*/ (0, external_react_namespaceObject.lazy)(loadComponent);
|
|
981
|
+
}
|
|
982
|
+
function SuspenseRemote({ fallback, children }) {
|
|
983
|
+
return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
984
|
+
fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
985
|
+
}, children);
|
|
986
|
+
}
|
|
987
|
+
function SuspenseRemoteLoader({ pkg, version = 'latest', moduleName, scopeName, fallback, errorFallback, componentProps }) {
|
|
988
|
+
const RemoteComponent = lazyRemote({
|
|
989
|
+
pkg,
|
|
990
|
+
version,
|
|
991
|
+
moduleName,
|
|
992
|
+
scopeName
|
|
993
|
+
});
|
|
994
|
+
if (errorFallback) return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
|
|
995
|
+
fallback: errorFallback
|
|
996
|
+
}, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
997
|
+
fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
998
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps)));
|
|
999
|
+
return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
1000
|
+
fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
1001
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps));
|
|
1002
|
+
}
|
|
1003
|
+
function withRemote(WrappedComponent, options) {
|
|
1004
|
+
const RemoteComponent = lazyRemote(options);
|
|
1005
|
+
return function(props) {
|
|
1006
|
+
return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
|
|
1007
|
+
fallback: /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
|
|
1008
|
+
}, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, props));
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
function useRemoteModuleHook(options) {
|
|
1012
|
+
const { pkg, version = 'latest', moduleName, scopeName } = options;
|
|
1013
|
+
const [state, setState] = (0, external_react_namespaceObject.useState)({
|
|
1014
|
+
component: null,
|
|
1015
|
+
loading: true,
|
|
1016
|
+
error: null
|
|
1017
|
+
});
|
|
1018
|
+
(0, external_react_namespaceObject.useEffect)(()=>{
|
|
1019
|
+
let mounted = true;
|
|
1020
|
+
async function load() {
|
|
1021
|
+
try {
|
|
1022
|
+
setState((prev)=>({
|
|
1023
|
+
...prev,
|
|
1024
|
+
loading: true,
|
|
1025
|
+
error: null
|
|
1026
|
+
}));
|
|
1027
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
1028
|
+
name: scopeName,
|
|
1029
|
+
pkg,
|
|
1030
|
+
version
|
|
1031
|
+
}, []);
|
|
1032
|
+
if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
|
|
1033
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
1034
|
+
if (mounted) if (mod && 'object' == typeof mod && 'default' in mod) setState({
|
|
1035
|
+
component: mod.default,
|
|
1036
|
+
loading: false,
|
|
1037
|
+
error: null
|
|
1038
|
+
});
|
|
1039
|
+
else throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
1040
|
+
} catch (err) {
|
|
1041
|
+
if (mounted) setState({
|
|
1042
|
+
component: null,
|
|
1043
|
+
loading: false,
|
|
1044
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
load();
|
|
1049
|
+
return ()=>{
|
|
1050
|
+
mounted = false;
|
|
1051
|
+
};
|
|
1052
|
+
}, [
|
|
1053
|
+
pkg,
|
|
1054
|
+
version,
|
|
1055
|
+
moduleName,
|
|
1056
|
+
scopeName
|
|
1057
|
+
]);
|
|
1058
|
+
return {
|
|
1059
|
+
component: state.component,
|
|
1060
|
+
loading: state.loading,
|
|
1061
|
+
error: state.error
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
exports.ErrorBoundary = __webpack_exports__.ErrorBoundary;
|
|
1065
|
+
exports.RemoteModuleProvider = __webpack_exports__.RemoteModuleProvider;
|
|
1066
|
+
exports.RemoteModuleRenderer = __webpack_exports__.RemoteModuleRenderer;
|
|
1067
|
+
exports.SuspenseRemote = __webpack_exports__.SuspenseRemote;
|
|
1068
|
+
exports.SuspenseRemoteLoader = __webpack_exports__.SuspenseRemoteLoader;
|
|
754
1069
|
exports.buildCdnUrls = __webpack_exports__.buildCdnUrls;
|
|
755
1070
|
exports.buildFinalUrls = __webpack_exports__.buildFinalUrls;
|
|
756
1071
|
exports.cancelPreload = __webpack_exports__.cancelPreload;
|
|
@@ -777,6 +1092,7 @@ exports.getStableVersions = __webpack_exports__.getStableVersions;
|
|
|
777
1092
|
exports.getVersionCache = __webpack_exports__.getVersionCache;
|
|
778
1093
|
exports.isPrerelease = __webpack_exports__.isPrerelease;
|
|
779
1094
|
exports.isRemoteLoaded = __webpack_exports__.isRemoteLoaded;
|
|
1095
|
+
exports.lazyRemote = __webpack_exports__.lazyRemote;
|
|
780
1096
|
exports.loadReactVersion = __webpack_exports__.loadReactVersion;
|
|
781
1097
|
exports.loadRemoteMultiVersion = __webpack_exports__.loadRemoteMultiVersion;
|
|
782
1098
|
exports.parseVersion = __webpack_exports__.parseVersion;
|
|
@@ -791,7 +1107,15 @@ exports.sortVersions = __webpack_exports__.sortVersions;
|
|
|
791
1107
|
exports.tryLoadRemote = __webpack_exports__.tryLoadRemote;
|
|
792
1108
|
exports.unloadAll = __webpack_exports__.unloadAll;
|
|
793
1109
|
exports.unloadRemote = __webpack_exports__.unloadRemote;
|
|
1110
|
+
exports.useRemoteModule = __webpack_exports__.useRemoteModule;
|
|
1111
|
+
exports.useRemoteModuleHook = __webpack_exports__.useRemoteModuleHook;
|
|
1112
|
+
exports.withRemote = __webpack_exports__.withRemote;
|
|
794
1113
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
1114
|
+
"ErrorBoundary",
|
|
1115
|
+
"RemoteModuleProvider",
|
|
1116
|
+
"RemoteModuleRenderer",
|
|
1117
|
+
"SuspenseRemote",
|
|
1118
|
+
"SuspenseRemoteLoader",
|
|
795
1119
|
"buildCdnUrls",
|
|
796
1120
|
"buildFinalUrls",
|
|
797
1121
|
"cancelPreload",
|
|
@@ -818,6 +1142,7 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
818
1142
|
"getVersionCache",
|
|
819
1143
|
"isPrerelease",
|
|
820
1144
|
"isRemoteLoaded",
|
|
1145
|
+
"lazyRemote",
|
|
821
1146
|
"loadReactVersion",
|
|
822
1147
|
"loadRemoteMultiVersion",
|
|
823
1148
|
"parseVersion",
|
|
@@ -831,7 +1156,10 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
831
1156
|
"sortVersions",
|
|
832
1157
|
"tryLoadRemote",
|
|
833
1158
|
"unloadAll",
|
|
834
|
-
"unloadRemote"
|
|
1159
|
+
"unloadRemote",
|
|
1160
|
+
"useRemoteModule",
|
|
1161
|
+
"useRemoteModuleHook",
|
|
1162
|
+
"withRemote"
|
|
835
1163
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
836
1164
|
Object.defineProperty(exports, '__esModule', {
|
|
837
1165
|
value: true
|
package/dist/main.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createInstance } from "@module-federation/enhanced/runtime";
|
|
2
|
+
import react, { Suspense, lazy, useCallback, useEffect, useState } from "react";
|
|
2
3
|
async function loadReactVersion(version) {
|
|
3
4
|
const runtime = await createInstance({
|
|
4
5
|
name: `react_${version}_runtime`,
|
|
@@ -684,4 +685,294 @@ const eventBus = EventBusClass.create();
|
|
|
684
685
|
function createEventBus() {
|
|
685
686
|
return EventBusClass.create();
|
|
686
687
|
}
|
|
687
|
-
|
|
688
|
+
class ErrorBoundary extends react.Component {
|
|
689
|
+
constructor(props){
|
|
690
|
+
super(props);
|
|
691
|
+
this.state = {
|
|
692
|
+
hasError: false,
|
|
693
|
+
error: null,
|
|
694
|
+
errorInfo: null
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
static getDerivedStateFromError(error) {
|
|
698
|
+
return {
|
|
699
|
+
hasError: true,
|
|
700
|
+
error,
|
|
701
|
+
errorInfo: null
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
componentDidCatch(error, errorInfo) {
|
|
705
|
+
this.setState({
|
|
706
|
+
errorInfo
|
|
707
|
+
});
|
|
708
|
+
this.props.onError?.(error, errorInfo);
|
|
709
|
+
console.error('[RemoteReloadUtils] ErrorBoundary caught error:', error, errorInfo);
|
|
710
|
+
}
|
|
711
|
+
handleReset = ()=>{
|
|
712
|
+
this.setState({
|
|
713
|
+
hasError: false,
|
|
714
|
+
error: null,
|
|
715
|
+
errorInfo: null
|
|
716
|
+
});
|
|
717
|
+
this.props.onReset?.();
|
|
718
|
+
};
|
|
719
|
+
render() {
|
|
720
|
+
if (this.state.hasError && this.state.error) {
|
|
721
|
+
if ('function' == typeof this.props.fallback) return this.props.fallback(this.state.error, this.handleReset);
|
|
722
|
+
if (void 0 !== this.props.fallback) return this.props.fallback;
|
|
723
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
724
|
+
role: "alert",
|
|
725
|
+
style: {
|
|
726
|
+
padding: '16px',
|
|
727
|
+
border: '1px solid #ffcccc',
|
|
728
|
+
backgroundColor: '#fff5f5',
|
|
729
|
+
borderRadius: '4px'
|
|
730
|
+
}
|
|
731
|
+
}, /*#__PURE__*/ react.createElement("h3", null, "Something went wrong"), /*#__PURE__*/ react.createElement("p", null, this.state.error.message), /*#__PURE__*/ react.createElement("button", {
|
|
732
|
+
onClick: this.handleReset,
|
|
733
|
+
style: {
|
|
734
|
+
marginTop: '8px',
|
|
735
|
+
padding: '8px 16px',
|
|
736
|
+
cursor: 'pointer'
|
|
737
|
+
}
|
|
738
|
+
}, "Try again"));
|
|
739
|
+
}
|
|
740
|
+
return this.props.children;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function useRemoteModule({ pkg, version, moduleName, scopeName, onError, onLoad, retryKey = 0 }) {
|
|
744
|
+
const [moduleState, setModuleState] = useState({
|
|
745
|
+
loading: true,
|
|
746
|
+
error: null,
|
|
747
|
+
component: null
|
|
748
|
+
});
|
|
749
|
+
useEffect(()=>{
|
|
750
|
+
let mounted = true;
|
|
751
|
+
async function loadModule() {
|
|
752
|
+
try {
|
|
753
|
+
setModuleState((prev)=>({
|
|
754
|
+
...prev,
|
|
755
|
+
loading: true,
|
|
756
|
+
error: null
|
|
757
|
+
}));
|
|
758
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
759
|
+
name: scopeName,
|
|
760
|
+
pkg,
|
|
761
|
+
version
|
|
762
|
+
}, []);
|
|
763
|
+
if (!mf || !mounted) return;
|
|
764
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
765
|
+
if (!mounted) return;
|
|
766
|
+
if (mod && 'object' == typeof mod && 'default' in mod) {
|
|
767
|
+
const Component = mod.default;
|
|
768
|
+
setModuleState({
|
|
769
|
+
loading: false,
|
|
770
|
+
error: null,
|
|
771
|
+
component: Component
|
|
772
|
+
});
|
|
773
|
+
onLoad?.(Component);
|
|
774
|
+
} else throw new Error(`Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
775
|
+
} catch (err) {
|
|
776
|
+
if (mounted) {
|
|
777
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
778
|
+
setModuleState({
|
|
779
|
+
loading: false,
|
|
780
|
+
error,
|
|
781
|
+
component: null
|
|
782
|
+
});
|
|
783
|
+
onError?.(error);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
loadModule();
|
|
788
|
+
return ()=>{
|
|
789
|
+
mounted = false;
|
|
790
|
+
};
|
|
791
|
+
}, [
|
|
792
|
+
pkg,
|
|
793
|
+
version,
|
|
794
|
+
moduleName,
|
|
795
|
+
scopeName,
|
|
796
|
+
onError,
|
|
797
|
+
onLoad,
|
|
798
|
+
retryKey
|
|
799
|
+
]);
|
|
800
|
+
return moduleState;
|
|
801
|
+
}
|
|
802
|
+
function RemoteModuleRenderer({ pkg, version, moduleName, scopeName, loadingFallback, errorFallback, componentProps, className, style, onError, onLoad }) {
|
|
803
|
+
const moduleState = useRemoteModule({
|
|
804
|
+
pkg,
|
|
805
|
+
version,
|
|
806
|
+
moduleName,
|
|
807
|
+
scopeName,
|
|
808
|
+
onError,
|
|
809
|
+
onLoad
|
|
810
|
+
});
|
|
811
|
+
const [retryKey, setRetryKey] = useState(0);
|
|
812
|
+
const handleRetry = useCallback(()=>{
|
|
813
|
+
setRetryKey((prev)=>prev + 1);
|
|
814
|
+
}, []);
|
|
815
|
+
useEffect(()=>{}, [
|
|
816
|
+
retryKey
|
|
817
|
+
]);
|
|
818
|
+
if (moduleState.loading) return /*#__PURE__*/ react.createElement("div", {
|
|
819
|
+
className: className,
|
|
820
|
+
style: style,
|
|
821
|
+
role: "status",
|
|
822
|
+
"aria-live": "polite"
|
|
823
|
+
}, loadingFallback || /*#__PURE__*/ react.createElement("div", {
|
|
824
|
+
className: "module-card module-card--loading"
|
|
825
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
826
|
+
className: "loading-spinner",
|
|
827
|
+
"aria-hidden": "true"
|
|
828
|
+
}), /*#__PURE__*/ react.createElement("span", null, "Loading ", moduleName, "...")));
|
|
829
|
+
if (moduleState.error) {
|
|
830
|
+
if ('function' == typeof errorFallback) return /*#__PURE__*/ react.createElement(react.Fragment, null, errorFallback(moduleState.error, handleRetry));
|
|
831
|
+
if (void 0 !== errorFallback) return /*#__PURE__*/ react.createElement(react.Fragment, null, errorFallback);
|
|
832
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
833
|
+
className: className,
|
|
834
|
+
style: style,
|
|
835
|
+
role: "alert"
|
|
836
|
+
}, /*#__PURE__*/ react.createElement("div", {
|
|
837
|
+
className: "module-card module-card--error"
|
|
838
|
+
}, /*#__PURE__*/ react.createElement("span", {
|
|
839
|
+
className: "error-icon",
|
|
840
|
+
"aria-hidden": "true"
|
|
841
|
+
}, "!"), /*#__PURE__*/ react.createElement("span", null, "Failed to load ", moduleName), /*#__PURE__*/ react.createElement("p", {
|
|
842
|
+
className: "error-message"
|
|
843
|
+
}, moduleState.error.message), /*#__PURE__*/ react.createElement("button", {
|
|
844
|
+
onClick: handleRetry,
|
|
845
|
+
className: "retry-button",
|
|
846
|
+
type: "button"
|
|
847
|
+
}, "Retry")));
|
|
848
|
+
}
|
|
849
|
+
if (!moduleState.component) return null;
|
|
850
|
+
const Component = moduleState.component;
|
|
851
|
+
return /*#__PURE__*/ react.createElement("div", {
|
|
852
|
+
className: className,
|
|
853
|
+
style: style
|
|
854
|
+
}, /*#__PURE__*/ react.createElement(Component, componentProps));
|
|
855
|
+
}
|
|
856
|
+
function RemoteModuleProvider(props) {
|
|
857
|
+
const { disableErrorBoundary, errorFallback, loadingFallback, errorBoundaryOptions } = props;
|
|
858
|
+
if (disableErrorBoundary) return /*#__PURE__*/ react.createElement(Suspense, {
|
|
859
|
+
fallback: loadingFallback || /*#__PURE__*/ react.createElement("div", null, "Loading...")
|
|
860
|
+
}, /*#__PURE__*/ react.createElement(RemoteModuleRenderer, props));
|
|
861
|
+
return /*#__PURE__*/ react.createElement(ErrorBoundary, {
|
|
862
|
+
fallback: errorFallback,
|
|
863
|
+
onError: errorBoundaryOptions?.onError,
|
|
864
|
+
onReset: errorBoundaryOptions?.onReset
|
|
865
|
+
}, /*#__PURE__*/ react.createElement(Suspense, {
|
|
866
|
+
fallback: loadingFallback || /*#__PURE__*/ react.createElement("div", null, "Loading...")
|
|
867
|
+
}, /*#__PURE__*/ react.createElement(RemoteModuleRenderer, props)));
|
|
868
|
+
}
|
|
869
|
+
function lazyRemote(options) {
|
|
870
|
+
const { pkg, version = 'latest', moduleName, scopeName, maxRetries = 3, retryDelay = 1000 } = options;
|
|
871
|
+
let retryCount = 0;
|
|
872
|
+
const loadComponent = async ()=>{
|
|
873
|
+
try {
|
|
874
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
875
|
+
name: scopeName,
|
|
876
|
+
pkg,
|
|
877
|
+
version
|
|
878
|
+
}, []);
|
|
879
|
+
if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
|
|
880
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
881
|
+
if (!mod || 'object' != typeof mod || !('default' in mod)) throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
882
|
+
return {
|
|
883
|
+
default: mod.default
|
|
884
|
+
};
|
|
885
|
+
} catch (error) {
|
|
886
|
+
if (retryCount < maxRetries) {
|
|
887
|
+
retryCount++;
|
|
888
|
+
await new Promise((resolve)=>setTimeout(resolve, retryDelay * retryCount));
|
|
889
|
+
return loadComponent();
|
|
890
|
+
}
|
|
891
|
+
throw error;
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
return /*#__PURE__*/ lazy(loadComponent);
|
|
895
|
+
}
|
|
896
|
+
function SuspenseRemote({ fallback, children }) {
|
|
897
|
+
return /*#__PURE__*/ react.createElement(Suspense, {
|
|
898
|
+
fallback: fallback || /*#__PURE__*/ react.createElement("div", null, "Loading...")
|
|
899
|
+
}, children);
|
|
900
|
+
}
|
|
901
|
+
function SuspenseRemoteLoader({ pkg, version = 'latest', moduleName, scopeName, fallback, errorFallback, componentProps }) {
|
|
902
|
+
const RemoteComponent = lazyRemote({
|
|
903
|
+
pkg,
|
|
904
|
+
version,
|
|
905
|
+
moduleName,
|
|
906
|
+
scopeName
|
|
907
|
+
});
|
|
908
|
+
if (errorFallback) return /*#__PURE__*/ react.createElement(ErrorBoundary, {
|
|
909
|
+
fallback: errorFallback
|
|
910
|
+
}, /*#__PURE__*/ react.createElement(Suspense, {
|
|
911
|
+
fallback: fallback || /*#__PURE__*/ react.createElement("div", null, "Loading...")
|
|
912
|
+
}, /*#__PURE__*/ react.createElement(RemoteComponent, componentProps)));
|
|
913
|
+
return /*#__PURE__*/ react.createElement(Suspense, {
|
|
914
|
+
fallback: fallback || /*#__PURE__*/ react.createElement("div", null, "Loading...")
|
|
915
|
+
}, /*#__PURE__*/ react.createElement(RemoteComponent, componentProps));
|
|
916
|
+
}
|
|
917
|
+
function withRemote(WrappedComponent, options) {
|
|
918
|
+
const RemoteComponent = lazyRemote(options);
|
|
919
|
+
return function(props) {
|
|
920
|
+
return /*#__PURE__*/ react.createElement(Suspense, {
|
|
921
|
+
fallback: /*#__PURE__*/ react.createElement("div", null, "Loading...")
|
|
922
|
+
}, /*#__PURE__*/ react.createElement(RemoteComponent, props));
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function useRemoteModuleHook(options) {
|
|
926
|
+
const { pkg, version = 'latest', moduleName, scopeName } = options;
|
|
927
|
+
const [state, setState] = useState({
|
|
928
|
+
component: null,
|
|
929
|
+
loading: true,
|
|
930
|
+
error: null
|
|
931
|
+
});
|
|
932
|
+
useEffect(()=>{
|
|
933
|
+
let mounted = true;
|
|
934
|
+
async function load() {
|
|
935
|
+
try {
|
|
936
|
+
setState((prev)=>({
|
|
937
|
+
...prev,
|
|
938
|
+
loading: true,
|
|
939
|
+
error: null
|
|
940
|
+
}));
|
|
941
|
+
const { mf } = await loadRemoteMultiVersion({
|
|
942
|
+
name: scopeName,
|
|
943
|
+
pkg,
|
|
944
|
+
version
|
|
945
|
+
}, []);
|
|
946
|
+
if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
|
|
947
|
+
const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
|
|
948
|
+
if (mounted) if (mod && 'object' == typeof mod && 'default' in mod) setState({
|
|
949
|
+
component: mod.default,
|
|
950
|
+
loading: false,
|
|
951
|
+
error: null
|
|
952
|
+
});
|
|
953
|
+
else throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
|
|
954
|
+
} catch (err) {
|
|
955
|
+
if (mounted) setState({
|
|
956
|
+
component: null,
|
|
957
|
+
loading: false,
|
|
958
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
load();
|
|
963
|
+
return ()=>{
|
|
964
|
+
mounted = false;
|
|
965
|
+
};
|
|
966
|
+
}, [
|
|
967
|
+
pkg,
|
|
968
|
+
version,
|
|
969
|
+
moduleName,
|
|
970
|
+
scopeName
|
|
971
|
+
]);
|
|
972
|
+
return {
|
|
973
|
+
component: state.component,
|
|
974
|
+
loading: state.loading,
|
|
975
|
+
error: state.error
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
export { ErrorBoundary, RemoteModuleProvider, RemoteModuleRenderer, SuspenseRemote, SuspenseRemoteLoader, buildCdnUrls, buildFinalUrls, cancelPreload, checkModuleLoadable, checkRemoteHealth, checkVersionCompatibility, clearPreloadCache, compareVersions, createEventBus, eventBus, extractMajorVersion, fallbackPlugin, fetchAvailableVersions, fetchLatestVersion, findCompatibleVersion, formatHealthStatus, getCompatibleReactVersions, getFinalSharedConfig, getLatestVersion, getLoadedRemotes, getPreloadStatus, getRemoteHealthReport, getStableVersions, getVersionCache, isPrerelease, isRemoteLoaded, lazyRemote, loadReactVersion, loadRemoteMultiVersion, parseVersion, preloadRemote, preloadRemoteList, registerLoadedModule, registerRemoteInstance, resolveFinalVersion, satisfiesVersion, setVersionCache, sortVersions, tryLoadRemote, unloadAll, unloadRemote, useRemoteModule, useRemoteModuleHook, withRemote };
|