mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 17:27:09 +00:00
fix(core): bundling a project no longer blocks the host's thread on esbuild (#5714)
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// @vitest-environment node
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterAll, expect, it, vi } from "vitest";
|
||||
import { bundleToSingleHtml } from "./htmlBundler";
|
||||
|
||||
const syncCalls = vi.hoisted(() => [] as string[]);
|
||||
|
||||
vi.mock("esbuild", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("esbuild")>();
|
||||
const blocked = (name: string) => () => {
|
||||
syncCalls.push(name);
|
||||
throw new Error(`esbuild.${name} blocks the caller's thread`);
|
||||
};
|
||||
return { ...actual, transformSync: blocked("transformSync"), buildSync: blocked("buildSync") };
|
||||
});
|
||||
|
||||
const dir = mkdtempSync(join(tmpdir(), "hf-bundle-esbuild-async-"));
|
||||
afterAll(() => rmSync(dir, { recursive: true, force: true }));
|
||||
|
||||
it("strips script comments without a synchronous esbuild call", async () => {
|
||||
writeFileSync(
|
||||
join(dir, "index.html"),
|
||||
`<!doctype html><html><body>
|
||||
<div data-composition-id="root" data-width="320" data-height="180"></div>
|
||||
<script src="local.js"></script>
|
||||
<script>/* authored block note */ window.__timelines = window.__timelines || {}; window.__timelines.root = {};</script>
|
||||
</body></html>`,
|
||||
);
|
||||
writeFileSync(join(dir, "local.js"), "window.__a = 1; // authored line note");
|
||||
|
||||
const bundled = await bundleToSingleHtml(dir);
|
||||
|
||||
expect(syncCalls).toEqual([]);
|
||||
expect(bundled).toContain("window.__a = 1");
|
||||
expect(bundled).not.toContain("authored line note");
|
||||
expect(bundled).not.toContain("authored block note");
|
||||
});
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
splitUrlSuffix,
|
||||
} from "@hyperframes/parsers/asset-paths";
|
||||
import { CSS_URL_RE, isNonRelativeUrl } from "./assetPaths.js";
|
||||
import { transformSync } from "esbuild";
|
||||
import { transform } from "esbuild";
|
||||
import { compileHtml, type MediaDurationProber } from "./htmlCompiler";
|
||||
import {
|
||||
RUNTIME_BOOTSTRAP_ATTR,
|
||||
@@ -733,10 +733,10 @@ function pushRun<T>(runs: PartRun<T>[], scene: string | undefined, chunk: T): vo
|
||||
else runs.push({ scene, chunks: [chunk] });
|
||||
}
|
||||
|
||||
function coalesceHeadStylesAndBodyScripts(
|
||||
async function coalesceHeadStylesAndBodyScripts(
|
||||
document: Document,
|
||||
provenance?: BundleOptions["scriptProvenance"],
|
||||
): void {
|
||||
): Promise<void> {
|
||||
const allHeadStyles = [...document.querySelectorAll("head style")];
|
||||
const isScenePart = (el: Element) => el.hasAttribute(SCENE_PART_ATTR);
|
||||
for (const run of allHeadStyles.length > 1 ? headStyleRuns(allHeadStyles, isScenePart) : []) {
|
||||
@@ -756,13 +756,13 @@ function coalesceHeadStylesAndBodyScripts(
|
||||
const mergedJs = joinJsChunks(members.map((el) => el.textContent || ""));
|
||||
if (mergedJs && !parsesAsScript(mergedJs)) {
|
||||
for (const el of members)
|
||||
el.textContent = inlineScriptSource(el.textContent || "", provenance);
|
||||
el.textContent = await inlineScriptSource(el.textContent || "", provenance);
|
||||
continue;
|
||||
}
|
||||
for (const el of members) el.remove();
|
||||
if (!mergedJs) continue;
|
||||
const inlineScript = document.createElement("script");
|
||||
inlineScript.textContent = inlineScriptSource(mergedJs, provenance);
|
||||
inlineScript.textContent = await inlineScriptSource(mergedJs, provenance);
|
||||
if (anchor) anchor.before(inlineScript);
|
||||
else document.body.appendChild(inlineScript);
|
||||
}
|
||||
@@ -825,13 +825,13 @@ export function parsesAsScript(source: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function stripJsCommentsParserSafe(
|
||||
async function stripJsCommentsParserSafe(
|
||||
source: string,
|
||||
provenance?: BundleOptions["scriptProvenance"],
|
||||
): string {
|
||||
): Promise<string> {
|
||||
if (!source) return source;
|
||||
try {
|
||||
const result = transformSync(source, {
|
||||
const result = await transform(source, {
|
||||
loader: "js",
|
||||
minify: false,
|
||||
legalComments: "none",
|
||||
@@ -850,8 +850,11 @@ function stripJsCommentsParserSafe(
|
||||
}
|
||||
}
|
||||
|
||||
function inlineScriptSource(js: string, provenance?: BundleOptions["scriptProvenance"]): string {
|
||||
return escapeInlineScriptSource(stripJsCommentsParserSafe(js, provenance));
|
||||
async function inlineScriptSource(
|
||||
js: string,
|
||||
provenance?: BundleOptions["scriptProvenance"],
|
||||
): Promise<string> {
|
||||
return escapeInlineScriptSource(await stripJsCommentsParserSafe(js, provenance));
|
||||
}
|
||||
|
||||
export interface BundleScriptProvenance {
|
||||
@@ -1360,7 +1363,7 @@ async function bundleProject(projectDir: string, options?: BundleOptions): Promi
|
||||
const script = document.createElement("script");
|
||||
if (scene) script.setAttribute(SCENE_PART_ATTR, scene);
|
||||
script.textContent = skipsRunMerge
|
||||
? inlineScriptSource(text, options?.scriptProvenance)
|
||||
? await inlineScriptSource(text, options?.scriptProvenance)
|
||||
: text;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
@@ -1371,11 +1374,11 @@ async function bundleProject(projectDir: string, options?: BundleOptions): Promi
|
||||
|
||||
enforceCompositionPixelSizing(document);
|
||||
autoHealMissingCompositionIds(document);
|
||||
coalesceHeadStylesAndBodyScripts(document, options?.scriptProvenance);
|
||||
await coalesceHeadStylesAndBodyScripts(document, options?.scriptProvenance);
|
||||
for (const el of document.querySelectorAll(`script[${INLINED_FILE_ATTR}]`)) {
|
||||
const js = el.textContent ?? "";
|
||||
el.textContent = escapeInlineScriptSource(
|
||||
isJavaScriptType(el) ? stripJsCommentsParserSafe(js, options?.scriptProvenance) : js,
|
||||
isJavaScriptType(el) ? await stripJsCommentsParserSafe(js, options?.scriptProvenance) : js,
|
||||
);
|
||||
}
|
||||
deferScriptsUntilFonts(document, (el) => el.hasAttribute(RUNTIME_BOOTSTRAP_ATTR));
|
||||
|
||||
Reference in New Issue
Block a user