@blumintinc/eslint-plugin-blumint 1.7.3 → 1.8.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/README.md +39 -62
- package/lib/index.js +37 -1
- package/lib/rules/enforce-assert-throws.js +48 -3
- package/lib/rules/enforce-assertSafe-object-key.d.ts +2 -0
- package/lib/rules/enforce-assertSafe-object-key.js +284 -0
- package/lib/rules/enforce-centralized-mock-firestore.js +192 -14
- package/lib/rules/enforce-firestore-facade.js +8 -0
- package/lib/rules/enforce-microdiff.d.ts +3 -0
- package/lib/rules/enforce-microdiff.js +379 -0
- package/lib/rules/enforce-object-literal-as-const.d.ts +4 -0
- package/lib/rules/enforce-object-literal-as-const.js +88 -0
- package/lib/rules/enforce-positive-naming.d.ts +1 -0
- package/lib/rules/enforce-positive-naming.js +363 -0
- package/lib/rules/enforce-props-argument-name.d.ts +8 -0
- package/lib/rules/enforce-props-argument-name.js +182 -0
- package/lib/rules/enforce-render-hits-memoization.js +155 -26
- package/lib/rules/enforce-timestamp-now.d.ts +1 -0
- package/lib/rules/enforce-timestamp-now.js +223 -0
- package/lib/rules/extract-global-constants.d.ts +1 -1
- package/lib/rules/extract-global-constants.js +109 -1
- package/lib/rules/global-const-style.js +3 -2
- package/lib/rules/no-always-true-false-conditions.d.ts +3 -0
- package/lib/rules/no-always-true-false-conditions.js +1202 -0
- package/lib/rules/no-firestore-jest-mock.js +27 -4
- package/lib/rules/no-mock-firebase-admin.js +20 -7
- package/lib/rules/no-type-assertion-returns.d.ts +9 -0
- package/lib/rules/no-type-assertion-returns.js +289 -0
- package/lib/rules/no-unnecessary-verb-suffix.d.ts +1 -0
- package/lib/rules/no-unnecessary-verb-suffix.js +203 -0
- package/lib/rules/prefer-clone-deep.js +294 -22
- package/lib/rules/prefer-fragment-component.js +265 -34
- package/lib/rules/prefer-global-router-state-key.d.ts +5 -0
- package/lib/rules/prefer-global-router-state-key.js +89 -0
- package/lib/rules/prefer-utility-function-over-private-static.d.ts +1 -0
- package/lib/rules/prefer-utility-function-over-private-static.js +77 -0
- package/lib/rules/react-usememo-should-be-component.d.ts +1 -0
- package/lib/rules/react-usememo-should-be-component.js +256 -0
- package/package.json +5 -5
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reactUseMemoShouldBeComponent = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a node is a JSX element or fragment
|
|
8
|
+
*/
|
|
9
|
+
const isJsxElement = (node) => {
|
|
10
|
+
if (!node)
|
|
11
|
+
return false;
|
|
12
|
+
if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
|
13
|
+
return isJsxElement(node.consequent) || isJsxElement(node.alternate);
|
|
14
|
+
}
|
|
15
|
+
return (node.type === utils_1.AST_NODE_TYPES.JSXElement ||
|
|
16
|
+
node.type === utils_1.AST_NODE_TYPES.JSXFragment);
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Checks if an object contains JSX elements in its properties
|
|
20
|
+
*/
|
|
21
|
+
const containsJsxInObject = (node) => {
|
|
22
|
+
for (const property of node.properties) {
|
|
23
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value) {
|
|
24
|
+
if (isJsxElement(property.value)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
// Check nested objects
|
|
28
|
+
if (property.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
29
|
+
if (containsJsxInObject(property.value)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Checks if a switch statement contains JSX elements
|
|
39
|
+
*/
|
|
40
|
+
const containsJsxInSwitchStatement = (node) => {
|
|
41
|
+
for (const switchCase of node.cases) {
|
|
42
|
+
for (const statement of switchCase.consequent) {
|
|
43
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
44
|
+
if (isJsxElement(statement.argument)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (statement.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
49
|
+
if (containsJsxInBlockStatement(statement)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Checks if a function contains JSX elements
|
|
59
|
+
*/
|
|
60
|
+
const containsJsxInFunction = (node) => {
|
|
61
|
+
const body = node.body;
|
|
62
|
+
// Direct JSX return
|
|
63
|
+
if (isJsxElement(body)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// JSX in block statement
|
|
67
|
+
if (body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
68
|
+
return containsJsxInBlockStatement(body);
|
|
69
|
+
}
|
|
70
|
+
// Check for array methods returning JSX
|
|
71
|
+
if (body.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
72
|
+
if (body.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
73
|
+
body.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
74
|
+
// Check array methods like map, filter, find, etc.
|
|
75
|
+
const arrayMethods = ['map', 'filter', 'find', 'findIndex', 'some', 'every', 'reduce'];
|
|
76
|
+
if (arrayMethods.includes(body.callee.property.name) && body.arguments.length > 0) {
|
|
77
|
+
const callback = body.arguments[0];
|
|
78
|
+
if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
79
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
80
|
+
containsJsxInFunction(callback)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Check for IIFE
|
|
86
|
+
if ((body.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
87
|
+
body.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
88
|
+
containsJsxInFunction(body.callee)) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
// Check for IIFE with parentheses
|
|
92
|
+
if (body.callee.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
93
|
+
return containsJsxInExpression(body);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Checks if an expression contains JSX elements
|
|
100
|
+
*/
|
|
101
|
+
const containsJsxInExpression = (node) => {
|
|
102
|
+
if (!node)
|
|
103
|
+
return false;
|
|
104
|
+
if (isJsxElement(node)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
switch (node.type) {
|
|
108
|
+
case utils_1.AST_NODE_TYPES.ConditionalExpression:
|
|
109
|
+
return containsJsxInExpression(node.consequent) || containsJsxInExpression(node.alternate);
|
|
110
|
+
case utils_1.AST_NODE_TYPES.LogicalExpression:
|
|
111
|
+
return containsJsxInExpression(node.left) || containsJsxInExpression(node.right);
|
|
112
|
+
case utils_1.AST_NODE_TYPES.ObjectExpression:
|
|
113
|
+
return containsJsxInObject(node);
|
|
114
|
+
case utils_1.AST_NODE_TYPES.CallExpression:
|
|
115
|
+
// Check if it's an IIFE
|
|
116
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
117
|
+
node.callee.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
118
|
+
return containsJsxInFunction(node.callee);
|
|
119
|
+
}
|
|
120
|
+
// Check array methods
|
|
121
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
122
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
123
|
+
const arrayMethods = ['map', 'filter', 'find', 'findIndex', 'some', 'every', 'reduce'];
|
|
124
|
+
if (arrayMethods.includes(node.callee.property.name) && node.arguments.length > 0) {
|
|
125
|
+
const callback = node.arguments[0];
|
|
126
|
+
if ((callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
127
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
128
|
+
return containsJsxInFunction(callback);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Check arguments for JSX
|
|
133
|
+
for (const arg of node.arguments) {
|
|
134
|
+
if (arg.type !== utils_1.AST_NODE_TYPES.SpreadElement && containsJsxInExpression(arg)) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
140
|
+
case utils_1.AST_NODE_TYPES.FunctionExpression:
|
|
141
|
+
return containsJsxInFunction(node);
|
|
142
|
+
default:
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Checks if a block statement contains JSX elements
|
|
148
|
+
*/
|
|
149
|
+
const containsJsxInBlockStatement = (node) => {
|
|
150
|
+
for (const statement of node.body) {
|
|
151
|
+
// Check return statements
|
|
152
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement && statement.argument) {
|
|
153
|
+
if (containsJsxInExpression(statement.argument)) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Check if statements
|
|
158
|
+
if (statement.type === utils_1.AST_NODE_TYPES.IfStatement) {
|
|
159
|
+
if (statement.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
160
|
+
statement.consequent.argument && containsJsxInExpression(statement.consequent.argument)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
if (statement.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
164
|
+
containsJsxInBlockStatement(statement.consequent)) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
if (statement.alternate) {
|
|
168
|
+
if (statement.alternate.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
169
|
+
statement.alternate.argument && containsJsxInExpression(statement.alternate.argument)) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
if (statement.alternate.type === utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
173
|
+
containsJsxInBlockStatement(statement.alternate)) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
if (statement.alternate.type === utils_1.AST_NODE_TYPES.IfStatement) {
|
|
177
|
+
// Handle else if
|
|
178
|
+
if (containsJsxInExpression(statement.alternate.test)) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
if (statement.alternate.consequent &&
|
|
182
|
+
((statement.alternate.consequent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
|
|
183
|
+
statement.alternate.consequent.argument &&
|
|
184
|
+
containsJsxInExpression(statement.alternate.consequent.argument)) ||
|
|
185
|
+
(statement.alternate.consequent.type === utils_1.AST_NODE_TYPES.BlockStatement &&
|
|
186
|
+
containsJsxInBlockStatement(statement.alternate.consequent)))) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Check switch statements
|
|
193
|
+
if (statement.type === utils_1.AST_NODE_TYPES.SwitchStatement) {
|
|
194
|
+
if (containsJsxInSwitchStatement(statement)) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Check variable declarations for JSX
|
|
199
|
+
if (statement.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
200
|
+
for (const declarator of statement.declarations) {
|
|
201
|
+
if (declarator.init && containsJsxInExpression(declarator.init)) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Check expressions
|
|
207
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
208
|
+
containsJsxInExpression(statement.expression)) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Checks if a useMemo call contains JSX elements
|
|
216
|
+
*/
|
|
217
|
+
const containsJsxInUseMemo = (node) => {
|
|
218
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
219
|
+
node.callee.name === 'useMemo' &&
|
|
220
|
+
node.arguments.length > 0) {
|
|
221
|
+
const callback = node.arguments[0];
|
|
222
|
+
if (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
223
|
+
callback.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
224
|
+
return containsJsxInFunction(callback);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
};
|
|
229
|
+
exports.reactUseMemoShouldBeComponent = (0, createRule_1.createRule)({
|
|
230
|
+
name: 'react-usememo-should-be-component',
|
|
231
|
+
meta: {
|
|
232
|
+
type: 'suggestion',
|
|
233
|
+
docs: {
|
|
234
|
+
description: 'Enforce that useMemo hooks returning React nodes should be abstracted into separate React components',
|
|
235
|
+
recommended: 'error',
|
|
236
|
+
},
|
|
237
|
+
schema: [],
|
|
238
|
+
messages: {
|
|
239
|
+
useMemoShouldBeComponent: 'useMemo returning JSX should be extracted into a separate component. Use React.memo() for component memoization instead.',
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
defaultOptions: [],
|
|
243
|
+
create(context) {
|
|
244
|
+
return {
|
|
245
|
+
CallExpression(node) {
|
|
246
|
+
if (containsJsxInUseMemo(node)) {
|
|
247
|
+
context.report({
|
|
248
|
+
node,
|
|
249
|
+
messageId: 'useMemoShouldBeComponent',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
//# sourceMappingURL=react-usememo-should-be-component.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blumintinc/eslint-plugin-blumint",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Custom eslint rules for use within BluMint",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Brodie McGuire",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"dotenv-cli": "5.0.0",
|
|
72
72
|
"eslint": "8.57.0",
|
|
73
73
|
"eslint-config-prettier": "8.5.0",
|
|
74
|
-
"eslint-doc-generator": "
|
|
74
|
+
"eslint-doc-generator": "1.7.1",
|
|
75
75
|
"eslint-import-resolver-typescript": "3.5.5",
|
|
76
76
|
"eslint-plugin-eslint-plugin": "5.0.0",
|
|
77
77
|
"eslint-plugin-import": "2.26.0",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"eslint-plugin-security": "1.5.0",
|
|
82
82
|
"fs": "0.0.1-security",
|
|
83
83
|
"husky": "9.1.7",
|
|
84
|
-
"jest": "29.
|
|
84
|
+
"jest": "29.7.0",
|
|
85
85
|
"jest-junit": "14.0.0",
|
|
86
86
|
"jsonc-eslint-parser": "2.3.0",
|
|
87
87
|
"npm-run-all": "4.1.5",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"remark-lint": "9.1.1",
|
|
91
91
|
"remark-preset-lint-recommended": "6.1.2",
|
|
92
92
|
"semantic-release": "19.0.3",
|
|
93
|
-
"ts-jest": "29.
|
|
93
|
+
"ts-jest": "29.2.6",
|
|
94
94
|
"ts-node": "10.9.1",
|
|
95
95
|
"typescript": "4.9.5"
|
|
96
96
|
},
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"eslint": ">=7"
|
|
99
99
|
},
|
|
100
100
|
"engines": {
|
|
101
|
-
"node": "
|
|
101
|
+
"node": ">=22.0.0"
|
|
102
102
|
},
|
|
103
103
|
"config": {
|
|
104
104
|
"commitizen": {
|