Oxlint and Oxfmt configuration

If you already have an .oxlintrc.json or .oxfmtrc.json, it works here unchanged. This page is the exact support matrix: which fields the native commands honor, how a Vite+ config participates, and what fails loudly instead of being silently ignored.

Configuration is loaded once when a native session starts and reused for every file in that batch. Ordinary .js, .jsx, .ts, and .tsx files stay on the direct canonical path. Stock oxlint and oxfmt still do not recognize .tsrx.

Installed through npm? You run the oxlint and oxfmt commands from oxc-tsrx, and everything on this page applies to them too. They can also take lint and fmt options from a Vite+ vite.config.*; that contract is the Vite+ configuration boundary. The examples below invoke the native binaries directly because this page documents the native boundary.

This is the whole resolution story in one picture. Select a node or step through the buttons:

Select a diagram node to read its explanation.
Explicit --config pathUpward search from the working directoryVite plus config forwardingOne session loads the config oncePer-file overrides and ignore patternsEvery file in the batch reuses the sessionUnsupported keys fail loudly
How configuration is resolved. Three ways a config can arrive, one load per session, and a loud failure for anything the native boundary does not support.

The config examples on this page are annotated: hover any highlighted field name to read what the native boundary does with it.

Lint configuration#

oxc-tsrx searches upward from the working directory for one .oxlintrc.json or .oxlintrc.jsonc, or takes an explicit JSON/JSONC file with --config or -c. One config covers the whole batch; nested per-directory discovery is not implemented yet.

The native boundary supports the following.

Config file fields:

  • configured built-in rules and built-in plugins, including rule options;
  • env, globals, and built-in-plugin settings through canonical ConfigStoreBuilder;
  • JSON/JSONC extends, resolved relative to the declaring config;
  • Vite+ object extends materialized through canonical OXC's public extends_configs model;
  • per-file overrides for .tsrx and ordinary JS/JSX/TS/TSX paths; and
  • ignorePatterns rooted at the config directory.

CLI flags and exit policy:

  • --allow/-A, --warn/-W, and --deny/-D precedence over configured severities; and
  • options.denyWarnings and options.maxWarnings exit policy.

Output and fixes:

  • explicit multi-file batches and identity-mapped --fix; and
  • JSON diagnostic output at original TSRX byte spans.

Opt-in type lanes:

  • official tsgolint rules through --type-aware; and
  • TypeScript syntactic and semantic diagnostics through --type-check.

Custom JavaScript plugins:

  • jsPlugins on ordinary files and on .tsrx, through the oxlint command and the language server, with your own severities, rule options, extends, and overrides resolved by Oxlint itself. See jsPlugins and the two lanes for the extra parse this costs and the one command that still refuses it.

For example:

{
  "plugins": ["react"],
  "env": { "browser": true },
  "globals": { "frameworkGlobal": "readonly" },
  "rules": {
    "no-debugger": "error",
    "eqeqeq": ["error", "always"],
    "react/jsx-no-undef": "error"
  },
  "overrides": [
    {
      "files": ["**/*.tsrx"],
      "rules": { "no-console": "warn" }
    }
  ],
  "ignorePatterns": ["generated/**"]
}

In the demo below, the discovered .oxlintrc.json (also passed as config/lint.json) is this example plus the type-aware additions described in the next section. src/View.tsrx has a console call, a debugger statement, a wrong type annotation, and an unawaited call into src/service.tsrx.

See it run
# Discovered .oxlintrc.json: console is a warning, debugger an error
oxc-tsrx --format=json src/View.tsrx src/View.tsx \
  | jq '.diagnostics'
[
  {
    "filename": "src/View.tsrx",
    "rule": "no-console",
    "code": "eslint(no-console)",
    "severity": "warning",
    "message": "Unexpected console statement.",
    "labels": [
      {
        "span": {
          "offset": 172,
          "length": 11
        }
      }
    ]
  },
  {
    "filename": "src/View.tsrx",
    "rule": "no-debugger",
    "code": "eslint(no-debugger)",
    "severity": "error",
    "message": "`debugger` statement is not allowed",
    "labels": [
      {
        "span": {
          "offset": 204,
          "length": 9
        }
      }
    ]
  },
  {
    "filename": "src/View.tsx",
    "rule": "no-unused-vars",
    "code": "eslint(no-unused-vars)",
    "severity": "warning",
    "message": "Variable 'seen' is declared but never used. Unused variables should start with a '_'.",
    "labels": [
      {
        "span": {
          "offset": 59,
          "length": 4
        },
        "message": "'seen' is declared here"
      }
    ]
  }
]


