-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add support for sorting string enums
Fixes #50
- Loading branch information
Showing
5 changed files
with
232 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# TypeScript String Enum Sorting (sort/string-enums) | ||
|
||
🔧 The `--fix` option on the command line can automatically fix the problems | ||
reported by this rule. | ||
|
||
Sorts TypeScript string enums alphabetically and case insensitive in ascending | ||
order. | ||
|
||
## Rule Details | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```typescript | ||
enum Fruit { | ||
Orange = "orange", | ||
Apple = "apple", | ||
Grape = "grape", | ||
} | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```typescript | ||
enum Fruit { | ||
Apple = "apple", | ||
Grape = "grape", | ||
Orange = "orange", | ||
} | ||
``` | ||
|
||
## Options | ||
|
||
This rule has an options object with the following defaults. | ||
|
||
```json | ||
{ | ||
"sort/string-enums": ["error", { "caseSensitive": false, "natural": true }] | ||
} | ||
``` | ||
|
||
### `caseSensitive` | ||
|
||
If `true`, enforce exports to be in case-sensitive order. | ||
|
||
### `natural` | ||
|
||
If `true`, enforce imports to be in natural order. Natural order compares | ||
strings containing combination of letters and numbers in the way a human being | ||
would sort. For example, `A10` would come after `A3` when using natural | ||
ordering. | ||
|
||
## When Not To Use It | ||
|
||
This rule is a formatting preference and not following it won't negatively | ||
affect the quality of your code. If alphabetizing string enums isn't a part of | ||
your coding standards, then you can leave this rule off. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { TSESLint } from "@typescript-eslint/experimental-utils" | ||
import rule from "../rules/string-enums.js" | ||
import { createTsRuleTester } from "../test-utils.js" | ||
|
||
const ruleTester = createTsRuleTester() | ||
|
||
const createValidCodeVariants = ( | ||
code: string | ||
): TSESLint.RunTests< | ||
"unsorted", | ||
[{ caseSensitive?: boolean; natural?: boolean }] | ||
>["valid"] => [ | ||
{ code, options: [{ caseSensitive: false, natural: false }] }, | ||
{ code, options: [{ caseSensitive: true, natural: false }] }, | ||
{ code, options: [{ caseSensitive: false, natural: true }] }, | ||
{ code, options: [{ caseSensitive: true, natural: true }] }, | ||
] | ||
|
||
ruleTester.run("sort/string-enums", rule, { | ||
valid: [ | ||
...createValidCodeVariants("enum Foo {A='a'}"), | ||
...createValidCodeVariants("enum Foo {a='a', b='b'}"), | ||
...createValidCodeVariants("enum Foo {_='_', a='a', b='b'}"), | ||
|
||
// Ignores mixed types | ||
...createValidCodeVariants("enum Foo {b='b', a='a', c=1}"), | ||
|
||
// Options | ||
{ | ||
code: "enum Foo {a1='a1', A1='A1', a12='a12', a2='a2', B2='B2'}", | ||
options: [{ caseSensitive: false, natural: false }], | ||
}, | ||
{ | ||
code: "enum Foo {A1='A1', B1='B1', a1='a1', a12='a12', a2='a2'}", | ||
options: [{ caseSensitive: true, natural: false }], | ||
}, | ||
{ | ||
code: "enum Foo {a1='a1', A1='A1', a2='a2', a12='a12', B2='B2'}", | ||
options: [{ caseSensitive: false, natural: true }], | ||
}, | ||
{ | ||
code: "enum Foo {A1='A1', B2='B2', a1='a1', a2='a2', a12='a12'}", | ||
options: [{ caseSensitive: true, natural: true }], | ||
}, | ||
], | ||
invalid: [ | ||
{ | ||
code: "enum Foo {b='b', a='a'}", | ||
output: "enum Foo {a='a', b='b'}", | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
{ | ||
code: "enum Foo {b='b', a='a', c='c'}", | ||
output: "enum Foo {a='a', b='b', c='c'}", | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
{ | ||
code: "enum Foo {b='b', _='_', c='c'}", | ||
output: "enum Foo {_='_', b='b', c='c'}", | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
|
||
// Options | ||
{ | ||
code: "enum Foo {a12='a12', B2='B2', a1='a1', a2='a2'}", | ||
output: "enum Foo {a1='a1', a12='a12', a2='a2', B2='B2'}", | ||
options: [{ caseSensitive: false, natural: false }], | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
{ | ||
code: "enum Foo {a1='a1', B2='B2', a2='a2', a12='a12'}", | ||
output: "enum Foo {B2='B2', a1='a1', a12='a12', a2='a2'}", | ||
options: [{ caseSensitive: true, natural: false }], | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
{ | ||
code: "enum Foo {a2='a2', a1='a1', a12='a12', B2='B2'}", | ||
output: "enum Foo {a1='a1', a2='a2', a12='a12', B2='B2'}", | ||
options: [{ caseSensitive: false, natural: true }], | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
{ | ||
code: "enum Foo {a12='a12', a2='a2', B2='B2', a1='a1'}", | ||
output: "enum Foo {B2='B2', a1='a1', a2='a2', a12='a12'}", | ||
options: [{ caseSensitive: true, natural: true }], | ||
errors: [{ messageId: "unsorted" }], | ||
}, | ||
], | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { | ||
ESLintUtils, | ||
TSESLint, | ||
TSESTree, | ||
} from "@typescript-eslint/experimental-utils" | ||
import { getNodeText } from "../ts-utils.js" | ||
import { docsURL, enumerate, getSorter, isUnsorted } from "../utils.js" | ||
|
||
function getSortValue(node: TSESTree.TSEnumMember) { | ||
return node.initializer?.type === TSESTree.AST_NODE_TYPES.Literal && | ||
typeof node.initializer.value === "string" | ||
? node.initializer.value | ||
: null | ||
} | ||
|
||
export default ESLintUtils.RuleCreator.withoutDocs< | ||
[{ caseSensitive?: boolean; natural?: boolean }], | ||
"unsorted" | ||
>({ | ||
create(context) { | ||
const source = context.getSourceCode() | ||
const options = context.options[0] | ||
const sorter = getSorter(options) | ||
|
||
return { | ||
TSEnumDeclaration(node) { | ||
const nodes = node.members | ||
|
||
// If there are one or fewer properties, there is nothing to sort | ||
if (nodes.length < 2) return | ||
|
||
// Ignore mixed type enums | ||
if (nodes.map(getSortValue).some((value) => value === null)) return | ||
|
||
const sorted = nodes | ||
.slice() | ||
.sort((a, b) => sorter(getSortValue(a) ?? "", getSortValue(b) ?? "")) | ||
|
||
const firstUnsortedNode = isUnsorted(nodes, sorted) | ||
if (firstUnsortedNode) { | ||
context.report({ | ||
node: firstUnsortedNode, | ||
messageId: "unsorted", | ||
*fix(fixer) { | ||
for (const [node, complement] of enumerate(nodes, sorted)) { | ||
yield fixer.replaceText(node, getNodeText(source, complement)) | ||
} | ||
}, | ||
}) | ||
} | ||
}, | ||
} | ||
}, | ||
meta: { | ||
docs: { | ||
recommended: false, | ||
url: docsURL("string-enums"), | ||
description: `Sorts TypeScript string enums alphabetically and case insensitive in ascending order.`, | ||
}, | ||
fixable: "code", | ||
messages: { | ||
unsorted: "String enums should be sorted alphabetically.", | ||
}, | ||
schema: [ | ||
{ | ||
additionalProperties: false, | ||
default: { caseSensitive: false, natural: true }, | ||
properties: { | ||
caseSensitive: { | ||
type: "boolean", | ||
default: false, | ||
}, | ||
natural: { | ||
type: "boolean", | ||
default: true, | ||
}, | ||
}, | ||
type: "object", | ||
}, | ||
], | ||
type: "suggestion", | ||
}, | ||
defaultOptions: [{}], | ||
}) as TSESLint.RuleModule<string, unknown[]> |