Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [member-ordering] add option to sort case insensitive #3896

81 changes: 74 additions & 7 deletions packages/eslint-plugin/docs/rules/member-ordering.md
Expand Up @@ -26,10 +26,10 @@ These options allow to specify how to group the members and sort their groups.
type TypeOptions<T> =
| {
memberTypes: Array<T> | 'never',
order?: 'alphabetically' | 'as-written',
order?: 'alphabetically' | 'alphabetically-ci' | 'as-written',
}
| {
order: 'alphabetically',
order: 'alphabetically' | 'alphabetically-ci' | 'as-written',
};

{
Expand Down Expand Up @@ -797,21 +797,21 @@ It is possible to sort all members within a group alphabetically.

#### Configuration: `{ "default": { "memberTypes": <Default Order>, "order": "alphabetically" } }`

This will apply the default order (see above) and enforce an alphabetic order within each group.
This will apply the default order (see above) and enforce an alphabetic case-sensitive order within each group.

##### Incorrect examples

```ts
interface Foo {
B: x;
a: x;
b: x;
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
c: x;

new (): Bar;
(): Baz;

B(): void;
a(): void;
b(): void;
c(): void;

// Wrong group order, should be placed before all field definitions
Expand All @@ -823,16 +823,16 @@ interface Foo {
interface Foo {
[a: string]: number;

B: x;
a: x;
b: x;
c: x;

new (): Bar;
(): Baz;

// Wrong alphabetic order within group
c(): void;
b(): void;
B(): void;
a(): void;
}
```
Expand All @@ -858,6 +858,73 @@ interface Foo {

Note: Wrong alphabetic order `b(): void` should come after `a: b`.

### Sorting alphabetically case-insensitive within member groups

It is possible to sort all members within a group alphabetically with case insensitivity.

#### Configuration: `{ "default": { "memberTypes": <Default Order>, "order": "alphabetically-ci" } }`

This will apply the default order (see above) and enforce an alphabetic case-insensitive order within each group.

##### Incorrect examples

```ts
interface Foo {
a: x;
B: x;
c: x;

new (): Bar;
(): Baz;

a(): void;
b(): void;
C(): void;

// Wrong group order, should be placed before all field definitions
[a: string]: number;
}
```

```ts
interface Foo {
[a: string]: number;

a: x;
B: x;
c: x;

new (): Bar;
(): Baz;

// Wrong alphabetic order within group
C(): void;
b(): void;
a(): void;
}
```

### Sorting alphabetically case-insensitive while ignoring member groups

It is also possible to sort all members with case insensitivity and ignore the member groups completely.

#### Configuration: `{ "default": { "memberTypes": "never", "order": "alphabetically-ci" } }`

##### Incorrect example

```ts
interface Foo {
B(): void;
a: number;

[a: string]: number; // Order doesn't matter (no sortable identifier)
new (): Bar; // Order doesn't matter (no sortable identifier)
(): Baz; // Order doesn't matter (no sortable identifier)
}
```

Note: Wrong alphabetic order `B(): void` should come after `a: number`.

## When Not To Use It

If you don't care about the general structure of your classes and interfaces, then you will not need this rule.
Expand Down
34 changes: 25 additions & 9 deletions packages/eslint-plugin/src/rules/member-ordering.ts
Expand Up @@ -8,9 +8,11 @@ import * as util from '../util';

export type MessageIds = 'incorrectGroupOrder' | 'incorrectOrder';

type Order = 'alphabetically' | 'alphabetically-ci' | 'as-written';
VincentRoth marked this conversation as resolved.
Show resolved Hide resolved

interface SortedOrderConfig {
memberTypes?: string[] | 'never';
order: 'alphabetically' | 'as-written';
order: Order;
}

type OrderConfig = string[] | SortedOrderConfig | 'never';
Expand Down Expand Up @@ -46,7 +48,7 @@ const objectConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({
},
order: {
type: 'string',
enum: ['alphabetically', 'as-written'],
enum: ['alphabetically', 'alphabetically-ci', 'as-written'],
},
},
additionalProperties: false,
Expand Down Expand Up @@ -468,10 +470,14 @@ export default util.createRule<Options, MessageIds>({
* Checks if the members are alphabetically sorted.
*
* @param members Members to be validated.
* @param caseSensitive indicates if the alpha ordering is case sensitive or not.
*
* @return True if all members are correctly sorted.
*/
function checkAlphaSort(members: Member[]): boolean {
function checkAlphaSort(
members: Member[],
caseSensitive: boolean,
): boolean {
let previousName = '';
let isCorrectlySorted = true;

Expand All @@ -481,7 +487,11 @@ export default util.createRule<Options, MessageIds>({

// Note: Not all members have names
if (name) {
if (name < previousName) {
if (
caseSensitive
? name < previousName
: name.toLowerCase() < previousName.toLowerCase()
) {
context.report({
node: member,
messageId: 'incorrectOrder',
Expand Down Expand Up @@ -518,7 +528,7 @@ export default util.createRule<Options, MessageIds>({
}

// Standardize config
let order = null;
let order: Order | null = null;
let memberTypes;

if (Array.isArray(orderConfig)) {
Expand All @@ -528,6 +538,9 @@ export default util.createRule<Options, MessageIds>({
memberTypes = orderConfig.memberTypes;
}

const hasAlphaSort = order?.startsWith('alphabetically');
const alphaSortIsCaseSensitive = order !== 'alphabetically-ci';

// Check order
if (Array.isArray(memberTypes)) {
const grouped = checkGroupSort(members, memberTypes, supportsModifiers);
Expand All @@ -536,11 +549,14 @@ export default util.createRule<Options, MessageIds>({
return;
}

if (order === 'alphabetically') {
grouped.some(groupMember => !checkAlphaSort(groupMember));
if (hasAlphaSort) {
grouped.some(
groupMember =>
!checkAlphaSort(groupMember, alphaSortIsCaseSensitive),
);
}
} else if (order === 'alphabetically') {
checkAlphaSort(members);
} else if (hasAlphaSort) {
checkAlphaSort(members, alphaSortIsCaseSensitive);
}
}

Expand Down