Skip to content

Commit 5c846aa

Browse files
committed
feat(eslint-plugin): [naming-convention] split property and method selectors into more granular classXXX, objectLiteralXXX, typeXXX
Fixes #1477 Closes #2802 This allows users to target different types of properties differently. Adds the following selectors (self explanatory - just breaking the selectors up): - `classProperty` - `objectLiteralProperty` - `typeProperty` - `classMethod` - `objectLiteralMethod` - `typeMethod` Converts - `property` to a meta selector for `classProperty`, `objectLiteralProperty`, `typeProperty` - `method` to a meta selector for `classMethod`, `objectLiteralMethod`, `typeMethod`
1 parent a8227a6 commit 5c846aa

File tree

3 files changed

+361
-90
lines changed

3 files changed

+361
-90
lines changed

Diff for: packages/eslint-plugin/docs/rules/naming-convention.md

+32-8
Original file line numberDiff line numberDiff line change
@@ -154,21 +154,27 @@ If these are provided, the identifier must start with one of the provided values
154154

155155
### Selector Options
156156

157-
- `selector` (see "Allowed Selectors, Modifiers and Types" below).
157+
- `selector` allows you to specify what types of identifiers to target.
158158
- Accepts one or array of selectors to define an option block that applies to one or multiple selectors.
159159
- For example, if you provide `{ selector: ['variable', 'function'] }`, then it will apply the same option to variable and function nodes.
160+
- See [Allowed Selectors, Modifiers and Types](#allowed-selectors-modifiers-and-types) below for the complete list of allowed selectors.
160161
- `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`private`/`public`/`protected`), or if the thing is `static`, etc.
161162
- The name must match _all_ of the modifiers.
162163
- For example, if you provide `{ modifiers: ['private', 'static', 'readonly'] }`, then it will only match something that is `private static readonly`, and something that is just `private` will not match.
164+
- The following `modifiers` are allowed:
165+
- `const` - matches a variable declared as being `const` (`const x = 1`).
166+
- `public` - matches any member that is either explicitly declared as `public`, or has no visibility modifier (i.e. implicitly public).
167+
- `readonly`, `static`, `abstract`, `protected`, `private` - matches any member explicitly declared with the given modifier.
163168
- `types` allows you to specify which types to match. This option supports simple, primitive types only (`boolean`, `string`, `number`, `array`, `function`).
164169
- The name must match _one_ of the types.
165170
- **_NOTE - Using this option will require that you lint with type information._**
166171
- For example, this lets you do things like enforce that `boolean` variables are prefixed with a verb.
167-
- `boolean` matches any type assignable to `boolean | null | undefined`
168-
- `string` matches any type assignable to `string | null | undefined`
169-
- `number` matches any type assignable to `number | null | undefined`
170-
- `array` matches any type assignable to `Array<unknown> | null | undefined`
171-
- `function` matches any type assignable to `Function | null | undefined`
172+
- The following `types` are allowed:
173+
- `boolean` matches any type assignable to `boolean | null | undefined`
174+
- `string` matches any type assignable to `string | null | undefined`
175+
- `number` matches any type assignable to `number | null | undefined`
176+
- `array` matches any type assignable to `Array<unknown> | null | undefined`
177+
- `function` matches any type assignable to `Function | null | undefined`
172178

173179
The ordering of selectors does not matter. The implementation will automatically sort the selectors to ensure they match from most-specific to least specific. It will keep checking selectors in that order until it finds one that matches the name.
174180

@@ -202,13 +208,25 @@ Individual Selectors match specific, well-defined sets. There is no overlap betw
202208
- `parameter` - matches any function parameter. Does not match parameter properties.
203209
- Allowed `modifiers`: none.
204210
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
205-
- `property` - matches any object, class, or object type property. Does not match properties that have direct function expression or arrow function expression values.
211+
- `classProperty` - matches any class property. Does not match properties that have direct function expression or arrow function expression values.
212+
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
213+
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
214+
- `objectLiteralProperty` - matches any object literal property. Does not match properties that have direct function expression or arrow function expression values.
215+
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
216+
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
217+
- `typeProperty` - matches any object type property. Does not match properties that have direct function expression or arrow function expression values.
206218
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
207219
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
208220
- `parameterProperty` - matches any parameter property.
209221
- Allowed `modifiers`: `private`, `protected`, `public`, `readonly`.
210222
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
211-
- `method` - matches any object, class, or object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.
223+
- `classMethod` - matches any class method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.
224+
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
225+
- Allowed `types`: none.
226+
- `objectLiteralMethod` - matches any object literal method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.
227+
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
228+
- Allowed `types`: none.
229+
- `typeMethod` - matches any object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.
212230
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
213231
- Allowed `types`: none.
214232
- `accessor` - matches any accessor.
@@ -249,6 +267,12 @@ Group Selectors are provided for convenience, and essentially bundle up sets of
249267
- `typeLike` - matches the same as `class`, `interface`, `typeAlias`, `enum`, `typeParameter`.
250268
- Allowed `modifiers`: `abstract`.
251269
- Allowed `types`: none.
270+
- `property` - matches the same as `classProperty`, `objectLiteralProperty`, `typeProperty`.
271+
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
272+
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
273+
- `method` - matches the same as `classMethod`, `objectLiteralMethod`, `typeMethod`.
274+
- Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`.
275+
- Allowed `types`: none.
252276

253277
## Examples
254278

Diff for: packages/eslint-plugin/src/rules/naming-convention.ts

+108-41
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,24 @@ enum Selectors {
4141
parameter = 1 << 2,
4242

4343
// memberLike
44-
property = 1 << 3,
45-
parameterProperty = 1 << 4,
46-
method = 1 << 5,
47-
accessor = 1 << 6,
48-
enumMember = 1 << 7,
44+
parameterProperty = 1 << 3,
45+
accessor = 1 << 4,
46+
enumMember = 1 << 5,
47+
classMethod = 1 << 6,
48+
objectLiteralMethod = 1 << 7,
49+
typeMethod = 1 << 8,
50+
classProperty = 1 << 9,
51+
objectLiteralProperty = 1 << 10,
52+
typeProperty = 1 << 11,
4953

5054
// typeLike
51-
class = 1 << 8,
52-
interface = 1 << 9,
53-
typeAlias = 1 << 10,
54-
enum = 1 << 11,
55-
typeParameter = 1 << 12,
55+
class = 1 << 12,
56+
interface = 1 << 13,
57+
typeAlias = 1 << 14,
58+
enum = 1 << 15,
59+
typeParameter = 1 << 17,
5660
}
5761
type SelectorsString = keyof typeof Selectors;
58-
const SELECTOR_COUNT = util.getEnumNames(Selectors).length;
5962

6063
enum MetaSelectors {
6164
default = -1,
@@ -64,17 +67,29 @@ enum MetaSelectors {
6467
Selectors.function |
6568
Selectors.parameter,
6669
memberLike = 0 |
67-
Selectors.property |
70+
Selectors.classProperty |
71+
Selectors.objectLiteralProperty |
72+
Selectors.typeProperty |
6873
Selectors.parameterProperty |
6974
Selectors.enumMember |
70-
Selectors.method |
75+
Selectors.classMethod |
76+
Selectors.objectLiteralMethod |
77+
Selectors.typeMethod |
7178
Selectors.accessor,
7279
typeLike = 0 |
7380
Selectors.class |
7481
Selectors.interface |
7582
Selectors.typeAlias |
7683
Selectors.enum |
7784
Selectors.typeParameter,
85+
method = 0 |
86+
Selectors.classMethod |
87+
Selectors.objectLiteralMethod |
88+
Selectors.typeProperty,
89+
property = 0 |
90+
Selectors.classProperty |
91+
Selectors.objectLiteralProperty |
92+
Selectors.typeMethod,
7893
}
7994
type MetaSelectorsString = keyof typeof MetaSelectors;
8095
type IndividualAndMetaSelectorsString = SelectorsString | MetaSelectorsString;
@@ -321,7 +336,23 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
321336
'readonly',
322337
'abstract',
323338
]),
324-
...selectorSchema('property', true, [
339+
...selectorSchema('classProperty', true, [
340+
'private',
341+
'protected',
342+
'public',
343+
'static',
344+
'readonly',
345+
'abstract',
346+
]),
347+
...selectorSchema('objectLiteralProperty', true, [
348+
'private',
349+
'protected',
350+
'public',
351+
'static',
352+
'readonly',
353+
'abstract',
354+
]),
355+
...selectorSchema('typeProperty', true, [
325356
'private',
326357
'protected',
327358
'public',
@@ -335,6 +366,36 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
335366
'public',
336367
'readonly',
337368
]),
369+
...selectorSchema('property', true, [
370+
'private',
371+
'protected',
372+
'public',
373+
'static',
374+
'readonly',
375+
'abstract',
376+
]),
377+
378+
...selectorSchema('classMethod', false, [
379+
'private',
380+
'protected',
381+
'public',
382+
'static',
383+
'abstract',
384+
]),
385+
...selectorSchema('objectLiteralMethod', false, [
386+
'private',
387+
'protected',
388+
'public',
389+
'static',
390+
'abstract',
391+
]),
392+
...selectorSchema('typeMethod', false, [
393+
'private',
394+
'protected',
395+
'public',
396+
'static',
397+
'abstract',
398+
]),
338399
...selectorSchema('method', false, [
339400
'private',
340401
'protected',
@@ -584,7 +645,7 @@ export default util.createRule<Options, MessageIds>({
584645
node: TSESTree.PropertyNonComputedName,
585646
): void {
586647
const modifiers = new Set<Modifiers>([Modifiers.public]);
587-
handleMember(validators.property, node, modifiers);
648+
handleMember(validators.objectLiteralProperty, node, modifiers);
588649
},
589650

590651
':matches(ClassProperty, TSAbstractClassProperty)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]'(
@@ -593,7 +654,7 @@ export default util.createRule<Options, MessageIds>({
593654
| TSESTree.TSAbstractClassPropertyNonComputedName,
594655
): void {
595656
const modifiers = getMemberModifiers(node);
596-
handleMember(validators.property, node, modifiers);
657+
handleMember(validators.classProperty, node, modifiers);
597658
},
598659

599660
'TSPropertySignature[computed = false]'(
@@ -604,7 +665,7 @@ export default util.createRule<Options, MessageIds>({
604665
modifiers.add(Modifiers.readonly);
605666
}
606667

607-
handleMember(validators.property, node, modifiers);
668+
handleMember(validators.typeProperty, node, modifiers);
608669
},
609670

610671
// #endregion property
@@ -615,14 +676,13 @@ export default util.createRule<Options, MessageIds>({
615676
'Property[computed = false][kind = "init"][value.type = "ArrowFunctionExpression"]',
616677
'Property[computed = false][kind = "init"][value.type = "FunctionExpression"]',
617678
'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]',
618-
'TSMethodSignature[computed = false]',
619679
].join(', ')](
620680
node:
621681
| TSESTree.PropertyNonComputedName
622682
| TSESTree.TSMethodSignatureNonComputedName,
623683
): void {
624684
const modifiers = new Set<Modifiers>([Modifiers.public]);
625-
handleMember(validators.method, node, modifiers);
685+
handleMember(validators.objectLiteralMethod, node, modifiers);
626686
},
627687

628688
[[
@@ -638,7 +698,14 @@ export default util.createRule<Options, MessageIds>({
638698
| TSESTree.TSAbstractMethodDefinitionNonComputedName,
639699
): void {
640700
const modifiers = getMemberModifiers(node);
641-
handleMember(validators.method, node, modifiers);
701+
handleMember(validators.classMethod, node, modifiers);
702+
},
703+
704+
'TSMethodSignature[computed = false]'(
705+
node: TSESTree.TSMethodSignatureNonComputedName,
706+
): void {
707+
const modifiers = new Set<Modifiers>([Modifiers.public]);
708+
handleMember(validators.typeMethod, node, modifiers);
642709
},
643710

644711
// #endregion method
@@ -851,21 +918,20 @@ function createValidator(
851918
return b.modifierWeight - a.modifierWeight;
852919
}
853920

854-
/*
855-
meta selectors will always be larger numbers than the normal selectors they contain, as they are the sum of all
856-
of the selectors that they contain.
857-
to give normal selectors a higher priority, shift them all SELECTOR_COUNT bits to the left before comparison, so
858-
they are instead always guaranteed to be larger than the meta selectors.
859-
*/
860-
const aSelector = isMetaSelector(a.selector)
861-
? a.selector
862-
: a.selector << SELECTOR_COUNT;
863-
const bSelector = isMetaSelector(b.selector)
864-
? b.selector
865-
: b.selector << SELECTOR_COUNT;
921+
const aIsMeta = isMetaSelector(a.selector);
922+
const bIsMeta = isMetaSelector(b.selector);
866923

924+
// non-meta selectors should go ahead of meta selectors
925+
if (aIsMeta && !bIsMeta) {
926+
return 1;
927+
}
928+
if (!aIsMeta && bIsMeta) {
929+
return -1;
930+
}
931+
932+
// both aren't meta selectors
867933
// sort descending - the meta selectors are "least important"
868-
return bSelector - aSelector;
934+
return b.selector - a.selector;
869935
});
870936

871937
return (
@@ -1314,21 +1380,22 @@ function normalizeOption(option: Selector): NormalizedSelector[] {
13141380
? option.selector
13151381
: [option.selector];
13161382

1317-
const selectorsAllowedToHaveTypes: (Selectors | MetaSelectors)[] = [
1318-
Selectors.variable,
1319-
Selectors.parameter,
1320-
Selectors.property,
1321-
Selectors.parameterProperty,
1322-
Selectors.accessor,
1323-
];
1383+
const selectorsAllowedToHaveTypes =
1384+
Selectors.variable |
1385+
Selectors.parameter |
1386+
Selectors.classProperty |
1387+
Selectors.objectLiteralProperty |
1388+
Selectors.typeProperty |
1389+
Selectors.parameterProperty |
1390+
Selectors.accessor;
13241391

13251392
const config: NormalizedSelector[] = [];
13261393
selectors
13271394
.map(selector =>
13281395
isMetaSelector(selector) ? MetaSelectors[selector] : Selectors[selector],
13291396
)
13301397
.forEach(selector =>
1331-
selectorsAllowedToHaveTypes.includes(selector)
1398+
(selectorsAllowedToHaveTypes & selector) !== 0
13321399
? config.push({ selector: selector, ...normalizedOption })
13331400
: config.push({
13341401
selector: selector,

0 commit comments

Comments
 (0)