mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(core): wrap bundled composition scripts as string literals
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { parseHTML } from "linkedom";
|
||||
import { scopeCssToComposition, wrapScopedCompositionScript } from "./compositionScoping";
|
||||
import {
|
||||
scopeCssToComposition,
|
||||
wrapInlineScriptWithErrorBoundary,
|
||||
wrapScopedCompositionScript,
|
||||
} from "./compositionScoping";
|
||||
|
||||
describe("composition scoping", () => {
|
||||
it("scopes regular selectors while preserving global at-rules", () => {
|
||||
@@ -568,6 +572,26 @@ window.__afterTimeline = window.__timelines.scene;
|
||||
expect(scoped).toContain('[data-composition-id="chrome-overlay"] .child-element');
|
||||
});
|
||||
|
||||
it("wraps scoped composition script source as a string literal", () => {
|
||||
const wrapped = wrapScopedCompositionScript(
|
||||
'window.payload = "</script><script>window.pwned = true;</script>";',
|
||||
"scene",
|
||||
);
|
||||
|
||||
expect(wrapped).toContain('Function("document", "gsap", "window", "__hyperframes", ');
|
||||
expect(wrapped).toContain('\\"</script><script>window.pwned = true;</script>\\"');
|
||||
});
|
||||
|
||||
it("wraps unscoped composition script source as a string literal", () => {
|
||||
const wrapped = wrapInlineScriptWithErrorBoundary(
|
||||
'window.payload = "</script><script>window.pwned = true;</script>";',
|
||||
"[HyperFrames] composition script error:",
|
||||
);
|
||||
|
||||
expect(wrapped).toContain("Function(");
|
||||
expect(wrapped).toContain('\\"</script><script>window.pwned = true;</script>\\"');
|
||||
});
|
||||
|
||||
it("rewrites #id CSS selectors to [data-hf-authored-id] when authoredRootId is provided", () => {
|
||||
const scoped = scopeCssToComposition(
|
||||
`#intro { background: #111; }
|
||||
|
||||
@@ -216,6 +216,7 @@ export function wrapScopedCompositionScript(
|
||||
const authoredRootIdFormsLiteral = JSON.stringify(
|
||||
getAuthoredRootIdSelectorForms(authoredRootId?.trim() || ""),
|
||||
);
|
||||
const sourceLiteral = JSON.stringify(source);
|
||||
return `(function(){
|
||||
var __hfCompId = ${compositionIdLiteral};
|
||||
var __hfTimelineCompId = ${timelineCompositionIdLiteral};
|
||||
@@ -485,9 +486,8 @@ export function wrapScopedCompositionScript(
|
||||
});
|
||||
var __hfRun = function() {
|
||||
try {
|
||||
(function(document, gsap, window, __hyperframes) {
|
||||
${source}
|
||||
}).call(window, __hfScopedDocument, __hfScopedGsap, __hfScopedWindow, __hfScopedHyperframes);
|
||||
var __hfScript = Function("document", "gsap", "window", "__hyperframes", ${sourceLiteral});
|
||||
__hfScript.call(window, __hfScopedDocument, __hfScopedGsap, __hfScopedWindow, __hfScopedHyperframes);
|
||||
} catch (_err) {
|
||||
console.error(__hfErrorLabel, __hfCompId, _err);
|
||||
}
|
||||
@@ -496,3 +496,7 @@ ${source}
|
||||
__hfRun();
|
||||
})();`;
|
||||
}
|
||||
|
||||
export function wrapInlineScriptWithErrorBoundary(source: string, errorLabel: string): string {
|
||||
return `(function(){ try { Function(${JSON.stringify(source)}).call(window); } catch (_err) { console.error(${JSON.stringify(errorLabel)}, _err); } })();`;
|
||||
}
|
||||
|
||||
@@ -758,7 +758,8 @@ describe("bundleToSingleHtml", () => {
|
||||
expect(bundled).toContain('[data-composition-id="scene"] .title { color: red; }');
|
||||
expect(bundled).toContain("new Proxy(window.document");
|
||||
expect(bundled).toContain("new Proxy(__hfBaseGsap");
|
||||
expect(bundled).toContain('tl.to(".title"');
|
||||
expect(bundled).toContain('Function("document", "gsap", "window", "__hyperframes",');
|
||||
expect(bundled).toContain("tl.to('.title'");
|
||||
});
|
||||
|
||||
it("isolates sibling instances of the same external sub-composition", async () => {
|
||||
|
||||
@@ -8,7 +8,11 @@ import {
|
||||
stripEmbeddedRuntimeScripts,
|
||||
} from "./htmlDocument";
|
||||
// rewriteSubCompPaths functions are used by inlineSubCompositions (shared module)
|
||||
import { scopeCssToComposition, wrapScopedCompositionScript } from "./compositionScoping";
|
||||
import {
|
||||
scopeCssToComposition,
|
||||
wrapInlineScriptWithErrorBoundary,
|
||||
wrapScopedCompositionScript,
|
||||
} from "./compositionScoping";
|
||||
import { validateHyperframeHtmlContract } from "./staticGuard";
|
||||
import { getHyperframeRuntimeScript } from "../generated/runtime-inline";
|
||||
import { readDeclaredDefaults } from "../runtime/getVariables";
|
||||
@@ -824,7 +828,10 @@ export async function bundleToSingleHtml(
|
||||
runtimeCompId || compId,
|
||||
authoredRootId,
|
||||
)
|
||||
: `(function(){ try { ${scriptEl.textContent || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`,
|
||||
: wrapInlineScriptWithErrorBoundary(
|
||||
scriptEl.textContent || "",
|
||||
"[HyperFrames] composition script error:",
|
||||
),
|
||||
);
|
||||
}
|
||||
scriptEl.remove();
|
||||
@@ -875,7 +882,10 @@ export async function bundleToSingleHtml(
|
||||
runtimeScope,
|
||||
runtimeCompId || compId,
|
||||
)
|
||||
: `(function(){ try { ${scriptEl.textContent || ""} } catch (_err) { console.error('[HyperFrames] composition script error:', _err); } })();`,
|
||||
: wrapInlineScriptWithErrorBoundary(
|
||||
scriptEl.textContent || "",
|
||||
"[HyperFrames] composition script error:",
|
||||
),
|
||||
);
|
||||
}
|
||||
scriptEl.remove();
|
||||
|
||||
@@ -13,7 +13,11 @@ import {
|
||||
rewriteCssAssetUrls,
|
||||
rewriteInlineStyleAssetUrls,
|
||||
} from "./rewriteSubCompPaths";
|
||||
import { scopeCssToComposition, wrapScopedCompositionScript } from "./compositionScoping";
|
||||
import {
|
||||
scopeCssToComposition,
|
||||
wrapInlineScriptWithErrorBoundary,
|
||||
wrapScopedCompositionScript,
|
||||
} from "./compositionScoping";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public interface
|
||||
@@ -287,7 +291,7 @@ export function inlineSubCompositions(
|
||||
runtimeCompId || scopeCompId,
|
||||
authoredRootId,
|
||||
)
|
||||
: `(function(){ try { ${s.textContent || ""} } catch (_err) { console.error(${JSON.stringify(scriptErrorLabel)}, _err); } })();`;
|
||||
: wrapInlineScriptWithErrorBoundary(s.textContent || "", scriptErrorLabel);
|
||||
scripts.push(wrappedScript);
|
||||
scriptItems.push({ kind: "inline", content: wrappedScript });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user