@ice/mf-runtime 1.0.3-beta.2 → 1.0.3-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;