@blumintinc/eslint-plugin-blumint 1.8.1 → 1.9.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.
- package/lib/index.js +4 -1
- package/lib/rules/enforce-assertSafe-object-key.js +1 -1
- package/lib/rules/enforce-id-capitalization.js +43 -3
- package/lib/rules/enforce-object-literal-as-const.js +37 -0
- package/lib/rules/enforce-positive-naming.js +87 -163
- package/lib/rules/enforce-singular-type-names.js +1 -1
- package/lib/rules/no-hungarian.d.ts +1 -5
- package/lib/rules/no-hungarian.js +333 -177
- package/lib/rules/no-type-assertion-returns.js +143 -128
- package/lib/rules/no-unused-props.js +100 -7
- package/lib/rules/no-uuidv4-base62-as-key.d.ts +1 -0
- package/lib/rules/no-uuidv4-base62-as-key.js +350 -0
- package/lib/rules/react-usememo-should-be-component.js +75 -348
- package/package.json +5 -5
|
@@ -104,10 +104,15 @@ const containsJsxInFunction = (node) => {
|
|
|
104
104
|
};
|
|
105
105
|
/**
|
|
106
106
|
* Checks if an expression contains JSX elements
|
|
107
|
+
*
|
|
108
|
+
* This function is used to determine if a useMemo hook is returning JSX directly.
|
|
109
|
+
* We want to avoid flagging useMemo hooks that return data structures that happen to contain JSX,
|
|
110
|
+
* as these are typically used for configuration or data preparation, not for rendering components.
|
|
107
111
|
*/
|
|
108
112
|
const containsJsxInExpression = (node) => {
|
|
109
113
|
if (!node)
|
|
110
114
|
return false;
|
|
115
|
+
// Direct JSX element or fragment
|
|
111
116
|
if (isJsxElement(node)) {
|
|
112
117
|
return true;
|
|
113
118
|
}
|
|
@@ -117,8 +122,63 @@ const containsJsxInExpression = (node) => {
|
|
|
117
122
|
case utils_1.AST_NODE_TYPES.LogicalExpression:
|
|
118
123
|
return containsJsxInExpression(node.left) || containsJsxInExpression(node.right);
|
|
119
124
|
case utils_1.AST_NODE_TYPES.ObjectExpression:
|
|
125
|
+
// Special case: If this is an object with both JSX and non-JSX properties,
|
|
126
|
+
// it's likely a data structure that happens to contain JSX, not a component
|
|
127
|
+
let hasNonJsxProperties = false;
|
|
128
|
+
let hasJsxProperties = false;
|
|
129
|
+
for (const property of node.properties) {
|
|
130
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
|
|
131
|
+
if (isJsxElement(property.value)) {
|
|
132
|
+
hasJsxProperties = true;
|
|
133
|
+
}
|
|
134
|
+
else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
135
|
+
hasNonJsxProperties = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// If the object has both JSX and non-JSX properties, it's likely a data object
|
|
140
|
+
// that happens to contain JSX, not a component that should be extracted
|
|
141
|
+
if (hasNonJsxProperties && hasJsxProperties) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
// If it only has JSX properties, it might be a collection of components
|
|
145
|
+
if (hasJsxProperties && !hasNonJsxProperties) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
120
148
|
return containsJsxInObject(node);
|
|
121
149
|
case utils_1.AST_NODE_TYPES.CallExpression:
|
|
150
|
+
// Special case for array methods that return data structures with JSX
|
|
151
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
152
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
153
|
+
node.callee.property.name === 'map' &&
|
|
154
|
+
node.arguments.length > 0) {
|
|
155
|
+
const callback = node.arguments[0];
|
|
156
|
+
if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
157
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
158
|
+
// Check if the callback returns an object with both JSX and non-JSX properties
|
|
159
|
+
if (callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
160
|
+
callback.body.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
161
|
+
let hasNonJsxProperties = false;
|
|
162
|
+
let hasJsxProperties = false;
|
|
163
|
+
for (const property of callback.body.properties) {
|
|
164
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
|
|
165
|
+
if (isJsxElement(property.value)) {
|
|
166
|
+
hasJsxProperties = true;
|
|
167
|
+
}
|
|
168
|
+
else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
169
|
+
hasNonJsxProperties = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// If the object has both JSX and non-JSX properties, it's likely a data object
|
|
174
|
+
if (hasNonJsxProperties && hasJsxProperties) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// For other cases, check if the function returns JSX directly
|
|
179
|
+
return containsJsxInFunction(callback);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
122
182
|
// Check if it's an IIFE
|
|
123
183
|
if (node.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
124
184
|
node.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
@@ -127,7 +187,7 @@ const containsJsxInExpression = (node) => {
|
|
|
127
187
|
// Check array methods
|
|
128
188
|
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
129
189
|
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
130
|
-
const arrayMethods = ['
|
|
190
|
+
const arrayMethods = ['filter', 'find', 'findIndex', 'some', 'every', 'reduce'];
|
|
131
191
|
if (arrayMethods.includes(node.callee.property.name) && node.arguments.length > 0) {
|
|
132
192
|
const callback = node.arguments[0];
|
|
133
193
|
if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
@@ -224,6 +284,9 @@ const containsJsxInBlockStatement = (node) => {
|
|
|
224
284
|
* This function distinguishes between:
|
|
225
285
|
* 1. useMemo returning JSX directly (invalid)
|
|
226
286
|
* 2. useMemo returning an object that contains JSX properties (valid)
|
|
287
|
+
* 3. useMemo returning non-JSX values like numbers, strings, etc. (valid)
|
|
288
|
+
*
|
|
289
|
+
* The rule should only fire when useMemo explicitly returns ReactNode from JSX.
|
|
227
290
|
*/
|
|
228
291
|
const containsJsxInUseMemo = (node) => {
|
|
229
292
|
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
@@ -232,362 +295,26 @@ const containsJsxInUseMemo = (node) => {
|
|
|
232
295
|
const callback = node.arguments[0];
|
|
233
296
|
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
234
297
|
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
235
|
-
//
|
|
236
|
-
// Check for specific patterns in the code that match the failing test cases
|
|
237
|
-
// Check for the pattern: data.map(renderItem) where renderItem is a function that returns JSX
|
|
298
|
+
// For block statements, we need to check if any return statement contains JSX
|
|
238
299
|
if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
239
|
-
|
|
240
|
-
let returnsMapWithRenderItem = false;
|
|
300
|
+
// Check each return statement to see if it returns JSX
|
|
241
301
|
for (const statement of callback.body.body) {
|
|
242
|
-
// Check for a function named renderItem that returns JSX
|
|
243
|
-
if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
244
|
-
for (const declarator of statement.declarations) {
|
|
245
|
-
if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
246
|
-
declarator.id.name === 'renderItem' &&
|
|
247
|
-
declarator.init &&
|
|
248
|
-
(declarator.init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
249
|
-
declarator.init.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
250
|
-
const func = declarator.init;
|
|
251
|
-
if (func.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
252
|
-
for (const funcStmt of func.body.body) {
|
|
253
|
-
if (funcStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
254
|
-
funcStmt.argument &&
|
|
255
|
-
isJsxElement(funcStmt.argument)) {
|
|
256
|
-
hasRenderItemFunction = true;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else if (isJsxElement(func.body)) {
|
|
261
|
-
hasRenderItemFunction = true;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
// Check for return data.map(renderItem)
|
|
267
302
|
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
268
303
|
statement.argument &&
|
|
269
|
-
statement.argument
|
|
270
|
-
|
|
271
|
-
statement.argument.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
272
|
-
statement.argument.callee.property.name === 'map') {
|
|
273
|
-
if (statement.argument.arguments.length > 0 &&
|
|
274
|
-
statement.argument.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
275
|
-
statement.argument.arguments[0].name === 'renderItem') {
|
|
276
|
-
returnsMapWithRenderItem = true;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if (hasRenderItemFunction && returnsMapWithRenderItem) {
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
// Check for if/else if/else statements that return JSX
|
|
284
|
-
let hasIfStatementsReturningJsx = false;
|
|
285
|
-
for (const statement of callback.body.body) {
|
|
286
|
-
if (statement.type === utils_1.AST_NODE_TYPES.IfStatement) {
|
|
287
|
-
// Check if the if statement returns JSX
|
|
288
|
-
if (statement.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
289
|
-
for (const ifStmt of statement.consequent.body) {
|
|
290
|
-
if (ifStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
291
|
-
ifStmt.argument &&
|
|
292
|
-
isJsxElement(ifStmt.argument)) {
|
|
293
|
-
hasIfStatementsReturningJsx = true;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
else if (statement.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
298
|
-
statement.consequent.argument &&
|
|
299
|
-
isJsxElement(statement.consequent.argument)) {
|
|
300
|
-
hasIfStatementsReturningJsx = true;
|
|
301
|
-
}
|
|
302
|
-
// Check if the else clause returns JSX
|
|
303
|
-
if (statement.alternate) {
|
|
304
|
-
if (statement.alternate.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
305
|
-
for (const elseStmt of statement.alternate.body) {
|
|
306
|
-
if (elseStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
307
|
-
elseStmt.argument &&
|
|
308
|
-
isJsxElement(elseStmt.argument)) {
|
|
309
|
-
hasIfStatementsReturningJsx = true;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
else if (statement.alternate.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
314
|
-
statement.alternate.argument &&
|
|
315
|
-
isJsxElement(statement.alternate.argument)) {
|
|
316
|
-
hasIfStatementsReturningJsx = true;
|
|
317
|
-
}
|
|
318
|
-
else if (statement.alternate.type === utils_1.AST_NODE_TYPES.IfStatement) {
|
|
319
|
-
// Handle else if
|
|
320
|
-
if (statement.alternate.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
321
|
-
for (const elseIfStmt of statement.alternate.consequent.body) {
|
|
322
|
-
if (elseIfStmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
323
|
-
elseIfStmt.argument &&
|
|
324
|
-
isJsxElement(elseIfStmt.argument)) {
|
|
325
|
-
hasIfStatementsReturningJsx = true;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
else if (statement.alternate.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
330
|
-
statement.alternate.consequent.argument &&
|
|
331
|
-
isJsxElement(statement.alternate.consequent.argument)) {
|
|
332
|
-
hasIfStatementsReturningJsx = true;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (hasIfStatementsReturningJsx) {
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
// Check for components object with JSX properties
|
|
342
|
-
let hasComponentsObject = false;
|
|
343
|
-
let returnsComponentsProperty = false;
|
|
344
|
-
for (const statement of callback.body.body) {
|
|
345
|
-
// Check for const components = { ... } with JSX properties
|
|
346
|
-
if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
347
|
-
for (const declarator of statement.declarations) {
|
|
348
|
-
if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
349
|
-
declarator.id.name === 'components' &&
|
|
350
|
-
declarator.init &&
|
|
351
|
-
declarator.init.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
352
|
-
for (const property of declarator.init.properties) {
|
|
353
|
-
if (property.type === utils_1.AST_NODE_TYPES.Property &&
|
|
354
|
-
property.value &&
|
|
355
|
-
isJsxElement(property.value)) {
|
|
356
|
-
hasComponentsObject = true;
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
// Check for return components[componentType] || <div>...</div>
|
|
364
|
-
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
365
|
-
statement.argument) {
|
|
366
|
-
if (statement.argument.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
|
|
367
|
-
statement.argument.operator === '||' &&
|
|
368
|
-
statement.argument.left.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
369
|
-
statement.argument.left.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
370
|
-
statement.argument.left.object.name === 'components') {
|
|
371
|
-
returnsComponentsProperty = true;
|
|
372
|
-
}
|
|
373
|
-
else if (statement.argument.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
374
|
-
statement.argument.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
375
|
-
statement.argument.object.name === 'components') {
|
|
376
|
-
returnsComponentsProperty = true;
|
|
377
|
-
}
|
|
304
|
+
isJsxElement(statement.argument)) {
|
|
305
|
+
return true;
|
|
378
306
|
}
|
|
379
307
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
308
|
+
// If we didn't find any return statements with JSX, check for more complex patterns
|
|
309
|
+
return containsJsxInBlockStatement(callback.body);
|
|
383
310
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
// Direct JSX element
|
|
311
|
+
else {
|
|
312
|
+
// Direct JSX element - this is the primary case we want to catch
|
|
387
313
|
if (isJsxElement(callback.body)) {
|
|
388
314
|
return true;
|
|
389
315
|
}
|
|
390
|
-
//
|
|
391
|
-
|
|
392
|
-
callback.body.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
393
|
-
callback.body.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
394
|
-
['map', 'filter', 'find'].includes(callback.body.callee.property.name) &&
|
|
395
|
-
callback.body.arguments.length > 0) {
|
|
396
|
-
const mapCallback = callback.body.arguments[0];
|
|
397
|
-
if ((mapCallback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
398
|
-
mapCallback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
399
|
-
isJsxElement(mapCallback.body)) {
|
|
400
|
-
return true;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
// Conditional expression with JSX
|
|
404
|
-
if (callback.body.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
|
|
405
|
-
(isJsxElement(callback.body.consequent) || isJsxElement(callback.body.alternate))) {
|
|
406
|
-
return true;
|
|
407
|
-
}
|
|
408
|
-
// Don't flag objects that contain JSX properties and are returned directly
|
|
409
|
-
// This is the key fix for the bug
|
|
410
|
-
if (callback.body.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
411
|
-
// Check if this is a pure data object or if it's being used to store JSX for later use
|
|
412
|
-
let hasNonJsxProperties = false;
|
|
413
|
-
let hasJsxProperties = false;
|
|
414
|
-
for (const property of callback.body.properties) {
|
|
415
|
-
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
|
|
416
|
-
if (isJsxElement(property.value)) {
|
|
417
|
-
hasJsxProperties = true;
|
|
418
|
-
}
|
|
419
|
-
else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
420
|
-
hasNonJsxProperties = true;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
// If the object has both JSX and non-JSX properties, it's likely a data object
|
|
425
|
-
// that happens to contain JSX, not a component that should be extracted
|
|
426
|
-
if (hasNonJsxProperties && hasJsxProperties) {
|
|
427
|
-
return false;
|
|
428
|
-
}
|
|
429
|
-
// If it only has JSX properties, it might be a collection of components
|
|
430
|
-
// that should be extracted
|
|
431
|
-
if (hasJsxProperties && !hasNonJsxProperties) {
|
|
432
|
-
return true;
|
|
433
|
-
}
|
|
434
|
-
return false;
|
|
435
|
-
}
|
|
436
|
-
// Member expression that might return JSX
|
|
437
|
-
if (callback.body.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
438
|
-
// We need to check if this is accessing a property of an object that contains JSX
|
|
439
|
-
// This is a complex case, but we'll assume it's returning JSX
|
|
440
|
-
return true;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
// For block statements, we need to analyze the return statements
|
|
444
|
-
if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
445
|
-
// Check for variable declarations that might be used to return JSX
|
|
446
|
-
const jsxVariables = new Set();
|
|
447
|
-
const objectVariables = new Set();
|
|
448
|
-
const functionVariables = new Set();
|
|
449
|
-
// First pass: collect information about variables
|
|
450
|
-
for (const statement of callback.body.body) {
|
|
451
|
-
if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
452
|
-
for (const declarator of statement.declarations) {
|
|
453
|
-
if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier && declarator.init) {
|
|
454
|
-
const varName = declarator.id.name;
|
|
455
|
-
// Check for JSX assignments
|
|
456
|
-
if (isJsxElement(declarator.init)) {
|
|
457
|
-
jsxVariables.add(varName);
|
|
458
|
-
}
|
|
459
|
-
// Check for object assignments
|
|
460
|
-
if (declarator.init.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
461
|
-
let hasJsxProperty = false;
|
|
462
|
-
for (const property of declarator.init.properties) {
|
|
463
|
-
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value && isJsxElement(property.value)) {
|
|
464
|
-
hasJsxProperty = true;
|
|
465
|
-
break;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (hasJsxProperty) {
|
|
469
|
-
objectVariables.add(varName);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
// Check for function assignments
|
|
473
|
-
if (declarator.init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
474
|
-
declarator.init.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
475
|
-
if (containsJsxInFunction(declarator.init)) {
|
|
476
|
-
functionVariables.add(varName);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
// Second pass: check return statements
|
|
484
|
-
for (const statement of callback.body.body) {
|
|
485
|
-
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
486
|
-
// Direct JSX return
|
|
487
|
-
if (isJsxElement(statement.argument)) {
|
|
488
|
-
return true;
|
|
489
|
-
}
|
|
490
|
-
// Return of a JSX variable
|
|
491
|
-
if (statement.argument.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
492
|
-
jsxVariables.has(statement.argument.name)) {
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
// Return of a function call that might return JSX
|
|
496
|
-
if (statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
497
|
-
// Check for array methods
|
|
498
|
-
if (statement.argument.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
499
|
-
statement.argument.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
500
|
-
['map', 'filter', 'find'].includes(statement.argument.callee.property.name) &&
|
|
501
|
-
statement.argument.arguments.length > 0) {
|
|
502
|
-
const mapCallback = statement.argument.arguments[0];
|
|
503
|
-
if ((mapCallback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
504
|
-
mapCallback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
505
|
-
(isJsxElement(mapCallback.body) ||
|
|
506
|
-
(mapCallback.body.type === utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
507
|
-
mapCallback.body.body.some(stmt => stmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
508
|
-
stmt.argument &&
|
|
509
|
-
isJsxElement(stmt.argument))))) {
|
|
510
|
-
return true;
|
|
511
|
-
}
|
|
512
|
-
// Check for array.map(renderItem) where renderItem is a variable
|
|
513
|
-
if (statement.argument.arguments[0].type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
514
|
-
functionVariables.has(statement.argument.arguments[0].name)) {
|
|
515
|
-
return true;
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
// Check for IIFE
|
|
519
|
-
if ((statement.argument.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
520
|
-
statement.argument.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
521
|
-
containsJsxInFunction(statement.argument.callee)) {
|
|
522
|
-
return true;
|
|
523
|
-
}
|
|
524
|
-
// Check for function calls that might return JSX
|
|
525
|
-
if (statement.argument.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
526
|
-
// Check if we're calling a function that returns JSX
|
|
527
|
-
if (functionVariables.has(statement.argument.callee.name)) {
|
|
528
|
-
return true;
|
|
529
|
-
}
|
|
530
|
-
// Check if we're calling a function defined in the useMemo
|
|
531
|
-
for (const innerStatement of callback.body.body) {
|
|
532
|
-
if (innerStatement.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
|
|
533
|
-
innerStatement.id &&
|
|
534
|
-
innerStatement.id.name === statement.argument.callee.name) {
|
|
535
|
-
// Check if this function returns JSX
|
|
536
|
-
if (containsJsxInFunction(innerStatement)) {
|
|
537
|
-
return true;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
// Return of a conditional expression with JSX
|
|
544
|
-
if (statement.argument.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
|
|
545
|
-
(isJsxElement(statement.argument.consequent) || isJsxElement(statement.argument.alternate))) {
|
|
546
|
-
return true;
|
|
547
|
-
}
|
|
548
|
-
// Return of an object property that might be JSX
|
|
549
|
-
if (statement.argument.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
550
|
-
// Check if we're accessing a property of an object that contains JSX
|
|
551
|
-
if (statement.argument.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
552
|
-
objectVariables.has(statement.argument.object.name)) {
|
|
553
|
-
return true;
|
|
554
|
-
}
|
|
555
|
-
// If we can't determine, assume it might return JSX
|
|
556
|
-
return true;
|
|
557
|
-
}
|
|
558
|
-
// Return of an object with JSX properties
|
|
559
|
-
if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
560
|
-
// Check if this is a pure data object or if it's being used to store JSX
|
|
561
|
-
let hasNonJsxProperties = false;
|
|
562
|
-
let hasJsxProperties = false;
|
|
563
|
-
for (const property of statement.argument.properties) {
|
|
564
|
-
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
|
|
565
|
-
if (isJsxElement(property.value)) {
|
|
566
|
-
hasJsxProperties = true;
|
|
567
|
-
}
|
|
568
|
-
else if (property.value.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
569
|
-
hasNonJsxProperties = true;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
// If the object has both JSX and non-JSX properties, it's likely a data object
|
|
574
|
-
// that happens to contain JSX, not a component that should be extracted
|
|
575
|
-
if (hasNonJsxProperties && hasJsxProperties) {
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
// If it only has JSX properties, it might be a collection of components
|
|
579
|
-
// that should be extracted
|
|
580
|
-
if (hasJsxProperties && !hasNonJsxProperties) {
|
|
581
|
-
return true;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
// Check switch statements in the block
|
|
586
|
-
if (statement.type === utils_1.AST_NODE_TYPES.SwitchStatement &&
|
|
587
|
-
containsJsxInSwitchStatement(statement)) {
|
|
588
|
-
return true;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
316
|
+
// For non-JSX expressions, we need to check if they contain JSX
|
|
317
|
+
return containsJsxInExpression(callback.body);
|
|
591
318
|
}
|
|
592
319
|
}
|
|
593
320
|
}
|
|
@@ -598,7 +325,7 @@ exports.reactUseMemoShouldBeComponent = (0, createRule_1.createRule)({
|
|
|
598
325
|
meta: {
|
|
599
326
|
type: 'suggestion',
|
|
600
327
|
docs: {
|
|
601
|
-
description: 'Enforce that useMemo hooks returning
|
|
328
|
+
description: 'Enforce that useMemo hooks explicitly returning JSX should be abstracted into separate React components',
|
|
602
329
|
recommended: 'error',
|
|
603
330
|
},
|
|
604
331
|
schema: [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blumintinc/eslint-plugin-blumint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Custom eslint rules for use within BluMint",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Brodie McGuire",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"commitlint": "commitlint --edit"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@types/pluralize": "
|
|
48
|
+
"@types/pluralize": "0.0.33",
|
|
49
49
|
"compromise": "14.14.4",
|
|
50
50
|
"minimatch": "10.0.1",
|
|
51
51
|
"pluralize": "8.0.0",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"fs": "0.0.1-security",
|
|
86
86
|
"husky": "9.1.7",
|
|
87
87
|
"jest": "29.7.0",
|
|
88
|
-
"jest-junit": "
|
|
88
|
+
"jest-junit": "14.0.0",
|
|
89
89
|
"jsonc-eslint-parser": "2.3.0",
|
|
90
90
|
"npm-run-all": "4.1.5",
|
|
91
91
|
"prettier": "2.7.1",
|
|
@@ -93,9 +93,9 @@
|
|
|
93
93
|
"remark-lint": "9.1.1",
|
|
94
94
|
"remark-preset-lint-recommended": "6.1.2",
|
|
95
95
|
"semantic-release": "19.0.3",
|
|
96
|
-
"ts-jest": "
|
|
96
|
+
"ts-jest": "29.2.6",
|
|
97
97
|
"ts-node": "10.9.1",
|
|
98
|
-
"typescript": "
|
|
98
|
+
"typescript": "4.9.5"
|
|
99
99
|
},
|
|
100
100
|
"peerDependencies": {
|
|
101
101
|
"eslint": ">=7"
|