2
2
AST_NODE_TYPES ,
3
3
ESLintUtils ,
4
4
TSESTree ,
5
- TSESLint ,
6
5
} from "@typescript-eslint/utils" ;
7
6
8
7
/**
@@ -98,7 +97,6 @@ const arrayInitStyle: ESLintUtils.RuleModule<
98
97
* Limitations:
99
98
* - Only static properties and indices are supported
100
99
* - Dynamic properties (obj[variable]) are ignored
101
- * - Constants, enums, and imports are skipped
102
100
*
103
101
* Example:
104
102
* // Bad - repeated access
@@ -125,7 +123,7 @@ const noRepeatedMemberAccess: ESLintUtils.RuleModule<
125
123
ESLintUtils . RuleListener // Listener type
126
124
> = createRule ( {
127
125
name : "no-repeated-member-access" ,
128
- defaultOptions : [ { minOccurrences : 0 } ] , // Provide a default object matching the options structure
126
+ defaultOptions : [ { minOccurrences : 2 } ] , // Provide a default object matching the options structure
129
127
meta : {
130
128
type : "suggestion" ,
131
129
docs : {
@@ -147,9 +145,6 @@ const noRepeatedMemberAccess: ESLintUtils.RuleModule<
147
145
] ,
148
146
} ,
149
147
create ( context ) {
150
- // Store nodes for each object chain in each scope for auto-fixing
151
- const chainNodesMap = new Map < string , TSESTree . MemberExpression [ ] > ( ) ;
152
-
153
148
function getObjectChain ( node : TSESTree . Node ) {
154
149
// node is the outermost MemberExpression, e.g. ctx.data.v1
155
150
let current = node ;
@@ -263,6 +258,10 @@ const noRepeatedMemberAccess: ESLintUtils.RuleModule<
263
258
}
264
259
return null ;
265
260
}
261
+
262
+ // Store nodes for each object chain in each scope for auto-fixing
263
+ const chainNodesMap = new Map < string , TSESTree . MemberExpression [ ] > ( ) ;
264
+
266
265
const occurrences = new Map ( ) ;
267
266
const minOccurrences = context . options [ 0 ] ?. minOccurrences || 2 ;
268
267
@@ -275,60 +274,13 @@ const noRepeatedMemberAccess: ESLintUtils.RuleModule<
275
274
if ( ! objectChain ) return ;
276
275
277
276
const baseObjectName = objectChain . split ( / [ . [ ] / ) [ 0 ] ;
277
+ // no need to continue if what we extract is the same as the base object
278
+ if ( objectChain === baseObjectName ) return ;
279
+
278
280
// Use scope range as part of the key
279
281
const scope = context . sourceCode . getScope ( node ) ;
280
282
if ( ! scope || ! scope . block || ! scope . block . range ) return ;
281
283
282
- // Find variable in scope chain
283
- const variable = findVariableInScopeChain ( scope , baseObjectName ) ;
284
-
285
- // Skip certain variable types that shouldn't be extracted
286
- if (
287
- variable &&
288
- ( isConstVariable ( variable ) ||
289
- isEnumVariable ( variable ) ||
290
- isImportVariable ( variable ) )
291
- ) {
292
- return ;
293
- }
294
-
295
- // Helper functions
296
- function findVariableInScopeChain ( scope : TSESLint . Scope . Scope , name : string ) {
297
- let currentScope : TSESLint . Scope . Scope | null = scope ;
298
- while ( currentScope ) {
299
- const variable = currentScope . variables . find (
300
- ( v ) => v . name === name
301
- ) ;
302
- if ( variable ) return variable ;
303
- currentScope = currentScope . upper ;
304
- }
305
- return null ;
306
- }
307
-
308
- function isConstVariable ( variable : TSESLint . Scope . Variable ) {
309
- return variable . defs . every (
310
- ( def ) => def . node && "kind" in def . node && def . node . kind === "const"
311
- ) ;
312
- }
313
-
314
- function isEnumVariable ( variable : TSESLint . Scope . Variable ) {
315
- return variable . defs . some (
316
- ( def ) =>
317
- ( def . parent as TSESTree . Node ) ?. type ===
318
- AST_NODE_TYPES . TSEnumDeclaration
319
- ) ;
320
- }
321
-
322
- function isImportVariable ( variable : TSESLint . Scope . Variable ) {
323
- return variable . defs . some (
324
- ( def ) =>
325
- def . type === "ImportBinding" ||
326
- ( def . node &&
327
- "type" in def . node &&
328
- def . node . type === AST_NODE_TYPES . ImportDeclaration )
329
- ) ;
330
- }
331
-
332
284
const key = `${ scope . block . range . join ( "-" ) } -${ objectChain } ` ;
333
285
334
286
// Store node for auto-fixing
0 commit comments