# Explicit config path plus CLI severity overrides
oxc-tsrx --format=json --config config/lint.json \
  --warn no-console --deny no-debugger src/View.tsrx | jq '.diagnostics'
[
  {
    "filename": "src/View.tsrx",
    "rule": "no-console",
    "code": "eslint(no-console)",
    "severity": "warning",
    "message": "Unexpected console statement.",
    "labels": [
      {
        "span": {
          "offset": 172,
          "length": 11
        }
      }
    ]
  },
  {
    "filename": "src/View.tsrx",
    "rule": "no-debugger",
    "code": "eslint(no-debugger)",
    "severity": "error",
    "message": "`debugger` statement is not allowed",
    "labels": [
      {
        "span": {
          "offset": 204,
          "length": 9
        }
      }
    ]
  }
]


# One TypeScript-Go process covers the whole explicit batch; showing only what --type-aware adds
oxc-tsrx --format=json --type-aware src/View.tsrx src/service.tsrx \
  | jq '[.diagnostics[] | select(.code | startswith("typescript"))]'
[
  {
    "filename": "src/View.tsrx",
    "rule": "no-floating-promises",
    "code": "typescript(no-floating-promises)",
    "severity": "error",
    "message": "Promises must be awaited, add void operator to ignore.",
    "labels": [
      {
        "span": {
          "offset": 216,
          "length": 12
        }
      }
    ]
  }
]


# --type-check additionally lands compiler diagnostics on your authored bytes; showing only those
oxc-tsrx --format=json --type-check src/View.tsrx src/service.tsrx \
  | jq '[.diagnostics[] | select(.code | startswith("typescript(TS"))]'
[
  {
    "filename": "src/View.tsrx",
    "rule": "parse-error",
    "code": "typescript(TS2322)",
    "severity": "error",
    "message": "Type 'string' is not assignable to type 'number'.",
    "labels": [
      {
        "span": {
          "offset": 143,
          "length": 7
        }
      }
    ]
  }
]
Real output, captured at build time by running the release binaries against the sample project described above. The type-aware and type-check runs are filtered to the diagnostics each flag adds.

Type-aware linting#

The direct native command requires --type-aware or --type-check even when the config contains options.typeAware or options.typeCheck. Config alone fails actionably instead of unexpectedly starting a TypeScript-Go process. --type-check implies the type-aware lane and additionally reports TypeScript syntactic and semantic diagnostics. Through the npm toolchain, a resolved Vite+ config forwards the corresponding explicit flag automatically.

{
  "plugins": ["typescript"],
  "rules": {
    "typescript/no-floating-promises": "off"
  },
  "overrides": [
    {
      "files": ["**/*.tsrx"],
      "rules": {
        "typescript/no-floating-promises": "error"
      }
    }
  ],
  "options": {
    "typeAware": true,
    "typeCheck": false
  }
}

