mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 10:06:21 +00:00
Brand-loop live test (SDS duplicate, plans/figma/brand-loop-test-plan.md) proved the recolor chain end-to-end and surfaced three gaps: - runtime now defines every declared composition variable as a CSS custom property (document root at init + scoped sub-comp hosts in the loader), so imported var(--slug, literal) fills resolve live — without this the frozen literal always won and variable-driven rebranding could not propagate. Slug kept byte-compatible with the figma importer (parity test). render --variables overrides win. - figma component --name: variant frames are often all named 'Platform=Desktop' and slug-collided across imports. - imported fragments carry data-hf-snippet and the project linter skips composition-root rules for them. - /figma skill documents the field-tested non-Enterprise tokens path (MCP get_variable_defs joined with REST boundVariables ids). Shared-helper extractions (injectScopedStyles, flattenedRoot module, parseHostVariableValues, rasterizeFallback, shapeCss) satisfy the dedup/complexity audit the runtime changes tripped. Validated live: brand-loop renders purple from the attribute alone (no manual :root); 118 figma + 662 runtime/compiler + 331 lint tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 lines
1.8 KiB
TypeScript
48 lines
1.8 KiB
TypeScript
import { describe, it, expect, afterEach } from "vitest";
|
|
import { mkdirSync, mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
import { lintProject } from "./project.js";
|
|
|
|
let dirs: string[] = [];
|
|
afterEach(() => {
|
|
for (const d of dirs) rmSync(d, { recursive: true, force: true });
|
|
dirs = [];
|
|
});
|
|
|
|
const INDEX = `<html><body>
|
|
<div id="root" data-composition-id="main" data-start="0" data-duration="2" data-width="640" data-height="360"></div>
|
|
<script>window.__timelines = { main: { paused: true } };</script>
|
|
</body></html>`;
|
|
|
|
async function lintWithFragment(name: string, html: string) {
|
|
const dir = mkdtempSync(join(tmpdir(), "hf-snippet-lint-"));
|
|
dirs.push(dir);
|
|
writeFileSync(join(dir, "index.html"), INDEX);
|
|
mkdirSync(join(dir, "compositions"), { recursive: true });
|
|
writeFileSync(join(dir, "compositions", name), html);
|
|
const result = await lintProject(dir);
|
|
return result.results.filter((r) => r.file.includes(name.replace(".html", "")));
|
|
}
|
|
|
|
describe("snippet fragment exemption", () => {
|
|
it("skips composition-root rules for files whose ROOT carries data-hf-snippet", async () => {
|
|
const findings = await lintWithFragment(
|
|
"frag.html",
|
|
'<div id="frag" data-hf-snippet="" data-figma-id="1:1" style="width: 10px"></div>\n',
|
|
);
|
|
expect(findings).toHaveLength(0);
|
|
});
|
|
|
|
it("still lints a composition that merely CONTAINS snippet markup", async () => {
|
|
const results = await lintWithFragment(
|
|
"scene.html",
|
|
'<div id="scene"><div data-hf-snippet="" data-figma-id="1:1"></div></div>\n',
|
|
);
|
|
expect(results).toHaveLength(1);
|
|
expect(results[0]?.result.findings.some((f) => f.code === "root_missing_composition_id")).toBe(
|
|
true,
|
|
);
|
|
});
|
|
});
|