Introduction
OXC (opens in new tab) is a JavaScript and TypeScript toolchain written in
Rust: a parser, the oxlint linter, and the oxfmt formatter. It does the
same jobs as ESLint and Prettier, just much faster.
OXC for TSRX teaches those tools to read .tsrx files, so you can lint and
format them like any other file in your project. The parser behind them is
also available as a library, oxc-tsrx/parser, for
building your own tooling.
The problem it solves#
TSRX is TypeScript/JSX plus template control flow: @if, @for, @switch,
@try, @{ } statement containers (a successor and extension of JSX
expression containers), dynamic tags like <{expr}>, and inline raw
<style> blocks.
OXC on its own doesn't understand TSRX. Point oxlint or oxfmt at a
.tsrx file and they see a parse error at the first @if. So TSRX projects
would normally lose linting and formatting entirely. That's the gap this
project closes.
How it works, in plain terms#
OXC only understands regular TS/TSX, so every .tsrx file goes through the
same four steps.
One pass finds the TSRX-only lines, highlighted on the left. OXC on its own cannot parse them.
Each construct becomes a valid TSX placeholder, highlighted on the right. Every other byte is your code, untouched.
The real, unmodified OXC runs on the copy, exactly once, and flags the highlighted lines: eslint(no-unused-vars) and eslint(no-debugger).
Each warning lands back on the bytes you wrote, highlighted on the left. Formatting is lifted back the same way.
Your TSRX
export function Cart({ items }: Props) @{ var total = 0; debugger; <section class="cart"> @if (items.length > 0) { @for (const item of items; key item.id) { <Row item={item} /> } } @else { <Empty /> } </section>}What OXC actually sees
export function Cart({ items }: Props) /*_t0_0*/{ var total = 0; debugger; <section class="cart"> {_t0_W0_({async *_t0_M0_(){/*_t0_N0S__*//*_t0_1*/if (items.length > 0) { /*_t0_2*/for (const item of _t0_H0_(/*_t0_R0S__*/items/*_t0_R0E__*/,_t0_KH0_(/*_t0_K0S__*/item.id/*_t0_K0E__*/),_t0_HE0_)) { <Row item={item} /> } } /*_t0_3*/else { <Empty /> }/*_t0_N0E__*/}},_t0_E0_)} </section>}Your file on disk is always real TSRX. The TSX copy never touches disk; it exists only so OXC can do its job.
Ordinary .js, .jsx, .ts, and .tsx files skip all of this and go
straight to OXC, byte-for-byte identical to running oxlint and oxfmt
yourself.
What it promises#
- ✅ Real rules, your config. OXC's own lint rules and Oxfmt's own
formatting, driven by the
.oxlintrc.jsonand.oxfmtrc.jsonyou already have. In a Vite+ project,vp lintreads thelintblock of yourvite.config.tsinstead. - 🎯 Errors point at your code. Every warning lands on a line you actually wrote. Ones that fire on the hidden placeholder code are counted, not shown.
- 🛡️ Safe fixes.
--fixedits your original TSRX and confirms the result still parses before writing anything. - 🧠 Opt-in type-aware rules.
--type-awareadds the official tsgolint rules,--type-checkadds full TypeScript diagnostics. The default lane starts zero type processes. See Linting. - ✏️ Editor support. The official OXC extension picks up the project-local
oxlintthatoxc-tsrxinstalls, and your.tsrxfiles get live diagnostics, formatting, and quick fixes. Your.tsand.tsxfiles keep working exactly as before. See Editor integration. - 🔗 No fork. Nothing is snapshotted and nothing is patched. Every OXC call lives in one small adapter crate, so upgrading OXC means updating that one crate.
- 🚦 Fail closed. Unsupported TSRX syntax gets a clear error, never a silently skipped file or a half-right result.
The commands#
Install oxc-tsrx and you get the familiar commands, now with TSRX support:
# Lint .tsrx and ordinary JS/TS with real OXC rules npx oxlint src/Counter.tsrx src/View.tsx src/Counter.tsrx:4:3: warning eslint(no-debugger): `debugger` statement is not allowed src/View.tsx:2:7: warning eslint(no-unused-vars): Variable 'seen' is declared but never used. Unused variables should start with a '_'. help: Consider removing this declaration. Found 0 error(s) and 2 warning(s). # Format .tsrx and ordinary JS/TS with real Oxfmt layout npx oxfmt --check src/Counter.tsrx src/View.tsx Checking formatting... src/View.tsx (0ms) src/Counter.tsrx (1ms) Format issues found in above 2 files. Run without `--check` to fix. Finished in 86ms on 2 files using 18 threads. No config found, using defaults. Please add a config file or try `oxfmt --init` if needed.
Every command is the same native Rust binary, oxc-tsrx: the linter, the
formatter, and the editor language server in one download, picked by
subcommand. See Getting Started to install it and the
CLI reference for every flag.
What it deliberately is not#
- Not a compiler. Your framework's TSRX plugin, installed from tsrx.dev/getting-started (opens in new tab), owns compilation, CSS, source maps, and HMR. Nothing here touches your build or dev server. See Vite and Vite+.
- Not a CSS formatter. Anything inside a raw
<style>block is left alone, byte for byte. - Not finished. Some syntax and config options aren't supported yet. They fail with a clear error instead of quietly doing the wrong thing. See Limitations.