@blumintinc/eslint-plugin-blumint 1.7.3 → 1.8.1
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 +83 -61
- package/lib/index.js +85 -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-css-media-queries.d.ts +5 -0
- package/lib/rules/enforce-css-media-queries.js +87 -0
- package/lib/rules/enforce-dynamic-imports.d.ts +9 -0
- package/lib/rules/enforce-dynamic-imports.js +84 -0
- package/lib/rules/enforce-firestore-facade.js +8 -0
- package/lib/rules/enforce-id-capitalization.d.ts +6 -0
- package/lib/rules/enforce-id-capitalization.js +78 -0
- package/lib/rules/enforce-microdiff.d.ts +3 -0
- package/lib/rules/enforce-microdiff.js +379 -0
- package/lib/rules/enforce-mui-rounded-icons.d.ts +1 -0
- package/lib/rules/enforce-mui-rounded-icons.js +54 -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 +387 -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-react-type-naming.d.ts +3 -0
- package/lib/rules/enforce-react-type-naming.js +191 -0
- package/lib/rules/enforce-render-hits-memoization.js +155 -26
- package/lib/rules/enforce-singular-type-names.d.ts +2 -0
- package/lib/rules/enforce-singular-type-names.js +112 -0
- package/lib/rules/enforce-timestamp-now.d.ts +1 -0
- package/lib/rules/enforce-timestamp-now.js +223 -0
- package/lib/rules/enforce-verb-noun-naming.js +5 -2
- package/lib/rules/ensure-pointer-events-none.d.ts +1 -0
- package/lib/rules/ensure-pointer-events-none.js +211 -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/key-only-outermost-element.d.ts +3 -0
- package/lib/rules/key-only-outermost-element.js +152 -0
- package/lib/rules/no-always-true-false-conditions.d.ts +3 -0
- package/lib/rules/no-always-true-false-conditions.js +1221 -0
- package/lib/rules/no-circular-references.d.ts +1 -0
- package/lib/rules/no-circular-references.js +523 -0
- package/lib/rules/no-firestore-jest-mock.js +27 -4
- package/lib/rules/no-hungarian.d.ts +5 -0
- package/lib/rules/no-hungarian.js +223 -0
- package/lib/rules/no-mock-firebase-admin.js +20 -7
- package/lib/rules/no-object-values-on-strings.d.ts +2 -0
- package/lib/rules/no-object-values-on-strings.js +294 -0
- package/lib/rules/no-type-assertion-returns.d.ts +9 -0
- package/lib/rules/no-type-assertion-returns.js +299 -0
- package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
- package/lib/rules/no-unnecessary-destructuring.js +69 -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/no-unused-props.js +10 -5
- package/lib/rules/no-unused-usestate.d.ts +8 -0
- package/lib/rules/no-unused-usestate.js +84 -0
- package/lib/rules/omit-index-html.d.ts +7 -0
- package/lib/rules/omit-index-html.js +91 -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-usememo-over-useeffect-usestate.d.ts +5 -0
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -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 +623 -0
- package/package.json +10 -7
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceReactTypeNaming = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
// Types that should have lowercase variable names
|
|
7
|
+
const LOWERCASE_TYPES = ['ReactNode', 'JSX.Element'];
|
|
8
|
+
// Types that should have uppercase variable names
|
|
9
|
+
const UPPERCASE_TYPES = ['ComponentType', 'FC', 'FunctionComponent'];
|
|
10
|
+
exports.enforceReactTypeNaming = (0, createRule_1.createRule)({
|
|
11
|
+
name: 'enforce-react-type-naming',
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'suggestion',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Enforce naming conventions for React types',
|
|
16
|
+
recommended: 'error',
|
|
17
|
+
},
|
|
18
|
+
fixable: 'code',
|
|
19
|
+
schema: [],
|
|
20
|
+
messages: {
|
|
21
|
+
reactNodeShouldBeLowercase: 'Variables or parameters of type "{{type}}" should use lowercase naming (e.g., "{{suggestion}}").',
|
|
22
|
+
componentTypeShouldBeUppercase: 'Variables or parameters of type "{{type}}" should use uppercase naming (e.g., "{{suggestion}}").',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultOptions: [],
|
|
26
|
+
create(context) {
|
|
27
|
+
/**
|
|
28
|
+
* Checks if a string starts with an uppercase letter
|
|
29
|
+
*/
|
|
30
|
+
function isUppercase(str) {
|
|
31
|
+
return /^[A-Z]/.test(str);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Converts a string to start with lowercase
|
|
35
|
+
*/
|
|
36
|
+
function toLowercase(str) {
|
|
37
|
+
if (!str)
|
|
38
|
+
return str;
|
|
39
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Converts a string to start with uppercase
|
|
43
|
+
*/
|
|
44
|
+
function toUppercase(str) {
|
|
45
|
+
if (!str)
|
|
46
|
+
return str;
|
|
47
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extracts the type name from a type annotation
|
|
51
|
+
*/
|
|
52
|
+
function getTypeName(typeAnnotation) {
|
|
53
|
+
if (!typeAnnotation)
|
|
54
|
+
return null;
|
|
55
|
+
// Handle TSTypeReference (e.g., ReactNode, ComponentType)
|
|
56
|
+
if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
|
|
57
|
+
if (typeAnnotation.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
58
|
+
return typeAnnotation.typeName.name;
|
|
59
|
+
}
|
|
60
|
+
// Handle qualified names like JSX.Element
|
|
61
|
+
if (typeAnnotation.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
|
|
62
|
+
const left = typeAnnotation.typeName.left.type === utils_1.AST_NODE_TYPES.Identifier
|
|
63
|
+
? typeAnnotation.typeName.left.name
|
|
64
|
+
: '';
|
|
65
|
+
const right = typeAnnotation.typeName.right.name;
|
|
66
|
+
return `${left}.${right}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Checks if a node is a destructured variable
|
|
73
|
+
*/
|
|
74
|
+
function isDestructured(node) {
|
|
75
|
+
return (node.parent?.type === utils_1.AST_NODE_TYPES.Property ||
|
|
76
|
+
node.parent?.type === utils_1.AST_NODE_TYPES.ArrayPattern ||
|
|
77
|
+
(node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
78
|
+
(node.parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
|
|
79
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.ArrayPattern)));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Checks if a node is a default import
|
|
83
|
+
*/
|
|
84
|
+
function isDefaultImport(node) {
|
|
85
|
+
return (node.parent?.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier ||
|
|
86
|
+
(node.parent?.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
87
|
+
node.parent.local.name !== node.parent.imported?.name));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check variable declarations for React type naming conventions
|
|
91
|
+
*/
|
|
92
|
+
function checkVariableDeclaration(node) {
|
|
93
|
+
if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
94
|
+
return;
|
|
95
|
+
// Skip destructured variables
|
|
96
|
+
if (isDestructured(node.id))
|
|
97
|
+
return;
|
|
98
|
+
const variableName = node.id.name;
|
|
99
|
+
// Get the type annotation
|
|
100
|
+
const typeAnnotation = node.id.typeAnnotation?.typeAnnotation;
|
|
101
|
+
const typeName = getTypeName(typeAnnotation);
|
|
102
|
+
if (!typeName)
|
|
103
|
+
return;
|
|
104
|
+
// Check if it's a ReactNode or JSX.Element (should be lowercase)
|
|
105
|
+
if (LOWERCASE_TYPES.includes(typeName) && isUppercase(variableName)) {
|
|
106
|
+
const suggestion = toLowercase(variableName);
|
|
107
|
+
context.report({
|
|
108
|
+
node: node.id,
|
|
109
|
+
messageId: 'reactNodeShouldBeLowercase',
|
|
110
|
+
data: {
|
|
111
|
+
type: typeName,
|
|
112
|
+
suggestion,
|
|
113
|
+
},
|
|
114
|
+
fix: (fixer) => fixer.replaceText(node.id, suggestion),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// Check if it's a ComponentType or FC (should be uppercase)
|
|
118
|
+
if (UPPERCASE_TYPES.includes(typeName) && !isUppercase(variableName)) {
|
|
119
|
+
const suggestion = toUppercase(variableName);
|
|
120
|
+
context.report({
|
|
121
|
+
node: node.id,
|
|
122
|
+
messageId: 'componentTypeShouldBeUppercase',
|
|
123
|
+
data: {
|
|
124
|
+
type: typeName,
|
|
125
|
+
suggestion,
|
|
126
|
+
},
|
|
127
|
+
fix: (fixer) => fixer.replaceText(node.id, suggestion),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check function parameters for React type naming conventions
|
|
133
|
+
*/
|
|
134
|
+
function checkParameter(node) {
|
|
135
|
+
if (node.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
136
|
+
return;
|
|
137
|
+
// Skip destructured parameters
|
|
138
|
+
if (isDestructured(node))
|
|
139
|
+
return;
|
|
140
|
+
// Skip default imports
|
|
141
|
+
if (isDefaultImport(node))
|
|
142
|
+
return;
|
|
143
|
+
const paramName = node.name;
|
|
144
|
+
// Get the type annotation
|
|
145
|
+
const typeAnnotation = node.typeAnnotation?.typeAnnotation;
|
|
146
|
+
const typeName = getTypeName(typeAnnotation);
|
|
147
|
+
if (!typeName)
|
|
148
|
+
return;
|
|
149
|
+
// Check if it's a ReactNode or JSX.Element (should be lowercase)
|
|
150
|
+
if (LOWERCASE_TYPES.includes(typeName) && isUppercase(paramName)) {
|
|
151
|
+
const suggestion = toLowercase(paramName);
|
|
152
|
+
context.report({
|
|
153
|
+
node,
|
|
154
|
+
messageId: 'reactNodeShouldBeLowercase',
|
|
155
|
+
data: {
|
|
156
|
+
type: typeName,
|
|
157
|
+
suggestion,
|
|
158
|
+
},
|
|
159
|
+
fix: (fixer) => fixer.replaceText(node, suggestion),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// Check if it's a ComponentType or FC (should be uppercase)
|
|
163
|
+
if (UPPERCASE_TYPES.includes(typeName) && !isUppercase(paramName)) {
|
|
164
|
+
const suggestion = toUppercase(paramName);
|
|
165
|
+
context.report({
|
|
166
|
+
node,
|
|
167
|
+
messageId: 'componentTypeShouldBeUppercase',
|
|
168
|
+
data: {
|
|
169
|
+
type: typeName,
|
|
170
|
+
suggestion,
|
|
171
|
+
},
|
|
172
|
+
fix: (fixer) => fixer.replaceText(node, suggestion),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
VariableDeclarator: checkVariableDeclaration,
|
|
178
|
+
Identifier(node) {
|
|
179
|
+
// Check parameter names in function declarations
|
|
180
|
+
if (node.parent &&
|
|
181
|
+
(node.parent.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
182
|
+
node.parent.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
183
|
+
node.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) &&
|
|
184
|
+
node.parent.params.includes(node)) {
|
|
185
|
+
checkParameter(node);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=enforce-react-type-naming.js.map
|
|
@@ -21,63 +21,182 @@ exports.enforceRenderHitsMemoization = (0, createRule_1.createRule)({
|
|
|
21
21
|
},
|
|
22
22
|
defaultOptions: [],
|
|
23
23
|
create(context) {
|
|
24
|
+
const isReactComponent = (node) => {
|
|
25
|
+
if (node.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
26
|
+
return false;
|
|
27
|
+
return /^[A-Z]/.test(node.name);
|
|
28
|
+
};
|
|
24
29
|
const isMemoizedCall = (node) => {
|
|
25
30
|
if (node.type !== utils_1.AST_NODE_TYPES.CallExpression)
|
|
26
31
|
return false;
|
|
27
|
-
|
|
28
|
-
if (callee.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
29
|
-
return false;
|
|
30
|
-
return callee.name === 'useCallback' || callee.name === 'useMemo';
|
|
31
|
-
};
|
|
32
|
-
const isReactComponent = (node) => {
|
|
33
|
-
if (node.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
32
|
+
if (!node.callee || node.callee.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
34
33
|
return false;
|
|
35
|
-
|
|
36
|
-
return /^[A-Z]/.test(name);
|
|
34
|
+
return (node.callee.name === 'useCallback' || node.callee.name === 'useMemo');
|
|
37
35
|
};
|
|
38
36
|
const isMemoizedVariable = (node) => {
|
|
39
37
|
if (node.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
40
38
|
return false;
|
|
41
39
|
// Get the variable declaration for this identifier
|
|
42
|
-
const variable = context
|
|
40
|
+
const variable = context
|
|
41
|
+
.getScope()
|
|
42
|
+
.variables.find((v) => v.name === node.name);
|
|
43
43
|
if (!variable)
|
|
44
44
|
return false;
|
|
45
45
|
// Check if the variable is initialized with a memoized call
|
|
46
|
-
const def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
for (const def of variable.defs) {
|
|
47
|
+
if (!def || !def.node)
|
|
48
|
+
continue;
|
|
49
|
+
if (def.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
50
|
+
def.node.init) {
|
|
51
|
+
if (isMemoizedCall(def.node.init)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
51
55
|
}
|
|
52
56
|
return false;
|
|
53
57
|
};
|
|
54
58
|
const isInsideMemoizedCall = (node) => {
|
|
59
|
+
// Handle the case when node is already a memoized call
|
|
60
|
+
if (isMemoizedCall(node))
|
|
61
|
+
return true;
|
|
55
62
|
// Check if the node is a reference to a memoized variable
|
|
56
63
|
if (isMemoizedVariable(node))
|
|
57
64
|
return true;
|
|
58
65
|
// Check if the node is inside a memoization hook call
|
|
59
66
|
let current = node;
|
|
60
67
|
while (current?.parent) {
|
|
61
|
-
if (
|
|
62
|
-
|
|
68
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
69
|
+
const callee = current.parent.callee;
|
|
70
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
71
|
+
(callee.name === 'useCallback' || callee.name === 'useMemo')) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
63
75
|
current = current.parent;
|
|
64
76
|
}
|
|
77
|
+
// Check if the node is a reference to a memoized value
|
|
78
|
+
const scope = context.getScope();
|
|
79
|
+
// Make sure node is an Identifier before accessing name property
|
|
80
|
+
if (node.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const variable = scope.variables.find((v) => v.name === node.name);
|
|
84
|
+
if (!variable) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// Check if any definition is a memoized value
|
|
88
|
+
for (const def of variable.defs) {
|
|
89
|
+
const parent = def.node.parent;
|
|
90
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
91
|
+
parent.init?.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
92
|
+
const callee = parent.init.callee;
|
|
93
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
94
|
+
(callee.name === 'useCallback' || callee.name === 'useMemo')) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Check if any reference is inside a memoized call
|
|
100
|
+
for (const ref of variable.references) {
|
|
101
|
+
let current = ref.identifier;
|
|
102
|
+
while (current?.parent) {
|
|
103
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
104
|
+
const callee = current.parent.callee;
|
|
105
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
106
|
+
(callee.name === 'useCallback' || callee.name === 'useMemo')) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
current = current.parent;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Check if the node is a property of an object that is memoized
|
|
114
|
+
const parent = node.parent;
|
|
115
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.Property &&
|
|
116
|
+
parent.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
117
|
+
const objectExpression = parent.parent;
|
|
118
|
+
let current = objectExpression;
|
|
119
|
+
while (current?.parent) {
|
|
120
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
121
|
+
const callee = current.parent.callee;
|
|
122
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
123
|
+
(callee.name === 'useCallback' || callee.name === 'useMemo')) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
current = current.parent;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
65
130
|
return false;
|
|
66
131
|
};
|
|
132
|
+
let useRenderHitsName = 'useRenderHits';
|
|
133
|
+
let renderHitsName = 'renderHits';
|
|
67
134
|
return {
|
|
135
|
+
ImportDeclaration(node) {
|
|
136
|
+
if (node.source.value.endsWith('useRenderHits')) {
|
|
137
|
+
for (const specifier of node.specifiers) {
|
|
138
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
139
|
+
specifier.imported.name === 'useRenderHits') {
|
|
140
|
+
useRenderHitsName = specifier.local.name;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (node.source.value.endsWith('renderHits')) {
|
|
146
|
+
for (const specifier of node.specifiers) {
|
|
147
|
+
if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
|
|
148
|
+
specifier.imported.name === 'renderHits') {
|
|
149
|
+
renderHitsName = specifier.local.name;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
68
155
|
CallExpression(node) {
|
|
69
|
-
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
156
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
157
|
+
node.callee.name === useRenderHitsName) {
|
|
70
158
|
if (node.arguments.length === 0)
|
|
71
159
|
return;
|
|
72
160
|
const options = node.arguments[0];
|
|
73
161
|
if (options.type !== utils_1.AST_NODE_TYPES.ObjectExpression)
|
|
74
162
|
return;
|
|
163
|
+
// Variable to track if we need to check properties (check both by default)
|
|
164
|
+
const checkProps = {
|
|
165
|
+
transformBefore: true,
|
|
166
|
+
render: true,
|
|
167
|
+
};
|
|
168
|
+
// First pass: Check if transformBefore or render properties exist as shorthand
|
|
75
169
|
for (const prop of options.properties) {
|
|
76
170
|
if (prop.type !== utils_1.AST_NODE_TYPES.Property)
|
|
77
171
|
continue;
|
|
78
172
|
if (prop.key.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
79
173
|
continue;
|
|
80
|
-
|
|
174
|
+
// If it's shorthand property syntax like { transformBefore } and already a memoized variable
|
|
175
|
+
if (prop.key.name === 'transformBefore' &&
|
|
176
|
+
prop.shorthand &&
|
|
177
|
+
prop.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
178
|
+
checkProps.transformBefore = !isMemoizedVariable(prop.key);
|
|
179
|
+
}
|
|
180
|
+
else if (prop.key.name === 'render' &&
|
|
181
|
+
prop.shorthand &&
|
|
182
|
+
prop.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
183
|
+
checkProps.render = !isMemoizedVariable(prop.key);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Second pass: Check non-shorthand properties
|
|
187
|
+
for (const prop of options.properties) {
|
|
188
|
+
if (prop.type !== utils_1.AST_NODE_TYPES.Property)
|
|
189
|
+
continue;
|
|
190
|
+
if (prop.key.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
191
|
+
continue;
|
|
192
|
+
// Skip shorthand properties that we already checked
|
|
193
|
+
if (prop.shorthand)
|
|
194
|
+
continue;
|
|
195
|
+
if (prop.key.name === 'transformBefore' &&
|
|
196
|
+
checkProps.transformBefore) {
|
|
197
|
+
// Skip if the value is already a memoized call
|
|
198
|
+
if (isMemoizedCall(prop.value))
|
|
199
|
+
continue;
|
|
81
200
|
if (!isInsideMemoizedCall(prop.value)) {
|
|
82
201
|
context.report({
|
|
83
202
|
node: prop.value,
|
|
@@ -85,7 +204,7 @@ exports.enforceRenderHitsMemoization = (0, createRule_1.createRule)({
|
|
|
85
204
|
});
|
|
86
205
|
}
|
|
87
206
|
}
|
|
88
|
-
if (prop.key.name === 'render') {
|
|
207
|
+
else if (prop.key.name === 'render' && checkProps.render) {
|
|
89
208
|
if (isReactComponent(prop.value)) {
|
|
90
209
|
context.report({
|
|
91
210
|
node: prop.value,
|
|
@@ -101,13 +220,23 @@ exports.enforceRenderHitsMemoization = (0, createRule_1.createRule)({
|
|
|
101
220
|
}
|
|
102
221
|
}
|
|
103
222
|
}
|
|
104
|
-
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
223
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
224
|
+
node.callee.name === renderHitsName) {
|
|
225
|
+
let current = node;
|
|
226
|
+
while (current?.parent) {
|
|
227
|
+
if (current.parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
228
|
+
const callee = current.parent.callee;
|
|
229
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
230
|
+
(callee.name === 'useCallback' || callee.name === 'useMemo')) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
current = current.parent;
|
|
110
235
|
}
|
|
236
|
+
context.report({
|
|
237
|
+
node,
|
|
238
|
+
messageId: 'requireMemoizedRenderHits',
|
|
239
|
+
});
|
|
111
240
|
}
|
|
112
241
|
},
|
|
113
242
|
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.enforceSingularTypeNames = void 0;
|
|
27
|
+
const createRule_1 = require("../utils/createRule");
|
|
28
|
+
const pluralize = __importStar(require("pluralize"));
|
|
29
|
+
exports.enforceSingularTypeNames = (0, createRule_1.createRule)({
|
|
30
|
+
create(context) {
|
|
31
|
+
/**
|
|
32
|
+
* Check if a name is plural
|
|
33
|
+
* @param name The name to check
|
|
34
|
+
* @returns true if the name is plural, false otherwise
|
|
35
|
+
*/
|
|
36
|
+
function isPlural(name) {
|
|
37
|
+
// Skip checking if name is too short (less than 3 characters)
|
|
38
|
+
if (name.length < 3)
|
|
39
|
+
return false;
|
|
40
|
+
// Skip checking if name ends with 'Props' or 'Params'
|
|
41
|
+
if (name.endsWith('Props') || name.endsWith('Params'))
|
|
42
|
+
return false;
|
|
43
|
+
// Skip checking if name is already singular according to pluralize
|
|
44
|
+
if (pluralize.isSingular(name))
|
|
45
|
+
return false;
|
|
46
|
+
// Check if the singular form is different from the name
|
|
47
|
+
const singular = pluralize.singular(name);
|
|
48
|
+
return singular !== name;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the singular form of a name
|
|
52
|
+
* @param name The name to get the singular form of
|
|
53
|
+
* @returns The singular form of the name
|
|
54
|
+
*/
|
|
55
|
+
function getSingularForm(name) {
|
|
56
|
+
return pluralize.singular(name);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Report a plural type name
|
|
60
|
+
* @param node The node to report
|
|
61
|
+
* @param name The plural name
|
|
62
|
+
* @param suggestedName The suggested singular name
|
|
63
|
+
*/
|
|
64
|
+
function reportPluralName(node, name, suggestedName) {
|
|
65
|
+
context.report({
|
|
66
|
+
node,
|
|
67
|
+
messageId: 'typeShouldBeSingular',
|
|
68
|
+
data: {
|
|
69
|
+
name,
|
|
70
|
+
suggestedName,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
// Check type aliases
|
|
76
|
+
TSTypeAliasDeclaration(node) {
|
|
77
|
+
const name = node.id.name;
|
|
78
|
+
if (isPlural(name)) {
|
|
79
|
+
reportPluralName(node.id, name, getSingularForm(name));
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
// Check interfaces
|
|
83
|
+
TSInterfaceDeclaration(node) {
|
|
84
|
+
const name = node.id.name;
|
|
85
|
+
if (isPlural(name)) {
|
|
86
|
+
reportPluralName(node.id, name, getSingularForm(name));
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
// Check enums
|
|
90
|
+
TSEnumDeclaration(node) {
|
|
91
|
+
const name = node.id.name;
|
|
92
|
+
if (isPlural(name)) {
|
|
93
|
+
reportPluralName(node.id, name, getSingularForm(name));
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
name: 'enforce-singular-type-names',
|
|
99
|
+
meta: {
|
|
100
|
+
type: 'suggestion',
|
|
101
|
+
docs: {
|
|
102
|
+
description: 'Enforce TypeScript type names to be singular',
|
|
103
|
+
recommended: 'error',
|
|
104
|
+
},
|
|
105
|
+
schema: [],
|
|
106
|
+
messages: {
|
|
107
|
+
typeShouldBeSingular: "Type name '{{name}}' should be singular. Consider using '{{suggestedName}}' instead.",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
defaultOptions: [],
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=enforce-singular-type-names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const enforceTimestampNow: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferTimestampNow", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|