feat: code blocks beta

This commit is contained in:
obvTiger 2025-03-27 17:28:51 +01:00
parent 362b7aa15e
commit 1ecb6d8682
19 changed files with 756 additions and 94 deletions

View file

@ -6,6 +6,8 @@ const StandardElementGenerator = require("./generators/StandardElementGenerator"
const RootNodeGenerator = require("./generators/RootNodeGenerator");
const InputElementGenerator = require("./generators/InputElementGenerator");
const MediaElementGenerator = require("./generators/MediaElementGenerator");
const JavaScriptGenerator = require("./generators/JavaScriptGenerator");
const ServerCodeGenerator = require("./generators/ServerCodeGenerator");
const HTMLTemplate = require("./templates/HTMLTemplate");
const StringUtils = require("./utils/StringUtils");
@ -21,6 +23,9 @@ class HTMLGenerator {
this.options = options;
this.cssGenerator = cssGenerator;
this.htmlTemplate = new HTMLTemplate(options);
this.serverGenerator = new ServerCodeGenerator(options);
this.jsGenerator = new JavaScriptGenerator(options, this.serverGenerator);
this.currentElement = null;
this.generators = [
@ -86,6 +91,10 @@ class HTMLGenerator {
console.log("[HTMLGenerator] Node details:", StringUtils.safeStringify(node));
}
// Handle client and server blocks
if (node.type === "client" || node.type === "server") {
return this.handleScriptBlock(node);
}
if (node.type === "element" && node.tag === "page") {
if (this.options.debug) {
@ -94,20 +103,120 @@ class HTMLGenerator {
return "";
}
const prevElement = this.currentElement;
this.currentElement = node;
// Check if this element has an explicit ID in its props
if (node.type === "element") {
const idProp = node.props.find(p => typeof p === "string" && p.startsWith("id:"));
if (idProp) {
const idValue = idProp.substring(idProp.indexOf(":") + 1).trim().replace(/^"|"$/g, "");
node.elementId = idValue;
// Register this element as reactive
this.jsGenerator.registerReactiveElement(idValue);
if (this.options.debug) {
console.log(`[HTMLGenerator] Found explicit ID: ${idValue}, registered as reactive`);
}
}
}
let result = "";
for (const generator of this.generators) {
if (generator.canHandle(node)) {
if (this.options.debug) {
console.log(`[HTMLGenerator] Using ${generator.constructor.name} for node`);
}
return generator.generate(node);
// If this is an element that might have event handlers,
// add a unique ID to it for client scripts if it doesn't already have one
if (node.type === "element" && node.children.some(child => child.type === "client")) {
// Generate a unique ID for this element if it doesn't already have one
if (!node.elementId) {
node.elementId = this.jsGenerator.generateElementId();
if (this.options.debug) {
console.log(`[HTMLGenerator] Generated ID for element: ${node.elementId}`);
}
}
result = generator.generate(node);
// Process all client blocks inside this element
node.children
.filter(child => child.type === "client")
.forEach(clientBlock => {
this.handleScriptBlock(clientBlock, node.elementId);
});
} else {
result = generator.generate(node);
}
this.currentElement = prevElement;
return result;
}
}
if (this.options.debug) {
console.log(`[HTMLGenerator] No generator found for node type: ${node.type}`);
}
this.currentElement = prevElement;
return "";
}
/**
* Handles client and server script blocks.
* @param {Object} node - The script node to handle
* @param {string} [elementId] - The ID of the parent element, if any
* @returns {string} - Empty string as script blocks don't directly generate HTML
*/
handleScriptBlock(node, elementId = null) {
if (this.options.debug) {
console.log(`\n[HTMLGenerator] Processing ${node.type} script block`);
console.log(`[HTMLGenerator] Script content (first 50 chars): "${node.script.substring(0, 50)}..."`);
if (elementId) {
console.log(`[HTMLGenerator] Attaching to element: ${elementId}`);
}
}
if (node.type === "client") {
if (!elementId && this.currentElement) {
if (!this.currentElement.elementId) {
this.currentElement.elementId = this.jsGenerator.generateElementId();
}
elementId = this.currentElement.elementId;
}
if (elementId) {
this.jsGenerator.addClientScript(elementId, node.script);
} else {
if (this.options.debug) {
console.log(`[HTMLGenerator] Warning: Client script with no parent element`);
}
}
} else if (node.type === "server") {
if (!elementId && this.currentElement) {
if (!this.currentElement.elementId) {
this.currentElement.elementId = this.jsGenerator.generateElementId();
}
elementId = this.currentElement.elementId;
}
if (elementId) {
const params = node.params || [];
if (this.options.debug && params.length > 0) {
console.log(`[HTMLGenerator] Server block parameters: ${params.join(", ")}`);
}
this.jsGenerator.addServerScript(elementId, node.script, params);
} else {
if (this.options.debug) {
console.log(`[HTMLGenerator] Warning: Server script with no parent element`);
}
}
}
return "";
}
@ -177,7 +286,28 @@ class HTMLGenerator {
* @returns {string} - A complete HTML document containing the provided head and body content.
*/
generateFinalHtml(headContent, bodyContent) {
return this.htmlTemplate.generateDocument(headContent, bodyContent);
const clientScripts = this.jsGenerator.generateClientScripts();
return this.htmlTemplate.generateDocument(
headContent,
bodyContent + clientScripts
);
}
/**
* Generates server-side code for Express.js API routes.
* @returns {string} - Express.js server code
*/
generateServerCode() {
return this.serverGenerator.generateServerCode();
}
/**
* Checks if there is any server code to generate.
* @returns {boolean} - Whether there is server code
*/
hasServerCode() {
return this.serverGenerator.hasServerCodeToGenerate();
}
}