@ice/mf-runtime 1.0.3-beta.2 → 1.0.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.
- package/README.md +1 -221
- package/es2017/RemoteModule.js +21 -73
- package/es2017/index.d.ts +0 -2
- package/es2017/index.js +3 -5
- package/es2017/runtime-plugin.js +27 -29
- package/es2017/types.d.ts +0 -67
- package/es2017/types.js +1 -18
- package/esm/RemoteModule.js +30 -73
- package/esm/index.d.ts +0 -2
- package/esm/index.js +3 -5
- package/esm/runtime-plugin.js +37 -33
- package/esm/types.d.ts +0 -67
- package/esm/types.js +1 -20
- package/package.json +3 -14
- package/docs/PLUGIN_SYSTEM_GUIDE.md +0 -294
- package/es2017/__tests__/plugin-manager.test.d.ts +0 -1
- package/es2017/__tests__/plugin-manager.test.js +0 -291
- package/es2017/__tests__/setup.d.ts +0 -1
- package/es2017/__tests__/setup.js +0 -28
- package/es2017/plugin-manager.d.ts +0 -74
- package/es2017/plugin-manager.js +0 -128
- package/esm/__tests__/plugin-manager.test.d.ts +0 -1
- package/esm/__tests__/plugin-manager.test.js +0 -343
- package/esm/__tests__/setup.d.ts +0 -1
- package/esm/__tests__/setup.js +0 -43
- package/esm/plugin-manager.d.ts +0 -74
- package/esm/plugin-manager.js +0 -201
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
# 微前端插件系统使用指南
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
|
-
`@ice/mf-runtime` 提供了一套增强插件系统,允许开发者对远程组件进行功能扩展,如错误边界、日志记录、性能监控等。
|
|
6
|
-
|
|
7
|
-
## 快速开始
|
|
8
|
-
|
|
9
|
-
### 1. 安装依赖
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install @ice/mf-runtime
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
### 2. 基本使用
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import { RemoteModule, registerEnhancedPlugins } from '@ice/mf-runtime';
|
|
19
|
-
import { createEnhancedLoggingPlugin } from '@ice/mf-plugin-example';
|
|
20
|
-
|
|
21
|
-
// 注册插件
|
|
22
|
-
registerEnhancedPlugins([createEnhancedLoggingPlugin()]);
|
|
23
|
-
|
|
24
|
-
// 使用远程组件
|
|
25
|
-
function App() {
|
|
26
|
-
return (
|
|
27
|
-
<RemoteModule
|
|
28
|
-
scope="remote-app"
|
|
29
|
-
module="Button"
|
|
30
|
-
/>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## 插件开发
|
|
36
|
-
|
|
37
|
-
### 插件接口
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
export interface EnhancedRuntimePlugin {
|
|
41
|
-
name: string;
|
|
42
|
-
wrapComponent?: ComponentWrapper;
|
|
43
|
-
injectProps?: PropInjector;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface ComponentWrapper {
|
|
47
|
-
(
|
|
48
|
-
WrappedComponent: React.ComponentType<any>,
|
|
49
|
-
context: WrapperContext
|
|
50
|
-
): React.ComponentType<any>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface PropInjector {
|
|
54
|
-
(
|
|
55
|
-
props: Record<string, any>,
|
|
56
|
-
context: InjectionContext
|
|
57
|
-
): Record<string, any>;
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 上下文信息
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
export interface WrapperContext {
|
|
65
|
-
remoteName: string;
|
|
66
|
-
moduleName: string;
|
|
67
|
-
props: Record<string, any>;
|
|
68
|
-
React: typeof import('react'); // 正确的 React 实例
|
|
69
|
-
ReactDOM?: typeof import('react-dom'); // 正确的 ReactDOM 实例
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface InjectionContext {
|
|
73
|
-
remoteName: string;
|
|
74
|
-
moduleName: string;
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### 创建插件
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
import type { EnhancedRuntimePlugin, WrapperContext } from '@ice/mf-runtime';
|
|
82
|
-
|
|
83
|
-
export function createMyPlugin(options = {}): EnhancedRuntimePlugin {
|
|
84
|
-
return {
|
|
85
|
-
name: 'my-plugin',
|
|
86
|
-
|
|
87
|
-
wrapComponent: (WrappedComponent, context: WrapperContext) => {
|
|
88
|
-
// 🔑 使用上下文提供的 React 实例
|
|
89
|
-
const { React: ContextReact } = context;
|
|
90
|
-
|
|
91
|
-
const MyWrapper = ContextReact.forwardRef<any, any>((props, ref) => {
|
|
92
|
-
ContextReact.useEffect(() => {
|
|
93
|
-
console.log(`Component ${context.remoteName}/${context.moduleName} mounted`);
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
return ContextReact.createElement(WrappedComponent, { ...props, ref });
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
MyWrapper.displayName = `MyWrapper(${WrappedComponent.displayName || 'Component'})`;
|
|
100
|
-
return MyWrapper;
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
injectProps: (props, context) => {
|
|
104
|
-
return {
|
|
105
|
-
...props,
|
|
106
|
-
pluginData: 'injected by my plugin'
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## 内置插件示例
|
|
114
|
-
|
|
115
|
-
### 日志记录插件
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
import {
|
|
119
|
-
createEnhancedLoggingPlugin,
|
|
120
|
-
developmentEnhancedLoggingPlugin,
|
|
121
|
-
productionEnhancedLoggingPlugin
|
|
122
|
-
} from '@ice/mf-plugin-example';
|
|
123
|
-
|
|
124
|
-
// 自定义配置
|
|
125
|
-
registerEnhancedPlugins([createEnhancedLoggingPlugin({
|
|
126
|
-
level: 'info',
|
|
127
|
-
logComponentRender: true,
|
|
128
|
-
enablePerformanceMonitoring: true
|
|
129
|
-
})]);
|
|
130
|
-
|
|
131
|
-
// 或使用预定义插件
|
|
132
|
-
registerEnhancedPlugins([
|
|
133
|
-
process.env.NODE_ENV === 'development'
|
|
134
|
-
? developmentEnhancedLoggingPlugin
|
|
135
|
-
: productionEnhancedLoggingPlugin
|
|
136
|
-
]);
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 属性注入插件
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
import { createPropsInjectionPlugin } from '@ice/mf-plugin-example';
|
|
143
|
-
|
|
144
|
-
registerEnhancedPlugins([createPropsInjectionPlugin({
|
|
145
|
-
globalProps: {
|
|
146
|
-
theme: 'dark',
|
|
147
|
-
version: '1.0.0'
|
|
148
|
-
}
|
|
149
|
-
})]);
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
## 插件管理
|
|
153
|
-
|
|
154
|
-
### 注册插件
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
import { registerEnhancedPlugins } from '@ice/mf-runtime';
|
|
158
|
-
|
|
159
|
-
// 注册多个插件(推荐方式)
|
|
160
|
-
registerEnhancedPlugins([plugin1, plugin2, plugin3]);
|
|
161
|
-
|
|
162
|
-
// 或通过插件管理器注册
|
|
163
|
-
import { getEnhancedPluginManager } from '@ice/mf-runtime';
|
|
164
|
-
const manager = getEnhancedPluginManager();
|
|
165
|
-
manager.register(myPlugin);
|
|
166
|
-
manager.registerPlugins([plugin1, plugin2]);
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### 获取插件管理器
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
import { getEnhancedPluginManager } from '@ice/mf-runtime';
|
|
173
|
-
|
|
174
|
-
const manager = getEnhancedPluginManager();
|
|
175
|
-
|
|
176
|
-
// 获取所有插件
|
|
177
|
-
const plugins = manager.getPlugins();
|
|
178
|
-
|
|
179
|
-
// 获取插件信息
|
|
180
|
-
const info = manager.getPluginInfo();
|
|
181
|
-
|
|
182
|
-
// 移除插件
|
|
183
|
-
manager.removePlugin('plugin-name');
|
|
184
|
-
|
|
185
|
-
// 清空所有插件
|
|
186
|
-
manager.clear();
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### 在组件中使用
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
import { useEnhancedPluginManager } from '@ice/mf-runtime';
|
|
193
|
-
|
|
194
|
-
function MyComponent() {
|
|
195
|
-
const pluginManager = useEnhancedPluginManager();
|
|
196
|
-
|
|
197
|
-
// 动态注册插件
|
|
198
|
-
React.useEffect(() => {
|
|
199
|
-
pluginManager.register(createMyPlugin());
|
|
200
|
-
}, []);
|
|
201
|
-
|
|
202
|
-
return <div>My Component</div>;
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## 远程组件配置
|
|
207
|
-
|
|
208
|
-
### 基本配置
|
|
209
|
-
|
|
210
|
-
```tsx
|
|
211
|
-
<RemoteModule
|
|
212
|
-
scope="remote-app"
|
|
213
|
-
module="Button"
|
|
214
|
-
componentProps={{ text: 'Click me' }}
|
|
215
|
-
/>
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
## 最佳实践
|
|
220
|
-
|
|
221
|
-
### 1. React 实例一致性
|
|
222
|
-
|
|
223
|
-
始终使用上下文提供的 React 实例,避免使用 JSX 语法:
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
// ✅ 正确 - 使用上下文的 React 实例
|
|
227
|
-
wrapComponent: (WrappedComponent, context) => {
|
|
228
|
-
const { React: ContextReact } = context;
|
|
229
|
-
return ContextReact.forwardRef((props, ref) => {
|
|
230
|
-
// 使用 createElement 确保实例一致性
|
|
231
|
-
return ContextReact.createElement(WrappedComponent, { ...props, ref });
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ❌ 错误 - 使用导入的 React 实例
|
|
236
|
-
import React from 'react';
|
|
237
|
-
wrapComponent: (WrappedComponent, context) => {
|
|
238
|
-
return React.forwardRef((props, ref) => { // 可能是错误的 React 实例
|
|
239
|
-
return <WrappedComponent {...props} ref={ref} />; // JSX 使用编译时的 React
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**为什么不能直接用 JSX?**
|
|
245
|
-
|
|
246
|
-
在微前端场景中:
|
|
247
|
-
- 主应用和远程模块可能使用不同版本的 React
|
|
248
|
-
- JSX 编译时绑定到特定的 React 实例
|
|
249
|
-
- 使用 `ContextReact.createElement` 确保运行时使用正确的实例
|
|
250
|
-
|
|
251
|
-
### 2. Ref 转发
|
|
252
|
-
|
|
253
|
-
确保正确转发 ref:
|
|
254
|
-
|
|
255
|
-
```typescript
|
|
256
|
-
const MyWrapper = ContextReact.forwardRef<any, any>((props, ref) => {
|
|
257
|
-
return ContextReact.createElement(WrappedComponent, { ...props, ref });
|
|
258
|
-
});
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## 故障排除
|
|
262
|
-
|
|
263
|
-
### 常见问题
|
|
264
|
-
|
|
265
|
-
1. **插件不生效**
|
|
266
|
-
- 确保插件已正确注册
|
|
267
|
-
- 检查插件名称是否唯一
|
|
268
|
-
- 验证插件函数是否正确实现
|
|
269
|
-
|
|
270
|
-
2. **React Hook 错误**
|
|
271
|
-
- 确保使用上下文提供的 React 实例
|
|
272
|
-
- 检查是否在条件语句中使用 Hook
|
|
273
|
-
|
|
274
|
-
3. **Ref 转发失败**
|
|
275
|
-
- 确保使用 `React.forwardRef`
|
|
276
|
-
- 检查 ref 是否正确传递给底层组件
|
|
277
|
-
|
|
278
|
-
4. **类型错误**
|
|
279
|
-
- 确保导入正确的类型定义
|
|
280
|
-
- 使用 `React.ComponentType<any>` 处理动态组件类型
|
|
281
|
-
|
|
282
|
-
### 调试技巧
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
// 启用调试日志
|
|
286
|
-
registerEnhancedPlugin(createEnhancedLoggingPlugin({
|
|
287
|
-
level: 'debug',
|
|
288
|
-
logComponentRender: true
|
|
289
|
-
}));
|
|
290
|
-
|
|
291
|
-
// 检查插件状态
|
|
292
|
-
const manager = getEnhancedPluginManager();
|
|
293
|
-
console.log('Registered plugins:', manager.getPluginInfo());
|
|
294
|
-
```
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import { _ as _object_spread } from "@swc/helpers/_/_object_spread";
|
|
2
|
-
import { _ as _object_spread_props } from "@swc/helpers/_/_object_spread_props";
|
|
3
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
4
|
-
import { EnhancedPluginManager, getEnhancedPluginManager, registerEnhancedPlugins } from '../plugin-manager';
|
|
5
|
-
import * as React from 'react';
|
|
6
|
-
describe('EnhancedPluginManager', ()=>{
|
|
7
|
-
let manager;
|
|
8
|
-
beforeEach(()=>{
|
|
9
|
-
manager = new EnhancedPluginManager();
|
|
10
|
-
});
|
|
11
|
-
describe('Plugin Registration', ()=>{
|
|
12
|
-
it('should register an enhanced plugin', ()=>{
|
|
13
|
-
const enhancedPlugin = {
|
|
14
|
-
name: 'test-enhanced',
|
|
15
|
-
wrapComponent: (Component)=>Component,
|
|
16
|
-
injectProps: (props)=>props
|
|
17
|
-
};
|
|
18
|
-
manager.register(enhancedPlugin);
|
|
19
|
-
const plugins = manager.getPlugins();
|
|
20
|
-
expect(plugins).toHaveLength(1);
|
|
21
|
-
expect(plugins[0].name).toBe('test-enhanced');
|
|
22
|
-
});
|
|
23
|
-
it('should replace existing plugin with same name', ()=>{
|
|
24
|
-
const plugin1 = {
|
|
25
|
-
name: 'test-plugin',
|
|
26
|
-
wrapComponent: (Component)=>Component
|
|
27
|
-
};
|
|
28
|
-
const plugin2 = {
|
|
29
|
-
name: 'test-plugin',
|
|
30
|
-
injectProps: (props)=>props
|
|
31
|
-
};
|
|
32
|
-
manager.register(plugin1);
|
|
33
|
-
manager.register(plugin2);
|
|
34
|
-
const plugins = manager.getPlugins();
|
|
35
|
-
expect(plugins).toHaveLength(1);
|
|
36
|
-
expect(plugins[0]).toBe(plugin2);
|
|
37
|
-
});
|
|
38
|
-
it('should register multiple plugins', ()=>{
|
|
39
|
-
const plugins = [
|
|
40
|
-
{
|
|
41
|
-
name: 'plugin1',
|
|
42
|
-
wrapComponent: (Component)=>Component
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
name: 'plugin2',
|
|
46
|
-
injectProps: (props)=>props
|
|
47
|
-
}
|
|
48
|
-
];
|
|
49
|
-
manager.registerPlugins(plugins);
|
|
50
|
-
expect(manager.getPlugins()).toHaveLength(2);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe('Component Wrapping', ()=>{
|
|
54
|
-
it('should wrap component with single plugin', ()=>{
|
|
55
|
-
const TestComponent = ()=>React.createElement('div', null, 'test');
|
|
56
|
-
const mockWrapper = vi.fn((Component)=>Component);
|
|
57
|
-
const plugin = {
|
|
58
|
-
name: 'wrapper-plugin',
|
|
59
|
-
wrapComponent: mockWrapper
|
|
60
|
-
};
|
|
61
|
-
manager.register(plugin);
|
|
62
|
-
const context = {
|
|
63
|
-
remoteName: 'test-remote',
|
|
64
|
-
moduleName: 'test-module',
|
|
65
|
-
props: {
|
|
66
|
-
test: true
|
|
67
|
-
},
|
|
68
|
-
React: React,
|
|
69
|
-
ReactDOM: undefined
|
|
70
|
-
};
|
|
71
|
-
const wrappedComponent = manager.wrapComponent(TestComponent, context);
|
|
72
|
-
expect(mockWrapper).toHaveBeenCalledWith(TestComponent, context);
|
|
73
|
-
expect(wrappedComponent).toBe(TestComponent);
|
|
74
|
-
});
|
|
75
|
-
it('should apply multiple wrappers in order', ()=>{
|
|
76
|
-
const TestComponent = ()=>React.createElement('div', null, 'original');
|
|
77
|
-
const Wrapper1 = ()=>React.createElement('div', null, 'wrapper1');
|
|
78
|
-
const Wrapper2 = ()=>React.createElement('div', null, 'wrapper2');
|
|
79
|
-
const plugin1 = {
|
|
80
|
-
name: 'wrapper1',
|
|
81
|
-
wrapComponent: ()=>Wrapper1
|
|
82
|
-
};
|
|
83
|
-
const plugin2 = {
|
|
84
|
-
name: 'wrapper2',
|
|
85
|
-
wrapComponent: ()=>Wrapper2
|
|
86
|
-
};
|
|
87
|
-
// 注册顺序很重要
|
|
88
|
-
manager.register(plugin1);
|
|
89
|
-
manager.register(plugin2);
|
|
90
|
-
const context = {
|
|
91
|
-
remoteName: 'test-remote',
|
|
92
|
-
moduleName: 'test-module',
|
|
93
|
-
props: {},
|
|
94
|
-
React: React,
|
|
95
|
-
ReactDOM: undefined
|
|
96
|
-
};
|
|
97
|
-
const result = manager.wrapComponent(TestComponent, context);
|
|
98
|
-
// plugin2 应该最后执行,所以返回 Wrapper2
|
|
99
|
-
expect(result).toBe(Wrapper2);
|
|
100
|
-
});
|
|
101
|
-
it('should handle wrapper errors gracefully', ()=>{
|
|
102
|
-
const TestComponent = ()=>React.createElement('div', null, 'test');
|
|
103
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(()=>{});
|
|
104
|
-
const plugin = {
|
|
105
|
-
name: 'error-plugin',
|
|
106
|
-
wrapComponent: ()=>{
|
|
107
|
-
throw new Error('Wrapper error');
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
manager.register(plugin);
|
|
111
|
-
const context = {
|
|
112
|
-
remoteName: 'test-remote',
|
|
113
|
-
moduleName: 'test-module',
|
|
114
|
-
props: {},
|
|
115
|
-
React: React,
|
|
116
|
-
ReactDOM: undefined
|
|
117
|
-
};
|
|
118
|
-
const result = manager.wrapComponent(TestComponent, context);
|
|
119
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Error applying wrapper from plugin "error-plugin"'), expect.any(Error));
|
|
120
|
-
expect(result).toBe(TestComponent); // 应该返回原始组件
|
|
121
|
-
consoleSpy.mockRestore();
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
describe('Props Injection', ()=>{
|
|
125
|
-
it('should inject props with single plugin', ()=>{
|
|
126
|
-
const mockInjector = vi.fn((props)=>_object_spread_props(_object_spread({}, props), {
|
|
127
|
-
injected: true
|
|
128
|
-
}));
|
|
129
|
-
const plugin = {
|
|
130
|
-
name: 'injector-plugin',
|
|
131
|
-
injectProps: mockInjector
|
|
132
|
-
};
|
|
133
|
-
manager.register(plugin);
|
|
134
|
-
const context = {
|
|
135
|
-
remoteName: 'test-remote',
|
|
136
|
-
moduleName: 'test-module'
|
|
137
|
-
};
|
|
138
|
-
const result = manager.injectProps({
|
|
139
|
-
original: true
|
|
140
|
-
}, context);
|
|
141
|
-
expect(mockInjector).toHaveBeenCalledWith({
|
|
142
|
-
original: true
|
|
143
|
-
}, context);
|
|
144
|
-
expect(result).toEqual({
|
|
145
|
-
original: true,
|
|
146
|
-
injected: true
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
it('should apply multiple injectors in order', ()=>{
|
|
150
|
-
const plugin1 = {
|
|
151
|
-
name: 'injector1',
|
|
152
|
-
injectProps: (props)=>_object_spread_props(_object_spread({}, props), {
|
|
153
|
-
step1: true
|
|
154
|
-
})
|
|
155
|
-
};
|
|
156
|
-
const plugin2 = {
|
|
157
|
-
name: 'injector2',
|
|
158
|
-
injectProps: (props)=>_object_spread_props(_object_spread({}, props), {
|
|
159
|
-
step2: true
|
|
160
|
-
})
|
|
161
|
-
};
|
|
162
|
-
manager.register(plugin1);
|
|
163
|
-
manager.register(plugin2);
|
|
164
|
-
const context = {
|
|
165
|
-
remoteName: 'test-remote',
|
|
166
|
-
moduleName: 'test-module'
|
|
167
|
-
};
|
|
168
|
-
const result = manager.injectProps({
|
|
169
|
-
original: true
|
|
170
|
-
}, context);
|
|
171
|
-
expect(result).toEqual({
|
|
172
|
-
original: true,
|
|
173
|
-
step1: true,
|
|
174
|
-
step2: true
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
it('should handle injection errors gracefully', ()=>{
|
|
178
|
-
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(()=>{});
|
|
179
|
-
const plugin = {
|
|
180
|
-
name: 'error-plugin',
|
|
181
|
-
injectProps: ()=>{
|
|
182
|
-
throw new Error('Injection error');
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
manager.register(plugin);
|
|
186
|
-
const context = {
|
|
187
|
-
remoteName: 'test-remote',
|
|
188
|
-
moduleName: 'test-module'
|
|
189
|
-
};
|
|
190
|
-
const result = manager.injectProps({
|
|
191
|
-
original: true
|
|
192
|
-
}, context);
|
|
193
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Error injecting props from plugin "error-plugin"'), expect.any(Error));
|
|
194
|
-
expect(result).toEqual({
|
|
195
|
-
original: true
|
|
196
|
-
}); // 应该返回原始props
|
|
197
|
-
consoleSpy.mockRestore();
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
describe('Plugin Management', ()=>{
|
|
201
|
-
it('should get plugin info', ()=>{
|
|
202
|
-
const wrapperPlugin = {
|
|
203
|
-
name: 'wrapper-plugin',
|
|
204
|
-
wrapComponent: (Component)=>Component
|
|
205
|
-
};
|
|
206
|
-
const injectorPlugin = {
|
|
207
|
-
name: 'injector-plugin',
|
|
208
|
-
injectProps: (props)=>props
|
|
209
|
-
};
|
|
210
|
-
const hybridPlugin = {
|
|
211
|
-
name: 'hybrid-plugin',
|
|
212
|
-
wrapComponent: (Component)=>Component,
|
|
213
|
-
injectProps: (props)=>props
|
|
214
|
-
};
|
|
215
|
-
manager.register(wrapperPlugin);
|
|
216
|
-
manager.register(injectorPlugin);
|
|
217
|
-
manager.register(hybridPlugin);
|
|
218
|
-
const info = manager.getPluginInfo();
|
|
219
|
-
expect(info).toHaveLength(3);
|
|
220
|
-
expect(info[0]).toEqual({
|
|
221
|
-
name: 'wrapper-plugin',
|
|
222
|
-
hasWrapper: true,
|
|
223
|
-
hasInjector: false
|
|
224
|
-
});
|
|
225
|
-
expect(info[1]).toEqual({
|
|
226
|
-
name: 'injector-plugin',
|
|
227
|
-
hasWrapper: false,
|
|
228
|
-
hasInjector: true
|
|
229
|
-
});
|
|
230
|
-
expect(info[2]).toEqual({
|
|
231
|
-
name: 'hybrid-plugin',
|
|
232
|
-
hasWrapper: true,
|
|
233
|
-
hasInjector: true
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
it('should remove plugin', ()=>{
|
|
237
|
-
const plugin = {
|
|
238
|
-
name: 'test-plugin',
|
|
239
|
-
wrapComponent: (Component)=>Component
|
|
240
|
-
};
|
|
241
|
-
manager.register(plugin);
|
|
242
|
-
expect(manager.getPlugins()).toHaveLength(1);
|
|
243
|
-
const removed = manager.removePlugin('test-plugin');
|
|
244
|
-
expect(removed).toBe(true);
|
|
245
|
-
expect(manager.getPlugins()).toHaveLength(0);
|
|
246
|
-
});
|
|
247
|
-
it('should return false when removing non-existent plugin', ()=>{
|
|
248
|
-
const removed = manager.removePlugin('non-existent');
|
|
249
|
-
expect(removed).toBe(false);
|
|
250
|
-
});
|
|
251
|
-
it('should clear all plugins', ()=>{
|
|
252
|
-
manager.register({
|
|
253
|
-
name: 'plugin1',
|
|
254
|
-
wrapComponent: (Component)=>Component
|
|
255
|
-
});
|
|
256
|
-
manager.register({
|
|
257
|
-
name: 'plugin2',
|
|
258
|
-
injectProps: (props)=>props
|
|
259
|
-
});
|
|
260
|
-
expect(manager.getPlugins()).toHaveLength(2);
|
|
261
|
-
manager.clear();
|
|
262
|
-
expect(manager.getPlugins()).toHaveLength(0);
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
describe('Global Enhanced Plugin Manager', ()=>{
|
|
266
|
-
beforeEach(()=>{
|
|
267
|
-
// 清理全局状态
|
|
268
|
-
getEnhancedPluginManager().clear();
|
|
269
|
-
});
|
|
270
|
-
it('should provide singleton instance', ()=>{
|
|
271
|
-
const manager1 = getEnhancedPluginManager();
|
|
272
|
-
const manager2 = getEnhancedPluginManager();
|
|
273
|
-
expect(manager1).toBe(manager2);
|
|
274
|
-
});
|
|
275
|
-
it('should register multiple plugins via convenience function', ()=>{
|
|
276
|
-
const plugins = [
|
|
277
|
-
{
|
|
278
|
-
name: 'plugin1',
|
|
279
|
-
wrapComponent: (Component)=>Component
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
name: 'plugin2',
|
|
283
|
-
injectProps: (props)=>props
|
|
284
|
-
}
|
|
285
|
-
];
|
|
286
|
-
registerEnhancedPlugins(plugins);
|
|
287
|
-
const manager = getEnhancedPluginManager();
|
|
288
|
-
expect(manager.getPlugins()).toHaveLength(2);
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest';
|
|
2
|
-
// Test setup for ice-mf-runtime
|
|
3
|
-
// Mock @ice/stark-app dependency
|
|
4
|
-
vi.mock('@ice/stark-app', ()=>({
|
|
5
|
-
getBasename: ()=>'/app'
|
|
6
|
-
}));
|
|
7
|
-
// Mock @module-federation/runtime
|
|
8
|
-
vi.mock('@module-federation/runtime', ()=>({
|
|
9
|
-
init: vi.fn(),
|
|
10
|
-
registerPlugins: vi.fn(),
|
|
11
|
-
loadRemote: vi.fn()
|
|
12
|
-
}));
|
|
13
|
-
// Mock performance API
|
|
14
|
-
Object.defineProperty(globalThis, 'performance', {
|
|
15
|
-
value: {
|
|
16
|
-
now: ()=>Date.now()
|
|
17
|
-
},
|
|
18
|
-
writable: true
|
|
19
|
-
});
|
|
20
|
-
// Suppress console warnings during tests
|
|
21
|
-
const originalWarn = console.warn;
|
|
22
|
-
console.warn = (...args)=>{
|
|
23
|
-
var _args__includes, _args_;
|
|
24
|
-
if ((_args_ = args[0]) === null || _args_ === void 0 ? void 0 : (_args__includes = _args_.includes) === null || _args__includes === void 0 ? void 0 : _args__includes.call(_args_, 'Warning: ReactDOM.render is no longer supported')) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
originalWarn.call(console, ...args);
|
|
28
|
-
};
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import type { EnhancedRuntimePlugin, WrapperContext, InjectionContext } from './types';
|
|
2
|
-
import * as React from 'react';
|
|
3
|
-
/**
|
|
4
|
-
* 增强插件管理器 - 专门负责管理和执行增强运行时插件
|
|
5
|
-
* 标准 MF 插件交由底层 @module-federation/runtime 管理
|
|
6
|
-
*/
|
|
7
|
-
export declare class EnhancedPluginManager {
|
|
8
|
-
private plugins;
|
|
9
|
-
/**
|
|
10
|
-
* 注册增强插件
|
|
11
|
-
* @param plugin 增强运行时插件
|
|
12
|
-
*/
|
|
13
|
-
register(plugin: EnhancedRuntimePlugin): void;
|
|
14
|
-
/**
|
|
15
|
-
* 注册多个增强插件
|
|
16
|
-
* @param plugins 增强插件数组
|
|
17
|
-
*/
|
|
18
|
-
registerPlugins(plugins: EnhancedRuntimePlugin[]): void;
|
|
19
|
-
/**
|
|
20
|
-
* 获取所有增强插件列表
|
|
21
|
-
* @returns 增强插件数组
|
|
22
|
-
*/
|
|
23
|
-
getPlugins(): EnhancedRuntimePlugin[];
|
|
24
|
-
/**
|
|
25
|
-
* 应用组件包装器
|
|
26
|
-
* 按插件注册顺序依次应用所有组件包装器
|
|
27
|
-
* @param Component 原始组件
|
|
28
|
-
* @param context 包装器上下文
|
|
29
|
-
* @returns 包装后的组件
|
|
30
|
-
*/
|
|
31
|
-
wrapComponent(Component: React.ComponentType, context: WrapperContext): React.ComponentType;
|
|
32
|
-
/**
|
|
33
|
-
* 注入属性
|
|
34
|
-
* 按插件注册顺序依次应用所有属性注入器
|
|
35
|
-
* @param props 原始属性
|
|
36
|
-
* @param context 注入上下文
|
|
37
|
-
* @returns 注入后的属性
|
|
38
|
-
*/
|
|
39
|
-
injectProps(props: Record<string, any>, context: InjectionContext): Record<string, any>;
|
|
40
|
-
/**
|
|
41
|
-
* 获取所有插件信息
|
|
42
|
-
* @returns 插件信息数组
|
|
43
|
-
*/
|
|
44
|
-
getPluginInfo(): Array<{
|
|
45
|
-
name: string;
|
|
46
|
-
hasWrapper: boolean;
|
|
47
|
-
hasInjector: boolean;
|
|
48
|
-
}>;
|
|
49
|
-
/**
|
|
50
|
-
* 移除插件
|
|
51
|
-
* @param name 插件名称
|
|
52
|
-
* @returns 是否成功移除
|
|
53
|
-
*/
|
|
54
|
-
removePlugin(name: string): boolean;
|
|
55
|
-
/**
|
|
56
|
-
* 清空所有插件
|
|
57
|
-
*/
|
|
58
|
-
clear(): void;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* 获取全局增强插件管理器实例
|
|
62
|
-
* @returns 增强插件管理器实例
|
|
63
|
-
*/
|
|
64
|
-
export declare function getEnhancedPluginManager(): EnhancedPluginManager;
|
|
65
|
-
/**
|
|
66
|
-
* 注册多个增强插件(便捷函数)
|
|
67
|
-
* @param plugins 增强插件数组
|
|
68
|
-
*/
|
|
69
|
-
export declare function registerEnhancedPlugins(plugins: EnhancedRuntimePlugin[]): void;
|
|
70
|
-
/**
|
|
71
|
-
* React Hook:使用增强插件管理器
|
|
72
|
-
* @returns 增强插件管理器实例
|
|
73
|
-
*/
|
|
74
|
-
export declare function useEnhancedPluginManager(): EnhancedPluginManager;
|