import { moduleConfig } from './config.js'; import { Logger } from '../core/logger.js'; import { moduleErrorBoundary } from '../core/ModuleErrorBoundary.js'; import { stateManager } from '../core/StateManager.js'; import { dependencyManager } from '../core/DependencyManager.js'; export const activeModules = new Map(); // key: modulename → { mod, config, state } export async function registerModules() { // Use EAGER loading for single bundle - all modules in one chunk let modules; if (typeof global !== 'undefined' && global.importMeta?.glob) { modules = global.importMeta.glob('./*/index.js', { eager: true }); } else { modules = import.meta.glob('./*/index.js', { eager: true }); } Logger.info('[Modules] Found modules:', Object.keys(modules)); const domModules = new Set( Array.from(document.querySelectorAll('[data-module]')).map(el => el.dataset.module).filter(Boolean) ); // Always load these core modules const coreModules = new Set(['spa-router', 'form-handling', 'api-manager']); const usedModules = new Set([...domModules, ...coreModules]); const fallbackMode = usedModules.size === coreModules.size && domModules.size === 0; Logger.info('[Modules] DOM modules found:', [...domModules]); Logger.info('[Modules] Core modules:', [...coreModules]); Logger.info('[Modules] Used modules:', [...usedModules]); Logger.info('[Modules] Fallback mode:', fallbackMode); // Phase 1: Register only USED modules with dependency manager Object.entries(modules).forEach(([path, mod]) => { const name = path.split('/').slice(-2, -1)[0]; if(!fallbackMode && !usedModules.has(name)) { Logger.info(`⏭️ [Module] Skipping unused module: ${name}`); return; } // Register module definition if provided if (typeof mod.definition === 'object') { dependencyManager.register(mod.definition); } else { // Create default definition for modules without explicit dependencies const defaultDef = { name, version: '1.0.0', dependencies: [], provides: [], priority: 0 }; dependencyManager.register(defaultDef); } }); // Phase 2: Calculate initialization order const initOrder = dependencyManager.calculateInitializationOrder(); // Phase 3: Initialize modules in dependency order for (const name of initOrder) { if(!fallbackMode && !usedModules.has(name)) { Logger.info(`⏭️ [Module] Skipped (not used in DOM): ${name}`); continue; } const modulePath = Object.keys(modules).find(path => path.split('/').slice(-2, -1)[0] === name ); if (!modulePath) { Logger.warn(`⛔ [Module] No implementation found for: ${name}`); continue; } const mod = modules[modulePath]; const config = moduleConfig[name] || {}; // Check dependencies before initialization const depCheck = dependencyManager.checkDependencies(name); if (!depCheck.satisfied) { Logger.error(`❌ [Module] Cannot initialize ${name}: ${depCheck.reason}`); activeModules.set(name, { mod: null, config, error: new Error(depCheck.reason), original: mod }); continue; } if (typeof mod.init === 'function') { try { dependencyManager.markInitializing(name); // Create scoped state manager for module const scopedState = stateManager.createScope(name); // Wrap module with error boundary const protectedMod = moduleErrorBoundary.wrapModule(mod, name); // Initialize module with config and state await protectedMod.init(config, scopedState); dependencyManager.markInitialized(name); activeModules.set(name, { mod: protectedMod, config, state: scopedState, original: mod }); Logger.info(`✅ [Module] Initialized: ${name}`); } catch (error) { Logger.error(`❌ [Module] Failed to initialize ${name}:`, error); activeModules.set(name, { mod: null, config, error, original: mod }); } } else { Logger.warn(`⛔ [Module] No init() in ${name}`); } } if (fallbackMode) { Logger.info('⚠️ [Module] No data-module usage detected, fallback to full init mode'); } } export function destroyModules() { for (const [name, { mod, state }] of activeModules.entries()) { if (mod && typeof mod.destroy === 'function') { try { mod.destroy(); Logger.info(`🧹 [Module] Destroyed: ${name}`); } catch (error) { Logger.error(`❌ [Module] Failed to destroy ${name}:`, error); } } // Clean up state subscriptions if (state && typeof state.cleanup === 'function') { state.cleanup(); } } activeModules.clear(); moduleErrorBoundary.reset(); stateManager.reset(); dependencyManager.reset(); } /** * Gets health status of all active modules * @returns {Object} Module health report */ export function getModuleHealth() { const health = { total: activeModules.size, active: 0, failed: 0, modules: {}, errorBoundary: moduleErrorBoundary.getHealthStatus() }; for (const [name, { mod, error }] of activeModules.entries()) { if (error) { health.failed++; health.modules[name] = { status: 'failed', error: error.message }; } else if (mod) { health.active++; health.modules[name] = { status: 'active' }; } else { health.modules[name] = { status: 'unknown' }; } } return health; } // Debug function - access via console if (typeof window !== 'undefined') { window.moduleHealth = getModuleHealth; }