( projectDir: string, options?: BundleOptions, )
| 697 | } |
| 698 | |
| 699 | export async function bundleToSingleHtml( |
| 700 | projectDir: string, |
| 701 | options?: BundleOptions, |
| 702 | ): Promise<string> { |
| 703 | const indexPath = join(projectDir, "index.html"); |
| 704 | if (!existsSync(indexPath)) throw new Error("index.html not found in project directory"); |
| 705 | |
| 706 | const rawHtml = readFileSync(indexPath, "utf-8"); |
| 707 | const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration); |
| 708 | |
| 709 | const staticGuard = await validateHyperframeHtmlContract(compiled); |
| 710 | if (!staticGuard.isValid) { |
| 711 | console.warn( |
| 712 | `[StaticGuard] Invalid HyperFrame contract: ${staticGuard.missingKeys.join("; ")}`, |
| 713 | ); |
| 714 | } |
| 715 | |
| 716 | const withInterceptor = injectInterceptor(compiled, options?.runtime ?? "inline"); |
| 717 | const document = parseHTMLContent(withInterceptor); |
| 718 | |
| 719 | // Inline local CSS |
| 720 | const localCssChunks: string[] = []; |
| 721 | let cssAnchorPlaced = false; |
| 722 | for (const el of [...document.querySelectorAll('link[rel="stylesheet"]')]) { |
| 723 | const href = el.getAttribute("href"); |
| 724 | if (!href || !isRelativeUrl(href)) continue; |
| 725 | const cssPath = resolveWithinProject(projectDir, href); |
| 726 | if (!cssPath) continue; |
| 727 | const css = safeReadFile(cssPath); |
| 728 | if (css == null) continue; |
| 729 | localCssChunks.push(inlineCssFile(css, dirname(cssPath), projectDir)); |
| 730 | if (!cssAnchorPlaced) { |
| 731 | const anchor = document.createElement("style"); |
| 732 | anchor.setAttribute("data-hf-bundled-local-css", "1"); |
| 733 | el.replaceWith(anchor); |
| 734 | cssAnchorPlaced = true; |
| 735 | } else { |
| 736 | el.remove(); |
| 737 | } |
| 738 | } |
| 739 | if (localCssChunks.length > 0) { |
| 740 | const anchor = document.querySelector('style[data-hf-bundled-local-css="1"]'); |
| 741 | if (anchor) { |
| 742 | anchor.removeAttribute("data-hf-bundled-local-css"); |
| 743 | anchor.textContent = localCssChunks.join("\n\n"); |
| 744 | } else { |
| 745 | const style = document.createElement("style"); |
| 746 | style.textContent = localCssChunks.join("\n\n"); |
| 747 | document.head.appendChild(style); |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | // Inline local JS |
| 752 | const localJsChunks: string[] = []; |
| 753 | let jsAnchorPlaced = false; |
| 754 | for (const el of [...document.querySelectorAll("script[src]")]) { |
| 755 | const src = el.getAttribute("src"); |
| 756 | if (!src || !isRelativeUrl(src)) continue; |
no test coverage detected