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] support static blocks #5417

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
33 changes: 27 additions & 6 deletions packages/eslint-plugin/docs/rules/member-ordering.md
Expand Up @@ -94,6 +94,9 @@ The default configuration looks as follows:

"field",

// Static initialization
"static-iniatialization",
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved

// Constructors
"public-constructor",
"protected-constructor",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand All @@ -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",
Expand All @@ -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"]
Expand All @@ -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
Expand All @@ -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",

Expand Down
20 changes: 16 additions & 4 deletions packages/eslint-plugin/src/rules/member-ordering.ts
Expand Up @@ -15,7 +15,8 @@ type MemberKind =
| 'get'
| 'method'
| 'set'
| 'signature';
| 'signature'
| 'static-initialization';

type DecoratedMemberKind = 'field' | 'method' | 'get' | 'set';

Expand All @@ -25,7 +26,10 @@ type MemberScope = 'static' | 'instance' | 'abstract';

type BaseMemberType =
| MemberKind
| `${TSESTree.Accessibility}-${Exclude<MemberKind, 'signature'>}`
| `${TSESTree.Accessibility}-${Exclude<
MemberKind,
'signature' | 'static-initialization'
>}`
| `${TSESTree.Accessibility}-decorated-${DecoratedMemberKind}`
| `decorated-${DecoratedMemberKind}`
| `${TSESTree.Accessibility}-${MemberScope}-${NonCallableMemberKind}`
Expand Down Expand Up @@ -126,6 +130,9 @@ export const defaultOrder: MemberType[] = [

'field',

// Static initialization
'static-initialization',

// Constructors
'public-constructor',
'protected-constructor',
Expand Down Expand Up @@ -231,12 +238,13 @@ const allMemberTypes = Array.from(
'constructor',
'get',
'set',
'static-initialization',
] as const
).reduce<Set<MemberType>>((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`
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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}`);
Expand Down
Expand Up @@ -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
Expand Down
Expand Up @@ -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
Expand Down
71 changes: 71 additions & 0 deletions packages/eslint-plugin/tests/rules/member-ordering.test.ts
Expand Up @@ -1244,6 +1244,26 @@ 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'] }],
},
`
interface Foo {
[Z: string]: any;
Expand Down Expand Up @@ -3979,6 +3999,57 @@ 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 {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add test cases for static {} being intermixed with other blocks? Just to make sure there are no funky edge cases in ordering behavior.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By continuity principle (something I just made up) the tests should be sufficient. Let me know if there are specific quirks worth testing that you have in mind.

}
`,
options: [{ default: ['static-initialization', 'method', 'field'] }],
errors: [
{
messageId: 'incorrectGroupOrder',
data: {
name: 'static block',
rank: 'method',
},
line: 5,
column: 3,
},
],
},
],
};

Expand Down