CLI Reference
Build a command#
Pick a command, tick what you want, and copy the line. Every flag here is a real flag, and the sentence underneath says what that combination does.
At least one file is required, and files matching your ignorePatterns are
skipped. Calling a native binary directly means explicit file paths only:
directory walking and glob expansion belong to the oxc-tsrx npm commands and
to Vite+. An unsupported option is an error, never an ignored flag.
What a plain install puts on your path#
npm install --save-dev oxc-tsrx@0.2.3 is the whole setup for the command line
and for the editor. Vite+ needs one more command; see
the minimum steps per host.
The install links seven commands into node_modules/.bin:
| Command | Kind | What it is |
|---|---|---|
oxlint |
you type it | The linter. Sends .tsrx to the TSRX engine and everything else to official Oxlint. |
oxfmt |
you type it | The formatter, with the same split. |
oxc-tsrx |
you type it | providers, status, setup, remove. Described in the next section. |
oxc-tsrx-lint |
leaf executor | The native linter, given explicit files only. oxlint dispatches to it. It prints JSON, and it has no --help. |
oxc-tsrx-fmt |
leaf executor | The native formatter. oxfmt dispatches to it. --help works here. |
oxc-tsrx-lsp |
leaf executor | The native language server. Editors launch it through oxlint --lsp. |
tsgolint |
not this project | It arrives with the oxlint-tsgolint dependency, the official type-aware runner used by --type-aware and --type-check. You never call it directly, and calling it prints upstream's own "unsupported entrypoint" warning. |
Reach for a leaf executor directly only when your project pins official oxlint
or oxfmt, because those command names then belong to the pinned package. Note
that the oxc-tsrx sections further down describe the native binary of the
same name, which comes from a source build. It is a different program from the
oxc-tsrx npm command above.
npx oxlint with no path also lints node_modules#
A bare npx oxlint walks the current directory, node_modules included, and so
does --fix, which will rewrite files in there. In a scratch npm init -y
project that was 9260 warnings, 9257 of them from node_modules.
Name a path (npx oxlint src) or list node_modules in a .gitignore. oxfmt
is unaffected: it skips node_modules unless you pass --with-node-modules.
Official Oxlint behaves identically, so this is upstream behavior.
oxc-tsrx (npm command)#
Usage: oxc-tsrx providers [--project <directory>] [--json]
oxc-tsrx setup [--project <directory>] [--dry-run] [--write-tsconfig]
[--workspace-root <directory>] [--json]
oxc-tsrx status [--project <directory>] [--json]
oxc-tsrx remove [--project <directory>] [--dry-run] [--json]| Subcommand | What it does |
|---|---|
providers |
Reads the oxc.provider block of your direct dependencies and prints the index. It writes nothing and spawns nothing. routed extensions: .tsrx -> oxc-tsrx is the line that proves your install works. |
setup |
Writes the project-local oxlint, oxfmt, and oxc-parser facades that Vite+ resolves, plus the editor slot below. Only Vite+ needs it. |
status |
Reports whether those four slots are present, and for the editor slot whether your editor would really read the key. |
remove |
Removes them and restores any transitive official package it displaced. |
Running it with no subcommand prints the usage block, and so do --help, -h,
and help. --version, -V, and version print oxc-tsrx 0.1.3. A wrong
subcommand names the bad word, prints the usage block, and exits 2.
What setup writes, and what it only checks#
Three of the four slots are packages in node_modules. The fourth is one
setting in your own tree: "oxc.path.oxlint": "node_modules/oxc-tsrx/bin/oxlint"
in .vscode/settings.json, written only when node_modules/.bin/oxlint does not
already resolve into this package. The key is merged without disturbing another
key or a comment, never overwrites a value you set, and remove takes back
exactly it. package.json is never edited, and neither is tsconfig.json
unless you pass --write-tsconfig.
setup and status also report four TSRX editor prerequisites they never
install:
@tsrx/typescript-plugin- a framework binding
- the nearest
tsconfig.json, which has to declare that plugin - TypeScript at
>=5.9 <6
The editor slot has eight states#
Only the first two mean the editor is already wired up. The last two exist so
that the report never calls wiring active that it cannot prove.
| State | What it means |
|---|---|
active (editor) |
The key is written at your project root, and the extension would resolve and run it. |
unnecessary (editor) |
The extension's own lookup reaches this package from your project root and from every folder above it that looks like a workspace root, so no key was written. |
missing (editor) |
The shim does not reach this package and no key is written yet. setup writes one. |
stale (editor) |
A key this package wrote no longer resolves here. setup refreshes it. |
collision (editor) |
The key is already set to a value you wrote. It is left alone and reported. |
unreadable (editor) |
.vscode/settings.json is not a single top-level JSON object, so nothing was written. |
inert (editor) |
Two shapes, one meaning: what is right for this folder is not what the folder you open would do. Either the value is right here and a folder above it looks like the workspace root you actually open, or no key was needed here and a folder above it would run a different oxlint. VS Code reads .vscode/settings.json only from the folder you open. |
unresolvable (editor) |
The key is written and the extension would not run it, because the file is missing, the value contains a character the extension rejects, or it is not spawnable on this platform. A configured value replaces the extension's own lookup rather than adding to it, so this is worse than no key. |
When a key is written and any folder above your project root looks like a
workspace root, setup, status, and remove print a ! note naming each one
and the file that made it a candidate, in order: .code-workspace,
pnpm-workspace.yaml, a workspaces field, turbo.json, nx.json,
lerna.json, then .git. When no key is written, the same note appears only
for a folder that would really run a different oxlint, and it names that
binary too. Nothing is reported for a folder that would still reach this
package, because a false alarm costs more than it is worth. Either way the note
lists the two remedies, in order: open the project folder itself, or rerun
setup --workspace-root <directory>.
setup --workspace-root <directory>#
The only way to write the key above your project root, and it is never implied.
Use it when the folder you open in your editor is a monorepo root rather than
the project that has the package.json.
npx oxc-tsrx setup --workspace-root .pnpm exec oxc-tsrx setup --workspace-root .yarn oxc-tsrx setup --workspace-root .bunx oxc-tsrx setup --workspace-root .The path is resolved from your working directory, like --project, and it has
to be a real directory that contains your project. The value is written relative
to the folder you name, so a root two levels up gets
packages/app/node_modules/oxc-tsrx/bin/oxlint rather than
node_modules/oxc-tsrx/bin/oxlint. remove follows the receipt back to that
same folder.
One caveat the command prints for you: a multi-root window resolves a relative
oxc.path.oxlint against its first folder, not against the folder holding
the settings file. oxc.path.oxlint is window-scoped, so a multi-root workspace
is not a way to rescue a folder-scoped value. Editor
integration has the
rest.
setup --write-tsconfig#
The one flag that edits a tsconfig, opt-in so that the default stays
report-only. It adds "plugins": [{ "name": "@tsrx/typescript-plugin" }] under
compilerOptions, splicing in that single entry and leaving every other byte,
comments included, exactly as written. A solution-style root owns no files, so a
plugin declared there is inert; the flag follows the reference to the project
that includes your source and writes there instead. It refuses rather than
guesses in two cases: a compilerOptions.plugins list it did not write is
reported instead of appended to, and a file whose compilerOptions object
cannot be located is left alone. Running it twice is a no-op.
See Vite and Vite+.
status says missing in a healthy project#
$ npx oxc-tsrx status
oxc-tsrx 0.1.5 compatibility (npm)
oxc-parser: missing
oxlint: missing
oxfmt: missing
oxc.path.oxlint: unnecessary (editor)
…/node_modules/.bin/oxlint already resolves into this package, so the
editor needs no setting and none was written.That output is correct and the exit code is 0: status only ever talks about the
Vite+ compatibility slots, so missing means "not installed" and unnecessary
means the ordinary lookup reaches this package from every folder you might open,
checked rather than assumed. Run setup only if you use Vite+, and run npx oxc-tsrx providers to confirm TSRX support is wired up.
Exit codes#
Both native commands use the same three codes:
| Code | oxc-tsrx (lint) |
oxc-tsrx-fmt (format) |
|---|---|---|
0 |
No errors, warning policy satisfied. | Formatted successfully, or --check found no differences. |
1 |
At least one error diagnostic, or options.denyWarnings/options.maxWarnings failed. |
--check found files that differ. |
2 |
Usage, configuration, or engine error. | Usage, configuration, or engine error. |
oxc-tsrx-lsp (language server)#
The third binary hosts the same Rust lint and format sessions behind OXC's
language-server transport. An editor client launches it, not you: oxlint --lsp
starts it beside official Oxlint and registers .tsrx. You get live diagnostics
on the spans you wrote, whole-document formatting, validated quick fixes, and
opt-in type-aware diagnostics. See Editor
integration.
npm direct upstream route#
Some invocations never need TSRX at all: the delegate-only flags below, and explicit batches of ordinary JS/JSX/TS/TSX files. Those load the pinned package's own launcher in the same Node process, so upstream diagnostics, config, fixes, stdin, and signals are preserved with no second process.
| Command | Delegate-only flags |
|---|---|
oxlint |
--help, -h, --version, -V, --rules, --init |
oxfmt |
--help, -h, --version, -V, --init, --migrate, --lsp |
Ambiguous paths, directories, globs, unknown options, and any .tsrx input stay
on the TSRX-aware bridge. oxlint --lsp is the deliberate exception: it
multiplexes official Oxlint and oxc-tsrx-lsp over one connection, keeping
ordinary documents with official Oxlint and isolating both directions' request
IDs. Non-LSP invocations never enter that multiplexer.
Environment variables#
The linter, the formatter, and the language server are one native binary that
dispatches on a leading subcommand (fmt, lsp, or lint, the default) and on
the name it was invoked under. The toolchain normally locates it through platform
packages; during source development these three overrides name a release binary
explicitly, and all three point at that same executable:
| Variable | Description |
|---|---|
OXC_TSRX_LINT_BIN |
Absolute path to the native oxc-tsrx binary. |
OXC_TSRX_FORMAT_BIN |
The same path; the wrapper selects the formatter with its fmt subcommand. |
OXC_TSRX_LSP_BIN |
The same path, used by the editor test harness. The editor client starts it with lsp. |
A missing native artifact is an error; .tsrx is never silently delegated to
the official tools.