Skip to content

ntnyq/eslint-plugin-oxfmt

Repository files navigation

eslint-plugin-oxfmt

CI NPM VERSION NPM DOWNLOADS LICENSE

An ESLint plugin for formatting code with oxfmt - A blazing fast formatter powered by Rust.

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 - Supports .oxfmtrc.json, .oxfmtrc.jsonc, and oxfmt.config.ts
  • 📝 EditorConfig Integration - Respects a subset of .editorconfig options via oxfmt's strategy
  • 🎨 Highly Configurable - Supports all oxfmt formatting options
  • 🌐 Multi-language Support - JavaScript, TypeScript, JSX, TSX and more

Requirements

  • ESLint: >= 9.0.0 (Only supports ESLint flat config)
  • Node.js: ^20.19.0 || >=22.12.0

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}'],
  },
]

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
          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 via load-oxfmt-config (with .editorconfig merge support). Set to false to rely only on inline options.
configPath string Custom path to an oxfmt config file. Resolved from ESLint cwd when set.

Note: cwd is taken from ESLint automatically; you usually do not need to set it manually. .editorconfig merge 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 load-oxfmt-config.

  • Config discovery order (from cwd, walking upward): .oxfmtrc.json.oxfmtrc.jsoncoxfmt.config.ts
  • .editorconfig support: nearest .editorconfig (including section overrides) is merged into the final options
  • configPath overrides discovery and directly targets the specified config file
  • ESLint rule options generally take highest priority because inline rule options are merged after loaded config. However, when useConfig is true, overrides are taken from the loaded config (not from rule options).

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 (relative to cwd) to skip formatting

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' | 'always' '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)

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 (default true)
  • internalPattern: Glob patterns for internal imports
  • newlinesBetween: Insert blank lines between groups (default true)
  • order: 'asc' | 'desc' (default 'asc')
  • partitionByComment: Split groups by comments (default false)
  • partitionByNewline: Split groups by blank lines (default false)
  • sortSideEffects: Sort side-effect imports (default false)

package.json sorting (sortPackageJson)

  • Boolean to toggle (default true).
  • Object form: { sortScripts?: boolean } (default false).

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"
  }
}

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

Integration

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

Related Projects

  • oxc - The Oxidation Compiler
  • oxlint - A fast linter
  • ESLint - Pluggable JavaScript linter

License

MIT License © 2025-PRESENT ntnyq

About

📦 An ESLint plugin for formatting code with oxfmt - A blazing fast formatter powered by Rust.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors