TSRX Syntax Support

The native scanner (tsrx_syntax::scan) reads your .tsrx file once, byte by byte, and records where every piece of TSRX-only syntax starts and ends. That record is called the overlay.

This page lists exactly which syntax the overlay recognizes today. Anything outside this list fails closed: instead of guessing what your code means and possibly producing wrong output, the command stops and reports an error that tells you what it found and where.

Control flow#

Statement containers#

@{ } is a statement container, the successor and extension of JSX expression containers: where JSX's { } holds a single expression, @{ } holds ordinary statements.

@{
  const doubled = count * 2;
}

Conditionals#

@if (user.isAdmin) {
  <AdminPanel />
} @else if (user.isMember) {
  <MemberPanel />
} @else {
  <SignIn />
}

Loops#

@for supports for await, declaration and assignment bindings, and index/key annotations. @empty renders when the iterable produces nothing:

@for (const item of items; index i; key item.id) {
  <Row item={item} position={i} />
} @empty {
  <EmptyState />
}

Switch#

@switch (status) {
  @case 'loading': { <Spinner /> }
  @case 'error': { <ErrorBanner /> }
  @default: { <Content /> }
}

Try / pending / catch#

Catch clauses support the headerless, error-binding, and error-plus-reset-binding forms:

@try {
  <Profile user={await loadUser()} />
} @pending {
  <Skeleton />
} @catch (error; reset) {
  <Retry error={error} onRetry={reset} />
}

Positions#

Every control form is recognized in statement, direct JSX-child, nested, and expression positions.

Dynamic JSX tags#

Matched dynamic opening/closing tags are recognized and validated against the real expression AST from the single canonical OXC parse, with no lexical approximation and no second parser:

<{condition ? Primary : Fallback} prop={value}>
  children
</{condition ? Primary : Fallback}>

Identities are structurally normalized (enclosing parentheses and trivia are stripped; edge comments are retained), so equivalent opening and closing expressions match. Dynamic tag expressions containing nested dynamic JSX are not yet supported.

Raw style elements#

Lowercase raw <style> elements are recognized with opaque payload spans. The CSS bytes are preserved exactly: carried through lint and format untouched, never CSS-formatted or CSS-validated.

Protected regions#

The scanner protects strings, comments, regex literals, template text and interpolation, JSX text and attributes, and multi-byte Unicode. An @if inside a string literal is just text.

Failing closed#

The scanner rejects:

  • orphan or reordered clauses (an @else without an @if, @case outside @switch);
  • mismatched static or dynamic JSX closing tags;
  • stale same-length overlays, by source fingerprint; and
  • unsupported or incomplete grammar, including malformed editor states.

Failing closed is a deliberate contract: a fast path must not achieve its numbers by silently dropping requested behavior.