@@ -5,10 +5,7 @@ import {
55} from "@typescript-eslint/utils/json-schema" ;
66import { type RuleContext } from "@typescript-eslint/utils/ts-eslint" ;
77import { deepmerge } from "deepmerge-ts" ;
8- import { isNodeFlagSet } from "ts-api-utils" ;
9- import type ts from "typescript" ;
108
11- import typescript from "#eslint-plugin-functional/conditional-imports/typescript" ;
129import {
1310 type IgnoreAccessorPatternOption ,
1411 type IgnoreIdentifierPatternOption ,
@@ -30,7 +27,11 @@ import {
3027 type RuleResult ,
3128 type NamedCreateRuleMetaWithCategory ,
3229} 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" ;
3435import {
3536 isArrayConstructorType ,
3637 isArrayExpression ,
@@ -166,39 +167,6 @@ const objectConstructorMutatorFunctions = new Set([
166167 "setPrototypeOf" ,
167168] ) ;
168169
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-
202170/**
203171 * Check if the given assignment expression violates this rule.
204172 */
@@ -231,15 +199,17 @@ function checkAssignmentExpression(
231199 } ;
232200 }
233201
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+ }
243213 }
244214
245215 return {
@@ -282,15 +252,17 @@ function checkUnaryExpression(
282252 } ;
283253 }
284254
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+ }
294266 }
295267
296268 return {
@@ -332,15 +304,17 @@ function checkUpdateExpression(
332304 } ;
333305 }
334306
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+ }
344318 }
345319
346320 return {
@@ -424,15 +398,25 @@ function checkCallExpression(
424398 arrayMutatorMethods . has ( node . callee . property . name ) &&
425399 ( ! ignoreImmediateMutation ||
426400 ! 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 ) )
431402 ) {
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+ }
436420 }
437421
438422 // Non-array object mutation (ex. Object.assign on identifier)?
@@ -448,15 +432,25 @@ function checkCallExpression(
448432 ignoreIdentifierPattern ,
449433 ignoreAccessorPattern ,
450434 ) &&
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 ) )
455436 ) {
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+ }
460454 }
461455
462456 return {
0 commit comments