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', 'image-manager', 'livecomponent']); 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 console.log('๐Ÿ” [Modules] Starting Phase 1 - Module Registration'); console.log('๐Ÿ” [Modules] All discovered modules:', Object.keys(modules)); console.log('๐Ÿ” [Modules] Used modules:', [...usedModules]); console.log('๐Ÿ” [Modules] Fallback mode:', fallbackMode); Object.entries(modules).forEach(([path, mod]) => { const name = path.split('/').slice(-2, -1)[0]; console.log(`๐Ÿ” [Module] Processing: ${name} from ${path}`); console.log(`๐Ÿ” [Module] Used modules has ${name}:`, usedModules.has(name)); if(!fallbackMode && !usedModules.has(name)) { console.log(`โญ๏ธ [Module] Skipping unused module: ${name}`); return; } console.log(`โœ… [Module] Registering ${name} with dependency manager`); // Register module definition if provided if (typeof mod.definition === 'object') { Logger.info(`๐Ÿ“‹ [Module] Using definition for ${name}:`, mod.definition); dependencyManager.register(mod.definition); } else { // Create default definition for modules without explicit dependencies const defaultDef = { name, version: '1.0.0', dependencies: [], provides: [], priority: 0 }; Logger.info(`๐Ÿ“‹ [Module] Using default definition for ${name}:`, defaultDef); dependencyManager.register(defaultDef); } }); // Phase 2: Calculate initialization order const initOrder = dependencyManager.calculateInitializationOrder(); console.log('๐Ÿ” [Modules] Phase 2 - Initialization Order:', initOrder); // Phase 3: Initialize modules in dependency order for (const name of initOrder) { console.log(`๐Ÿ” [Module] Phase 3 - Processing ${name} for initialization`); console.log(`๐Ÿ” [Module] Used modules has ${name}:`, usedModules.has(name)); console.log(`๐Ÿ” [Module] Fallback mode:`, fallbackMode); if(!fallbackMode && !usedModules.has(name)) { console.log(`โญ๏ธ [Module] Skipped (not used in DOM): ${name}`); continue; } const modulePath = Object.keys(modules).find(path => path.split('/').slice(-2, -1)[0] === name ); console.log(`๐Ÿ” [Module] Looking for module path for ${name}, found:`, modulePath); if (!modulePath) { console.log(`โ›” [Module] No implementation found for: ${name}`); continue; } const mod = modules[modulePath]; const config = moduleConfig[name] || {}; console.log(`๐Ÿ” [Module] Module ${name} object:`, mod); console.log(`๐Ÿ” [Module] Config for ${name}:`, config); // Check dependencies before initialization const depCheck = dependencyManager.checkDependencies(name); console.log(`๐Ÿ” [Module] Dependency check for ${name}:`, depCheck); if (!depCheck.satisfied) { console.log(`โŒ [Module] Cannot initialize ${name}: ${depCheck.reason}`); activeModules.set(name, { mod: null, config, error: new Error(depCheck.reason), original: mod }); continue; } console.log(`๐Ÿ” [Module] Checking init function for ${name}:`, typeof mod.init, mod.init); console.log(`๐Ÿ” [Module] Module object keys for ${name}:`, Object.keys(mod)); console.log(`๐Ÿ” [Module] Full module object for ${name}:`, mod); if (typeof mod.init === 'function') { try { Logger.info(`๐Ÿš€ [Module] Starting initialization for ${name}`); 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} - typeof:`, typeof mod.init); Logger.warn(`โ›” [Module] Available properties:`, Object.getOwnPropertyNames(mod)); } } 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; window.activeModules = activeModules; }