8000 feat(eslint-plugin): [member-ordering] add decorators support (#1870) · DudaGod/typescript-eslint@f7ec192 · GitHub
[go: up one dir, main page]

Skip to content

Commit f7ec192

Browse files
authored
feat(eslint-plugin): [member-ordering] add decorators support (typescript-eslint#1870)
1 parent 1b4e430 commit f7ec192

File tree

3 files changed

+414
-66
lines changed

3 files changed

+414
-66
lines changed

packages/eslint-plugin/docs/rules/member-ordering.md

Lines changed: 63 additions & 17 deletions
< 9E88 td data-grid-cell-id="diff-76f7820640084772a4df6af9a91b71d64e8e2ec5f1933a727b52317660f47858-112-139-0" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionNum-bgColor, var(--diffBlob-addition-bgColor-num));text-align:center" tabindex="-1" valign="top" class="focusable-grid-cell diff-line-number position-relative left-side">
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ There are multiple ways to specify the member types. The most explicit and granu
6262
"public-static-field",
6363
"protected-static-field",
6464
"private-static-field",
65+
"public-decorated-field",
66+
"protected-decorated-field",
67+
"private-decorated-field",
6568
"public-instance-field",
6669
"protected-instance-field",
6770
"private-instance-field",
@@ -78,6 +81,9 @@ There are multiple ways to specify the member types. The most explicit and granu
7881
"public-static-method",
7982
"protected-static-method",
8083
"private-static-method",
84+
"public-decorated-method",
85+
"protected-decorated-method",
86+
"private-decorated-method",
8187
"public-instance-method",
8288
"protected-instance-method",
8389
"private-instance-method",
@@ -99,17 +105,45 @@ It is also possible to group member types by their accessibility (`static`, `ins
99105
// No accessibility for index signature. See above.
100106

101107
// Fields
102-
"public-field", // = ["public-static-field", "public-instance-field"])
103-
"protected-field", // = ["protected-static-field", "protected-instance-field"])
104-
"private-field", // = ["private-static-field", "private-instance-field"])
108+
"public-field", // = ["public-static-field", "public-instance-field"]
109+
"protected-field", // = ["protected-static-field", "protected-instance-field"]
110+
"private-field", // = ["private-static-field", "private-instance-field"]
105111

106112
// Constructors
107113
// Only the accessibility of constructors is configurable. See below.
108114

109115
// Methods
110-
"public-method", // = ["public-static-method", "public-instance-method"])
111-
"protected-method", // = ["protected-static-method", "protected-instance-method"])
112-
"private-method" // = ["private-static-method", "private-instance-method"])
116+
"public-method", // = ["public-static-method", "public-instance-method"]
117+
"protected-method", // = ["protected-static-method", "protected-instance-method"]
118+
"private-method" // = ["private-static-method", "private-instance-method"]
119+
]
120+
```
121+
122+
### Member group types (with accessibility and a decorator)
123+
124+
It is also possible to group methods or fields with a decorator separately, optionally specifying
125+
their accessibility.
126+
127+
```jsonc
128+
[
129+
// Index signature
130+
// No decorators for index signature.
131+
132+
// Fields
133+
"public-decorated-field",
134+
"protected-decorated-field",
135+
"private-decorated-field",
136+
137+
"decorated-field", // = ["public-decorated-field", "protected-decorated-field", "private-decorated-field"]
138+
139+
// Constructors
140+
// There are no decorators for constructors.
141+
142+
"public-decorated-method",
143+
"protected-decorated-method",
144+
"private-decorated-method",
145+
146+
"decorated-method" // = ["public-decorated-method", "protected-decorated-method", "private-decorated-method"]
113147
]
114148
```
115149

@@ -123,17 +157,17 @@ Another option is to group the member types by their scope (`public`, `protected
123157
// No scope for index signature. See above.
124158

125159
// Fields
126-
"static-field", // = ["public-static-field", "protected-static-field", "private-static-field"])
127-
"instance-field", // = ["public-instance-field", "protected-instance-field", "private-instance-field"])
128-
"abstract-field", // = ["public-abstract-field", "protected-abstract-field", "private-abstract-field"])
160+
"static-field", // = ["public-static-field", "protected-static-field", "private-static-field"]
161+
"instance-field", // = ["public-instance-field", "protected-instance-field", "private-instance-field"]
162+
"abstract-field", // = ["public-abstract-field", "protected-abstract-field", "private-abstract-field"]
129163

130164
// Constructors
131-
"constructor", // = ["public-constructor", "protected-constructor", "private-constructor"])
165+
"constructor", // = ["public-constructor", "protected-constructor", "private-constructor"]
132166

133167
// Methods
134-
"static-method", // = ["public-static-method", "protected-static-method", "private-static-method"])
135-
"instance-method", // = ["public-instance-method", "protected-instance-method", "private-instance-method"])
136-
"abstract-method" // = ["public-abstract-method", "protected-abstract-method", "private-abstract-method"])
168+
"static-method", // = ["public-static-method", "protected-static-method", "private-static-method"]
169+
"instance-method", // = ["public-instance-method", "protected-instance-method", "private-instance-method"]
170+
"abstract-method" // = ["public-abstract-method", "protected-abstract-method", "private-abstract-method"]
137171
]
138172
```
139173

@@ -148,14 +182,14 @@ The third grouping option is to ignore both scope and accessibility.
148182

149183
// Fields
150184
"field", // = ["public-static-field", "protected-static-field", "private-static-field", "public-instance-field", "protected-instance-field", "private-instance-field",
151-
// "public-abstract-field", "protected-abstract-field", private-abstract-field"])
185+
// "public-abstract-field", "protected-abstract-field", private-abstract-field"]
152186

153187
// Constructors
154188
// Only the accessibility of constructors is configurable. See above.
155189

156190
// Methods
157191
"method" // = ["public-static-method", "protected-static-method", "private-static-method", "public-instance-method", "protected-instance-method", "private-instance-method",
158-
// "public-abstract-method", "protected-abstract-method", "private-abstract-method"])
192+
// "public-abstract-method", "protected-abstract-method", "private-abstract-method"]
159193
]
160194
```
161195

@@ -174,6 +208,10 @@ The default configuration looks as follows:
174208
"protected-static-field",
175209
"private-static-field",
176210

211+
"public-decorated-field",
212+
"protected-decorated-field",
213+
"private-decorated-field",
214+
177215
"public-instance-field",
178216
"protected-instance-field",
179217
"private-instance-field",
@@ -190,6 +228,8 @@ The default configuration looks as follows:
190228
"instance-field",
191229
"abstract-field",
192230

231+
"decorated-field",
232+
193233
"field",
194234

195235
// Constructors
@@ -204,6 +244,10 @@ The default configuration looks as follows:
204244
"protected-static-method",
205245
"private-static-method",
206246

247+
"public-decorated-method",
248+
"protected-decorated-method",
249+
"private-decorated-method",
250+
207251
"public-instance-method",
208252
"protected-instance-method",
209253
"private-instance-method",
@@ -220,6 +264,8 @@ The default configuration looks as follows:
220264
"instance-method",
221265
"abstract-method",
222266

267+
"decorated-method",
268+
223269
"method"
224270
]
225271
}
@@ -749,7 +795,7 @@ type Foo = {
749795

750796
It is possible to sort all members within a group alphabetically.
751797

752-
#### Configuration: `{ default: { memberTypes: <Default Order>, order: "alphabetically" } }`
798+
#### Configuration: `{ "default": { "memberTypes": <Default Order>, "order": "alphabetically" } }`
753799

754800
This will apply the default order (see above) and enforce an alphabetic order within each group.
755801

@@ -795,7 +841,7 @@ interface Foo {
795841

796842
It is also possible to sort all members and ignore the member groups completely.
797843

798-
#### Configuration: `{ default: { memberTypes: "never", order: "alphabetically" } }`
844+
#### Configuration: `{ "default": { "memberTypes": "never", "order": "alphabetically" } }`
799845

800846
##### Incorrect example
801847

packages/eslint-plugin/src/rules/member-ordering.ts

Lines changed: 75 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ export const defaultOrder = [
6060
'protected-static-field',
6161
'private-static-field',
6262

63+
'public-decorated-field',
64+
'protected-decorated-field',
65+
'private-decorated-field',
66+
6367
'public-instance-field',
6468
'protected-instance-field',
6569
'private-instance-field',
@@ -76,6 +80,8 @@ export const defaultOrder = [
7680
'instance-field',
7781
'abstract-field',
7882

83+
'decorated-field',
84+
7985
'field',
8086

8187
// Constructors
@@ -90,6 +96,10 @@ export const defaultOrder = [
9096
'protected-static-method',
9197
'private-static-method',
9298

99+
'public-decorated-method',
100+
'protected-decorated-method',
101+
'private-decorated-method',
102+
93103
'public-instance-method',
94104
'protected-instance-method',
95105
'private-instance-method',
@@ -106,6 +116,8 @@ export const defaultOrder = [
106116
'instance-method',
107117
'abstract-method',
108118

119+
'decorated-method',
120+
109121
'method',
110122
];
111123

@@ -119,6 +131,18 @@ const allMemberTypes = ['signature', 'field', 'method', 'constructor'].reduce<
119131
all.push(`${accessibility}-${type}`); // e.g. `public-field`
120132
}
121133

134+
// Only class instance fields and methods can have decorators attached to them
135+
if (type === 'field' || type === 'method') {
136+
const decoratedMemberType = `${accessibility}-decorated-${type}`;
137+
const decoratedMemberTypeNoAccessibility = `decorated-${type}`;
138+
if (!all.includes(decoratedMemberType)) {
139+
all.push(decoratedMemberType);
140+
}
141+
if (!all.includes(decoratedMemberTypeNoAccessibility)) {
142+
all.push(decoratedMemberTypeNoAccessibility);
143+
}
144+
}
145+
122146
if (type !== 'constructor' && type !== 'signature') {
123147
// There is no `static-constructor` or `instance-constructor` or `abstract-constructor`
124148
['static', 'instance', 'abstract'].forEach(scope => {
@@ -258,6 +282,12 @@ function getRank(
258282
const memberGroups = [];
259283

260284
if (supportsModifiers) {
285+
const decorated = 'decorators' in node && node.decorators!.length > 0;
286+
if (decorated && (type === 'field' || type === 'method')) {
287+
memberGroups.push(`${accessibility}-decorated-${type}`);
288+
memberGroups.push(`decorated-${type}`);
289+
}
290+
261291
if (type !== 'constructor') {
262292
// Constructors have no scope
263293
memberGroups.push(`${accessibility}-${scope}-${type}`);
@@ -396,27 +426,29 @@ export default util.createRule<Options, MessageIds>({
396426
const name = getMemberName(member, context.getSourceCode());
397427
const rankLastMember = previousRanks[previousRanks.length - 1];
398428

399-
if (rank !== -1) {
400-
// Works for 1st item because x < undefined === false for any x (typeof string)
401-
if (rank < rankLastMember) {
402-
context.report({
403-
node: member,
404-
messageId: 'incorrectGroupOrder',
405-
data: {
406-
name,
407-
rank: getLowestRank(previousRanks, rank, groupOrder),
408-
},
409-
});
429+
if (rank === -1) {
430+
return;
431+
}
410432

411-
isCorrectlySorted = false;
412-
} else if (rank === rankLastMember) {
413-
// Same member group --> Push to existing member group array
414-
memberGroups[memberGroups.length - 1].push(member);
415-
} else {
416-
// New member group --> Create new member group array
417-
previousRanks.push(rank);
418-
memberGroups.push([member]);
419-
}
433+
// Works for 1st item because x < undefined === false for any x (typeof string)
434+
if (rank < rankLastMember) {
435+
context.report({
436+
node: member,
437+
messageId: 'incorrectGroupOrder',
438+
data: {
439+
name,
440+
rank: getLowestRank(previousRanks, rank, groupOrder),
441+
},
442+
});
443+
444+
isCorrectlySorted = false;
445+
} else if (rank === rankLastMember) {
446+
// Same member group --> Push to existing member group array
447+
memberGroups[memberGroups.length - 1].push(member);
448+
} else {
449+
// New member group --> Create new member group array
450+
previousRanks.push(rank);
451+
memberGroups.push([member]);
420452
}
421453
});
422454

@@ -472,36 +504,34 @@ export default util.createRule<Options, MessageIds>({
472504
orderConfig: OrderConfig,
473505
supportsModifiers: boolean,
474506
): void {
475-
if (orderConfig !== 'never') {
476-
// Standardize config
477-
let order = null;
478-
let memberTypes;
507+
if 10000 (orderConfig === 'never') {
508+
return;
509+
}
479510

480-
if (Array.isArray(orderConfig)) {
481-
memberTypes = orderConfig;
482-
} else {
483-
order = orderConfig.order;
484-
memberTypes = orderConfig.memberTypes;
485-
}
511+
// Standardize config
512+
let order = null;
513+
let memberTypes;
486514

487-
// Check order
488-
if (Array.isArray(memberTypes)) {
489-
const grouped = checkGroupSort(
490-
members,
491-
memberTypes,
492-
supportsModifiers,
493-
);
515+
if (Array.isArray(orderConfig)) {
516+
memberTypes = orderConfig;
517+
} else {
518+
order = orderConfig.order;
519+
memberTypes = orderConfig.memberTypes;
520+
}
494521

495-
if (grouped === null) {
496-
return;
497-
}
522+
// Check order
523+
if (Array.isArray(memberTypes)) {
524+
const grouped = checkGroupSort(members, memberTypes, supportsModifiers);
498525

499-
if (order === 'alphabetically') {
500-
grouped.some(groupMember => !checkAlphaSort(groupMember));
501-
}
502-
} else if (order === 'alphabetically') {
503-
checkAlphaSort(members);
526+
if (grouped === null) {
527+
return;
528+
}
529+
530+
if (order === 'alphabetically') {
531+
grouped.some(groupMember => !checkAlphaSort(groupMember));
504532
}
533+
} else if (order === 'alphabetically') {
534+
checkAlphaSort(members);
505535
}
506536
}
507537

0 commit comments

Comments
 (0)
0