/** * JQHTML Integration - Component Registration and Hydration Bootstrap * * This module bridges RSpade's manifest system with jqhtml's component runtime. * * == TWO-PHASE INITIALIZATION == * * Phase 1: _on_framework_modules_define() - Component Registration * - Runs early in framework boot, before DOM is processed * - Registers all ES6 classes extending Component with jqhtml runtime * - Tags static methods with cache IDs for jqhtml's caching system * - After this phase, jqhtml knows: "User_Card" → UserCardClass * * Phase 2: _on_framework_modules_init() - DOM Hydration * - Calls jqhtml.boot() to hydrate all ._Component_Init placeholders * - Triggers 'jqhtml_ready' when all components are initialized * * == KEY PARTICIPANTS == * * JqhtmlBladeCompiler.php - Transforms tags into ._Component_Init divs * jqhtml runtime - Maintains registry of component names → classes * jqhtml.boot() - Finds and hydrates all ._Component_Init placeholders * This module - Orchestrates registration and triggers hydration */ class Jqhtml_Integration { /** * Phase 1: Register Component Classes * * Compiled .jqhtml templates self-register their render methods with jqhtml. * But the framework must separately register ES6 component classes (the ones * extending Component with lifecycle methods like on_create, on_load, etc). * * This runs during framework_modules_define, before any DOM processing. */ static _on_framework_modules_define() { // ───────────────────────────────────────────────────────────────────── // Register Component Classes with jqhtml Runtime // // The Manifest knows all classes extending Component. We register each // with jqhtml so it can instantiate them by name during hydration. // ───────────────────────────────────────────────────────────────────── let jqhtml_components = Manifest.get_extending('Component'); console_debug('JQHTML_INIT', 'Registering ' + jqhtml_components.length + ' Components'); for (let component of jqhtml_components) { jqhtml.register_component(component.class_name, component.class_object); } // ───────────────────────────────────────────────────────────────────── // Tag Static Methods with Cache IDs // // jqhtml caches component renders based on a hash of their args. // Problem: Functions can't be serialized, so passing one (e.g., a // DataGrid's data_source callback) would defeat caching entirely. // // Solution: Tag every static method with a stable string identifier. // When jqhtml hashes component args, it uses _jqhtml_cache_id instead // of the function reference, making the cache key deterministic. // // Example: // // // Without tagging: args hash includes [Function] → uncacheable // With tagging: args hash includes "Controller.fetch" → cacheable // // This enables Ajax endpoints and other callbacks to be passed to // components without breaking the automatic caching system. // ───────────────────────────────────────────────────────────────────── const all_classes = Manifest.get_all_classes(); let methods_tagged = 0; for (const class_info of all_classes) { const class_object = class_info.class_object; const class_name = class_info.class_name; const property_names = Object.getOwnPropertyNames(class_object); for (const property_name of property_names) { if (property_name === 'length' || property_name === 'name' || property_name === 'prototype') { continue; } const property_value = class_object[property_name]; if (typeof property_value === 'function') { property_value._jqhtml_cache_id = `${class_name}.${property_name}`; methods_tagged++; } } } console_debug('JQHTML_INIT', `Tagged ${methods_tagged} static methods with _jqhtml_cache_id`); // ───────────────────────────────────────────────────────────────────── // Configure jqhtml Caching // // scope_key() changes when: app code changes, user logs out, site changes. // This automatically invalidates cached component renders. // ───────────────────────────────────────────────────────────────────── jqhtml.set_cache_key(Rsx.scope_key()); window.jqhtml.debug.verbose = false; } /** * Phase 2: DOM Hydration * * Delegates to jqhtml.boot() which finds all ._Component_Init placeholders * and converts them into live components. * * jqhtml.boot() handles: * - Finding ._Component_Init elements * - Parsing data-component-init-name / data-component-args * - Calling $element.component(name, args) * - Recursive nested component handling * - Promise tracking for async components */ static _on_framework_modules_init() { jqhtml.boot().then(() => { Rsx.trigger('jqhtml_ready'); }); } } // Class is automatically made global by RSX manifest - no window assignment needed