/** * ModuleRegistry - 模块生命周期管理器 * 负责管理模块的加载、状态、依赖关系 */ import { NativeModules } from 'react-native'; import { ModuleLoader as ModuleLoaderMock } from './ModuleLoaderMock'; type ModuleState = 'idle' | 'loading' | 'loaded' | 'failed'; interface ModuleMeta { file: string; version: string; dependencies: string[]; lazy: boolean; } interface ModuleLoadPromise { promise: Promise; resolveRegister: () => void; rejectRegister: (error: Error) => void; } interface BundleManifest { formatVersion: number; main: { file: string; version: string; }; modules: Array<{ id: string; file: string; version: string; dependencies?: string[]; lazy?: boolean; }>; } const REGISTER_TIMEOUT_MS = 5000; // 内部闭包数据(不暴露给业务方) // 允许外部注入 ModuleLoader(主要用于测试) let injectedModuleLoader: any = null; const moduleMeta: Record = {}; const moduleState: Record = {}; const moduleExports: Record = {}; const moduleLoadPromises: Record = {}; // 循环依赖检测 function checkCircularDependency( moduleId: string, visited: Set, path: string[] ): void { if (visited.has(moduleId)) { const cycle = [...path, moduleId].join(' -> '); throw new Error(`Circular dependency detected: ${cycle}`); } visited.add(moduleId); path.push(moduleId); const deps = moduleMeta[moduleId]?.dependencies || []; deps.forEach((depId) => { if (moduleMeta[depId]) { checkCircularDependency(depId, new Set(visited), [...path]); } }); } /** * 设置 ModuleLoader(仅供测试使用) * 注意:实际接口是 CodePush.loadBusinessBundle,但为了兼容性保留此方法名 */ function setModuleLoader(loader: any) { injectedModuleLoader = loader; } function resetInternalState() { injectedModuleLoader = injectedModuleLoader; // 保留注入的 loader for (const key of Object.keys(moduleMeta)) delete moduleMeta[key]; for (const key of Object.keys(moduleState)) delete moduleState[key]; for (const key of Object.keys(moduleExports)) delete moduleExports[key]; for (const key of Object.keys(moduleLoadPromises)) delete moduleLoadPromises[key]; } /** * 初始化 ModuleRegistry * * @param manifest - Bundle manifest,包含所有模块的元数据 * * 注意: * - rootPath 不需要传入,Native 层会自己处理路径查找 * - manifest 只在初始化时使用,初始化后会被丢弃(信息已提取到 moduleMeta) */ function init(options: { manifest: BundleManifest }): void { // 每次 init 先 reset,避免残留状态 resetInternalState(); const { manifest } = options; // 校验 if (!manifest) { throw new Error('ModuleRegistry init failed: missing manifest'); } // 解析 meta manifest.modules.forEach((m) => { const id = m.id; moduleMeta[id] = { file: m.file, version: m.version, dependencies: m.dependencies || [], lazy: !!m.lazy, }; moduleState[id] = 'idle'; moduleExports[id] = undefined; }); // 检测循环依赖 manifest.modules.forEach((m) => { checkCircularDependency(m.id, new Set(), []); }); } const map = {}; /** * 注册模块(供模块 bundle 调用) */ function registerModule(moduleId: string, exportsObj: any): void { if (!moduleMeta[moduleId]) { console.warn(`registerModule: unknown moduleId=${moduleId}`); return; } moduleExports[moduleId] = exportsObj; moduleState[moduleId] = 'loaded'; const pending = moduleLoadPromises[moduleId]; if (pending?.resolveRegister) { pending.resolveRegister(); } } /** * 获取模块状态 */ function getModuleState(moduleId: string): ModuleState { return moduleState[moduleId] || 'idle'; } /** * 获取模块导出对象 */ function getModuleExports(moduleId: string): any { if (moduleState[moduleId] !== 'loaded') { throw new Error(`Module ${moduleId} not loaded yet`); } return moduleExports[moduleId]; } /** * 加载模块(核心逻辑) */ async function loadModule(moduleId: string): Promise { // 1. 非法模块 if (!moduleMeta[moduleId]) { throw new Error(`Unknown module: ${moduleId}`); } // 2. 已加载完成 if (moduleState[moduleId] === 'loaded') { return; } // 3. 并发加载处理 if (moduleLoadPromises[moduleId]) { return moduleLoadPromises[moduleId].promise; } // 4. 创建一个加载 Promise(缓存) let resolveRegister!: () => void; let rejectRegister!: (error: Error) => void; const registerPromise = new Promise((resolve, reject) => { resolveRegister = resolve; rejectRegister = reject; }); const loadPromise = (async () => { try { // Step 1:递归加载依赖 const deps = moduleMeta[moduleId].dependencies; for (const depId of deps) { await loadModule(depId); if (moduleState[depId] !== 'loaded') { throw new Error( `Dependency ${depId} failed for module ${moduleId}. State: ${moduleState[depId]}` ); } } // Step 2:设置加载状态 moduleState[moduleId] = 'loading'; // Step 3:选择 ModuleLoader let loader: any = injectedModuleLoader; if (!loader) { // 开发环境:优先使用 ModuleLoaderMock(HTTP 模式) if (__DEV__) { loader = ModuleLoaderMock; } else { // 生产环境:使用 Native CodePush 模块 if (NativeModules?.ModuleLoader.loadBusinessBundle) { loader = NativeModules.ModuleLoader; } else { throw new Error( 'ModuleLoader not available: Native ModuleLoader module not found in production' ); } } } // 传入 bundleId(即 moduleId)和 bundlePath(从 manifest 中获取) const bundlePath = moduleMeta[moduleId].file; const result = await loader.loadBusinessBundle(moduleId, bundlePath); if (!result?.success) { moduleState[moduleId] = 'failed'; const err = new Error(result?.errorMessage || 'LOAD_FAILED'); rejectRegister(err); // 提前结束 registerPromise throw err; } // Step 4:等待 registerModule 执行 await Promise.race([ registerPromise, new Promise((_, reject) => setTimeout( () => reject(new Error('REGISTER_TIMEOUT')), REGISTER_TIMEOUT_MS ) ), ]); // 验证模块确实已注册 // 注意:TypeScript 可能无法正确推断状态,但运行时检查是必要的 const finalState = moduleState[moduleId] as ModuleState; if (finalState !== 'loaded') { throw new Error(`registerModule not called: ${moduleId}, state: ${finalState}`); } } catch (err) { moduleState[moduleId] = 'failed'; const error = err instanceof Error ? err : new Error(String(err)); console.error(`[ModuleRegistry] load failed: ${moduleId}`, error); throw error; } finally { delete moduleLoadPromises[moduleId]; } })(); moduleLoadPromises[moduleId] = { promise: loadPromise, resolveRegister, rejectRegister, }; return loadPromise; } export const ModuleRegistry = { init, loadModule, registerModule, getModuleState, getModuleExports, setModuleLoader, map, }; // 将 ModuleRegistry 挂载到全局作用域,以便模块 bundle 可以访问 // 这在开发环境中特别重要,因为模块 bundle 和主 bundle 共享同一个 JS 上下文 if (typeof globalThis !== 'undefined') { globalThis.__ModuleRegistry = ModuleRegistry; } export type { BundleManifest, ModuleState };