fix(lint): block on digit-leading ids instead of only warning - #3980
miga-heygen wants to merge 1 commit into
Conversation
A digit-leading id (e.g. id="1-hook") breaks any bare `#1-hook` CSS selector with a SyntaxError, including inline GSAP string selectors like tl.fromTo("#1-hook", ...). That throw silently aborts the rest of the <script> block that built the selector, so the referenced element never animates — but the render itself still reports success, and the freeze has no visible error anywhere. id_requires_css_escape already detects this correctly; it was only "warning" severity, which hyperframes render and hyperframes check both treat as non-blocking by default. Promote it to "error" so `hyperframes check` (which always treats errors as blocking) catches this unconditionally instead of only under --strict. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>
somanshreddy
left a comment
There was a problem hiding this comment.
APPROVE at b378ec18 — correct one-line severity promotion; I verified the mechanism and the blast radius. Independent manual pass (codex disabled per workspace cap — my own source verification, flagged for honesty).
The promotion actually changes gating — I traced it to the exit-code math. hyperframes check computes ok in checkPipeline.ts:
ok: errorCount === 0 && (!options.strict || warningCount === 0)
and checkExitCode(report) = report.ok ? 0 : 1. So an error-severity finding forces ok=false → exit 1 unconditionally, whereas a warning only fails the gate under --strict. Moving id_requires_css_escape from warning→error therefore makes a digit-leading id block the standard pre-render check loudly, exactly as the PR claims — and hyperframes render's default lenient behavior (blocks only on --strict) is untouched, since it's the same severity plumbing.
The detection is unchanged and correct. The rule still fires on /^\d/ ids; only severity and the message string changed. #1-hook genuinely throws SyntaxError in querySelector(), and inside an inline GSAP string selector that aborts the rest of the <script> silently while the render still reports success — a real "succeeds but frozen" failure that warrants blocking.
Blast radius checked, not assumed. I scanned every template/fixture/scaffold/example/starter .html at head for a digit-leading id (id="[0-9]…") — zero hits. So no shipped template or check-tested fixture newly fails the gate. CI green corroborates: every tested fixture still passes, and Lint/Build/CLI-smoke are green. The rule already existed at warning tier, so any pre-existing digit-leading id would already have surfaced as a finding.
Test is meaningful. core.test.ts flips the expectation warning→error and still pins elementId and the CSS.escape fixHint, so it fails if the rule stops firing or mislabels the element — not just an echo of the severity string.
BLOCKED is branch protection awaiting this stamp, not a red check. Nothing to hold on — Home to merge.
Provenance: my own source pass at b378ec18 (core.ts, core.test.ts, check.ts, checkPipeline.ts) + template/fixture digit-id scan; codex disabled (owner cap). Did not run the suite locally (env); resting on source verification + the stated 606/606 run + green required checks.
Summary
An element
idthat starts with a digit (e.g.id="1-hook") breaks any bare CSS id selector built from it —#1-hookthrows aSyntaxErrorinquerySelector(). In a composition's inline<script>, this typically happens through a GSAP string selector (tl.fromTo("#1-hook", ...)), and the thrown error silently aborts the rest of that<script>block. The referenced element never gets its animation registered — it just sits frozen at its template-initial position — while the render itself completes and reports success, with no visible error anywhere.id_requires_css_escapealready detects this correctly (anyidmatching/^\d/), but it was"warning"severity, which bothhyperframes renderandhyperframes checktreat as non-blocking by default.Fix
Promote
id_requires_css_escapeto"error"severity.hyperframes checkalready treats"error"-severity findings as unconditionally blocking (regardless of--strict), so this makes a digit-leading id fail the standard pre-render gate loudly instead of shipping a silently-frozen composition.hyperframes renderitself is unaffected by default — it only blocks on errors under--strict, same as before.Test plan
"warning"→"error").packages/lintsuite (excluding 4 files that fail to load in this sandbox for an unrelated, pre-existingnode:path/posixinterop reason, confirmed not reproducible elsewhere): 606/606 passing, includingcore.test.tsat 77/77.hyperframes check's call site hardcodes strict-errors unconditionally, so this change has the intended effect;hyperframes render's default (lenient) behavior is untouched; no other file assumes this rule is warning-tier; no existing test fixture repo-wide contains a digit-leadingidthat would newly break; nothing else needs a companion update.tsc --noEmit,oxlint,oxfmt --checkclean on both touched files.