Files
hyperframes/packages/core/src/compiler/staticGuard.ts
T
Miguel Ángel 98d0bdd73c refactor: extract @hyperframes/lint from core (#1756)
* refactor: extract @hyperframes/lint package from core

Moves all lint rules, hyperframeLinter, lintProject, and related types
from packages/core/src/lint/ into a new standalone packages/lint package.

Core keeps a thin re-export stub at @hyperframes/core/lint for backward
compatibility. Consumer imports (cli lint command, producer hyperframeLint)
are updated to import from @hyperframes/lint directly.

Depends on @hyperframes/parsers (PR #1755).

* fix: restore postcss-selector-parser in core (sourceMutation.ts still uses it)

* fix(ci): add parsers+lint to Dockerfile and build before preview tests

* chore: update bun.lock after restoring postcss-selector-parser dep

* test(cli): update lintProject test for string-dir signature from @hyperframes/lint

* refactor(core): single-source the lint engine in @hyperframes/lint

Delete core's byte-identical copy of the lint rule engine and re-point
staticGuard at @hyperframes/lint, so the render-time render-gate and the
studio preview share one rule engine instead of two copies that could
silently diverge. Back-compat preserved via the @hyperframes/core/lint stub.

Addresses review feedback on the dual-copy footgun.
2026-06-27 01:16:40 -04:00

42 lines
1.4 KiB
TypeScript

import { lintHyperframeHtml } from "@hyperframes/lint";
export type HyperframeStaticFailureReason =
| "missing_composition_id"
| "missing_composition_dimensions"
| "missing_timeline_registry"
| "invalid_script_syntax"
| "invalid_static_hyperframe_contract";
export type HyperframeStaticGuardResult = {
isValid: boolean;
missingKeys: string[];
failureReason: HyperframeStaticFailureReason | null;
};
export async function validateHyperframeHtmlContract(
html: string,
): Promise<HyperframeStaticGuardResult> {
const result = await lintHyperframeHtml(html);
const missingKeys = result.findings
.filter((finding) => finding.severity === "error")
.map((finding) => finding.message);
if (missingKeys.length === 0) {
return { isValid: true, missingKeys: [], failureReason: null };
}
const joined = missingKeys.join(" ").toLowerCase();
let failureReason: HyperframeStaticFailureReason = "invalid_static_hyperframe_contract";
if (joined.includes("data-composition-id")) {
failureReason = "missing_composition_id";
} else if (joined.includes("data-width") || joined.includes("data-height")) {
failureReason = "missing_composition_dimensions";
} else if (joined.includes("window.__timelines")) {
failureReason = "missing_timeline_registry";
} else if (joined.includes("script syntax")) {
failureReason = "invalid_script_syntax";
}
return { isValid: false, missingKeys, failureReason };
}