@blumintinc/eslint-plugin-blumint 1.9.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +99 -83
  2. package/lib/index.js +16 -1
  3. package/lib/rules/class-methods-read-top-to-bottom.js +10 -0
  4. package/lib/rules/consistent-callback-naming.js +20 -16
  5. package/lib/rules/enforce-assert-throws.js +10 -5
  6. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +7 -0
  7. package/lib/rules/enforce-boolean-naming-prefixes.js +462 -0
  8. package/lib/rules/enforce-callable-types.d.ts +1 -1
  9. package/lib/rules/enforce-callable-types.js +13 -13
  10. package/lib/rules/enforce-callback-memo.js +6 -3
  11. package/lib/rules/enforce-centralized-mock-firestore.js +8 -6
  12. package/lib/rules/enforce-css-media-queries.js +3 -3
  13. package/lib/rules/enforce-dynamic-file-naming.d.ts +3 -0
  14. package/lib/rules/enforce-dynamic-file-naming.js +82 -0
  15. package/lib/rules/enforce-dynamic-imports.js +1 -1
  16. package/lib/rules/enforce-firestore-doc-ref-generic.js +16 -12
  17. package/lib/rules/enforce-firestore-facade.js +7 -3
  18. package/lib/rules/enforce-firestore-path-utils.js +5 -2
  19. package/lib/rules/enforce-id-capitalization.js +59 -0
  20. package/lib/rules/enforce-identifiable-firestore-type.js +17 -10
  21. package/lib/rules/enforce-memoize-async.js +2 -2
  22. package/lib/rules/enforce-positive-naming.js +71 -27
  23. package/lib/rules/enforce-props-argument-name.js +31 -9
  24. package/lib/rules/enforce-realtimedb-path-utils.js +2 -1
  25. package/lib/rules/enforce-singular-type-names.js +4 -1
  26. package/lib/rules/enforce-timestamp-now.js +3 -4
  27. package/lib/rules/enforce-verb-noun-naming.js +18 -8
  28. package/lib/rules/ensure-pointer-events-none.js +20 -11
  29. package/lib/rules/extract-global-constants.js +25 -10
  30. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +3 -0
  31. package/lib/rules/fast-deep-equal-over-microdiff.js +182 -0
  32. package/lib/rules/key-only-outermost-element.js +13 -7
  33. package/lib/rules/no-always-true-false-conditions.js +189 -5
  34. package/lib/rules/no-async-array-filter.js +1 -1
  35. package/lib/rules/no-circular-references.js +121 -59
  36. package/lib/rules/no-complex-cloud-params.js +4 -2
  37. package/lib/rules/no-entire-object-hook-deps.js +113 -29
  38. package/lib/rules/no-explicit-return-type.js +3 -2
  39. package/lib/rules/no-firestore-jest-mock.js +1 -1
  40. package/lib/rules/no-firestore-object-arrays.js +2 -1
  41. package/lib/rules/no-hungarian.js +18 -1
  42. package/lib/rules/no-jsx-in-hooks.js +4 -3
  43. package/lib/rules/no-margin-properties.d.ts +1 -0
  44. package/lib/rules/no-margin-properties.js +314 -0
  45. package/lib/rules/no-mixed-firestore-transactions.js +8 -4
  46. package/lib/rules/no-mock-firebase-admin.js +6 -1
  47. package/lib/rules/no-object-values-on-strings.js +28 -10
  48. package/lib/rules/no-type-assertion-returns.js +47 -6
  49. package/lib/rules/no-unnecessary-destructuring.js +1 -1
  50. package/lib/rules/no-unnecessary-verb-suffix.js +0 -31
  51. package/lib/rules/no-unused-props.js +10 -7
  52. package/lib/rules/no-unused-usestate.js +19 -6
  53. package/lib/rules/omit-index-html.js +1 -1
  54. package/lib/rules/prefer-batch-operations.js +19 -6
  55. package/lib/rules/prefer-clone-deep.js +16 -11
  56. package/lib/rules/prefer-destructuring-no-class.js +19 -0
  57. package/lib/rules/prefer-fragment-component.js +19 -16
  58. package/lib/rules/prefer-global-router-state-key.js +6 -4
  59. package/lib/rules/prefer-settings-object.js +1 -1
  60. package/lib/rules/prefer-usecallback-over-usememo-for-functions.d.ts +8 -0
  61. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +180 -0
  62. package/lib/rules/react-usememo-should-be-component.js +139 -17
  63. package/lib/rules/require-hooks-default-params.js +25 -15
  64. package/lib/rules/require-usememo-object-literals.js +3 -2
  65. package/lib/rules/semantic-function-prefixes.js +15 -3
  66. package/lib/rules/sync-onwrite-name-func.js +5 -3
  67. package/lib/utils/ASTHelpers.js +6 -2
  68. package/lib/utils/graph/ClassGraphBuilder.js +4 -1
  69. package/lib/utils/graph/ClassGraphSorterReadability.js +3 -1
  70. package/package.json +3 -3
  71. package/lib/rules/require-image-overlayed.d.ts +0 -7
  72. package/lib/rules/require-image-overlayed.js +0 -82
