@blumintinc/eslint-plugin-blumint 1.10.0 → 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 (67) hide show
  1. package/README.md +99 -84
  2. package/lib/index.js +7 -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.js +9 -7
  14. package/lib/rules/enforce-dynamic-imports.js +1 -1
  15. package/lib/rules/enforce-firestore-doc-ref-generic.js +16 -12
  16. package/lib/rules/enforce-firestore-facade.js +7 -3
  17. package/lib/rules/enforce-firestore-path-utils.js +5 -2
  18. package/lib/rules/enforce-id-capitalization.js +23 -5
  19. package/lib/rules/enforce-identifiable-firestore-type.js +17 -10
  20. package/lib/rules/enforce-memoize-async.js +2 -2
  21. package/lib/rules/enforce-positive-naming.js +71 -27
  22. package/lib/rules/enforce-props-argument-name.js +31 -9
  23. package/lib/rules/enforce-realtimedb-path-utils.js +2 -1
  24. package/lib/rules/enforce-singular-type-names.js +4 -1
  25. package/lib/rules/enforce-timestamp-now.js +3 -4
  26. package/lib/rules/enforce-verb-noun-naming.js +18 -8
  27. package/lib/rules/ensure-pointer-events-none.js +20 -11
  28. package/lib/rules/extract-global-constants.js +25 -10
  29. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +3 -0
  30. package/lib/rules/fast-deep-equal-over-microdiff.js +182 -0
  31. package/lib/rules/key-only-outermost-element.js +13 -7
  32. package/lib/rules/no-always-true-false-conditions.js +52 -18
  33. package/lib/rules/no-async-array-filter.js +1 -1
  34. package/lib/rules/no-circular-references.js +121 -59
  35. package/lib/rules/no-entire-object-hook-deps.js +113 -29
  36. package/lib/rules/no-explicit-return-type.js +3 -2
  37. package/lib/rules/no-firestore-jest-mock.js +1 -1
  38. package/lib/rules/no-firestore-object-arrays.js +2 -1
  39. package/lib/rules/no-hungarian.js +2 -2
  40. package/lib/rules/no-jsx-in-hooks.js +4 -3
  41. package/lib/rules/no-margin-properties.js +25 -18
  42. package/lib/rules/no-mixed-firestore-transactions.js +8 -4
  43. package/lib/rules/no-mock-firebase-admin.js +6 -1
  44. package/lib/rules/no-object-values-on-strings.js +28 -10
  45. package/lib/rules/no-unnecessary-destructuring.js +1 -1
  46. package/lib/rules/no-unnecessary-verb-suffix.js +0 -31
  47. package/lib/rules/no-unused-props.js +10 -7
  48. package/lib/rules/no-unused-usestate.js +19 -6
  49. package/lib/rules/omit-index-html.js +1 -1
  50. package/lib/rules/prefer-batch-operations.js +19 -6
  51. package/lib/rules/prefer-clone-deep.js +16 -11
  52. package/lib/rules/prefer-destructuring-no-class.js +19 -0
  53. package/lib/rules/prefer-fragment-component.js +19 -16
  54. package/lib/rules/prefer-global-router-state-key.js +6 -4
  55. package/lib/rules/prefer-settings-object.js +1 -1
  56. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +12 -4
  57. package/lib/rules/react-usememo-should-be-component.js +64 -18
  58. package/lib/rules/require-hooks-default-params.js +25 -15
  59. package/lib/rules/require-usememo-object-literals.js +3 -2
  60. package/lib/rules/semantic-function-prefixes.js +15 -3
  61. package/lib/rules/sync-onwrite-name-func.js +5 -3
  62. package/lib/utils/ASTHelpers.js +6 -2
  63. package/lib/utils/graph/ClassGraphBuilder.js +4 -1
  64. package/lib/utils/graph/ClassGraphSorterReadability.js +3 -1
  65. package/package.json +3 -3
  66. package/lib/rules/require-image-overlayed.d.ts +0 -7
  67. package/lib/rules/require-image-overlayed.js +0 -82
@@ -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) {
@@ -76,7 +76,7 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
76
76
  reactImportNode = node;
77
77
  }
78
78
  // Keep track of default import for prioritization
