From 14f9ec3160a592aef07acd95ee6261480af47b2a Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 2 Aug 2026 22:22:00 -0400 Subject: [PATCH] refactor: decompose render pipeline into decorate() + source value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the one branchy bootstrapTemplateWithHTMXetc (six positional args, five jobs) with small pure functions: headChrome, blissAttrs, decoratePage, decorateFragment, and a decorate() entry point. The route/clone/copy provenance triple becomes a single `source` value ({structureId, routeId, copyUrl}) threaded through render — the same data the inspector roadmap needs. Collapse the four copy-pasted render sites (res.render, ws.render, template preview, scaffold preview) onto renderTemplate + decorate, and dedupe the per-structure route() closure into makeRoute(). Emitted HTML (head chrome + data-bliss-* attributes) is unchanged; verified full-page and htmx-fragment output. Co-Authored-By: Claude Opus 4.8 --- index.js | 217 +++++++++++++++++++++++++++---------------------------- 1 file changed, 106 insertions(+), 111 deletions(-) diff --git a/index.js b/index.js index efa5c31..03b05fd 100644 --- a/index.js +++ b/index.js @@ -165,23 +165,15 @@ function bootstrapWebsocketHandler(route) { } }) } - ws.render = (template, context) => { - context = context || {}; - const eta = model.getTemplater(route.structure_id); + ws.render = (template, context = {}) => { const structure = model.getStructure(route.structure_id); - context.route = function (url) { - return withPrefix(structure.route_prefix, url); - }; - + context.route = makeRoute(structure); ws.send( - bootstrapTemplateWithHTMXetc( - eta.render(template, context), - `/workshop/${route.structure_id}/route/${route.id}`, - `/workshop/${route.structure_id}/clone_modal/`, - route.verb == "GET" ? embedHTML(req.originalUrl) : null, - structure.head_injection, - htmxRequest = true, - ), + decorate(renderTemplate(route.structure_id, template, context), { + headInjection: structure.head_injection, + source: sourceFor(route, req), + fragment: true, + }), ); }; @@ -297,30 +289,10 @@ app.all("/logout", async (req, res) => { // | _ || || | | || _ | _____| || _ || || | // |__| |__||_______||___| |_||___| |_||_______||__| |__||_______||___| // -function bootstrapTemplateWithHTMXetc( - htmlString, - blissRoute, - blissClone, - blissCopy, - headInjection, - htmxRequest = false, -) { - if ( - htmlString.toLowerCase().startsWith("") || - htmlString.toLowerCase().startsWith(""); - head = $("head"); - } - - head.attr("id", "head"); - - head.append(` +// The chrome injected into the of every rendered page: the client-side +// stack (htmx/hyperscript/tailwind) plus the in-page editor overlay. +function headChrome(headInjection) { + return ` @@ -335,48 +307,96 @@ function bootstrapTemplateWithHTMXetc( ${headInjection || ""} - `); + `; +} - if (blissRoute) { - $("body").attr("data-bliss-route", blissRoute); - $("body").attr("data-bliss-clone", blissClone); - if (blissCopy) $("body").attr("data-bliss-copy", blissCopy); - } +// A `source` is the provenance of a rendered fragment: which structure/route +// produced it, and (for GET routes) the snippet that re-embeds it. It becomes +// the data-bliss-* attributes the inspector reads. +function blissAttrs(source) { + if (!source) return null; + const attrs = { + "data-bliss-route": `/workshop/${source.structureId}/route/${source.routeId}`, + "data-bliss-clone": `/workshop/${source.structureId}/clone_modal/`, + }; + if (source.copyUrl) attrs["data-bliss-copy"] = source.copyUrl; + return attrs; +} - htmlString = $.html(); - } else if (htmxRequest && blissRoute) { - const $ = cheerio.load(htmlString, null, false); +// Full HTML document: inject the head chrome and stamp provenance on . +function decoratePage(html, { headInjection, source } = {}) { + const $ = cheerio.load(html); + let head = $("head"); + if (head.length === 0) { + $("html").prepend(""); + head = $("head"); + } + head.attr("id", "head"); + head.append(headChrome(headInjection)); + + const attrs = blissAttrs(source); + if (attrs) { + for (const [k, v] of Object.entries(attrs)) $("body").attr(k, v); + } + return $.html(); +} + +// HTMX partial: stamp provenance on the swapped-in elements (an oob swap loses +// its own attrs, so we descend into its children) and append an out-of-band +// so head injection still reaches the page. +function decorateFragment(html, { headInjection, source } = {}) { + const $ = cheerio.load(html, null, false); + const attrs = blissAttrs(source); + if (attrs) { const targets = []; - - // when we return an oob thing from htmx, we'll lose all the attrs when it swaps - // so this adds those attrs to the children of the swap if possible - for (let child of $.root().children()) { + for (const child of $.root().children()) { if (child.attribs && child.attribs["hx-swap-oob"]) { - for (let childchild of child.children) { - if (childchild.attribs) { - targets.push(childchild); - } + for (const grandchild of child.children) { + if (grandchild.attribs) targets.push(grandchild); } } else { targets.push(child); } } - - // Directly update attributes without wrapping in Cheerio - for (let target of targets) { - target.attribs["data-bliss-route"] = blissRoute; - target.attribs["data-bliss-clone"] = blissClone; - if (blissCopy) target.attribs["data-bliss-copy"] = blissCopy; - } - - htmlString = $.html(); - - if (htmxRequest) { - htmlString += `${headInjection}`; - } + for (const target of targets) Object.assign(target.attribs, attrs); } + return ( + $.html() + + `${headInjection || ""}` + ); +} - return htmlString; +// Single entry point for turning rendered HTML into a response body. A full +// document (or any non-htmx request) becomes a decorated page; an htmx partial +// carrying provenance becomes a decorated fragment; anything else passes through. +function decorate(html, { headInjection, source, fragment } = {}) { + const lower = html.toLowerCase(); + const isFullDoc = + lower.startsWith("") || lower.startsWith(" withPrefix(structure.route_prefix, url); +} + +function renderTemplate(structureId, templateName, context) { + return model.getTemplater(structureId).render(templateName, context); +} + +// Provenance for a rendered response: GET routes are re-embeddable, so they +// carry the copy-embed snippet; other verbs don't. +function sourceFor(route, req) { + return { + structureId: route.structure_id, + routeId: route.id, + copyUrl: route.verb === "GET" ? embedHTML(req.originalUrl) : null, + }; } app.post("/workshop", (req, res) => { @@ -391,7 +411,7 @@ app.get("/workshop", (req, res) => { }); function renderWorkshop(res, template, context) { - res.send(bootstrapTemplateWithHTMXetc(eta.render(template, context))); + res.send(decorate(eta.render(template, context))); } function smartRedirect(req, res, redirectUrl) { @@ -599,24 +619,16 @@ app.get("/workshop/:structure_id/template/:template_id", (req, res) => { app.get("/workshop/:structure_id/template/:template_id/preview", (req, res) => { const template = model.getTemplate(req.params.template_id); const struct = model.getStructure(req.params.structure_id); - const eta = model.getTemplater(req.params.structure_id); const context = vm.createContext({ it: null }); vm.runInContext(template.test_object, context); - context.it.route = function (url) { - return withPrefix(struct.route_prefix, url); - }; + context.it.route = makeRoute(struct); return res.send( - bootstrapTemplateWithHTMXetc( - eta.render(template.name, context.it), - null, - null, - null, - struct.head_injection, - false, - ), + decorate(renderTemplate(req.params.structure_id, template.name, context.it), { + headInjection: struct.head_injection, + }), ); }); @@ -647,21 +659,12 @@ app.get("/workshop/:structure_id/route/:route_id/preview", (req, res) => { const route = model.getRoute(req.params.route_id); const struct = model.getStructure(req.params.structure_id); - const it = { - route: function (url) { - return withPrefix(struct.route_prefix, url); - } - } + const it = { route: makeRoute(struct) }; return res.send( - bootstrapTemplateWithHTMXetc( - eta.renderString(route.scaffold_page_content, it), - null, - null, - null, - struct.head_injection, - false, - ), + decorate(eta.renderString(route.scaffold_page_content, it), { + headInjection: struct.head_injection, + }), ); }); @@ -806,7 +809,6 @@ app.all("*", async (req, res) => { .get(routeMatch.id); const structure = model.getStructure(route.structure_id); - const __urlPrefix = structure.route_prefix; try { // todo: only add req res to contexst when running the handler() @@ -817,21 +819,14 @@ app.all("*", async (req, res) => { eta, }); - res.render = (template, context) => { - context = context || {}; - context.route = function (url) { - return withPrefix(__urlPrefix, url); - }; - const eta = model.getTemplater(route.structure_id); + res.render = (template, context = {}) => { + context.route = makeRoute(structure); res.send( - bootstrapTemplateWithHTMXetc( - eta.render(template, context), - `/workshop/${route.structure_id}/route/${route.id}`, - `/workshop/${route.structure_id}/clone_modal/`, - verb == "GET" ? embedHTML(req.originalUrl) : null, - structure.head_injection, - req.headers["hx-request"], - ), + decorate(renderTemplate(route.structure_id, template, context), { + headInjection: structure.head_injection, + source: sourceFor(route, req), + fragment: req.headers["hx-request"], + }), ); }; const handlerScript = new vm.Script(route.handler, { filename: `handler_${route.id}.js` });