From 59a16559b991ea2f9b069ff626ace7dee961ca59 Mon Sep 17 00:00:00 2001 From: grabofus Date: Fri, 11 Feb 2022 01:53:28 +0000 Subject: [PATCH 1/3] feat(eslint-plugin): added member group support to member-ordering rule --- .../docs/rules/member-ordering.md | 23 +++++++++ .../src/rules/member-ordering.ts | 49 ++++++++++++++----- .../tests/rules/member-ordering.test.ts | 45 +++++++++++++++++ 3 files changed, 104 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index 2606fd35f9c..f825a5e96a5 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -296,6 +296,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: diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index ce2edabe058..92c794caf07 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -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 = [ @@ -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: { @@ -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; @@ -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); @@ -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 @@ -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]; @@ -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({ @@ -523,7 +546,7 @@ export default util.createRule({ */ function checkGroupSort( members: Member[], - groupOrder: string[], + groupOrder: MemberType[], supportsModifiers: boolean, ): Array | null { const previousRanks: number[] = []; diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index c9c1090c734..24b310a1e11 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -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'], + }, + ], + }, ], invalid: [ { @@ -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, + }, + ], + }, ], }; From 13a7c9bf255a5dc6465520d6db09d8b15502f189 Mon Sep 17 00:00:00 2001 From: grabofus Date: Thu, 24 Feb 2022 14:08:16 +0000 Subject: [PATCH 2/3] test(eslint-plugin): added more test cases for member-ordering --- .../tests/rules/member-ordering.test.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index 24b310a1e11..216d0157d83 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1434,6 +1434,40 @@ class Foo { }, ], }, + { + code: ` +class Foo { + A: string; + constructor() {} + B(): void; +} `, + options: [ + { + default: ['field', 'constructor', [], 'method'], + }, + ], + }, + { + code: ` +class Foo { + A: string; + constructor() {} + @Dec() private B: string; + private C(): void; + set D() {} + E(): void; +} `, + options: [ + { + default: [ + 'public-field', + 'constructor', + ['private-decorated-field', 'public-set', 'private-method'], + 'public-method', + ], + }, + ], + }, ], invalid: [ { @@ -3868,6 +3902,38 @@ class Foo { }, ], }, + { + code: ` +class Foo { + A: string; + private C(): void; + constructor() {} + @Dec() private B: string; + set D() {} + E(): void; +} `, + options: [ + { + default: [ + 'public-field', + 'constructor', + ['private-decorated-field', 'public-set', 'private-method'], + 'public-method', + ], + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'constructor', + rank: 'private decorated field, public set, private method', + }, + line: 5, + column: 5, + }, + ], + }, ], }; From 79883843ae999a1d589f6cc4a21a2f5424c1c9e8 Mon Sep 17 00:00:00 2001 From: grabofus Date: Thu, 24 Feb 2022 14:15:51 +0000 Subject: [PATCH 3/3] test(eslint-plugin): added more test cases for member-ordering --- .../tests/rules/member-ordering.test.ts | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index 216d0157d83..c21272ca510 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1468,6 +1468,23 @@ class Foo { }, ], }, + { + code: ` +class Foo { + A: string; + constructor() {} + get B() {} + get C() {} + set B() {} + set C() {} + D(): void; +} `, + options: [ + { + default: ['field', 'constructor', ['get'], ['set'], 'method'], + }, + ], + }, ], invalid: [ { @@ -3934,6 +3951,34 @@ 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'], + }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'C', + rank: 'set', + }, + line: 7, + column: 5, + }, + ], + }, ], };