import { readdirSync, statSync, existsSync } from 'fs' import { join, dirname } from 'path' import { fileURLToPath } from 'url' import type { ApiModule } from './types.js' import type { ModuleDefinition } from '../../shared/modules/types.js' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) function getSharedModulesPath(): string | null { const possiblePaths = [ join(__dirname, '../../shared/modules'), join(__dirname, '../../../shared/modules'), join((process as unknown as { resourcesPath?: string })?.resourcesPath || '', 'shared/modules'), ] for (const p of possiblePaths) { if (existsSync(p)) { return p } } return null } export interface SharedModuleDefinition { id: string name: string backend?: { enabled?: boolean } } function needsBackendImplementation(moduleDef: SharedModuleDefinition): boolean { return moduleDef.backend?.enabled !== false } async function loadModuleDefinitions(): Promise { const modules: SharedModuleDefinition[] = [] const sharedModulesPath = getSharedModulesPath() if (!sharedModulesPath) { return modules } const entries = readdirSync(sharedModulesPath) for (const entry of entries) { const entryPath = join(sharedModulesPath, entry) const stat = statSync(entryPath) if (!stat.isDirectory()) { continue } try { const moduleExports = await import(`../../shared/modules/${entry}/index.js`) for (const key of Object.keys(moduleExports)) { if (key.endsWith('_MODULE')) { const moduleDef = moduleExports[key] as ModuleDefinition if (moduleDef && moduleDef.id) { modules.push({ id: moduleDef.id, name: moduleDef.name, backend: moduleDef.backend, }) } } } } catch { // 模块加载失败,跳过 } } return modules } export async function validateModuleConsistency(apiModules: ApiModule[]): Promise { const sharedModules = await loadModuleDefinitions() if (sharedModules.length === 0) { console.log('[ModuleValidator] Skipping validation (shared modules not found, likely packaged mode)') return } const apiModuleIds = new Set(apiModules.map((m) => m.metadata.id)) const errors: string[] = [] for (const sharedModule of sharedModules) { const needsBackend = needsBackendImplementation(sharedModule) const hasApiModule = apiModuleIds.has(sharedModule.id) if (needsBackend && !hasApiModule) { errors.push( `Module '${sharedModule.id}' is defined in shared but not registered in API modules` ) } if (!needsBackend && hasApiModule) { errors.push( `Module '${sharedModule.id}' has backend disabled but is registered in API modules` ) } } if (errors.length > 0) { throw new Error(`Module consistency validation failed:\n - ${errors.join('\n - ')}`) } console.log( `[ModuleValidator] ✓ Module consistency validated: ${sharedModules.length} shared, ${apiModules.length} API` ) }