In plain terms:

  • The tool hands the type checker a temporary in-memory TSX copy of each .tsrx file, then maps every result back onto the bytes you actually wrote. Nothing is written to disk, and one tsgolint process covers the whole batch.
  • Rules and overrides are resolved against your authored .tsrx paths before that copy exists, so **/*.tsrx overrides keep working.
  • A fix is only applied when the affected text exists verbatim in your authored file and the result reparses as valid TSRX.

The precise projection and fix-eligibility contract lives in Architecture.

Compiler diagnostics from --type-check use the same JSON shape as lint diagnostics. A TypeScript compiler message has no lint rule name, so its rule field falls back to parse-error while code carries the real compiler code, such as typescript(TS2322).

Troubleshooting tsgolint discovery#

Type-aware runs need exactly oxlint-tsgolint 0.24.0, pinned through oxc-tsrx's lint implementation dependency. When --type-aware or --type-check fails to start:

  • Native discovery checks the project installation and PATH.
  • OXLINT_TSGOLINT_PATH selects an executable or its directory explicitly.
  • A standalone executable without package metadata also requires OXC_TSRX_TSGOLINT_VERSION=0.24.0.
  • A missing binary, unverifiable binary, or version mismatch fails the command with exit status 2 instead of silently downgrading or writing source.

jsPlugins and the two lanes#

jsPlugins works on both halves of a project, but which command you run decides whether it works at all, so it gets its own section.

  • The oxlint command and the language server run them on .tsrx. They build the legal-TSX projection of each .tsrx file, run the published Oxlint binary over it with your own config, and map every diagnostic back onto your authored bytes. Ordinary files are untouched and go straight to canonical Oxlint.
  • That costs one extra parse per linted .tsrx file, and it is never silent. oxlint prints one line to stderr ahead of the report and repeats the fact in --format=json under oxcTsrx.jsPluginProjection; the language server writes the same fact to its output log once per session.
  • settings.oxcTsrx.jsPluginsOnTsrx: false turns the lane off. Your plugins keep running on ordinary files. On .tsrx you then get the refusal below instead of quietly fewer rules.
  • The direct native target refuses jsPlugins and always did. oxc-tsrx-lint is a Rust process with no Node.js runtime, so it cannot host your module. It exits 2 with a message naming oxlint as the command that can. vp lint is not that target: it reaches this package through the oxlint command, so a Vite+ project keeps its plugins on both halves. See Vite and Vite+.

context.filename is the mirror path, @if and @for arrive already compiled, and a diagnostic that lands on projected-only text is dropped. Those are documented in full under Custom JavaScript plugins.

Not yet supported#

These fail before a source parse or write; they are not silently disabled:

  • JavaScript/TypeScript config modules passed to the direct native command.
  • Alternate reporters and nested per-directory config discovery.

The direct native command accepts explicit source files and JSON output; the npm toolchain adds directory/glob arguments, combined default/JSON reporting, and ordinary-file delegation. Limitations tracks every remaining boundary.

Format configuration#

oxc-tsrx-fmt searches upward for one .oxfmtrc.json or .oxfmtrc.jsonc. Pass an arbitrary JSON/JSONC config with --config or -c. One FormatSession resolves the base options, overrides, and ignore matcher and reuses them for stdin or every explicit file.

Supported JS/TSX options are:

  • useTabs, tabWidth, endOfLine, and printWidth;
  • singleQuote, jsxSingleQuote, and quoteProps;
  • trailingComma, semi, and arrowParens;
  • bracketSpacing, bracketSameLine, and objectWrap;
  • singleAttributePerLine and htmlWhitespaceSensitivity; and
  • insertFinalNewline.

overrides, override excludeFiles, and ignorePatterns are supported. Ordinary JS/TSX takes the canonical direct Oxfmt path with the same resolved options. A .tsrx file uses the same options: the tool formats a temporary TSX view of the file, then restores the TSRX syntax and verifies the result. Transactional multi-file writes and raw <style> payload byte preservation behave exactly as they do without a config file.

{
  "singleQuote": true,
  "semi": false,
  "printWidth": 100,
  "overrides": [
    {
      "files": ["**/*.tsrx"],
      "options": { "singleAttributePerLine": true }
    }
  ],
  "ignorePatterns": ["generated/**"]
}

In the demo below, the format configuration shown above is the discovered .oxfmtrc.json and also config/format.json; both sample files still use double quotes, so --check lists them. In the stdin output, the @{ } statement container and the ; before <section> are intentional TSRX syntax, not formatter damage.

See it run
# Both sample files differ from the configured single-quote layout
oxc-tsrx-fmt --check src/View.tsrx src/View.tsx
Checking formatting...

src/View.tsrx (0ms)
src/View.tsx (0ms)

Format issues found in above 2 files. Run without `--check` to fix.
Finished in 0ms on 2 files using 18 threads.


# Rewrite one file with the explicit config; no file list means success
oxc-tsrx-fmt --write --config config/format.json src/View.tsrx
Finished in 0ms on 1 files using 18 threads.


# Stdin mode prints the formatted source, single quotes and all
oxc-tsrx-fmt --stdin-filepath=src/View.tsrx < src/View.tsrx
/// <reference path="./jsx.d.ts" />
import { loadItems } from './service.tsrx'

export function View({ label }: { label: string }) @{
  const version: number = '0.1.0'
  console.log('render', label)
  debugger
  loadItems()

  ;<section class="view">
    <h2>{label}</h2>
    <p>v{version}</p>
  </section>
}
Real output, captured at build time by running the release binaries against the sample project described above.

Not supported#

Enabled sortImports, sortTailwindcss, jsdoc, embeddedLanguageFormatting, experimental formatter options, unknown TSRX-affecting keys, .editorconfig, and JavaScript/TypeScript config modules fail before output or writes. Oxfmt options that affect only non-JS languages, such as package-JSON or prose formatting, do not change .tsrx output. Raw CSS is preserved, not formatted or validated.

Performance evidence#

The retained release reports pin dedicated configuration invariants: one config load per session, unchanged parse counts and thresholds, and one tsgolint process per eligible type-aware batch. Benchmarks has the methodology, the full gate matrix, and the aggregate-selected reports.