Standardize settings file naming and relocate documentation files Fix code quality violations from rsx:check Reorganize user_management directory into logical subdirectories Move Quill Bundle to core and align with Tom Select pattern Simplify Site Settings page to focus on core site information Complete Phase 5: Multi-tenant authentication with login flow and site selection Add route query parameter rule and synchronize filename validation logic Fix critical bug in UpdateNpmCommand causing missing JavaScript stubs Implement filename convention rule and resolve VS Code auto-rename conflict Implement js-sanitizer RPC server to eliminate 900+ Node.js process spawns Implement RPC server architecture for JavaScript parsing WIP: Add RPC server infrastructure for JS parsing (partial implementation) Update jqhtml terminology from destroy to stop, fix datagrid DOM preservation Add JQHTML-CLASS-01 rule and fix redundant class names Improve code quality rules and resolve violations Remove legacy fatal error format in favor of unified 'fatal' error type Filter internal keys from window.rsxapp output Update button styling and comprehensive form/modal documentation Add conditional fly-in animation for modals Fix non-deterministic bundle compilation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
106 lines
4.2 KiB
JavaScript
106 lines
4.2 KiB
JavaScript
// JQHTML v2 Runtime - Processes instruction arrays from compiled templates
|
|
// Minimal implementation focused on MVP functionality
|
|
/// <reference path="./types.d.ts" />
|
|
// Process instruction array into DOM elements
|
|
export function process_instructions(instructions, component) {
|
|
const fragment = document.createDocumentFragment();
|
|
const stack = [];
|
|
let current = fragment;
|
|
for (const instruction of instructions) {
|
|
// Plain text
|
|
if (typeof instruction === 'string') {
|
|
const text = document.createTextNode(instruction);
|
|
current.appendChild(text);
|
|
continue;
|
|
}
|
|
// Tag instruction: {tag: [name, attrs, selfClosing]}
|
|
if (instruction.tag) {
|
|
const [tag_name, attrs, self_closing] = instruction.tag;
|
|
const element = document.createElement(tag_name);
|
|
// Set attributes
|
|
for (const [key, value] of Object.entries(attrs || {})) {
|
|
element.setAttribute(key, String(value));
|
|
}
|
|
current.appendChild(element);
|
|
// Push to stack if not self-closing
|
|
if (!self_closing) {
|
|
stack.push(current);
|
|
current = element;
|
|
}
|
|
continue;
|
|
}
|
|
// Component instruction: {comp: [name, props]}
|
|
if (instruction.comp) {
|
|
const [comp_name, props] = instruction.comp;
|
|
// Create placeholder div for component
|
|
const placeholder = document.createElement('div');
|
|
placeholder.setAttribute('data-component', comp_name);
|
|
placeholder.setAttribute('data-props', JSON.stringify(props || {}));
|
|
placeholder.setAttribute('data-state', 'uninitialized');
|
|
placeholder.className = 'jqhtml-component';
|
|
current.appendChild(placeholder);
|
|
// TODO: In future, instantiate component here
|
|
continue;
|
|
}
|
|
// Slot instruction: {slot: [name, props, renderFn]}
|
|
if (instruction.slot) {
|
|
const [slot_name, props, render_fn] = instruction.slot;
|
|
// Execute slot render function if provided
|
|
if (render_fn && typeof render_fn === 'function') {
|
|
const slot_context = { ...props };
|
|
const [slot_output] = render_fn.call(component, slot_context);
|
|
// Process slot output recursively
|
|
const slot_dom = process_instructions(slot_output, component);
|
|
slot_dom.each(function () {
|
|
current.appendChild(this);
|
|
});
|
|
}
|
|
continue;
|
|
}
|
|
// Closing tag (when we see a string like "</div>")
|
|
if (typeof instruction === 'string' && instruction.match(/^<\//)) {
|
|
if (stack.length > 0) {
|
|
current = stack.pop();
|
|
}
|
|
}
|
|
}
|
|
return $(fragment.childNodes);
|
|
}
|
|
// HTML escape function required by generated code
|
|
export function html(str) {
|
|
if (str == null)
|
|
return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = String(str);
|
|
return div.innerHTML;
|
|
}
|
|
// Component template registry
|
|
const template_registry = new Map();
|
|
// Register a compiled template
|
|
export function register_template(name, render_fn) {
|
|
template_registry.set(name, render_fn);
|
|
}
|
|
// Get a compiled template
|
|
export function get_template(name) {
|
|
return template_registry.get(name);
|
|
}
|
|
// Create component with template
|
|
export function create_templated_component(ComponentClass, template_name, args = {}, element) {
|
|
const template = get_template(template_name);
|
|
if (!template) {
|
|
throw new Error(`Template not found: ${template_name}`);
|
|
}
|
|
// Create component instance - element first, then args
|
|
const component = new ComponentClass(element, args);
|
|
// Override on_render to use template
|
|
component.on_render = async function () {
|
|
// Call template render function
|
|
const [instructions] = template.call(this, ComponentClass, this.data, this.args, {});
|
|
// Process instructions into DOM
|
|
const rendered = process_instructions(instructions, this);
|
|
// Clear and append
|
|
this.$.empty().append(rendered);
|
|
};
|
|
return component;
|
|
}
|
|
//# sourceMappingURL=runtime.js.map
|