mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
feat(core): sub-composition variable render path
This commit is contained in:
+4
-1
@@ -81,7 +81,10 @@ docs/plans/
|
||||
# Local proof / test artifacts
|
||||
qa-artifacts/
|
||||
my-video/
|
||||
.hyperframes/backup/
|
||||
# Studio project state (hf-id backups, per-file state) — always runtime-generated,
|
||||
# never committed. Broadened from .hyperframes/backup/ after preview-generated state
|
||||
# leaked into a fixture commit.
|
||||
.hyperframes/
|
||||
examples/*
|
||||
# Tracked OSS examples — negations override the blanket `examples/*` ignore.
|
||||
!examples/aws-lambda
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseHTML } from "linkedom";
|
||||
import { inlineSubCompositions } from "./inlineSubCompositions";
|
||||
import { readDeclaredDefaults, parseHostVariableValues } from "../runtime/getVariables";
|
||||
|
||||
// Fixtures reference GSAP CDN but are never loaded in a real browser — resolveHtml is mocked.
|
||||
|
||||
@@ -358,3 +359,46 @@ describe("inlineSubCompositions – #ID selector scoping divergence", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("inlineSubCompositions – variable defaults on a template sub-comp root div", () => {
|
||||
const SUB_COMP_WITH_VAR = `<template id="card-template">
|
||||
<div id="card" data-composition-id="card" data-width="1920" data-height="1080"
|
||||
data-composition-variables='[{"id":"headline","type":"string","label":"Headline","default":"Hi there"}]'>
|
||||
<h1 class="title" data-var-text="headline">Hi there</h1>
|
||||
</div>
|
||||
</template>`;
|
||||
|
||||
function hostDoc() {
|
||||
const { document } = parseHTML(`<!DOCTYPE html><html><body>
|
||||
<div data-composition-id="main">
|
||||
<div data-composition-id="card" data-composition-src="card.html"
|
||||
data-start="0" data-duration="4" data-track-index="0"></div>
|
||||
</div></body></html>`);
|
||||
return document;
|
||||
}
|
||||
|
||||
it("aggregates defaults declared on the inner root div (template comps have no <html> to hold them)", () => {
|
||||
const document = hostDoc();
|
||||
const host = document.querySelector('[data-composition-src="card.html"]')!;
|
||||
const result = inlineSubCompositions(document, [host], {
|
||||
resolveHtml: () => SUB_COMP_WITH_VAR,
|
||||
parseHtml: (h) => parseHTML(h).document,
|
||||
readVariableDefaults: readDeclaredDefaults,
|
||||
parseHostVariables: parseHostVariableValues,
|
||||
});
|
||||
expect(result.variablesByComp["card"]).toMatchObject({ headline: "Hi there" });
|
||||
});
|
||||
|
||||
it("lets a per-instance host value override the declared default", () => {
|
||||
const document = hostDoc();
|
||||
const host = document.querySelector('[data-composition-src="card.html"]')!;
|
||||
host.setAttribute("data-variable-values", JSON.stringify({ headline: "Overridden" }));
|
||||
const result = inlineSubCompositions(document, [host], {
|
||||
resolveHtml: () => SUB_COMP_WITH_VAR,
|
||||
parseHtml: (h) => parseHTML(h).document,
|
||||
readVariableDefaults: readDeclaredDefaults,
|
||||
parseHostVariables: parseHostVariableValues,
|
||||
});
|
||||
expect(result.variablesByComp["card"]).toMatchObject({ headline: "Overridden" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,6 +149,7 @@ function defaultBuildScopeSelector(compId: string): string {
|
||||
* 10. Remove `data-composition-src` from host
|
||||
* 11. Inject the content into the host element
|
||||
*/
|
||||
// fallow-ignore-next-line complexity
|
||||
export function inlineSubCompositions(
|
||||
document: Document,
|
||||
hosts: Element[],
|
||||
@@ -236,10 +237,14 @@ export function inlineSubCompositions(
|
||||
const scopeCompId = compId || inferredCompId;
|
||||
const runtimeScope = runtimeCompId ? buildScopeSelector(runtimeCompId) : "";
|
||||
|
||||
// Variable merging (bundler feature)
|
||||
// Variable merging (bundler feature). Read declared defaults from the
|
||||
// document element (full-document sub-comps) AND the inner composition root
|
||||
// (template/fragment sub-comps store their schema on the root div, not a
|
||||
// synthetic <html>), then let per-instance host values override.
|
||||
if (readVariableDefaults && parseHostVariables && runtimeCompId) {
|
||||
const mergedVariables = {
|
||||
...readVariableDefaults(compDoc.documentElement),
|
||||
...(innerRoot ? readVariableDefaults(innerRoot) : {}),
|
||||
...parseHostVariables(hostEl),
|
||||
};
|
||||
if (Object.keys(mergedVariables).length > 0) {
|
||||
|
||||
@@ -698,6 +698,10 @@ export async function loadExternalCompositions(
|
||||
headStyles,
|
||||
headScripts,
|
||||
headLinks,
|
||||
// TODO(template-var-carriers): reads `<html>` only. A template/fragment
|
||||
// sub-comp that declares on its `[data-composition-id]` root div (the
|
||||
// dual-carrier contract from #2081) loses its defaults on this lazy
|
||||
// external-load path — see inlineSubCompositions for the fixed path.
|
||||
declaredVariableDefaults: readDeclaredDefaults(doc.documentElement),
|
||||
onDiagnostic: params.onDiagnostic,
|
||||
});
|
||||
|
||||
@@ -782,6 +782,10 @@ export function extractCompositionMetadata(html: string): CompositionMetadata {
|
||||
const durationStr = htmlEl.getAttribute("data-composition-duration");
|
||||
const compositionDuration = durationStr ? parseFloat(durationStr) : null;
|
||||
|
||||
// TODO(template-var-carriers): reads `<html>` only. A template/fragment comp
|
||||
// that declares variables on its `[data-composition-id]` root div (the
|
||||
// dual-carrier contract from #2081) reports no variables when its metadata is
|
||||
// extracted standalone (e.g. CLI --variables validation of a sub-comp file).
|
||||
const variables = parseCompositionVariables(htmlEl);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user