79
- const hasDefaultImport = node.specifiers.some(spec => spec.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier);
79
+ const hasDefaultImport = node.specifiers.some((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier);
80
80
  if (hasDefaultImport) {
81
81
  defaultReactImportNode = node;
82
82
  }
@@ -98,14 +98,14 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
98
98
  const targetImportNode = defaultReactImportNode || reactImportNode;
99
99
  if (targetImportNode) {
100
100
  // Check if it's a namespace import
101
- const hasNamespaceImport = targetImportNode.specifiers.some(spec => spec.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier);
101
+ const hasNamespaceImport = targetImportNode.specifiers.some((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier);
102
102
  if (hasNamespaceImport) {
103
103
  // Add separate import for Fragment
104
104
  return fixer.insertTextAfter(targetImportNode, "\nimport { Fragment } from 'react';");
105
105
  }
106
106
  // Add Fragment to existing React import
107
107
  const lastSpecifier = targetImportNode.specifiers[targetImportNode.specifiers.length - 1];
108
- const hasNamedImports = targetImportNode.specifiers.some(spec => spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier);
108
+ const hasNamedImports = targetImportNode.specifiers.some((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier);
109
109
  if (hasNamedImports) {
110
110
  return fixer.insertTextAfter(lastSpecifier, ', Fragment');
111
111
  }
@@ -131,11 +131,14 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
131
131
  // Special handling for nested fragments
132
132
  const reactFragmentParent = findReactFragmentParent(node);
133
133
  // Check if this fragment contains React.Fragment children
134
- const hasReactFragmentChild = node.children.some(child => child.type === utils_1.AST_NODE_TYPES.JSXElement &&
135
- child.openingElement.name.type === utils_1.AST_NODE_TYPES.JSXMemberExpression &&
136
- child.openingElement.name.object.type === utils_1.AST_NODE_TYPES.JSXIdentifier &&
134
+ const hasReactFragmentChild = node.children.some((child) => child.type === utils_1.AST_NODE_TYPES.JSXElement &&
135
+ child.openingElement.name.type ===
136
+ utils_1.AST_NODE_TYPES.JSXMemberExpression &&
137
+ child.openingElement.name.object.type ===
138
+ utils_1.AST_NODE_TYPES.JSXIdentifier &&
137
139
  child.openingElement.name.object.name === 'React' &&
138
- child.openingElement.name.property.type === utils_1.AST_NODE_TYPES.JSXIdentifier &&
140
+ child.openingElement.name.property.type ===
141
+ utils_1.AST_NODE_TYPES.JSXIdentifier &&
139
142
  child.openingElement.name.property.name === 'Fragment');
140
143
  // For nested fragments, we have multiple test cases with different expected behaviors
141
144
  if (reactFragmentParent) {
@@ -170,13 +173,13 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
170
173
  fixes.push(fixer.replaceText(node.openingFragment, newInnerOpeningText));
171
174
  fixes.push(fixer.replaceText(node.closingFragment, newInnerClosingText));
172
175
  return fixes;
173
- }
176
+ },
174
177
  });
175
178
  // Also report on the parent React.Fragment
176
179
  context.report({
177
180
  node: reactFragmentParent.openingElement.name,
178
181
  messageId: 'preferFragment',
179
- data: { type: 'React.Fragment' }
182
+ data: { type: 'React.Fragment' },
180
183
  });
181
184
  // Mark the parent as already handled
182
185
  reportedNodes.add(reactFragmentParent);
@@ -215,7 +218,7 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
215
218
  fixes.push(fixer.replaceText(node.openingFragment, newOpeningText));
216
219
  fixes.push(fixer.replaceText(node.closingFragment, newClosingText));
217
220
  return fixes;
218
- }
221
+ },
219
222
  });
220
223
  },
221
224
  // Find React.Fragment usage
@@ -236,7 +239,7 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
236
239
  reportedNodes.add(node);
237
240
  reportedNodes.add(node.name);
238
241
  // Check if this React.Fragment has a JSXFragment child
239
- const hasJSXFragmentChild = jsxElement.children.some(child => child.type === utils_1.AST_NODE_TYPES.JSXFragment);
242
+ const hasJSXFragmentChild = jsxElement.children.some((child) => child.type === utils_1.AST_NODE_TYPES.JSXFragment);
240
243
  // Check if this React.Fragment is inside a JSXFragment
241
244
  const fragmentParent = isInsideJSXFragment(node);
242
245
  // Special case: React.Fragment inside a JSX Fragment
@@ -264,7 +267,7 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
264
267
  fixes.push(fixer.replaceText(jsxElement.closingElement, newClosingText));
265
268
  }
266
269
  return fixes;
267
- }
270
+ },
268
271
  });
269
272
  return;
270
273
  }
@@ -295,7 +298,7 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
295
298
  fixes.push(fixer.replaceText(jsxElement.closingElement, newClosingText));
296
299
  }
297
300
  return fixes;
298
- }
301
+ },
299
302
  });
300
303
  },
301
304
  'Program:exit'() {
@@ -312,7 +315,7 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
312
315
  // Prioritize default import for adding Fragment
313
316
  if (reactImports.length > 0) {
314
317
  for (const importNode of reactImports) {
315
- const hasDefaultImport = importNode.specifiers.some(spec => spec.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier);
318
+ const hasDefaultImport = importNode.specifiers.some((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier);
316
319
  if (hasDefaultImport) {
317
320
  defaultReactImportNode = importNode;
318
321
  reactImportNode = importNode;
@@ -325,8 +328,8 @@ exports.preferFragmentComponent = (0, createRule_1.createRule)({
325
328
  }
326
329
  }
327
330
  }
328
- }
331
+ },
329
332
  };
330
- }
333
+ },
331
334
  });
