Skip to content

[finding] the react props gate and the react syntax gate accept different sources — measured both directions (Sucrase vs TypeScript) #10696

Description

@os-zhuang

Recorded as "deliberately unmeasured" on #10653, whose seat declined to file it on reasoning alone — "an unmeasured divergence filed as a finding is exactly the borrowed authority this card is about." Measured now, while implementing #10653, so it is filed with numbers instead.

The claim that was unmeasured

validate-react-page-props.ts carried a catch { continue; } commented "the syntax gate reports unparseable sources". Two things were wrong with it, and #10653 fixes the first (the catch never ran; the live path was the unread parseDiagnostics). This issue is the second: the gate credited as cover parses with a different parser.

  • validate-jsx-pages.ts does not lint kind:'react' pages at all.
  • The real cover is validate-react-pages.ts, which uses Sucrase (transform, transpile-only).
  • validate-react-page-props.ts uses the TypeScript compiler (createSourceFile, ScriptKind.TSX).

So "the syntax gate reports it" is only true where the two acceptance sets agree, and they do not.

Measurement (2026-08-21, TypeScript 6.0.3, sucrase 3.35.x, at 55809a09a5)

Each source below is a kind:'react' page source. "sucrase" = does validate-react-pages report a react-page-syntax finding; "tsc" = parseDiagnostics.length under ScriptKind.TSX.

sourcesucrase (syntax gate)tsc (props gate)
const n = 0755;accepts1 parse diagnostic
const s = '\012';accepts1 parse diagnostic
const n = 0b2;accepts1 parse diagnostic
const n = 1__0;accepts1 parse diagnostic
with (o) { x = 1; }throws0 diagnostics
unterminated comment / template / merge markers / truncated JSXthrows≥1

Both directions are real. Four shapes pass the syntax gate and are unreadable to the props gate; one shape is refused by the syntax gate and parses fine for the props gate.

Why it matters

The first four rows are the case the deleted comment was most wrong about: the syntax gate is green, so nothing tells the author anything, while the props gate is reading a partially recovered tree. Before #10653 that combination was total silence. After #10653 the props gate reports react-page-source-unparseable on exactly those rows, which is why the fix does not double-report: the two rules fire on overlapping-but-different sets, by construction.

The last row is the opposite and is harmless today (the syntax gate reports; the props gate reads the source fine).

Pinned as a regression test in PR for #10653: packages/lint/src/validate-react-page-props.test.ts'the syntax gate is a DIFFERENT parser — measured, not assumed', which asserts the 0755 row in both directions.

What is NOT claimed here

That either parser is wrong. Sucrase is the family that actually transpiles a react page, so its verdict is the one closest to what ships; TypeScript's grammar is stricter about numeric literal legacy forms. What is filed is that two gates on the same authored surface answer "does this parse?" differently, that the divergence is unmeasured anywhere in the tree, and that a comment in the props gate asserted the opposite.

Possible dispositions, no recommendation made here (it needs a spec-level answer about which parser defines "a valid react page source"):

  1. Leave it, now that both sides report rather than going silent (what packages/lint's three validators score an unparseable source CLEAN — the defect is the unread parseDiagnostics, not the try/catch #10653 lands).
  2. Make the syntax gate authoritative and have the props gate defer to its verdict.
  3. Declare one parser the definition of the surface and pin the acceptance set with a shared corpus.

Refs: #10653 · #10606 · PR #10651

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions