Skip to content

Commit

Permalink
feat: add natural sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
azat-io committed May 8, 2023
1 parent ba51243 commit c50d585
Show file tree
Hide file tree
Showing 17 changed files with 418 additions and 146 deletions.
22 changes: 15 additions & 7 deletions docs/rules/sort-interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: sort-interfaces

> Enforce sorted interface properties.
## Rule details
## 📖 Rule details

This rule verifies that all TypeScript interface properties are sorted sorted in order of string length.

Expand Down Expand Up @@ -42,19 +42,27 @@ interface ButtonProps {
}
```

## Options
## 🔧 Options

This rule is not configurable.
### `type`

## Usage
- `natural` (default) - sorting, which is similar to alphabetical order.
- `line-length` - sort by code line length.

### `order`

- `asc` (default) - enforce properties to be in ascending order.
- `desc` - enforce properties to be in descending order.

## 🚀 Usage

### Legacy config

```json
// .eslintrc
{
"rules": {
"perfectionist/sort-interfaces": "error"
"perfectionist/sort-interfaces": ["error", { "type": "line-length", "order": "desc" }]
}
}
```
Expand All @@ -70,12 +78,12 @@ export default {
perfectionist,
},
rules: {
'perfectionist/sort-interfaces': 'error',
'perfectionist/sort-interfaces': ['error', { type: 'line-length', order: 'desc' }],
},
}
```

## Resources
## 📚 Resources

- [Rule source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/rules/sort-interfaces.ts)
- [Test source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/test/sort-interfaces.test.ts)
22 changes: 15 additions & 7 deletions docs/rules/sort-jsx-props.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: sort-jsx-props

> Enforce sorted JSX props.
## Rule details
## 📖 Rule details

This rule verifies that JSX props are sorted sorted in order of string length.

Expand Down Expand Up @@ -44,19 +44,27 @@ let Container = () => (
)
```

## Options
## 🔧 Options

This rule is not configurable.
### `type`

## Usage
- `natural` (default) - sorting, which is similar to alphabetical order.
- `line-length` - sort by code line length.

### `order`

- `asc` (default) - enforce properties to be in ascending order.
- `desc` - enforce properties to be in descending order.

## 🚀 Usage

### Legacy config

```json
// .eslintrc
{
"rules": {
"perfectionist/sort-jsx-props": "error"
"perfectionist/sort-jsx-props": ["error", { "type": "line-length", "order": "desc" }]
}
}
```
Expand All @@ -72,12 +80,12 @@ export default {
perfectionist,
},
rules: {
'perfectionist/sort-jsx-props': 'error',
'perfectionist/sort-jsx-props': ['error', { type: 'line-length', order: 'desc' }],
},
}
```

## Resources
## 📚 Resources

- [Rule source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/rules/sort-jsx-props.ts)
- [Test source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/test/sort-jsx-props.test.ts)
42 changes: 17 additions & 25 deletions docs/rules/sort-named-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,51 +6,43 @@ title: sort-named-imports

> Enforce sorted named imports.
## Rule details
## 📖 Rule details

This rule verifies that all named imports are sorted sorted in order of string length.

### Incorrect

```ts
import {
useEffect,
useState,
useMemo,
useId,
Suspense,
useCallback,
lazy,
} from 'react'
import { useEffect, useState, useMemo, useId, Suspense, useCallback, lazy } from 'react'
```

### Correct

```ts
import {
useCallback,
useEffect,
Suspense,
useState,
useMemo,
useId,
lazy,
} from 'react'
import { useCallback, useEffect, Suspense, useState, useMemo, useId, lazy } from 'react'
```

## Options
## 🔧 Options

This rule is not configurable.
### `type`

## Usage
- `natural` (default) - sorting, which is similar to alphabetical order.
- `line-length` - sort by code line length.

### `order`

- `asc` (default) - enforce properties to be in ascending order.
- `desc` - enforce properties to be in descending order.

## 🚀 Usage

### Legacy config

