import { join } from "node:path"; import { rmSync } from "node:fs"; import { writeText } from "../util/fsx.js"; import type { IR, IRNode, IRChild, IRTextNode } from "../normalize/ir.js"; import { isTextChild } from "../normalize/ir.js"; import { generateCss, RESET_CSS } from "./css.js"; import { generateInteractionCss } from "./interactionCss.js"; import { buildRuntimeSpecs, wiresJsx, dittoWireImportPath, DITTO_WIRE_TSX, accordionJsx, accordionImportPath, ACCORDION_TSX, type AccordionRuntimeSpec, type RuntimeSpec } from "./interactive.js"; import { buildMotionSpec, motionWireJsx, dittoMotionImportPath, motionHasContent, DITTO_MOTION_TSX, type MotionSpec } from "./motion.js"; import { buildLottieSpec, lottieWireJsx, dittoLottieImportPath, lottieHasContent, DITTO_LOTTIE_TSX, type LottieSpec as LottieRuntimeSpec } from "./lottie.js"; import { buildMenuSpecs, menusJsx, dropdownMenuImportPath, DROPDOWN_MENU_TSX, type RTMenu } from "./menu.js"; import type { AssetGraph } from "../infer/assets.js"; import type { FontGraph } from "../infer/fonts.js"; import { SYSTEM_FALLBACK } from "../infer/fonts.js"; import { detectComponents, type ComponentPlan, type ComponentCluster } from "../infer/components.js"; import { buildClassMap } from "./classMap.js"; import { buildTailwind, tailwindGlobalsCss } from "./tailwind.js"; import { planSections, type SectionPlan } from "./sectionSplit.js"; import type { RecipeReport } from "../infer/recipes.js"; import { emitSeoAssetFiles, emitSeoRoutes, jsonLdHeadMarkup, metadataExport, routeSummaryFromIr, seoStaticFiles, viewportExport, type SeoInventory } from "./seo.js"; import { emitGeneratedDocs } from "./docs.js"; const VOID_TAGS = new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]); // Containers whose only valid children are specific elements; the HTML parser // foster-parents stray (whitespace) text out, so emitting it breaks hydration. const ELEMENT_ONLY_PARENTS = new Set(["ul", "ol", "table", "thead", "tbody", "tfoot", "tr", "select", "colgroup", "optgroup", "menu", "dl"]); // Block-level element tags. A JS-built DOM (createElement+appendChild) can legally // place these inside a

/ (the DOM API allows it), but when serialized to // static HTML the parser auto-closes the

/ before the block child and // restructures the tree — so React's SSR markup and the browser's parsed DOM // disagree, throwing hydration errors #418/#423 (descript, mailchimp, posthog). const BLOCK_TAGS = new Set([ "address", "article", "aside", "blockquote", "details", "dialog", "dd", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "hr", "li", "main", "nav", "ol", "p", "pre", "section", "table", "ul", ]); // Table-scoped tags are only valid inside a . A live React app can create them anywhere // via the DOM API (so the captured DOM may hold an orphan /
/
/…), but when we // emit one as STATIC HTML with no ancestor the parser foster-parents/drops it, so the // SSR markup parses to a different tree than React expects → hydration errors #418/#423. Such // orphans are demoted to a neutral
(CSS is keyed by cid, so the box renders identically). const TABLE_SCOPED = new Set(["caption", "colgroup", "col", "thead", "tbody", "tfoot", "tr", "td", "th"]); // `