From 581e7a7eda27b1874dfafef5ccba9b04092c546b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sun, 17 May 2026 16:28:20 +0000 Subject: [PATCH] refactor: extract shared inlineSubCompositions from bundler and producer Both the core bundler (htmlBundler.ts) and the producer (htmlCompiler.ts) had parallel ~200-line implementations of sub-composition inlining. This divergence caused bug #911 (producer didn't set data-composition-file). Extract the shared logic into core/compiler/inlineSubCompositions.ts: - Single function handles: template/body extraction, CSS/script scoping, asset path rewriting, data-composition-file attribution, content injection - Callers provide environment-specific callbacks (HTML resolution, parsing, variable handling, inner root flattening) - Core bundler passes its advanced features (runtime IDs, variables, inline style rewriting, inner root flattening) - Producer passes a simpler resolver (map + filesystem fallback) and adds pixel sizing post-hoc Net: -215 lines, one source of truth for sub-comp inlining. Co-Authored-By: Claude Sonnet 4.6 --- packages/core/src/compiler/htmlBundler.ts | 165 ++------- packages/core/src/compiler/index.ts | 7 + .../src/compiler/inlineSubCompositions.ts | 319 ++++++++++++++++++ .../producer/src/services/htmlCompiler.ts | 185 ++-------- 4 files changed, 390 insertions(+), 286 deletions(-) create mode 100644 packages/core/src/compiler/inlineSubCompositions.ts diff --git a/packages/core/src/compiler/htmlBundler.ts b/packages/core/src/compiler/htmlBundler.ts index 3e1377d84..862c6460a 100644 --- a/packages/core/src/compiler/htmlBundler.ts +++ b/packages/core/src/compiler/htmlBundler.ts @@ -7,15 +7,12 @@ import { parseHTMLContent, stripEmbeddedRuntimeScripts, } from "./htmlDocument"; -import { - rewriteAssetPaths, - rewriteCssAssetUrls, - rewriteInlineStyleAssetUrls, -} from "./rewriteSubCompPaths"; +// rewriteSubCompPaths functions are used by inlineSubCompositions (shared module) import { scopeCssToComposition, wrapScopedCompositionScript } from "./compositionScoping"; import { validateHyperframeHtmlContract } from "./staticGuard"; import { getHyperframeRuntimeScript } from "../generated/runtime-inline"; import { readDeclaredDefaults } from "../runtime/getVariables"; +import { inlineSubCompositions } from "./inlineSubCompositions"; /** Resolve a relative path within projectDir, rejecting traversal outside it. */ function safePath(projectDir: string, relativePath: string): string | null { @@ -581,144 +578,36 @@ export async function bundleToSingleHtml( } } - // Inline sub-compositions - const compStyleChunks: string[] = []; - const compScriptChunks: string[] = []; - const compExternalScriptSrcs: string[] = []; - const compVariablesByComp: Record> = {}; + // Inline sub-compositions (via shared function) const trackedCompositionHosts = getBundledTrackedCompositionHosts(document); const hostIdentityByElement = assignBundledRuntimeCompositionIds(trackedCompositionHosts); const subCompositionHosts = trackedCompositionHosts.filter((host) => host.hasAttribute("data-composition-src"), ); - for (const hostEl of subCompositionHosts) { - const src = hostEl.getAttribute("data-composition-src"); - if (!src || !isRelativeUrl(src)) continue; - const compPath = safePath(projectDir, src); - const compHtml = compPath ? safeReadFile(compPath) : null; - if (compHtml == null) { - console.warn(`[Bundler] Composition file not found: ${src}`); - continue; - } - - const compDoc = parseHTMLContent(compHtml); - const hostIdentity = hostIdentityByElement.get(hostEl); - const compId = hostIdentity?.authoredCompositionId || null; - const runtimeCompId = hostIdentity?.runtimeCompositionId || compId || ""; - const contentRoot = compDoc.querySelector("template"); - const contentHtml = contentRoot ? contentRoot.innerHTML || "" : compDoc.body.innerHTML || ""; - const contentDoc = parseHTMLContent(contentHtml); - const innerRoot = compId - ? contentDoc.querySelector(`[data-composition-id="${compId}"]`) - : contentDoc.querySelector("[data-composition-id]"); - const inferredCompId = innerRoot?.getAttribute("data-composition-id")?.trim() || ""; - const authoredRootId = innerRoot?.getAttribute("id")?.trim() || null; - const scopeCompId = compId || inferredCompId; - const runtimeScope = runtimeCompId - ? cssAttributeSelector("data-composition-id", runtimeCompId) - : ""; - const mergedVariables = runtimeCompId - ? { - ...readDeclaredDefaults(compDoc.documentElement), - ...parseHostVariableValues(hostEl), - } - : {}; - if (runtimeCompId && Object.keys(mergedVariables).length > 0) { - compVariablesByComp[runtimeCompId] = mergedVariables; - } - - // When a sub-composition is a full HTML document (no