Skip to content

Commit c8a6d80

Browse files
feat(eslint-plugin): [naming-convention] add support for #private modifier on class members (#6259)
* Add `#private` as a modifier Numbers for TypeModifiers are changed as instructed by the comment in the Modifiers enum * Add support for `#private` modifier * Add tests * Add docs Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
1 parent d64a9b9 commit c8a6d80

5 files changed

Lines changed: 136 additions & 20 deletions

File tree

‎packages/eslint-plugin/docs/rules/naming-convention.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ If these are provided, the identifier must start with one of the provided values
159159
- Accepts one or array of selectors to define an option block that applies to one or multiple selectors.
160160
- For example, if you provide `{ selector: ['function', 'variable'] }`, then it will apply the same option to variable and function nodes.
161161
- See [Allowed Selectors, Modifiers and Types](#allowed-selectors-modifiers-and-types) below for the complete list of allowed selectors.
162-
- `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`private`/`protected`/`public`), or if the thing is `static`, etc.
162+
- `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`#private`/`private`/`protected`/`public`), or if the thing is `static`, etc.
163163
- The name must match _all_ of the modifiers.
164164
- For example, if you provide `{ modifiers: ['private','readonly','static'] }`, then it will only match something that is `private static readonly`, and something that is just `private` will not match.
165165
- The following `modifiers` are allowed:
@@ -172,6 +172,7 @@ If these are provided, the identifier must start with one of the provided values
172172
- `requiresQuotes` - matches any name that requires quotes as it is not a valid identifier (i.e. has a space, a dash, etc in it).
173173
- `public` - matches any member that is either explicitly declared as `public`, or has no visibility modifier (i.e. implicitly public).
174174
- `abstract`,`override`,`private`,`protected`,`readonly`,`static` - matches any member explicitly declared with the given modifier.
175+
- `#private` - matches any member with a private identifier (an identifier that starts with `#`)
175176
- `async` - matches any method, function, or function variable which is async via the `async` keyword (e.g. does not match functions that return promises without using `async` keyword)
176177
- `types` allows you to specify which types to match. This option supports simple, primitive types only (`array`,`boolean`,`function`,`number`,`string`).
177178
- The name must match _one_ of the types.
@@ -204,7 +205,7 @@ Individual Selectors match specific, well-defined sets. There is no overlap betw
204205
- Allowed `modifiers`: `destructured`, `unused`.
205206
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
206207
- `classProperty` - matches any class property. Does not match properties that have direct function expression or arrow function expression values.
207-
- Allowed `modifiers`: `abstract`, `override`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
208+
- Allowed `modifiers`: `abstract`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
208209
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`.
209210
- `objectLiteralProperty` - matches any object literal property. Does not match properties that have direct function expression or arrow function expression values.
210211
- Allowed `modifiers`: `public`, `requiresQuotes`.
@@ -216,7 +217,7 @@ Individual Selectors match specific, well-defined sets. There is no overlap betw
216217
- Allowed `modifiers`: `private`, `protected`, `public`, `readonly`.
217218
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`.
218219
- `classMethod` - matches any class method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.
219-
- Allowed `modifiers`: `abstract`, `async`, `override`, `private`, `protected`, `public`, `requiresQuotes`, `static`.
220+
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `requiresQuotes`, `static`.
220221
- Allowed `types`: none.
221222
- `objectLiteralMethod` - matches any object literal method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors.
222223
- Allowed `modifiers`: `async`, `public`, `requiresQuotes`.
@@ -257,16 +258,16 @@ Group Selectors are provided for convenience, and essentially bundle up sets of
257258
- Allowed `modifiers`: `async`, `unused`.
258259
- Allowed `types`: none.
259260
- `memberLike` - matches the same as `accessor`, `enumMember`, `method`, `parameterProperty`, `property`.
260-
- Allowed `modifiers`: `abstract`, `async`, `override`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
261+
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
261262
- Allowed `types`: none.
262263
- `typeLike` - matches the same as `class`, `enum`, `interface`, `typeAlias`, `typeParameter`.
263264
- Allowed `modifiers`: `abstract`, `unused`.
264265
- Allowed `types`: none.
265266
- `property` - matches the same as `classProperty`, `objectLiteralProperty`, `typeProperty`.
266-
- Allowed `modifiers`: `abstract`, `async`, `override`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
267+
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
267268
- Allowed `types`: `array`, `boolean`, `function`, `number`, `string`.
268269
- `method` - matches the same as `classMethod`, `objectLiteralMethod`, `typeMethod`.
269-
- Allowed `modifiers`: `abstract`, `async`, `override`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
270+
- Allowed `modifiers`: `abstract`, `async`, `override`, `#private`, `private`, `protected`, `public`, `readonly`, `requiresQuotes`, `static`.
270271
- Allowed `types`: none.
271272

272273
## FAQ

‎packages/eslint-plugin/src/rules/naming-convention-utils/enums.ts‎

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,32 +91,33 @@ enum Modifiers {
9191
public = 1 << 3,
9292
protected = 1 << 4,
9393
private = 1 << 5,
94-
abstract = 1 << 6,
94+
'#private' = 1 << 6,
95+
abstract = 1 << 7,
9596
// destructured variable
96-
destructured = 1 << 7,
97+
destructured = 1 << 8,
9798
// variables declared in the top-level scope
98-
global = 1 << 8,
99+
global = 1 << 9,
99100
// things that are exported
100-
exported = 1 << 9,
101+
exported = 1 << 10,
101102
// things that are unused
102-
unused = 1 << 10,
103+
unused = 1 << 11,
103104
// properties that require quoting
104-
requiresQuotes = 1 << 11,
105+
requiresQuotes = 1 << 12,
105106
// class members that are overridden
106-
override = 1 << 12,
107+
override = 1 << 13,
107108
// class methods, object function properties, or functions that are async via the `async` keyword
108-
async = 1 << 13,
109+
async = 1 << 14,
109110

110111
// make sure TypeModifiers starts at Modifiers + 1 or else sorting won't work
111112
}
112113
type ModifiersString = keyof typeof Modifiers;
113114

