eslint-plugin-oxfmt
An ESLint plugin for formatting code with oxfmt - A blazing fast formatter powered by Rust.
Maintained in the eslint-plugin-oxfmt monorepo. See the root README for development and release instructions.
Features
- ️ Blazing Fast - Powered by oxfmt, written in Rust
- Auto-fix - Automatically format code on save or via ESLint's fix command
- ESLint Integration - Seamlessly integrates with ESLint v9+ flat config
- Zero Config - Works out of the box with sensible defaults
- Config File Discovery - Auto-discovers
.oxfmtrc.json,.oxfmtrc.jsonc, andoxfmt.config.{ts,mts} - EditorConfig Integration - Respects a subset of
.editorconfigoptions via oxfmt's strategy - Highly Configurable - Supports all oxfmt formatting options
- Multi-language Support - JavaScript, TypeScript, JSX, TSX and more
Requirements
- ESLint:
^9.5.0 || ^10.0.0(Only supports ESLint flat config) - Node.js:
^22.13.0 || >=24 - oxfmt:
>= 0.71.0
Upgrading from oxfmt 0.70.0 to 0.71.0 preserves comment order around test-call
arguments and comments on either side of assignment operators. It also fixes
adjacent block comments, trailing spaces in block comments, and JSDoc hard
breaks, including /*** comments. The bundled Prettier upgrade from 3.9.6 to
3.9.9 fixes embedded template indentation and Markdown handling of single
tildes, dollar signs, Liquid syntax, blockquotes, and list indentation. Existing
files may receive formatting fixes after upgrading. No formatting options were
added, renamed, or removed, and config merge precedence is unchanged.
The upstream native Markdown formatter is not yet used by the published formatting API; Markdown still uses Prettier, including frontmatter formatting. The upstream fix for repeated CLI calls in one process does not affect this plugin's worker, which uses the formatting API. See the upstream 0.71.0 release notes.
The monorepo migration aligns the Node.js requirement with load-oxfmt-config; Node.js 20 is no longer supported.
Installation
npm install -D oxfmt eslint-plugin-oxfmt
yarn add -D oxfmt eslint-plugin-oxfmt
pnpm add -D oxfmt eslint-plugin-oxfmt
Usage
Quick Start
Add the plugin to your ESLint flat config file:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx}'],
},
]
recommended includes eslint-parser-plain as languageOptions.parser for convenience.
If you need parser-agnostic composition (for example to keep jsonc-eslint-parser from another config), use the dedicated preset:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommendedWithoutParser,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx,json,jsonc}'],
},
]
For CLI-like ignore/config behavior, use cliParity:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.cliParity,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx,svelte,json,jsonc,yaml,yml}'],
},
]
cliParity is parser-agnostic. For non-JS extensions (for example svelte, json, yaml), also configure a suitable languageOptions.parser (such as eslint-parser-plain) in that config block.
Custom Configuration
You can customize the formatting options by configuring the rule:
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx}'],
rules: {
'oxfmt/oxfmt': [
'error',
{
// Plugin options
useConfig: false,
configPath: 'configs/.oxfmtrc.json',
// Formatting options
semi: false,
singleQuote: true,
tabWidth: 2,
useTabs: false,
trailingComma: 'all',
printWidth: 100,
arrowParens: 'avoid',
// File handling
ignorePatterns: ['**/dist/**', '**/.next/**'],
// JSX specific options
jsxSingleQuote: false,
bracketSameLine: false,
singleAttributePerLine: false,
// Object formatting
bracketSpacing: true,
quoteProps: 'as-needed',
objectWrap: 'preserve',
// Line endings
endOfLine: 'lf',
insertFinalNewline: true,
// Prose / HTML
embeddedLanguageFormatting: 'auto',
htmlWhitespaceSensitivity: 'css',
proseWrap: 'preserve',
// JSDoc
jsdoc: {
commentLineStrategy: 'singleLine',
lineWrappingStyle: 'greedy',
separateTagGroups: false,
},
// Vue
vueIndentScriptAndStyle: false,
// Advanced
experimentalOperatorPosition: 'end',
sortImports: {
order: 'asc',
newlinesBetween: true,
},
sortPackageJson: { sortScripts: true },
sortTailwindcss: {
attributes: ['class', 'className', ':class'],
functions: ['clsx', 'cn'],
preserveDuplicates: false,
preserveWhitespace: false,
},
},
],
},
},
]
Configuration Options
All options are optional and default to sensible values.
Plugin Options
| Option | Type | Default | Description |
|---|---|---|---|
useConfig |
boolean |
true |
Load .oxfmtrc.json, .oxfmtrc.jsonc, or oxfmt.config.{ts,mts,cts,js,mjs,cjs} via load-oxfmt-config. Set to false to rely only on inline options. |
configPath |
string |
— | Custom path to an oxfmt config file. Relative paths resolve from ESLint cwd; absolute paths are used as-is. Explicit configPath accepts .json, .jsonc, .ts, .mts, .cts, .js, .mjs, .cjs. |
editorconfig |
boolean | { onlyCwd?: boolean; cwd?: string } |
true |
Control .editorconfig loading. Use false to disable, or object form for advanced resolution strategy. |
ignorePath |
string | string[] |
— | Ignore file path(s) for CLI-style ignore resolution (same role as CLI --ignore-path). |
withNodeModules |
boolean |
false |
Include files under node_modules during ignore checks. |
disableNestedConfig |
boolean |
false |
Disable nested config discovery and resolve config from cwd / configPath only. |
respectOxfmtDefaultIgnores |
boolean |
true |
Respect oxfmt default ignores (.gitignore, .prettierignore, default ignored directories, default ignored lockfiles). |
Note:
cwdis taken from ESLint automatically; you usually do not need to set it manually..editorconfigmerge behavior follows oxfmt's documented strategy: https://oxc.rs/docs/guide/usage/formatter/config#editorconfig
Config Discovery & Precedence
When useConfig is true, the plugin loads config using
loadOxfmtConfigForFile() from load-oxfmt-config. EditorConfig sections are
matched relative to their own directory, including when the oxfmt config is in
a nested package. Matching sections apply in source order, and basename patterns
such as [*.js] also match files in descendant directories.
- Config discovery order (from the formatted file's directory, walking upward):
.oxfmtrc.json→.oxfmtrc.jsonc→oxfmt.config.ts→oxfmt.config.mts .editorconfigsupport follows oxfmt behavior: only the nearest.editorconfigis loaded for the current file, and its root and section values provide fallbacks for fields not set by the oxfmt config- Set
editorconfig: falseto disable.editorconfigmerging - Set
editorconfig: { onlyCwd: true }to read only the currentcwd's.editorconfig(no upward traversal) - Set
editorconfig: { cwd: '/path/to/base' }to customize editorconfig resolution base directory configPathoverrides discovery and directly targets the specified config fileconfigPathsupports explicit file paths with extensions:.json,.jsonc,.ts,.mts,.cts,.js,.mjs,.cjs- ESLint rule options generally take highest priority because inline rule options are merged after loaded config.
- Rule-level
ignorePatternsare resolved relative to ESLintcwd; config-levelignorePatternsare resolved relative to the resolved config file directory and reject parent-directory (..) path segments. - Providing rule-level
ignorePatternsreplaces config-level patterns, including[]to clear them. Default ignores still apply independently. respectOxfmtDefaultIgnores: falsedisables default directories, lockfiles, and ignore files, while retaining config-levelignorePatternswhenuseConfigis enabled.- When
useConfigistrue, configoverridesare applied first and rule-leveloverridesare appended after them (later entries win on conflicts). - Invalid
filesorexcludeFilesoverride glob patterns are reported as formatting errors. - When
useConfigisfalse, config discovery and configignorePatternsare skipped, while global ignores still apply whenrespectOxfmtDefaultIgnoresis enabled.
For detailed behavior, see:
Basic Options
| Option | Type | Default | Description |
|---|---|---|---|
semi |
boolean |
true |
Add semicolons at the end of statements |
singleQuote |
boolean |
false |
Use single quotes instead of double quotes |
tabWidth |
number |
2 |
Number of spaces per indentation level |
useTabs |
boolean |
false |
Use tabs for indentation |
printWidth |
number |
100 |
Maximum line length for wrapping |
ignorePatterns |
string[] |
[] |
Glob patterns to skip formatting. Rule option patterns are resolved from ESLint cwd; config file patterns are resolved from the config file directory and cannot contain .. path segments. |
Trailing Commas
| Option | Type | Default | Description |
|---|---|---|---|
trailingComma |
'all' | 'es5' | 'none' |
'all' |
Where to add trailing commas |
Arrow Functions
| Option | Type | Default | Description |
|---|---|---|---|
arrowParens |
'always' | 'avoid' |
'always' |
Include parentheses around sole arrow function parameter |
JSX Options
| Option | Type | Default | Description |
|---|---|---|---|
jsxSingleQuote |
boolean |
false |
Use single quotes in JSX attributes |
bracketSameLine |
boolean |
false |
Put > on the same line in JSX |
singleAttributePerLine |
boolean |
false |
Force single attribute per line in JSX |
Vue SFC Options
| Option | Type | Default | Description |
|---|---|---|---|
vueIndentScriptAndStyle |
boolean |
false |
Indent code inside <script> / <style> in Vue |
Object Formatting
| Option | Type | Default | Description |
|---|---|---|---|
bracketSpacing |
boolean |
true |
Print spaces between brackets in object literals |
quoteProps |
'as-needed' | 'consistent' | 'preserve' |
'as-needed' |
When to quote object property names |
objectWrap |
'preserve' | 'collapse' |
'preserve' |
How to wrap object literals |
Line Endings
| Option | Type | Default | Description |
|---|---|---|---|
endOfLine |
'lf' | 'crlf' | 'cr' |
'lf' |
Line ending character(s) |
insertFinalNewline |
boolean |
true |
Insert a newline at the end of files |
Prose / HTML
| Option | Type | Default | Description |
|---|---|---|---|
embeddedLanguageFormatting |
'auto' | 'off' |
'auto' |
Format embedded code blocks (e.g., JS-in-Vue, CSS-in-JS) |
htmlWhitespaceSensitivity |
'css' | 'ignore' | 'strict' |
'css' |
Global whitespace sensitivity for HTML-like languages |
proseWrap |
'always' | 'never' | 'preserve' |
'preserve' |
Control prose wrapping in Markdown/MDX |
JSDoc Options
Use jsdoc to enable and configure JSDoc comment formatting. Pass true to enable defaults, or pass an object for custom behavior (for example jsdoc: {}).
| Option | Type | Default | Description |
|---|---|---|---|
jsdoc.commentLineStrategy |
'singleLine' | 'multiline' | 'keep' |
'singleLine' |
Comment block style policy |
jsdoc.lineWrappingStyle |
'greedy' | 'balance' |
'greedy' |
Wrapping strategy for long descriptions |
jsdoc.addDefaultToDescription |
boolean |
true |
Append default values to @param descriptions |
jsdoc.bracketSpacing |
boolean |
false |
Add spaces inside JSDoc type braces |
jsdoc.capitalizeDescriptions |
boolean |
true |
Capitalize the first letter of tag descriptions |
jsdoc.descriptionTag |
boolean |
false |
Emit @description tag instead of inline description |
jsdoc.descriptionWithDot |
boolean |
false |
Add a trailing dot to the end of descriptions |
jsdoc.keepUnparsableExampleIndent |
boolean |
false |
Preserve indentation in unparsable @example code |
jsdoc.preferCodeFences |
boolean |
false |
Prefer fenced code blocks over 4-space indentation |
jsdoc.separateReturnsFromParam |
boolean |
false |
Add a blank line between final @param and @returns |
jsdoc.separateTagGroups |
boolean |
false |
Add blank lines between different tag groups |
Tip: jsdoc: true is equivalent to enabling JSDoc with default settings.
Advanced Options
| Option | Type | Default | Description |
|---|---|---|---|
sortImports |
boolean | object |
disabled | Experimental import sorting configuration |
sortPackageJson |
boolean | object |
true |
Experimental package.json sorting (object form: { sortScripts?: boolean }) |
sortTailwindcss |
boolean | object |
disabled | Experimental Tailwind CSS class sorting (enable with {} for defaults) |
svelte |
boolean | object |
disabled | Enable/configure .svelte formatting via prettier-plugin-svelte |
Import sorting (sortImports)
Use sortImports: true to enable import sorting with defaults, or pass an object to customize behavior.
Available keys:
customGroups: Ordered custom group definitions{ elementNamePattern?: string[]; groupName?: string; modifiers?: string[]; selector?: string }[]groups: Ordered group list; supports nested arrays and boundary markers like{ newlinesBetween: boolean }ignoreCase: Ignore case when sorting (defaulttrue)internalPattern: Glob patterns for internal importsnewlinesBetween: Insert blank lines between groups (defaulttrue)order:'asc' | 'desc'(default'asc')partitionByComment: Split groups by comments (defaultfalse)partitionByNewline: Split groups by blank lines (defaultfalse)sortSideEffects: Sort side-effect imports (defaultfalse)
package.json sorting (sortPackageJson)
- Boolean to toggle (default
true). - Object form:
{ sortScripts?: boolean }(defaultfalse).
Tailwind CSS class sorting
Enable experimental Tailwind CSS class sorting powered by prettier-plugin-tailwindcss (set sortTailwindcss: true for defaults, or pass an object for custom options):
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,jsx,tsx}'],
rules: {
'oxfmt/oxfmt': [
'error',
{
sortTailwindcss: {},
},
],
},
},
]
You can pass the Tailwind plugin options to control which attributes/functions are sorted or keep duplicates:
{
"sortTailwindcss": {
"attributes": ["class", "className", ":class"],
"config": "./tailwind.config.js",
"functions": ["clsx", "cn"],
"preserveDuplicates": false,
"preserveWhitespace": false,
"stylesheet": "./src/app.css"
}
}
Paths in sortTailwindcss.config and sortTailwindcss.stylesheet are resolved relative to the oxfmt config file when set in that file or its overrides. Paths set in ESLint rule options or rule-level overrides are resolved relative to ESLint cwd. Absolute paths are used directly.
File-specific overrides
Use overrides to apply different oxfmt options per file glob (later entries win on conflicts):
// eslint.config.mjs
import pluginOxfmt from 'eslint-plugin-oxfmt'
export default [
{
...pluginOxfmt.configs.recommended,
files: ['**/*.{js,ts,jsx,tsx}'],
rules: {
'oxfmt/oxfmt': [
'error',
{
overrides: [
{
files: ['**/*.test.ts'],
excludeFiles: ['**/fixtures/**'],
options: {
semi: false,
trailingComma: 'none',
},
},
{
files: ['packages/**/src/**/*.{ts,tsx}'],
options: {
printWidth: 90,
sortImports: {
newlinesBetween: true,
},
},
},
],
},
],
},
},
]
Rules
oxfmt/oxfmt
This plugin provides a single rule that formats your code using oxfmt.
- Recommended:
error - Fixable: Yes (automatically applies formatting)
- Type: Layout
CLI parity mode
oxfmt/cli-parity tries to match oxfmt CLI behavior for files processed by ESLint.
It respects:
.oxfmtrc.json.oxfmtrc.jsoncoxfmt.config.{ts,mts,cts,js,mjs,cjs}.editorconfigignorePatterns.gitignore- parent
.gitignore .git/info/exclude.prettierignore- default ignored directories
- default ignored lockfiles
Note: ESLint still controls file discovery. Files excluded by ESLint will never reach this rule.
Integration
Parser Compatibility
recommended: forceseslint-parser-plainfor matched filesrecommendedWithoutParser: parser-agnostic (safe to compose with language-specific parsers)cliParity: parser-agnostic preset tuned for CLI-like config/ignore behavior
When composing shareable configs, prefer recommendedWithoutParser if parser ownership belongs to another preset.
Format on Save in VS Code
Add this to your .vscode/settings.json:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
]
}
Supported languages
Why oxfmt?
oxfmt is a modern, blazing-fast formatter written in Rust as part of the Oxc project. It aims to be a drop-in replacement for Prettier with significantly better performance.
Benefits
- Performance: 50-100x faster than Prettier
- Compatibility: Designed to be Prettier-compatible
- Modern: Built with modern JavaScript/TypeScript in mind
- Maintained: Part of the actively developed Oxc project