```json
// .eslintrc
{
"rules": {
"perfectionist/sort-named-imports": "error"
"perfectionist/sort-named-imports": ["error", { "type": "line-length", "order": "desc" }]
}
}
```
Expand All @@ -66,12 +58,12 @@ export default {
perfectionist,
},
rules: {
'perfectionist/sort-named-imports': 'error',
'perfectionist/sort-named-imports': ['error', { type: 'line-length', order: 'desc' }],
},
}
```

## Resources
## 📚 Resources

- [Rule source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/rules/sort-named-imports.ts)
- [Test source](https://github.com/azat-io/eslint-plugin-perfectionist/blob/main/test/sort-named-imports.test.ts)
22 changes: 22 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,33 @@ import sortJsxProps, { RULE_NAME as sortJsxPropsName } from '~/rules/sort-jsx-pr
import sortNamedImports, { RULE_NAME as sortNamedImportsName } from '~/rules/sort-named-imports'
import { name } from '~/package.json'

let getRulesWithOptions = (options: {
[key: string]: unknown
}): {
[key: string]: [string, { [key: string]: unknown }]
} => {
let recommendedRules = {
[sortInterfacesName]: ['error'],
[sortJsxPropsName]: ['error'],
[sortNamedImportsName]: ['error'],
}
return Object.fromEntries(
Object.entries(recommendedRules).map(([key, [message, baseOptions]]) => [
key,
[message, Object.assign(baseOptions, options)],
]),
)
}

export default {
name,
rules: {
[sortInterfacesName]: sortInterfaces,
[sortJsxPropsName]: sortJsxProps,
[sortNamedImportsName]: sortNamedImports,
},
configs: {
'recommended-natural': getRulesWithOptions({ type: 'natural', order: 'asc' }),
'recommended-line-length': getRulesWithOptions({ type: 'line-length', order: 'desc' }),
},
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@
},
"dependencies": {
"@typescript-eslint/types": "^5.59.2",
"@typescript-eslint/utils": "^5.59.2"
"@typescript-eslint/utils": "^5.59.2",
"natural-compare-lite": "^1.4.0"
},
"devDependencies": {
"@azat-io/eslint-config-typescript": "^0.8.0",
"@commitlint/cli": "^17.6.3",
"@commitlint/config-conventional": "^17.6.3",
"@types/natural-compare-lite": "^1.4.0",
"@types/node": "^20.1.0",
"@vitest/coverage-c8": "^0.31.0",
"bumpp": "^9.1.0",
Expand Down
11 changes: 10 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 36 additions & 5 deletions rules/sort-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ import { AST_NODE_TYPES } from '@typescript-eslint/types'

import { createEslintRule } from '~/utils/create-eslint-rule'
import { rangeToDiff } from '~/utils/range-to-diff'
import { SortType, SortOrder } from '~/typings'
import { sortNodes } from '~/utils/sort-nodes'
import type { SortingNode } from '~/typings'
import { complete } from '~/utils/complete'
import { compare } from '~/utils/compare'

type MESSAGE_ID = 'unexpectedInterfacePropertiesOrder'

type Options = []
type Options = [
Partial<{
order: SortOrder
type: SortType
}>,
]

export const RULE_NAME = 'sort-interfaces'

Expand All @@ -25,11 +33,34 @@ export default createEslintRule<Options, MESSAGE_ID>({
unexpectedInterfacePropertiesOrder: 'Expected "{{second}}" to come before "{{first}}"',
},
fixable: 'code',
schema: [],
schema: [
{
type: 'object',
properties: {
type: {
enum: [SortType.natural, SortType['line-length']],
},
order: {
enum: [SortOrder.asc, SortOrder.desc],
},
},
additionalProperties: false,
},
],
},
defaultOptions: [],
defaultOptions: [
{
type: SortType.natural,
order: SortOrder.asc,
},
],
create: context => ({
TSInterfaceBody: node => {
let options = complete(context.options.at(0), {
type: SortType.natural,
order: SortOrder.asc,
})

let values: SortingNode[] = node.body.map(typeElement => {
let name = ''
let size = 0
Expand Down Expand Up @@ -107,7 +138,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
let first = values.at(firstIndex)!
let second = values.at(secondIndex)!

if (first.size < second.size) {
if (compare(first, second, options)) {
let secondNode = node.body[secondIndex]

context.report({
Expand All @@ -120,7 +151,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
fix: fixer => {
let sourceCode = context.getSourceCode()
let { text } = sourceCode
return sortNodes(fixer, text, values)
return sortNodes(fixer, text, values, options)
},
})
}
Expand Down
Loading

0 comments on commit c50d585

Please sign in to comment.