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