@@ -5,10 +5,7 @@ import {
5
5
} from "@typescript-eslint/utils/json-schema" ;
6
6
import { type RuleContext } from "@typescript-eslint/utils/ts-eslint" ;
7
7
import { deepmerge } from "deepmerge-ts" ;
8
- import { isNodeFlagSet } from "ts-api-utils" ;
9
- import type ts from "typescript" ;
10
8
11
- import typescript from "#eslint-plugin-functional/conditional-imports/typescript" ;
12
9
import {
13
10
type IgnoreAccessorPatternOption ,
14
11
type IgnoreIdentifierPatternOption ,
@@ -30,7 +27,11 @@ import {
30
27
type RuleResult ,
31
28
type NamedCreateRuleMetaWithCategory ,
32
29
} from "#eslint-plugin-functional/utils/rule" ;
33
- import { isInConstructor } from "#eslint-plugin-functional/utils/tree" ;
30
+ import {
31
+ findRootIdentifier ,
32
+ isDefinedByMutableVaraible ,
33
+ isInConstructor ,
34
+ } from "#eslint-plugin-functional/utils/tree" ;
34
35
import {
35
36
isArrayConstructorType ,
36
37
isArrayExpression ,
@@ -166,39 +167,6 @@ const objectConstructorMutatorFunctions = new Set([
166
167
"setPrototypeOf" ,
167
168
] ) ;
168
169
169
- /**
170
- * Is the given identifier defined by a mutable variable (let or var)?
171
- */
172
- function isDefinedByMutableVaraible (
173
- node : TSESTree . Identifier ,
174
- context : Readonly < RuleContext < keyof typeof errorMessages , Options > > ,
175
- ) {
176
- if ( typescript === undefined ) {
177
- return true ;
178
- }
179
-
180
- const tsNode = context . parserServices ?. esTreeNodeToTSNodeMap . get ( node ) ;
181
- const variableDeclaration =
182
- tsNode !== undefined &&
183
- "flowNode" in tsNode &&
184
- typeof tsNode . flowNode === "object" &&
185
- tsNode . flowNode !== null &&
186
- "node" in tsNode . flowNode &&
187
- typeof tsNode . flowNode . node === "object" &&
188
- tsNode . flowNode . node !== null &&
189
- typescript . isVariableDeclaration ( tsNode . flowNode . node as ts . Node )
190
- ? ( tsNode . flowNode . node as ts . VariableDeclaration )
191
- : undefined ;
192
-
193
- const variableDeclarationList = variableDeclaration ?. parent ;
194
-
195
- return (
196
- variableDeclarationList === undefined ||
197
- ! typescript . isVariableDeclarationList ( variableDeclarationList ) ||
198
- ! isNodeFlagSet ( variableDeclarationList , typescript . NodeFlags . Const )
199
- ) ;
200
- }
201
-
202
170
/**
203
171
* Check if the given assignment expression violates this rule.
204
172
*/
@@ -231,15 +199,17 @@ function checkAssignmentExpression(
231
199
} ;
232
200
}
233
201
234
- if (
235
- ignoreNonConstDeclarations &&
236
- isIdentifier ( node . left . object ) &&
237
- isDefinedByMutableVaraible ( node . left . object , context )
238
- ) {
239
- return {
240
- context,
241
- descriptors : [ ] ,
242
- } ;
202
+ if ( ignoreNonConstDeclarations ) {
203
+ const rootIdentifier = findRootIdentifier ( node . left . object ) ;
204
+ if (
205
+ rootIdentifier !== undefined &&
206
+ isDefinedByMutableVaraible ( rootIdentifier , context )
207
+ ) {
208
+ return {
209
+ context,
210
+ descriptors : [ ] ,
211
+ } ;
212
+ }
243
213
}
244
214
245
215
return {
@@ -282,15 +252,17 @@ function checkUnaryExpression(
282
252
} ;
283
253
}
284
254
285
- if (
286
- ignoreNonConstDeclarations &&
287
- isIdentifier ( node . argument . object ) &&
288
- isDefinedByMutableVaraible ( node . argument . object , context )
289
- ) {
290
- return {
291
- context,
292
- descriptors : [ ] ,
293
- } ;
255
+ if ( ignoreNonConstDeclarations ) {
256
+ const rootIdentifier = findRootIdentifier ( node . argument . object ) ;
257
+ if (
258
+ rootIdentifier !== undefined &&
259
+ isDefinedByMutableVaraible ( rootIdentifier , context )
260
+ ) {
261
+ return {
262
+ context,
263
+ descriptors : [ ] ,
264
+ } ;
265
+ }
294
266
}
295
267
296
268
return {
@@ -332,15 +304,17 @@ function checkUpdateExpression(
332
304
} ;
333
305
}
334
306
335
- if (
336
- ignoreNonConstDeclarations &&
337
- isIdentifier ( node . argument . object ) &&
338
- isDefinedByMutableVaraible ( node . argument . object , context )
339
- ) {
340
- return {
341
- context,
342
- descriptors : [ ] ,
343
- } ;
307
+ if ( ignoreNonConstDeclarations ) {
308
+ const rootIdentifier = findRootIdentifier ( node . argument . object ) ;
309
+ if (
310
+ rootIdentifier !== undefined &&
311
+ isDefinedByMutableVaraible ( rootIdentifier , context )
312
+ ) {
313
+ return {
314
+ context,
315
+ descriptors : [ ] ,
316
+ } ;
317
+ }
344
318
}
345
319
346
320
return {
@@ -424,15 +398,25 @@ function checkCallExpression(
424
398
arrayMutatorMethods . has ( node . callee . property . name ) &&
425
399
( ! ignoreImmediateMutation ||
426
400
! isInChainCallAndFollowsNew ( node . callee , context ) ) &&
427
- isArrayType ( getTypeOfNode ( node . callee . object , context ) ) &&
428
- ( ! ignoreNonConstDeclarations ||
429
- ! isIdentifier ( node . callee . object ) ||
430
- ! isDefinedByMutableVaraible ( node . callee . object , context ) )
401
+ isArrayType ( getTypeOfNode ( node . callee . object , context ) )
431
402
) {
432
- return {
433
- context,
434
- descriptors : [ { node, messageId : "array" } ] ,
435
- } ;
403
+ if ( ignoreNonConstDeclarations ) {
404
+ const rootIdentifier = findRootIdentifier ( node . callee . object ) ;
405
+ if (
406
+ rootIdentifier === undefined ||
407
+ ! isDefinedByMutableVaraible ( rootIdentifier , context )
408
+ ) {
409
+ return {
410
+ context,
411
+ descriptors : [ { node, messageId : "array" } ] ,
412
+ } ;
413
+ }
414
+ } else {
415
+ return {
416
+ context,
417
+ descriptors : [ { node, messageId : "array" } ] ,
418
+ } ;
419
+ }
436
420
}
437
421
438
422
// Non-array object mutation (ex. Object.assign on identifier)?
@@ -448,15 +432,25 @@ function checkCallExpression(
448
432
ignoreIdentifierPattern ,
449
433
ignoreAccessorPattern ,
450
434
) &&
451
- isObjectConstructorType ( getTypeOfNode ( node . callee . object , context ) ) &&
452
- ( ! ignoreNonConstDeclarations ||
453
- ! isIdentifier ( node . callee . object ) ||
454
- ! isDefinedByMutableVaraible ( node . callee . object , context ) )
435
+ isObjectConstructorType ( getTypeOfNode ( node . callee . object , context ) )
455
436
) {
456
- return {
457
- context,
458
- descriptors : [ { node, messageId : "object" } ] ,
459
- } ;
437
+ if ( ignoreNonConstDeclarations ) {
438
+ const rootIdentifier = findRootIdentifier ( node . callee . object ) ;
439
+ if (
440
+ rootIdentifier === undefined ||
441
+ ! isDefinedByMutableVaraible ( rootIdentifier , context )
442
+ ) {
443
+ return {
444
+ context,
445
+ descriptors : [ { node, messageId : "object" } ] ,
446
+ } ;
447
+ }
448
+ } else {
449
+ return {
450
+ context,
451
+ descriptors : [ { node, messageId : "object" } ] ,
452
+ } ;
453
+ }
460
454
}
461
455
462
456
return {
0 commit comments