114115
enum TypeModifiers {
115-
boolean = 1 << 12,
116-
string = 1 << 13,
117-
number = 1 << 14,
118-
function = 1 << 15,
119-
array = 1 << 16,
116+
boolean = 1 << 15,
117+
string = 1 << 16,
118+
number = 1 << 17,
119+
function = 1 << 18,
120+
array = 1 << 19,
120121
}
121122
type TypeModifiersString = keyof typeof TypeModifiers;
122123

‎packages/eslint-plugin/src/rules/naming-convention-utils/schema.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
190190
...selectorSchema('memberLike', false, [
191191
'abstract',
192192
'private',
193+
'#private',
193194
'protected',
194195
'public',
195196
'readonly',
@@ -201,6 +202,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
201202
...selectorSchema('classProperty', true, [
202203
'abstract',
203204
'private',
205+
'#private',
204206
'protected',
205207
'public',
206208
'readonly',
@@ -226,6 +228,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
226228
...selectorSchema('property', true, [
227229
'abstract',
228230
'private',
231+
'#private',
229232
'protected',
230233
'public',
231234
'readonly',
@@ -238,6 +241,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
238241
...selectorSchema('classMethod', false, [
239242
'abstract',
240243
'private',
244+
'#private',
241245
'protected',
242246
'public',
243247
'requiresQuotes',
@@ -254,6 +258,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
254258
...selectorSchema('method', false, [
255259
'abstract',
256260
'private',
261+
'#private',
257262
'protected',
258263
'public',
259264
'requiresQuotes',

‎packages/eslint-plugin/src/rules/naming-convention.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ export default util.createRule<Options, MessageIds>({
127127
| TSESTree.TSParameterProperty,
128128
): Set<Modifiers> {
129129
const modifiers = new Set<Modifiers>();
130-
if (node.accessibility) {
130+
if ('key' in node && node.key.type === AST_NODE_TYPES.PrivateIdentifier) {
131+
modifiers.add(Modifiers['#private']);
132+
} else if (node.accessibility) {
131133
modifiers.add(Modifiers[node.accessibility]);
132134
} else {
133135
modifiers.add(Modifiers.public);

‎packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts‎

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,29 @@ ruleTester.run('naming-convention', rule, {
867867
},
868868
],
869869
},
870+
{
871+
code: `
872+
class foo {
873+
private someAttribute = 1;
874+
#some_attribute = 1;
875+
876+
private someMethod() {}
877+
#some_method() {}
878+
}
879+
`,
880+
parserOptions,
881+
options: [
882+
{
883+
selector: 'memberLike',
884+
format: ['camelCase'],
885+
},
886+
{
887+
selector: ['memberLike'],
888+
modifiers: ['#private'],
889+
format: ['snake_case'],
890+
},
891+
],
892+
},
870893
],
871894
invalid: [
872895
{
@@ -1972,5 +1995,89 @@ ruleTester.run('naming-convention', rule, {
19721995
},
19731996
],
19741997
},
1998+
{
1999+
code: `
2000+
class foo {
2001+
private firstPrivateField = 1;
2002+
// ❌ error
2003+
private first_private_field = 1;
2004+
// ❌ error
2005+
#secondPrivateField = 1;
2006+
#second_private_field = 1;
2007+
}
2008+
`,
2009+
parserOptions,
2010+
options: [
2011+
{
2012+
selector: 'memberLike',
2013+
format: ['camelCase'],
2014+
},
2015+
{
2016+
selector: ['memberLike'],
2017+
modifiers: ['#private'],
2018+
format: ['snake_case'],
2019+
},
2020+
],
2021+
errors: [
2022+
{
2023+
messageId: 'doesNotMatchFormat',
2024+
data: {
2025+
type: 'Class Property',
2026+
name: 'first_private_field',
2027+
formats: 'camelCase',
2028+
},
2029+
},
2030+
{
2031+
messageId: 'doesNotMatchFormat',
2032+
data: {
2033+
type: 'Class Property',
2034+
name: 'secondPrivateField',
2035+
formats: 'snake_case',
2036+
},
2037+
},
2038+
],
2039+
},
2040+
{
2041+
code: `
2042+
class foo {
2043+
private firstPrivateMethod() {}
2044+
// ❌ error
2045+
private first_private_method() {}
2046+
// ❌ error
2047+
#secondPrivateMethod() {}
2048+
#second_private_method() {}
2049+
}
2050+
`,
2051+
parserOptions,
2052+
options: [
2053+
{
2054+
selector: 'memberLike',
2055+
format: ['camelCase'],
2056+
},
2057+
{
2058+
selector: ['memberLike'],
2059+
modifiers: ['#private'],
2060+
format: ['snake_case'],
2061+
},
2062+
],
2063+
errors: [
2064+
{
2065+
messageId: 'doesNotMatchFormat',
2066+
data: {
2067+
type: 'Class Method',
2068+
name: 'first_private_method',
2069+
formats: 'camelCase',
2070+
},
2071+
},
2072+
{
2073+
messageId: 'doesNotMatchFormat',
2074+
data: {
2075+
type: 'Class Method',
2076+
name: 'secondPrivateMethod',
2077+
formats: 'snake_case',
2078+
},
2079+
},
2080+
],
2081+
},
19752082
],
19762083
});

0 commit comments

Comments
 (0)