332
335
  //# sourceMappingURL=prefer-fragment-component.js.map
@@ -27,7 +27,8 @@ exports.preferGlobalRouterStateKey = (0, createRule_1.createRule)({
27
27
  */
28
28
  function containsStringLiteral(node) {
29
29
  // Direct string literal
30
- if (node.type === utils_1.AST_NODE_TYPES.Literal && typeof node.value === 'string') {
30
+ if (node.type === utils_1.AST_NODE_TYPES.Literal &&
31
+ typeof node.value === 'string') {
31
32
  return true;
32
33
  }
33
34
  // String concatenation with + operator
@@ -38,17 +39,18 @@ exports.preferGlobalRouterStateKey = (0, createRule_1.createRule)({
38
39
  }
39
40
  // Conditional (ternary) expression with string literals
40
41
  if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
41
- (containsStringLiteral(node.consequent) || containsStringLiteral(node.alternate))) {
42
+ (containsStringLiteral(node.consequent) ||
43
+ containsStringLiteral(node.alternate))) {
42
44
  return true;
43
45
  }
44
46
  // Template literal with static parts
45
47
  if (node.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
46
48
  // Only report if there's a meaningful static part in the template
47
49
  // This allows dynamic templates like `${prefix}-${id}` but catches `static-${id}`
48
- const hasSignificantStaticPart = node.quasis.some(quasi => {
50
+ const hasSignificantStaticPart = node.quasis.some((quasi) => {
49
51
  const content = quasi.value.raw.trim();
50
52
  // Allow common separators like '-', '_', ':', '/' as they're just joining dynamic parts
51
- return content.length > 0 && !(/^[-_:/.]+$/.test(content));
53
+ return content.length > 0 && !/^[-_:/.]+$/.test(content);
52
54
  });
53
55
  return hasSignificantStaticPart;
54
56
  }
@@ -211,7 +211,7 @@ exports.preferSettingsObject = (0, createRule_1.createRule)({
211
211
  function hasABPattern(params) {
212
212
  if (params.length !== 2)
213
213
  return false;
214
- const paramNames = params.map(param => {
214
+ const paramNames = params.map((param) => {
215
215
  if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
216
216
  return param.name;
217
217
  }
@@ -34,7 +34,10 @@ exports.preferUseCallbackOverUseMemoForFunctions = (0, createRule_1.createRule)(
34
34
  },
35
35
  defaultOptions: [{ allowComplexBodies: false, allowFunctionFactories: true }],
36
36
  create(context) {
37
- const options = context.options[0] || { allowComplexBodies: false, allowFunctionFactories: true };
37
+ const options = context.options[0] || {
38
+ allowComplexBodies: false,
39
+ allowFunctionFactories: true,
40
+ };
38
41
  /**
39
42
  * Checks if a node is a function factory (returns an object with functions or a function that generates functions)
40
43
  */
@@ -87,7 +90,8 @@ exports.preferUseCallbackOverUseMemoForFunctions = (0, createRule_1.createRule)(
87
90
  }
88
91
  // If there's more than one statement, or the single statement isn't a return
89
92
  if (node.body.body.length > 1 ||
90
- (node.body.body.length === 1 && node.body.body[0].type !== utils_1.AST_NODE_TYPES.ReturnStatement)) {
93
+ (node.body.body.length === 1 &&
94
+ node.body.body[0].type !== utils_1.AST_NODE_TYPES.ReturnStatement)) {
91
95
  return true;
92
96
  }
93
97
  return false;
@@ -143,7 +147,9 @@ exports.preferUseCallbackOverUseMemoForFunctions = (0, createRule_1.createRule)(
143
147
  function reportAndFix(node, context) {
144
148
  const sourceCode = context.getSourceCode();
145
149
  const useMemoCallback = node.arguments[0];
146
- const dependencyArray = node.arguments[1] ? sourceCode.getText(node.arguments[1]) : '[]';
150
+ const dependencyArray = node.arguments[1]
151
+ ? sourceCode.getText(node.arguments[1])
152
+ : '[]';
147
153
  // Get the returned function from useMemo
148
154
  let returnedFunction;
149
155
  if (useMemoCallback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
@@ -159,7 +165,9 @@ function reportAndFix(node, context) {
159
165
  const returnedFunctionText = sourceCode.getText(returnedFunction);
160
166
  // Check if useMemo has TypeScript generic type parameters
161
167
  const hasTypeParameters = node.typeParameters !== undefined;
162
- const typeParametersText = hasTypeParameters ? sourceCode.getText(node.typeParameters) : '';
168
+ const typeParametersText = hasTypeParameters
169
+ ? sourceCode.getText(node.typeParameters)
170
+ : '';
163
171
  context.report({
164
172
  node,
165
173
  messageId: 'preferUseCallback',