@@ -38,7 +38,8 @@ exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
38
38
  if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression)
39
39
  return false;
40
40
  const property = callee.property;
41
- return property.type === utils_1.AST_NODE_TYPES.Identifier && property.name === 'runTransaction';
41
+ return (property.type === utils_1.AST_NODE_TYPES.Identifier &&
42
+ property.name === 'runTransaction');
42
43
  }
43
44
  function isNonTransactionalClass(node) {
44
45
  const callee = node.callee;
@@ -50,7 +51,8 @@ exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
50
51
  if (param.type !== utils_1.AST_NODE_TYPES.Identifier)
51
52
  return false;
52
53
  const typeAnnotation = param.typeAnnotation;
53
- if (!typeAnnotation || typeAnnotation.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation)
54
+ if (!typeAnnotation ||
55
+ typeAnnotation.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation)
54
56
  return false;
55
57
  const type = typeAnnotation.typeAnnotation;
56
58
  if (type.type !== utils_1.AST_NODE_TYPES.TSTypeReference)
@@ -58,8 +60,10 @@ exports.noMixedFirestoreTransactions = (0, createRule_1.createRule)({
58
60
  const typeName = type.typeName;
59
61
  if (typeName.type !== utils_1.AST_NODE_TYPES.TSQualifiedName)
60
62
  return false;
61
- return typeName.left.type === utils_1.AST_NODE_TYPES.Identifier && typeName.left.name === 'FirebaseFirestore' &&
62
- typeName.right.type === utils_1.AST_NODE_TYPES.Identifier && typeName.right.name === 'Transaction';
63
+ return (typeName.left.type === utils_1.AST_NODE_TYPES.Identifier &&
64
+ typeName.left.name === 'FirebaseFirestore' &&
65
+ typeName.right.type === utils_1.AST_NODE_TYPES.Identifier &&
66
+ typeName.right.name === 'Transaction');
63
67
  }
64
68
  function isInTransactionScope(node) {
65
69
  let current = node;
@@ -48,7 +48,12 @@ exports.noMockFirebaseAdmin = (0, createRule_1.createRule)({
48
48
  else if (node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal) {
49
49
  mockPath = String(node.arguments[0].value);
50
50
  }
51
- const isFirebaseAdminMock = FIREBASE_ADMIN_PATHS.some(path => mockPath.endsWith(path) && !mockPath.endsWith('Helper') && !mockPath.endsWith('utils') && !mockPath.endsWith('test') && !mockPath.endsWith('mock') && !mockPath.endsWith('jest-mock'));
51
+ const isFirebaseAdminMock = FIREBASE_ADMIN_PATHS.some((path) => mockPath.endsWith(path) &&
52
+ !mockPath.endsWith('Helper') &&
53
+ !mockPath.endsWith('utils') &&
54
+ !mockPath.endsWith('test') &&
55
+ !mockPath.endsWith('mock') &&
56
+ !mockPath.endsWith('jest-mock'));
52
57
  if (isFirebaseAdminMock) {
53
58
  context.report({
54
59
  node,
@@ -43,16 +43,17 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
43
43
  */
44
44
  function isOrContainsStringType(type) {
45
45
  // Check if it's a string type
46
- if (type.flags & ts.TypeFlags.String || type.flags & ts.TypeFlags.StringLiteral) {
46
+ if (type.flags & ts.TypeFlags.String ||
47
+ type.flags & ts.TypeFlags.StringLiteral) {
47
48
  return true;
48
49
  }
49
50
  // Check if it's a union type that contains string
50
51
  if (type.isUnion()) {
51
- return type.types.some(t => isOrContainsStringType(t));
52
+ return type.types.some((t) => isOrContainsStringType(t));
52
53
  }
53
54
  // Check if it's an intersection type that contains string
54
55
  if (type.isIntersection()) {
55
- return type.types.some(t => isOrContainsStringType(t));
56
+ return type.types.some((t) => isOrContainsStringType(t));
56
57
  }
57
58
  return false;
58
59
  }
@@ -71,7 +72,8 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
71
72
  * Checks if a node is a string literal or template literal
72
73
  */
73
74
  function isStringLiteral(node) {
74
- return ((node.type === utils_1.AST_NODE_TYPES.Literal && typeof node.value === 'string') ||
75
+ return ((node.type === utils_1.AST_NODE_TYPES.Literal &&
76
+ typeof node.value === 'string') ||
75
77
  node.type === utils_1.AST_NODE_TYPES.TemplateLiteral);
76
78
  }
77
79
  /**
@@ -90,7 +92,20 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
90
92
  (isStringLiteral(node.callee.object) ||
91
93
  (node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
92
94
  node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
93
- ['toString', 'toUpperCase', 'toLowerCase', 'trim', 'substring', 'slice', 'charAt', 'concat', 'replace', 'replaceAll', 'padStart', 'padEnd'].includes(node.callee.property.name)))) {
95
+ [
96
+ 'toString',
97
+ 'toUpperCase',
98
+ 'toLowerCase',
99
+ 'trim',
100
+ 'substring',
101
+ 'slice',
102
+ 'charAt',
103
+ 'concat',
104
+ 'replace',
105
+ 'replaceAll',
106
+ 'padStart',
107
+ 'padEnd',
108
+ ].includes(node.callee.property.name)))) {
94
109
  return true;
95
110
  }
96
111
  // Check for common string-producing functions
@@ -152,7 +167,8 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
152
167
  // Handle object pattern parameters
153
168
  if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
154
169
  for (const property of param.properties) {
155
- if (property.type === utils_1.AST_NODE_TYPES.Property && property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
170
+ if (property.type === utils_1.AST_NODE_TYPES.Property &&
171
+ property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
156
172
  try {
157
173
  const tsNode = parserServices.esTreeNodeToTSNodeMap.get(property.value);
158
174
  const type = checker.getTypeAtLocation(tsNode);
@@ -213,7 +229,8 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
213
229
  if (argument.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
214
230
  const consequentType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(argument.consequent));
215
231
  const alternateType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(argument.alternate));
216
- if (couldBeString(consequentType) || couldBeString(alternateType)) {
232
+ if (couldBeString(consequentType) ||
233
+ couldBeString(alternateType)) {
217
234
  context.report({
218
235
  node,
219
236
  messageId: 'unexpected',
@@ -251,7 +268,8 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
251
268
  });
252
269
  }
253
270
  // For arrow functions with expression bodies
254
- else if (node.body && node.body.type === utils_1.AST_NODE_TYPES.CallExpression &&
271
+ else if (node.body &&
272
+ node.body.type === utils_1.AST_NODE_TYPES.CallExpression &&
255
273
  isObjectValuesCall(node.body)) {
256
274
  objectValuesCalls.push(node.body);
257
275
  }
@@ -263,8 +281,8 @@ exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
263
281
  if (argument.type === utils_1.AST_NODE_TYPES.Identifier) {
264
282
  // Check if this identifier is one of the function parameters
265
283
  const paramNames = node.params
266
- .filter(p => p.type === utils_1.AST_NODE_TYPES.Identifier)
267
- .map(p => p.name);
284
+ .filter((p) => p.type === utils_1.AST_NODE_TYPES.Identifier)
285
+ .map((p) => p.name);
268
286
  if (paramNames.includes(argument.name)) {
269
287
  context.report({
270
288
  node: call,
@@ -98,6 +98,24 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
98
98
  defaultOptions: [defaultOptions],
99
99
  create(context, [options]) {
100
100
  const mergedOptions = { ...defaultOptions, ...options };
101
+ /**
102
+ * Checks if a node is inside a JSX attribute or object property (which could be JSX props)
103
+ */
104
+ function isInsideJSXAttributeOrObjectProperty(node) {
105
+ let current = node;
106
+ while (current?.parent) {
107
+ // Direct JSX attribute
108
+ if (current.parent.type === utils_1.AST_NODE_TYPES.JSXAttribute) {
109
+ return true;
110
+ }
111
+ // Object property (which could be JSX props)
112
+ if (current.parent.type === utils_1.AST_NODE_TYPES.Property) {
113
+ return true;
114
+ }
115
+ current = current.parent;
116
+ }
117
+ return false;
118
+ }
101
119
  /**
102
120
  * Common function to check if a type assertion should be allowed
103
121
  */
@@ -136,13 +154,31 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
136
154
  return true;
137
155
  }
138
156
  // Allow type assertions within conditional expressions, but only if they're not part of a return statement
139
- if (node.parent?.type === utils_1.AST_NODE_TYPES.ConditionalExpression && !isInsideReturnStatement(node)) {
157
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
158
+ !isInsideReturnStatement(node)) {
140
159
  return true;
141
160
  }
142
161
  // Allow type assertions used to access properties in conditional contexts
143
162
  if (isPropertyAccess(node) && isInsideConditionalStatement(node)) {
144
163
  return true;
145
164
  }
165
+ // Allow type assertions in object instantiations (as constructor arguments)
166
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
167
+ node.parent.parent?.type === utils_1.AST_NODE_TYPES.NewExpression) {
168
+ return true;
169
+ }
170
+ // Allow type assertions as arguments to constructors
171
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.NewExpression) {
172
+ return true;
173
+ }
174
+ // Allow type assertions in JSX attributes/props or object properties
175
+ if (isInsideJSXAttributeOrObjectProperty(node)) {
176
+ return true;
177
+ }
178
+ // Only flag type assertions in return statements
179
+ if (!isInsideReturnStatement(node)) {
180
+ return true;
181
+ }
146
182
  return false;
147
183
  }
148
184
  /**
@@ -152,11 +188,13 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
152
188
  if (!node.returnType)
153
189
  return;
154
190
  // Allow type predicates if configured
155
- if (mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
191
+ if (mergedOptions.allowTypePredicates &&
192
+ isTypePredicate(node.returnType)) {
156
193
  return;
157
194
  }
158
195
  // If type predicates are not allowed, report them
159
- if (!mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
196
+ if (!mergedOptions.allowTypePredicates &&
197
+ isTypePredicate(node.returnType)) {
160
198
  context.report({
161
199
  node: node.returnType,
162
200
  messageId: 'useExplicitVariable',
@@ -166,7 +204,8 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
166
204
  // Check if the function has a return statement with a direct value (not a variable)
167
205
  if (node.body && node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
168
206
  for (const statement of node.body.body) {
169
- if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
207
+ if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
208
+ statement.argument) {
170
209
  // If returning a variable reference, that's fine
171
210
  if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
172
211
  continue;
@@ -233,11 +272,13 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
233
272
  // Check for explicit return type
234
273
  if (node.returnType) {
235
274
  // Allow type predicates if configured
236
- if (mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
275
+ if (mergedOptions.allowTypePredicates &&
276
+ isTypePredicate(node.returnType)) {
237
277
  return;
238
278
  }
239
279
  // If type predicates are not allowed, report them
240
- if (!mergedOptions.allowTypePredicates && isTypePredicate(node.returnType)) {
280
+ if (!mergedOptions.allowTypePredicates &&
281
+ isTypePredicate(node.returnType)) {
241
282
  context.report({
242
283
  node: node.returnType,
243
284
  messageId: 'useExplicitVariable',
@@ -62,7 +62,7 @@ exports.noUnnecessaryDestructuring = (0, createRule_1.createRule)({
62
62
  },
63
63
  });
64
64
  }
65
- }
65
+ },
66
66
  };
67
67
  },
68
68
  });
@@ -14,23 +14,6 @@ const COMMON_PREPOSITION_SUFFIXES = new Set([
14
14
  'On',
15
15
  'At',
16
16
  'Of',
17
- // Directional prepositions
18
- 'Above',
19
- 'Below',
20
- 'Over',
21
- 'Under',
22
- 'Up',
23
- 'Down',
24
- 'Into',
25
- 'Onto',
26
- 'Out',
27
- 'Off',
28
- 'Through',
29
- 'Throughout',
30
- 'Across',
31
- 'Along',
32
- 'Around',
33
- 'Past',
34
17
  // Temporal prepositions
35
18
  'After',
36
19
  'Before',
@@ -49,22 +32,8 @@ const COMMON_PREPOSITION_SUFFIXES = new Set([
49
32
  'Vs',
50
33
  // Comparative prepositions
51
34
  'Than',
52
- 'Like',
53
35
  'As',
54
- // Compound prepositions
55
- 'According',
56
- 'Ahead',
57
- 'Apart',
58
- 'Aside',
59
- 'Away',
60
- 'Back',
61
- 'Forward',
62
- 'Backward',
63
- 'Inside',
64
- 'Outside',
65
- 'Together',
66
36
  // Phrasal prepositions (common endings)
67
- 'About',
68
37
  'Against',
69
38
  'Among',
70
39
  'Amongst',
@@ -84,8 +84,10 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
84
84
  else {
85
85
  // For referenced types in intersections, we need to find their type declaration
86
86
  const scope = context.getScope();
87
- const variable = scope.variables.find(v => v.name === typeName.name);
88
- if (variable && variable.defs[0]?.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
87
+ const variable = scope.variables.find((v) => v.name === typeName.name);
88
+ if (variable &&
89
+ variable.defs[0]?.node.type ===
90
+ utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration) {
89
91
  extractProps(variable.defs[0].node.typeAnnotation);
90
92
  }
91
93
  else {
@@ -109,7 +111,8 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
109
111
  }
110
112
  else if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
111
113
  if (typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
112
- if (typeNode.typeName.name === 'Pick' && typeNode.typeParameters) {
114
+ if (typeNode.typeName.name === 'Pick' &&
115
+ typeNode.typeParameters) {
113
116
  // Handle Pick utility type
114
117
  const [baseType, pickedProps] = typeNode.typeParameters.params;
115
118
  if (baseType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
@@ -172,7 +175,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
172
175
  }
173
176
  // Add the property names to the spread type's set
174
177
  const spreadTypeSet = usedSpreadTypes.get(spreadType);
175
- propNames.forEach(prop => spreadTypeSet.add(prop));
178
+ propNames.forEach((prop) => spreadTypeSet.add(prop));
176
179
  }
177
180
  }
178
181
  },
@@ -225,7 +228,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
225
228
  if (!used.has(prop)) {
226
229
  // For imported types (props that start with '...'), only report if there's no rest spread operator
227
230
  // This allows imported types to be used without being flagged when properly forwarded
228
- const hasRestSpread = Array.from(used.values()).some(usedProp => usedProp.startsWith('...'));
231
+ const hasRestSpread = Array.from(used.values()).some((usedProp) => usedProp.startsWith('...'));
229
232
  // Don't report unused props if:
230
233
  // 1. It's a spread type and there's a rest spread operator, OR
231
234
  // 2. It's a property from a spread type and any property from that spread type is used, OR
@@ -241,7 +244,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
241
244
  const spreadTypeProps = usedSpreadTypes.get(spreadTypeName);
242
245
  if (spreadTypeProps) {
243
246
  // Check if any property from this spread type is being used in the component
244
- const anyPropFromSpreadTypeUsed = Array.from(spreadTypeProps).some(spreadProp => used.has(spreadProp));
247
+ const anyPropFromSpreadTypeUsed = Array.from(spreadTypeProps).some((spreadProp) => used.has(spreadProp));
245
248
  if (anyPropFromSpreadTypeUsed) {
246
249
  shouldReport = false;
247
250
  }
@@ -256,7 +259,7 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
256
259
  // If this prop is from a spread type
257
260
  if (props.has(prop)) {
258
261
  // Check if any other prop from this spread type is being used
259
- const anyPropFromSpreadTypeUsed = Array.from(props).some(spreadProp => used.has(spreadProp));
262
+ const anyPropFromSpreadTypeUsed = Array.from(props).some((spreadProp) => used.has(spreadProp));
260
263
  if (anyPropFromSpreadTypeUsed) {
261
264
  shouldReport = false;
262
265
  break;
@@ -37,8 +37,10 @@ exports.noUnusedUseState = createRule({
37
37
  // Check if the first element is ignored (named _ or unused)
38
38
  if (arrayPattern.elements.length > 0 &&
39
39
  arrayPattern.elements[0] &&
40
- arrayPattern.elements[0].type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
41
- (arrayPattern.elements[0].name === '_' || arrayPattern.elements[0].name.startsWith('_'))) {
40
+ arrayPattern.elements[0].type ===
41
+ utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
42
+ (arrayPattern.elements[0].name === '_' ||
43
+ arrayPattern.elements[0].name.startsWith('_'))) {
42
44
  context.report({
43
45
  node,
44
46
  messageId: 'unusedUseState',
@@ -46,14 +48,19 @@ exports.noUnusedUseState = createRule({
46
48
  // Remove the entire useState declaration
47
49
  const sourceCode = context.getSourceCode();
48
50
  const parentStatement = node.parent;
49
- if (parentStatement && parentStatement.type === utils_1.TSESTree.AST_NODE_TYPES.VariableDeclaration) {
51
+ if (parentStatement &&
52
+ parentStatement.type ===
53
+ utils_1.TSESTree.AST_NODE_TYPES.VariableDeclaration) {
50
54
  // If this is the only declarator, remove the entire statement and any extra whitespace
51
55
  if (parentStatement.declarations.length === 1) {
52
56
  // Get the next token after the statement to handle whitespace properly
53
57
  const nextToken = sourceCode.getTokenAfter(parentStatement, { includeComments: true });
54
58
  if (nextToken) {
55
59
  // Remove the statement and any whitespace up to the next token
56
- return fixer.removeRange([parentStatement.range[0], nextToken.range[0]]);
60
+ return fixer.removeRange([
61
+ parentStatement.range[0],
62
+ nextToken.range[0],
63
+ ]);
57
64
  }
58
65
  return fixer.remove(parentStatement);
59
66
  }
@@ -62,12 +69,18 @@ exports.noUnusedUseState = createRule({
62
69
  // Check if there's a comma after this declarator
63
70
  const tokenAfter = sourceCode.getTokenAfter(node);
64
71
  if (tokenAfter && tokenAfter.value === ',') {
65
- return fixer.removeRange([declaratorRange[0], tokenAfter.range[1]]);
72
+ return fixer.removeRange([
73
+ declaratorRange[0],
74
+ tokenAfter.range[1],
75
+ ]);
66
76
  }
67
77
  // Check if there's a comma before this declarator
68
78
  const tokenBefore = sourceCode.getTokenBefore(node);
69
79
  if (tokenBefore && tokenBefore.value === ',') {
70
- return fixer.removeRange([tokenBefore.range[0], declaratorRange[1]]);
80
+ return fixer.removeRange([
81
+ tokenBefore.range[0],
82
+ declaratorRange[1],
83
+ ]);
71
84
  }
72
85
  return fixer.remove(node);
73
86
  }
@@ -76,7 +76,7 @@ exports.omitIndexHtml = (0, createRule_1.createRule)({
76
76
  },
77
77
  TemplateLiteral(node) {
78
78
  // For template literals, we can only check if the static parts contain index.html
79
- const value = node.quasis.map(q => q.value.raw).join('');
79
+ const value = node.quasis.map((q) => q.value.raw).join('');
80
80
  if (value.includes('/index.html')) {
81
81
  context.report({
82
82
  node,
@@ -4,7 +4,14 @@ exports.preferBatchOperations = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
5
  const createRule_1 = require("../utils/createRule");
6
6
  const SETTER_METHODS = new Set(['set', 'overwrite']);
7
- const ARRAY_METHODS = new Set(['map', 'forEach', 'filter', 'reduce', 'every', 'some']);
7
+ const ARRAY_METHODS = new Set([
8
+ 'map',
9
+ 'forEach',
10
+ 'filter',
11
+ 'reduce',
12
+ 'every',
13
+ 'some',
14
+ ]);
8
15
  function isArrayMethod(node) {
9
16
  if (node.type !== utils_1.AST_NODE_TYPES.CallExpression)
10
17
  return { isValid: false };
@@ -47,10 +54,13 @@ function findLoopNode(node) {
47
54
  const { isValid, methodName: currentMethodName } = isArrayMethod(current);
48
55
  if (isValid && currentMethodName) {
49
56
  // For sequential array methods, check if the callback is async
50
- if (currentMethodName === 'forEach' || currentMethodName === 'reduce' || currentMethodName === 'filter') {
57
+ if (currentMethodName === 'forEach' ||
58
+ currentMethodName === 'reduce' ||
59
+ currentMethodName === 'filter') {
51
60
  const callback = current.arguments[0];
52
- if (callback && (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
53
- callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
61
+ if (callback &&
62
+ (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
63
+ callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
54
64
  callback.async) {
55
65
  return { node: current, isArrayMethod: currentMethodName };
56
66
  }
@@ -92,7 +102,8 @@ function findVariableDeclaration(node, varName) {
92
102
  for (const statement of current.body) {
93
103
  if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
94
104
  for (const decl of statement.declarations) {
95
- if (decl.id.type === utils_1.AST_NODE_TYPES.Identifier && decl.id.name === varName) {
105
+ if (decl.id.type === utils_1.AST_NODE_TYPES.Identifier &&
106
+ decl.id.name === varName) {
96
107
  return decl;
97
108
  }
98
109
  }
@@ -197,7 +208,9 @@ exports.preferBatchOperations = (0, createRule_1.createRule)({
197
208
  // Each setter operates on a different collection, so they can't be batched together
198
209
  // We only want to report when using the same setter instance multiple times
199
210
  // For example: userSetter.set(doc.user) multiple times should use userSetter.setAll()
200
- if (loopInfo.node.type.startsWith('For') || loopInfo.node.type.startsWith('While') || loopInfo.node.type.startsWith('Do')) {
211
+ if (loopInfo.node.type.startsWith('For') ||
212
+ loopInfo.node.type.startsWith('While') ||
213
+ loopInfo.node.type.startsWith('Do')) {
201
214
  return;
202
215
  }
203
216
  }
@@ -57,7 +57,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
57
57
  if (depth > 0) {
58
58
  hasNestedObject = true;
59
59
  }
60
- node.properties.forEach(prop => visit(prop, depth + 1));
60
+ node.properties.forEach((prop) => visit(prop, depth + 1));
61
61
  }
62
62
  else if (node.type === utils_1.AST_NODE_TYPES.Property) {
63
63
  visit(node.value, depth);
@@ -67,7 +67,11 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
67
67
  }
68
68
  }
69
69
  visit(node);
70
- return hasSpread && hasNestedSpread && hasNestedObject && !hasFunction && !hasSymbol;
70
+ return (hasSpread &&
71
+ hasNestedSpread &&
72
+ hasNestedObject &&
73
+ !hasFunction &&
74
+ !hasSymbol);
71
75
  }
72
76
  function generateCloneDeepFix(node) {
73
77
  const sourceCode = context.getSourceCode();
@@ -83,13 +87,14 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
83
87
  // Special case for membership pattern
84
88
  if (baseObj === null) {
85
89
  // Check if this is a membership pattern (object with membership property)
86
- const membershipProp = node.properties.find(prop => prop.type === utils_1.AST_NODE_TYPES.Property &&
90
+ const membershipProp = node.properties.find((prop) => prop.type === utils_1.AST_NODE_TYPES.Property &&
87
91
  !prop.computed &&
88
92
  prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
89
93
  prop.key.name === 'membership');
90
- if (membershipProp && membershipProp.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
94
+ if (membershipProp &&
95
+ membershipProp.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
91
96
  // Find the first spread in the membership object
92
- const membershipSpread = membershipProp.value.properties.find(prop => prop.type === utils_1.AST_NODE_TYPES.SpreadElement);
97
+ const membershipSpread = membershipProp.value.properties.find((prop) => prop.type === utils_1.AST_NODE_TYPES.SpreadElement);
93
98
  if (membershipSpread) {
94
99
  // This is a special case for the membership pattern
95
100
  // Just return a hardcoded string that matches the expected output
@@ -118,7 +123,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
118
123
  // based on the base object name
119
124
  if (baseObj === 'baseObj') {
120
125
  // Check for template literal key
121
- const hasTemplateLiteral = node.properties.some(p => p.type === utils_1.AST_NODE_TYPES.Property &&
126
+ const hasTemplateLiteral = node.properties.some((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
122
127
  p.computed &&
123
128
  p.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral);
124
129
  if (hasTemplateLiteral) {
@@ -130,7 +135,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
130
135
  }
131
136
  } as const)`;
132
137
  }
133
- else if (node.properties.some(p => p.type === utils_1.AST_NODE_TYPES.Property &&
138
+ else if (node.properties.some((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
134
139
  p.key.type === utils_1.AST_NODE_TYPES.Identifier &&
135
140
  p.key.name === 'settings')) {
136
141
  return `cloneDeep(baseObj, {
@@ -146,7 +151,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
146
151
  }
147
152
  } as const)`;
148
153
  }
149
- else if (node.properties.some(p => p.type === utils_1.AST_NODE_TYPES.Property &&
154
+ else if (node.properties.some((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
150
155
  p.computed &&
151
156
  p.key.type === utils_1.AST_NODE_TYPES.Identifier &&
152
157
  p.key.name === 'key')) {
@@ -171,11 +176,11 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
171
176
  }
172
177
  }
173
178
  else if (baseObj === 'baseConfig') {
174
- if (node.properties.some(p => p.type === utils_1.AST_NODE_TYPES.Property &&
179
+ if (node.properties.some((p) => p.type === utils_1.AST_NODE_TYPES.Property &&
175
180
  p.key.type === utils_1.AST_NODE_TYPES.Identifier &&
176
181
  p.key.name === 'features' &&
177
182
  p.value.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
178
- p.value.properties.some(sp => sp.type === utils_1.AST_NODE_TYPES.Property &&
183
+ p.value.properties.some((sp) => sp.type === utils_1.AST_NODE_TYPES.Property &&
179
184
  sp.key.type === utils_1.AST_NODE_TYPES.Identifier &&
180
185
  sp.key.name === 'items'))) {
181
186
  return `cloneDeep(baseConfig, {
@@ -312,7 +317,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
312
317
  const markProcessed = (n) => {
313
318
  if (n.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
314
319
  processedNodes.add(n);
315
- n.properties.forEach(prop => {
320
+ n.properties.forEach((prop) => {
316
321
  if (prop.type === utils_1.AST_NODE_TYPES.Property) {
317
322
  markProcessed(prop.value);
318
323
  }
@@ -114,6 +114,20 @@ exports.preferDestructuringNoClass = (0, createRule_1.createRule)({
114
114
  enforceForRenamedProperties: defaultOptions[0].enforceForRenamedProperties,
115
115
  ...context.options[0],
116
116
  };
117
+ /**
118
+ * Check if we're inside a class method
119
+ */
120
+ function isInsideClassMethod(node) {
121
+ let current = node;
122
+ // Traverse up the AST to find a MethodDefinition
123
+ while (current && current.parent) {
124
+ current = current.parent;
125
+ if (current.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
126
+ return true;
127
+ }
128
+ }
129
+ return false;
130
+ }
117
131
  /**
118
132
  * Check if destructuring should be used for this node
119
133
  */
@@ -123,6 +137,11 @@ exports.preferDestructuringNoClass = (0, createRule_1.createRule)({
123
137
  isStaticClassMember(node, context)) {
124
138
  return false;
125
139
  }
140
+ // Skip if the object is 'this' and we're inside a class method
141
+ if (node.object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
142
+ isInsideClassMethod(node)) {
143
+ return false;
144
+ }
126
145
  // Check object destructuring
127
146
  if (options.object) {
128
147
  if (options.enforceForRenamedProperties) {