From 5983e5ab3bfb94fec782bea54a37457fe31db545 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sun, 7 Aug 2022 04:28:48 +0800 Subject: [PATCH] feat(eslint-plugin): [member-ordering] support static blocks (#5417) * feat(eslint-plugin): [member-ordering] support static blocks * Update packages/eslint-plugin/docs/rules/member-ordering.md Co-authored-by: Josh Goldberg * Add some tests Co-authored-by: Josh Goldberg --- .../docs/rules/member-ordering.md | 33 +++- .../src/rules/member-ordering.ts | 20 ++- ...habetically-case-insensitive-order.test.ts | 18 ++ ...mber-ordering-alphabetically-order.test.ts | 18 ++ .../tests/rules/member-ordering.test.ts | 157 ++++++++++++++++++ 5 files changed, 236 insertions(+), 10 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/member-ordering.md b/packages/eslint-plugin/docs/rules/member-ordering.md index 15b7cf4014a..35cc8ef6860 100644 --- a/packages/eslint-plugin/docs/rules/member-ordering.md +++ b/packages/eslint-plugin/docs/rules/member-ordering.md @@ -94,6 +94,9 @@ The default configuration looks as follows: "field", + // Static initialization + "static-initialization", + // Constructors "public-constructor", "protected-constructor", @@ -908,6 +911,9 @@ The most explicit and granular form is the following: "protected-abstract-field", "private-abstract-field", + // Static initialization + "static-initialization", + // Constructors "public-constructor", "protected-constructor", @@ -1006,6 +1012,9 @@ It is also possible to group member types by their accessibility (`static`, `ins "protected-field", // = ["protected-static-field", "protected-instance-field"] "private-field", // = ["private-static-field", "private-instance-field"] + // Static initialization + // No accessibility for static initialization. + // Constructors // Only the accessibility of constructors is configurable. See below. @@ -1043,6 +1052,9 @@ their accessibility. "decorated-field", // = ["public-decorated-field", "protected-decorated-field", "private-decorated-field"] + // Static initialization + // No decorators for static initialization. + // Constructors // There are no decorators for constructors. @@ -1051,14 +1063,14 @@ their accessibility. "protected-decorated-get", "private-decorated-get", - "decorated-get" // = ["public-decorated-get", "protected-decorated-get", "private-decorated-get"] + "decorated-get", // = ["public-decorated-get", "protected-decorated-get", "private-decorated-get"] // Setters "public-decorated-set", "protected-decorated-set", "private-decorated-set", - "decorated-set" // = ["public-decorated-set", "protected-decorated-set", "private-decorated-set"] + "decorated-set", // = ["public-decorated-set", "protected-decorated-set", "private-decorated-set"] // Methods "public-decorated-method", @@ -1083,18 +1095,21 @@ Another option is to group the member types by their scope (`public`, `protected "instance-field", // = ["public-instance-field", "protected-instance-field", "private-instance-field"] "abstract-field", // = ["public-abstract-field", "protected-abstract-field", "private-abstract-field"] + // Static initialization + // No scope for static initialization. + // Constructors "constructor", // = ["public-constructor", "protected-constructor", "private-constructor"] // Getters "static-get", // = ["public-static-get", "protected-static-get", "private-static-get"] "instance-get", // = ["public-instance-get", "protected-instance-get", "private-instance-get"] - "abstract-get" // = ["public-abstract-get", "protected-abstract-get", "private-abstract-get"] + "abstract-get", // = ["public-abstract-get", "protected-abstract-get", "private-abstract-get"] // Setters "static-set", // = ["public-static-set", "protected-static-set", "private-static-set"] "instance-set", // = ["public-instance-set", "protected-instance-set", "private-instance-set"] - "abstract-set" // = ["public-abstract-set", "protected-abstract-set", "private-abstract-set"] + "abstract-set", // = ["public-abstract-set", "protected-abstract-set", "private-abstract-set"] // Methods "static-method", // = ["public-static-method", "protected-static-method", "private-static-method"] @@ -1116,15 +1131,18 @@ The third grouping option is to ignore both scope and accessibility. "field", // = ["public-static-field", "protected-static-field", "private-static-field", "public-instance-field", "protected-instance-field", "private-instance-field", // "public-abstract-field", "protected-abstract-field", private-abstract-field"] + // Static initialization + // No grouping for static initialization. + // Constructors // Only the accessibility of constructors is configurable. // Getters - "get" // = ["public-static-get", "protected-static-get", "private-static-get", "public-instance-get", "protected-instance-get", "private-instance-get", + "get", // = ["public-static-get", "protected-static-get", "private-static-get", "public-instance-get", "protected-instance-get", "private-instance-get", // "public-abstract-get", "protected-abstract-get", "private-abstract-get"] // Setters - "set" // = ["public-static-set", "protected-static-set", "private-static-set", "public-instance-set", "protected-instance-set", "private-instance-set", + "set", // = ["public-static-set", "protected-static-set", "private-static-set", "public-instance-set", "protected-instance-set", "private-instance-set", // "public-abstract-set", "protected-abstract-set", "private-abstract-set"] // Methods @@ -1145,6 +1163,9 @@ It is also possible to group different member types at the same rank. // Fields "field", + // Static initialization + "static-initialization", + // Constructors "constructor", diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index 4ddb4954593..72784463da2 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -15,7 +15,8 @@ type MemberKind = | 'get' | 'method' | 'set' - | 'signature'; + | 'signature' + | 'static-initialization'; type DecoratedMemberKind = 'field' | 'method' | 'get' | 'set'; @@ -25,7 +26,10 @@ type MemberScope = 'static' | 'instance' | 'abstract'; type BaseMemberType = | MemberKind - | `${TSESTree.Accessibility}-${Exclude}` + | `${TSESTree.Accessibility}-${Exclude< + MemberKind, + 'signature' | 'static-initialization' + >}` | `${TSESTree.Accessibility}-decorated-${DecoratedMemberKind}` | `decorated-${DecoratedMemberKind}` | `${TSESTree.Accessibility}-${MemberScope}-${NonCallableMemberKind}` @@ -126,6 +130,9 @@ export const defaultOrder: MemberType[] = [ 'field', + // Static initialization + 'static-initialization', + // Constructors 'public-constructor', 'protected-constructor', @@ -231,12 +238,13 @@ const allMemberTypes = Array.from( 'constructor', 'get', 'set', + 'static-initialization', ] as const ).reduce>((all, type) => { all.add(type); (['public', 'protected', 'private'] as const).forEach(accessibility => { - if (type !== 'signature') { + if (type !== 'signature' && type !== 'static-initialization') { all.add(`${accessibility}-${type}`); // e.g. `public-field` } @@ -295,6 +303,8 @@ function getNodeType(node: Member): MemberKind | null { return 'field'; case AST_NODE_TYPES.TSIndexSignature: return 'signature'; + case AST_NODE_TYPES.StaticBlock: + return 'static-initialization'; default: return null; } @@ -352,6 +362,8 @@ function getMemberName( return 'call'; case AST_NODE_TYPES.TSIndexSignature: return util.getNameFromIndexSignature(node); + case AST_NODE_TYPES.StaticBlock: + return 'static block'; default: return null; } @@ -438,7 +450,7 @@ function getRank( memberGroups.push(`decorated-${type}`); } - if (type !== 'signature') { + if (type !== 'signature' && type !== 'static-initialization') { if (type !== 'constructor') { // Constructors have no scope memberGroups.push(`${accessibility}-${scope}-${type}`); diff --git a/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts index 24907a8d8f4..fe3425ba95c 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-case-insensitive-order.test.ts @@ -493,6 +493,24 @@ const foo = class Foo { }, ], }, + + // default option + static blocks; should always be valid + { + code: ` +class Foo { + static {} + static {} +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically-case-insensitive', + }, + }, + ], + }, ], invalid: [ // default option + interface + wrong order within group and wrong group order + alphabetically diff --git a/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts index dfd5f0de83c..cc02785efc4 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering-alphabetically-order.test.ts @@ -1701,6 +1701,24 @@ const foo = class Foo { }, ], }, + + // default option + static blocks; should always be valid + { + code: ` +class Foo { + static {} + static {} +} + `, + options: [ + { + default: { + memberTypes: 'never', + order: 'alphabetically', + }, + }, + ], + }, ], invalid: [ // default option + class + wrong order within group and wrong group order + alphabetically diff --git a/packages/eslint-plugin/tests/rules/member-ordering.test.ts b/packages/eslint-plugin/tests/rules/member-ordering.test.ts index c21272ca510..32cf004cf7a 100644 --- a/packages/eslint-plugin/tests/rules/member-ordering.test.ts +++ b/packages/eslint-plugin/tests/rules/member-ordering.test.ts @@ -1244,6 +1244,36 @@ class Foo { `, options: [{ default: ['method', 'constructor', 'signature', 'field'] }], }, + { + code: ` +class Foo { + static {} + m() {} + f = 1; +} + `, + options: [{ default: ['static-initialization', 'method', 'field'] }], + }, + { + code: ` +class Foo { + m() {} + f = 1; + static {} +} + `, + options: [{ default: ['method', 'field', 'static-initialization'] }], + }, + { + code: ` +class Foo { + f = 1; + static {} + m() {} +} + `, + options: [{ default: ['field', 'static-initialization', 'method'] }], + }, ` interface Foo { [Z: string]: any; @@ -3979,6 +4009,133 @@ class Foo { }, ], }, + { + code: ` +class Foo { + static {} + m() {} + f = 1; +} + `, + options: [{ default: ['method', 'field', 'static-initialization'] }], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'm', + rank: 'static initialization', + }, + line: 4, + column: 3, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'f', + rank: 'static initialization', + }, + line: 5, + column: 3, + }, + ], + }, + { + code: ` +class Foo { + m() {} + f = 1; + static {} +} + `, + options: [{ default: ['static-initialization', 'method', 'field'] }], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'static block', + rank: 'method', + }, + line: 5, + column: 3, + }, + ], + }, + { + code: ` +class Foo { + f = 1; + static {} + m() {} +} + `, + options: [{ default: ['static-initialization', 'field', 'method'] }], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'static block', + rank: 'field', + }, + line: 4, + column: 3, + }, + ], + }, + { + code: ` +class Foo { + static {} + f = 1; + m() {} +} + `, + options: [{ default: ['field', 'static-initialization', 'method'] }], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'f', + rank: 'static initialization', + }, + line: 4, + column: 3, + }, + ], + }, + { + code: ` +class Foo { + private mp() {} + static {} + public m() {} + @dec + md() {} +} + `, + options: [ + { default: ['decorated-method', 'static-initialization', 'method'] }, + ], + errors: [ + { + messageId: 'incorrectGroupOrder', + data: { + name: 'static block', + rank: 'method', + }, + line: 4, + column: 3, + }, + { + messageId: 'incorrectGroupOrder', + data: { + name: 'md', + rank: 'method', + }, + line: 6, + column: 3, + }, + ], + }, ], };