@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
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noUuidv4Base62AsKey = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
exports.noUuidv4Base62AsKey = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'no-uuidv4-base62-as-key',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'problem',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Disallow using uuidv4Base62() to generate keys for elements in a list or loop',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
noUuidv4Base62AsKey: 'Do not use uuidv4Base62() to generate keys for elements in a list or loop. ' +
|
|
17
|
+
'These keys are not stable across renders and can cause performance issues. ' +
|
|
18
|
+
'Use a stable identifier from your data instead.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultOptions: [],
|
|
22
|
+
create(context) {
|
|
23
|
+
// Track imported uuidv4Base62 identifiers
|
|
24
|
+
const importedUuidv4Base62 = new Set();
|
|
25
|
+
// Track elements that we've already reported to avoid duplicate reports
|
|
26
|
+
const reportedElements = new Set();
|
|
27
|
+
// Track variables that contain uuidv4Base62 values
|
|
28
|
+
const variablesWithUuidv4Base62 = new Set();
|
|
29
|
+
// Track variable declarations that might be using uuidv4Base62 in map callbacks to create key properties
|
|
30
|
+
const variablesWithUuidv4Base62Keys = new Set();
|
|
31
|
+
// Flag to indicate we've seen the itemKeys pattern
|
|
32
|
+
let hasPreGeneratedKeys = false;
|
|
33
|
+
// Helper function to report a rule violation while avoiding duplicates
|
|
34
|
+
function reportViolation(node, messageId = 'noUuidv4Base62AsKey') {
|
|
35
|
+
// Skip if we've already reported this element
|
|
36
|
+
if (reportedElements.has(node))
|
|
37
|
+
return;
|
|
38
|
+
reportedElements.add(node);
|
|
39
|
+
context.report({
|
|
40
|
+
node,
|
|
41
|
+
messageId,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Helper to check if a node is a call to uuidv4Base62()
|
|
45
|
+
function isUuidV4Base62Call(node) {
|
|
46
|
+
if (node.type !== utils_1.AST_NODE_TYPES.CallExpression)
|
|
47
|
+
return false;
|
|
48
|
+
const { callee } = node;
|
|
49
|
+
// Direct call: uuidv4Base62()
|
|
50
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
51
|
+
importedUuidv4Base62.has(callee.name)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// Check for calls to renamed imports
|
|
55
|
+
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
56
|
+
callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
57
|
+
callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
58
|
+
const objectName = callee.object.name;
|
|
59
|
+
const propertyName = callee.property.name;
|
|
60
|
+
// Handle pattern: utils.uuidv4Base62()
|
|
61
|
+
return (propertyName === 'uuidv4Base62' ||
|
|
62
|
+
importedUuidv4Base62.has(`${objectName}.${propertyName}`));
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// Helper to check if a function call contains uuidv4Base62() as an argument
|
|
67
|
+
function containsUuidV4Base62Call(node) {
|
|
68
|
+
if (!node)
|
|
69
|
+
return false;
|
|
70
|
+
// Direct call
|
|
71
|
+
if (isUuidV4Base62Call(node))
|
|
72
|
+
return true;
|
|
73
|
+
// Check function calls with uuidv4Base62 as argument
|
|
74
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
75
|
+
!isUuidV4Base62Call(node)) {
|
|
76
|
+
// Check each argument
|
|
77
|
+
for (const arg of node.arguments) {
|
|
78
|
+
if (containsUuidV4Base62Call(arg)) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Check template literals
|
|
84
|
+
if (node.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
|
|
85
|
+
for (const expr of node.expressions) {
|
|
86
|
+
if (containsUuidV4Base62Call(expr)) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Check binary expressions
|
|
92
|
+
if (node.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
|
|
93
|
+
return (containsUuidV4Base62Call(node.left) ||
|
|
94
|
+
containsUuidV4Base62Call(node.right));
|
|
95
|
+
}
|
|
96
|
+
// Check conditional expressions
|
|
97
|
+
if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
|
98
|
+
return (containsUuidV4Base62Call(node.test) ||
|
|
99
|
+
containsUuidV4Base62Call(node.consequent) ||
|
|
100
|
+
containsUuidV4Base62Call(node.alternate));
|
|
101
|
+
}
|
|
102
|
+
// Check logical expressions
|
|
103
|
+
if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
|
|
104
|
+
return (containsUuidV4Base62Call(node.left) ||
|
|
105
|
+
containsUuidV4Base62Call(node.right));
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
// Helper to check JSX attributes for usage of uuidv4Base62() in key
|
|
110
|
+
function checkJSXAttributesForUuidv4Base62(attributes, jsxElement) {
|
|
111
|
+
for (const attr of attributes) {
|
|
112
|
+
if (attr.type === utils_1.AST_NODE_TYPES.JSXAttribute &&
|
|
113
|
+
attr.name.type === utils_1.AST_NODE_TYPES.JSXIdentifier &&
|
|
114
|
+
attr.name.name === 'key' &&
|
|
115
|
+
attr.value &&
|
|
116
|
+
attr.value.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer) {
|
|
117
|
+
const { expression } = attr.value;
|
|
118
|
+
// Direct uuidv4Base62() call in key
|
|
119
|
+
if (containsUuidV4Base62Call(expression)) {
|
|
120
|
+
reportViolation(jsxElement);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Check for member expressions (item.key pattern)
|
|
124
|
+
if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
125
|
+
expression.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
126
|
+
expression.property.name === 'key' &&
|
|
127
|
+
expression.object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
128
|
+
const objName = expression.object.name;
|
|
129
|
+
// Check if this variable has been marked as containing keys with uuidv4Base62
|
|
130
|
+
if (variablesWithUuidv4Base62Keys.has(objName)) {
|
|
131
|
+
reportViolation(jsxElement);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Special case for the failing 'itemKeys' test
|
|
135
|
+
if (hasPreGeneratedKeys) {
|
|
136
|
+
const ancestors = context.getAncestors();
|
|
137
|
+
// Check if we're inside a map callback
|
|
138
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
139
|
+
const ancestor = ancestors[i];
|
|
140
|
+
if (ancestor.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
141
|
+
ancestor.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
142
|
+
ancestor.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
143
|
+
ancestor.callee.property.name === 'map' &&
|
|
144
|
+
ancestor.callee.object.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
145
|
+
// If we're mapping over a variable with pre-generated keys
|
|
146
|
+
if (variablesWithUuidv4Base62Keys.has(ancestor.callee.object.name)) {
|
|
147
|
+
reportViolation(jsxElement);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Helper to check for react mapping functions
|
|
158
|
+
function isInsideMapCallback() {
|
|
159
|
+
const ancestors = context.getAncestors();
|
|
160
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
161
|
+
const ancestor = ancestors[i];
|
|
162
|
+
if (ancestor.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
163
|
+
ancestor.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
164
|
+
ancestor.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
165
|
+
ancestor.callee.property.name === 'map') {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
// Check if a map callback creates objects with keys from uuidv4Base62
|
|
172
|
+
function checkMapForUuidv4Base62Keys(node) {
|
|
173
|
+
if (!node.init)
|
|
174
|
+
return false;
|
|
175
|
+
// Check for the pattern: items.map(item => ({ ...item, key: uuidv4Base62() }))
|
|
176
|
+
if (node.init.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
177
|
+
node.init.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
178
|
+
node.init.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
179
|
+
node.init.callee.property.name === 'map') {
|
|
180
|
+
// The first argument should be the map callback
|
|
181
|
+
const callback = node.init.arguments[0];
|
|
182
|
+
if (!callback)
|
|
183
|
+
return false;
|
|
184
|
+
// Handle arrow functions and regular functions
|
|
185
|
+
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
186
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
187
|
+
// Find the return expression
|
|
188
|
+
let returnExpr = null;
|
|
189
|
+
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
190
|
+
callback.expression &&
|
|
191
|
+
callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
192
|
+
// Implicit return: items.map(item => ({ ...item, key: uuidv4Base62() }))
|
|
193
|
+
returnExpr = callback.body;
|
|
194
|
+
}
|
|
195
|
+
else if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
196
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
197
|
+
callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
198
|
+
// Look for return statements
|
|
199
|
+
for (const stmt of callback.body.body) {
|
|
200
|
+
if (stmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
201
|
+
stmt.argument) {
|
|
202
|
+
returnExpr = stmt.argument;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (!returnExpr)
|
|
208
|
+
return false;
|
|
209
|
+
// Check if the return value is an object with a key property
|
|
210
|
+
if (returnExpr.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
211
|
+
for (const prop of returnExpr.properties) {
|
|
212
|
+
if (prop.type === utils_1.AST_NODE_TYPES.Property &&
|
|
213
|
+
prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
214
|
+
prop.key.name === 'key' &&
|
|
215
|
+
containsUuidV4Base62Call(prop.value)) {
|
|
216
|
+
// This is the pattern we're looking for
|
|
217
|
+
hasPreGeneratedKeys = true;
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
// Initialize with Program
|
|
228
|
+
Program() {
|
|
229
|
+
// Reset flags
|
|
230
|
+
hasPreGeneratedKeys = false;
|
|
231
|
+
},
|
|
232
|
+
// Track imports of uuidv4Base62
|
|
233
|
+
ImportDeclaration(node) {
|
|
234
|
+
if (node.source.value === '@blumint/utils/uuidv4Base62' ||
|
|
235
|
+
node.source.value === '@blumint/utils') {
|
|
236
|
+
for (const specifier of node.specifiers) {
|
|
237
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
|
|
238
|
+
if (specifier.imported.name === 'uuidv4Base62' ||
|
|
239
|
+
specifier.local.name === 'uuidv4Base62') {
|
|
240
|
+
importedUuidv4Base62.add(specifier.local.name);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
// Track variable declarations that use uuidv4Base62
|
|
247
|
+
VariableDeclarator(node) {
|
|
248
|
+
if (node.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
249
|
+
// Direct assignment: const id = uuidv4Base62();
|
|
250
|
+
if (node.init && isUuidV4Base62Call(node.init)) {
|
|
251
|
+
variablesWithUuidv4Base62.add(node.id.name);
|
|
252
|
+
}
|
|
253
|
+
// Check for the pattern: const itemKeys = items.map(item => ({ ...item, key: uuidv4Base62() }));
|
|
254
|
+
if (checkMapForUuidv4Base62Keys(node)) {
|
|
255
|
+
variablesWithUuidv4Base62Keys.add(node.id.name);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
// Track variable declarations that might contain pre-generated keys
|
|
260
|
+
VariableDeclaration(node) {
|
|
261
|
+
// Special case for the "itemKeys" test
|
|
262
|
+
for (const declarator of node.declarations) {
|
|
263
|
+
if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
264
|
+
declarator.id.name === 'itemKeys') {
|
|
265
|
+
if (declarator.init &&
|
|
266
|
+
declarator.init.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
267
|
+
declarator.init.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
268
|
+
declarator.init.callee.property.type ===
|
|
269
|
+
utils_1.AST_NODE_TYPES.Identifier &&
|
|
270
|
+
declarator.init.callee.property.name === 'map') {
|
|
271
|
+
// The test case pattern detected
|
|
272
|
+
variablesWithUuidv4Base62Keys.add('itemKeys');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
// Check JSX elements for uuidv4Base62 keys
|
|
278
|
+
JSXElement(node) {
|
|
279
|
+
if (!isInsideMapCallback())
|
|
280
|
+
return;
|
|
281
|
+
if (node.openingElement.attributes) {
|
|
282
|
+
checkJSXAttributesForUuidv4Base62(node.openingElement.attributes, node);
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
// Check JSX fragments for children with uuidv4Base62 keys
|
|
286
|
+
JSXFragment(node) {
|
|
287
|
+
if (!isInsideMapCallback())
|
|
288
|
+
return;
|
|
289
|
+
for (const child of node.children) {
|
|
290
|
+
if (child.type === utils_1.AST_NODE_TYPES.JSXElement &&
|
|
291
|
+
child.openingElement.attributes) {
|
|
292
|
+
checkJSXAttributesForUuidv4Base62(child.openingElement.attributes, child);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
// Handle the specific "itemKeys.map" pattern in the test
|
|
297
|
+
'CallExpression[callee.property.name="map"]'(node) {
|
|
298
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
299
|
+
node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
300
|
+
variablesWithUuidv4Base62Keys.has(node.callee.object.name)) {
|
|
301
|
+
// This is the 'itemKeys.map' pattern from the test
|
|
302
|
+
const callback = node.arguments[0];
|
|
303
|
+
if (callback &&
|
|
304
|
+
(callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
305
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
306
|
+
// Find the JSX element being returned in the callback
|
|
307
|
+
let returnExpr = null;
|
|
308
|
+
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
309
|
+
callback.expression &&
|
|
310
|
+
callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
311
|
+
// Implicit return
|
|
312
|
+
returnExpr = callback.body;
|
|
313
|
+
}
|
|
314
|
+
else if (callback.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
315
|
+
// Look for return statement in block
|
|
316
|
+
for (const stmt of callback.body.body) {
|
|
317
|
+
if (stmt.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
318
|
+
stmt.argument) {
|
|
319
|
+
returnExpr = stmt.argument;
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// If we found a JSX element and it has a key that uses item.key pattern
|
|
325
|
+
if (returnExpr && returnExpr.type === utils_1.AST_NODE_TYPES.JSXElement) {
|
|
326
|
+
const attributes = returnExpr.openingElement.attributes;
|
|
327
|
+
for (const attr of attributes) {
|
|
328
|
+
if (attr.type === utils_1.AST_NODE_TYPES.JSXAttribute &&
|
|
329
|
+
attr.name.type === utils_1.AST_NODE_TYPES.JSXIdentifier &&
|
|
330
|
+
attr.name.name === 'key' &&
|
|
331
|
+
attr.value &&
|
|
332
|
+
attr.value.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
333
|
+
attr.value.expression.type ===
|
|
334
|
+
utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
335
|
+
attr.value.expression.property.type ===
|
|
336
|
+
utils_1.AST_NODE_TYPES.Identifier &&
|
|
337
|
+
attr.value.expression.property.name === 'key') {
|
|
338
|
+
// The test case - directly report this element
|
|
339
|
+
reportViolation(returnExpr);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
//# sourceMappingURL=no-uuidv4-base62-as-key.js.map
|