eslint-plugin-simple-sort-keys
v1.0.2
Published
ESLint plugin to sort object keys with autofix
Maintainers
Readme
eslint-plugin-simple-sort-keys
Sort object keys alphabetically — with autofix.
The rule logic is based on ESLint's built-in sort-keys, extended with an autofix that reorders properties in one shot, and a plugin structure that mirrors eslint-plugin-simple-import-sort.
Why this plugin?
sort-keys only reports violations; it cannot fix them. This plugin adds the missing autofix so you can enforce sorted keys and let the linter clean them up automatically, the same way eslint-plugin-simple-import-sort does for imports.
Installation
npm install --save-dev eslint-plugin-simple-sort-keysPeer dependency:
eslint≥ 9 (flat config required). Node.js: ≥ 18.18.0
Usage
Flat config (eslint.config.js)
import simpleKeySort from 'eslint-plugin-simple-sort-keys'
export default [
{
plugins: {
'simple-sort-keys': simpleKeySort,
},
rules: {
'simple-sort-keys/sort': 'error',
},
},
]With options
'simple-sort-keys/sort': [
'error',
'asc',
{
allowLineSeparatedGroups: false,
caseSensitive: true,
ignoreComputedKeys: false,
minKeys: 2,
natural: false,
},
]Recommended config
The plugin ships a recommended config that enables the rule as a warning:
import simpleKeySort from 'eslint-plugin-simple-sort-keys'
export default [
simpleKeySort.configs.recommended,
]This is equivalent to:
rules: { 'simple-sort-keys/sort': 'warn' }Rule: simple-sort-keys/sort
Requires all object keys to be sorted. Reports a single error per unsorted group and provides an autofix that sorts the entire group at once.
Note: Only object expressions (
{ key: value }) are affected. Destructuring patterns (const { b, a } = obj) are intentionally ignored.
Options
Argument 1 — order
"asc" (default) | "desc"
Direction of the sort.
// ✓ "asc"
const a = { bar: 2, foo: 1 }
// ✓ "desc"
const b = { foo: 1, bar: 2 }Argument 2 — options object
| Option | Type | Default | Description |
|---|---|---|---|
| allowLineSeparatedGroups | boolean | false | When true, a blank line between properties starts a new independent sort group. Keys in different groups are not compared with each other. |
| caseSensitive | boolean | true | When true, uppercase letters sort before lowercase (A < a). When false, case is ignored during comparison. |
| ignoreComputedKeys | boolean | false | When true, any computed property (e.g. { [expr]: 1 } or { ["foo"]: 1 }) acts as a group separator and is excluded from sorting. When false (default), dynamic computed keys are skipped during comparison and left in place; computed keys with a static literal or template-literal value (e.g. { ["foo"]: 1 }, { [`foo`]: 1 }) are treated as regular keys and sorted. |
| minKeys | integer (≥ 2) | 2 | Minimum number of keys in an object before the rule is enforced. |
| natural | boolean | false | When true, uses natural sort order so that digit sequences are compared numerically (item2 before item10). |
Examples
allowLineSeparatedGroups
/* eslint simple-sort-keys/sort: ["error", "asc", { allowLineSeparatedGroups: true }] */
// ✓ each blank-line group is sorted independently
const config = {
alpha: 1,
beta: 2,
x: 10,
y: 20,
}
// ✗ within a group, keys must still be sorted
const config = {
beta: 2,
alpha: 1, // ← Expected object keys to be sorted. 'alpha' should not follow 'beta'.
}caseSensitive
/* eslint simple-sort-keys/sort: ["error", "asc", { caseSensitive: true }] */
// ✓ uppercase before lowercase (Unicode order)
const a = { Bar: 1, foo: 2 }
/* eslint simple-sort-keys/sort: ["error", "asc", { caseSensitive: false }] */
// ✓ case is ignored, so "bar" and "Bar" are equivalent
const b = { Bar: 1, foo: 2 }
const c = { bar: 1, Foo: 2 }ignoreComputedKeys
/* eslint simple-sort-keys/sort: ["error", "asc", { ignoreComputedKeys: true }] */
const KEY = 'dynamic'
// ✓ the dynamic computed key acts as a group separator; each segment is sorted independently
const a = {
alpha: 1,
beta: 2,
[KEY]: 3, // ← group separator — not included in any sorted group
x: 4,
y: 5,
}
// ✓ both properties are computed, so both act as separators — no group is formed, no violation
const b = { ["foo"]: 1, ["bar"]: 2 }With the default (ignoreComputedKeys: false), computed keys with static literal or no-expression template-literal values are treated as regular keys and sorted; truly dynamic computed keys are skipped during comparison and left in place:
// ✓ [getKey()] is skipped; a and b are compared with each other
const c = { [getKey()]: 1, a: 2, b: 3 }
// ✓ computed keys with a literal value are sorted like regular keys
const d = { ["bar"]: 1, ["foo"]: 2 }
// ✓ no-expression template literal keys are also sortable
const e = { [`bar`]: 1, [`foo`]: 2 }minKeys
/* eslint simple-sort-keys/sort: ["error", "asc", { minKeys: 3 }] */
// ✓ only 2 keys — below the threshold
const a = { b: 2, a: 1 }
// ✗ 3 keys — rule is active
const b = { c: 3, a: 1, b: 2 }natural
/* eslint simple-sort-keys/sort: ["error", "asc", { natural: true }] */
// ✓ natural order treats digit sequences as numbers
const a = {
item1: true,
item2: true,
item10: true, // after item2, not after item1
}
/* eslint simple-sort-keys/sort: ["error", "asc", { natural: false }] */
// ✓ lexicographic order
const b = {
item1: true,
item10: true, // "10" < "2" lexicographically
item2: true,
}Numeric keys: Numeric literal keys (e.g.
{ 1: 'a', 2: 'b', 10: 'c' }) are compared as strings by default, so"10"sorts before"2". Usenatural: trueto compare them numerically.
Spread elements
Spread elements (...obj) always act as group separators. Keys before and after a spread are sorted independently so that spreading semantics are never broken.
// ✓
const merged = {
alpha: 1,
beta: 2,
...defaults,
x: 10,
y: 20,
}Autofix
When the rule reports a violation, running eslint --fix (or the editor quick-fix action) reorders all keys in the unsorted group in a single operation.
The fixer preserves surrounding context exactly:
- Leading comments (lines above a key) travel with the key they annotate.
- Inline trailing comments (on the same line as the value) travel with their key.
- Trailing commas move with the property. If a property that had no trailing comma ends up in a non-last position, a comma is inserted automatically.
// Before fix
const options = {
// network
timeout: 5000,
method: 'GET', // ← Expected object keys to be sorted. 'method' should not follow 'timeout'.
baseUrl: 'https://api.example.com', // base
}
// After fix
const options = {
baseUrl: 'https://api.example.com', // base
method: 'GET',
// network
timeout: 5000,
}Comparison with sort-keys
| Feature | sort-keys | simple-sort-keys/sort |
|---|---|---|
| Autofix | No | Yes |
| Spread separators | Yes | Yes |
| allowLineSeparatedGroups | Yes | Yes |
| caseSensitive | Yes | Yes |
| ignoreComputedKeys | Yes | Yes |
| minKeys | Yes | Yes |
| natural | Yes | Yes |
| Error per key | Yes (one per violation) | No (one per group) |
Development
npm testTests use Node.js's built-in test runner (node --test) with ESLint's RuleTester.
License
MIT © Joaquin Aguirre
