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): added member group support to member-ordering rule #4538

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/eslint-plugin/docs/rules/member-ordering.md
Expand Up @@ -292,6 +292,29 @@ The third grouping option is to ignore both scope and accessibility.
]
```

### Grouping different member types at the same rank

It is also possible to group different member types at the same rank.

```jsonc
[
// Index signature
"signature",

// Fields
"field",

// Constructors
"constructor",

// Getters and Setters at the same rank
["get", "set"],

// Methods
"method"
]
```

### Default configuration

The default configuration looks as follows:
Expand Down
49 changes: 36 additions & 13 deletions packages/eslint-plugin/src/rules/member-ordering.ts
Expand Up @@ -13,12 +13,14 @@ type Order =
| 'alphabetically-case-insensitive'
| 'as-written';

type MemberType = string | string[];

interface SortedOrderConfig {
memberTypes?: string[] | 'never';
memberTypes?: MemberType[] | 'never';
order: Order;
}

type OrderConfig = string[] | SortedOrderConfig | 'never';
type OrderConfig = MemberType[] | SortedOrderConfig | 'never';
type Member = TSESTree.ClassElement | TSESTree.TypeElement;

export type Options = [
Expand All @@ -36,14 +38,24 @@ const neverConfig: JSONSchema.JSONSchema4 = {
enum: ['never'],
};

const arrayConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({
const arrayConfig = (memberTypes: MemberType[]): JSONSchema.JSONSchema4 => ({
type: 'array',
items: {
enum: memberTypes,
oneOf: [
{
enum: memberTypes,
},
{
type: 'array',
items: {
enum: memberTypes,
},
},
],
},
});

const objectConfig = (memberTypes: string[]): JSONSchema.JSONSchema4 => ({
const objectConfig = (memberTypes: MemberType[]): JSONSchema.JSONSchema4 => ({
type: 'object',
properties: {
memberTypes: {
Expand Down Expand Up @@ -339,12 +351,20 @@ function getMemberName(
*
* @return Index of the matching member type in the order configuration.
*/
function getRankOrder(memberGroups: string[], orderConfig: string[]): number {
function getRankOrder(
memberGroups: string[],
orderConfig: MemberType[],
): number {
let rank = -1;
const stack = memberGroups.slice(); // Get a copy of the member groups

while (stack.length > 0 && rank === -1) {
rank = orderConfig.indexOf(stack.shift()!);
const memberGroup = stack.shift()!;
rank = orderConfig.findIndex(memberType =>
Array.isArray(memberType)
? memberType.includes(memberGroup)
: memberType === memberGroup,
);
}

return rank;
Expand All @@ -358,7 +378,7 @@ function getRankOrder(memberGroups: string[], orderConfig: string[]): number {
*/
function getRank(
node: Member,
orderConfig: string[],
orderConfig: MemberType[],
supportsModifiers: boolean,
): number {
const type = getNodeType(node);
Expand Down Expand Up @@ -414,7 +434,7 @@ function getRank(
}

/**
* Gets the lowest possible rank higher than target.
* Gets the lowest possible rank(s) higher than target.
* e.g. given the following order:
* ...
* public-static-method
Expand All @@ -427,15 +447,16 @@ function getRank(
* and considering that a public-instance-method has already been declared, so ranks contains
* public-instance-method, then the lowest possible rank for public-static-method is
* public-instance-method.
* If a lowest possible rank is a member group, a comma separated list of ranks is returned.
* @param ranks the existing ranks in the object.
* @param target the target rank.
* @param order the current order to be validated.
* @returns the name of the lowest possible rank without dashes (-).
* @returns the name(s) of the lowest possible rank without dashes (-).
*/
function getLowestRank(
ranks: number[],
target: number,
order: string[],
order: MemberType[],
): string {
let lowest = ranks[ranks.length - 1];

Expand All @@ -445,7 +466,9 @@ function getLowestRank(
}
});

return order[lowest].replace(/-/g, ' ');
const lowestRank = order[lowest];
const lowestRanks = Array.isArray(lowestRank) ? lowestRank : [lowestRank];
return lowestRanks.map(rank => rank.replace(/-/g, ' ')).join(', ');
}

export default util.createRule<Options, MessageIds>({
Expand Down Expand Up @@ -523,7 +546,7 @@ export default util.createRule<Options, MessageIds>({
*/
function checkGroupSort(
members: Member[],
groupOrder: string[],
groupOrder: MemberType[],
supportsModifiers: boolean,
): Array<Member[]> | null {
const previousRanks: number[] = [];
Expand Down
45 changes: 45 additions & 0 deletions packages/eslint-plugin/tests/rules/member-ordering.test.ts
Expand Up @@ -1417,6 +1417,23 @@ class Foo {
},
],
},
{
code: `
class Foo {
A: string;
constructor() {}
get B() {}
set B() {}
get C() {}
set C() {}
D(): void;
} `,
options: [
{
default: ['field', 'constructor', ['get', 'set'], 'method'],
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
},
],
},
],
invalid: [
{
Expand Down Expand Up @@ -3823,6 +3840,34 @@ class Foo {
},
],
},
{
code: `
class Foo {
A: string;
get B() {}
constructor() {}
set B() {}
get C() {}
set C() {}
D(): void;
} `,
options: [
{
default: ['field', 'constructor', ['get', 'set'], 'method'],
},
],
errors: [
{
messageId: 'incorrectGroupOrder',
data: {
name: 'constructor',
rank: 'get, set',
},
line: 5,
column: 5,
},
],
},
],
};

Expand Down