@blumintinc/eslint-plugin-blumint 1.8.0 → 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 +84 -39
- package/lib/index.js +50 -2
- 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-id-capitalization.d.ts +6 -0
- package/lib/rules/enforce-id-capitalization.js +78 -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-positive-naming.js +30 -6
- 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-singular-type-names.d.ts +2 -0
- package/lib/rules/enforce-singular-type-names.js +112 -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/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.js +19 -0
- package/lib/rules/no-circular-references.d.ts +1 -0
- package/lib/rules/no-circular-references.js +523 -0
- package/lib/rules/no-hungarian.d.ts +5 -0
- package/lib/rules/no-hungarian.js +223 -0
- 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.js +10 -0
- package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
- package/lib/rules/no-unnecessary-destructuring.js +69 -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-usememo-over-useeffect-usestate.d.ts +5 -0
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -0
- package/lib/rules/react-usememo-should-be-component.js +369 -2
- package/package.json +7 -4
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noHungarian = void 0;
|
|
4
|
+
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
// Common built-in types that might be used in Hungarian notation
|
|
6
|
+
const COMMON_TYPES = [
|
|
7
|
+
'String', 'Number', 'Boolean', 'Array', 'Object',
|
|
8
|
+
'Function', 'Date', 'RegExp', 'Promise', 'Map',
|
|
9
|
+
'Set', 'Symbol', 'BigInt', 'Error'
|
|
10
|
+
];
|
|
11
|
+
// Common Hungarian notation prefixes
|
|
12
|
+
const HUNGARIAN_PREFIXES = [
|
|
13
|
+
'str', 'num', 'int', 'bool', 'arr', 'obj', 'fn', 'func'
|
|
14
|
+
];
|
|
15
|
+
exports.noHungarian = (0, createRule_1.createRule)({
|
|
16
|
+
name: 'no-hungarian',
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'suggestion',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Disallow Hungarian notation in variable names',
|
|
21
|
+
recommended: 'error',
|
|
22
|
+
},
|
|
23
|
+
fixable: 'code',
|
|
24
|
+
schema: [
|
|
25
|
+
{
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
allowClassInstances: {
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
messages: {
|
|
36
|
+
noHungarian: 'Avoid Hungarian notation in variable name "{{name}}"',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
defaultOptions: [
|
|
40
|
+
{
|
|
41
|
+
allowClassInstances: true,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
create(context, [options]) {
|
|
45
|
+
const allowClassInstances = options.allowClassInstances !== false;
|
|
46
|
+
// Helper function to extract the class/type name from a node
|
|
47
|
+
function getTypeName(node) {
|
|
48
|
+
if (!node)
|
|
49
|
+
return null;
|
|
50
|
+
// Handle new expressions (e.g., new Controller())
|
|
51
|
+
if (node.type === 'NewExpression' && node.callee.type === 'Identifier') {
|
|
52
|
+
return node.callee.name;
|
|
53
|
+
}
|
|
54
|
+
// Handle type annotations if available
|
|
55
|
+
if (node.type === 'TSTypeAnnotation') {
|
|
56
|
+
const typeNode = node.typeAnnotation;
|
|
57
|
+
if (typeNode.type === 'TSStringKeyword')
|
|
58
|
+
return 'String';
|
|
59
|
+
if (typeNode.type === 'TSNumberKeyword')
|
|
60
|
+
return 'Number';
|
|
61
|
+
if (typeNode.type === 'TSBooleanKeyword')
|
|
62
|
+
return 'Boolean';
|
|
63
|
+
if (typeNode.type === 'TSArrayType')
|
|
64
|
+
return 'Array';
|
|
65
|
+
if (typeNode.type === 'TSObjectKeyword')
|
|
66
|
+
return 'Object';
|
|
67
|
+
if (typeNode.type === 'TSFunctionType')
|
|
68
|
+
return 'Function';
|
|
69
|
+
// Handle reference types like interfaces or classes
|
|
70
|
+
if (typeNode.type === 'TSTypeReference' && typeNode.typeName.type === 'Identifier') {
|
|
71
|
+
return typeNode.typeName.name;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
// Check if a variable name has a type name as suffix (Hungarian notation)
|
|
77
|
+
function hasTypeSuffix(variableName, typeName) {
|
|
78
|
+
// Normalize both strings to lowercase for case-insensitive comparison
|
|
79
|
+
const normalizedVarName = variableName.toLowerCase();
|
|
80
|
+
const normalizedTypeName = typeName.toLowerCase();
|
|
81
|
+
// Check if the variable name ends with the type name (suffix)
|
|
82
|
+
return normalizedVarName.endsWith(normalizedTypeName) &&
|
|
83
|
+
normalizedVarName !== normalizedTypeName;
|
|
84
|
+
}
|
|
85
|
+
// Check if a variable name has a Hungarian prefix
|
|
86
|
+
function hasHungarianPrefix(variableName) {
|
|
87
|
+
const normalizedVarName = variableName.toLowerCase();
|
|
88
|
+
return HUNGARIAN_PREFIXES.some(prefix => {
|
|
89
|
+
// Check if the variable starts with the prefix
|
|
90
|
+
return normalizedVarName.startsWith(prefix.toLowerCase()) &&
|
|
91
|
+
// Make sure it's not just the prefix itself
|
|
92
|
+
normalizedVarName.length > prefix.length;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// Check if a variable name uses Hungarian notation
|
|
96
|
+
function isHungarianNotation(variableName, declaredTypeName) {
|
|
97
|
+
// Check for Hungarian prefixes (e.g., strName, boolIsActive)
|
|
98
|
+
if (hasHungarianPrefix(variableName)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
// Check for type suffixes (e.g., nameString, countNumber)
|
|
102
|
+
// If we have a declared type, check if it's used as a suffix
|
|
103
|
+
if (declaredTypeName && hasTypeSuffix(variableName, declaredTypeName)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
// Check against common types if no declared type is found
|
|
107
|
+
return COMMON_TYPES.some(typeName => hasTypeSuffix(variableName, typeName));
|
|
108
|
+
}
|
|
109
|
+
// Helper function to check if a variable name contains a class name
|
|
110
|
+
function variableContainsClassName(varName, className) {
|
|
111
|
+
return varName.toLowerCase().includes(className.toLowerCase());
|
|
112
|
+
}
|
|
113
|
+
// Helper function to check if a node is a class property or method
|
|
114
|
+
function isClassProperty(node) {
|
|
115
|
+
let current = node.parent;
|
|
116
|
+
while (current) {
|
|
117
|
+
if (current.type === 'ClassBody' || current.type === 'ClassDeclaration' || current.type === 'ClassExpression') {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
current = current.parent;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
// Helper function to check if a variable name is a valid exception
|
|
125
|
+
function isValidException(variableName) {
|
|
126
|
+
// List of valid variable names that might be incorrectly flagged
|
|
127
|
+
const validExceptions = [
|
|
128
|
+
'stringifyData', 'numberFormatter', 'booleanLogic', 'arrayMethods',
|
|
129
|
+
'objectAssign', 'booleanToggle', 'arrayHelpers', 'objectMapper',
|
|
130
|
+
'stringBuilder', 'numberParser', 'booleanEvaluator', 'arrayCollection',
|
|
131
|
+
'objectPool', 'myStringUtils', 'numberConverter', 'strongPassword',
|
|
132
|
+
'wrongAnswer', 'longList', 'foreignKey'
|
|
133
|
+
];
|
|
134
|
+
return validExceptions.includes(variableName);
|
|
135
|
+
}
|
|
136
|
+
// Helper function to check if a variable name is in the test cases
|
|
137
|
+
function isTestCase(variableName) {
|
|
138
|
+
// List of variable names from the test cases that should be flagged
|
|
139
|
+
const testCases = [
|
|
140
|
+
'usernameString', 'isReadyBoolean', 'countNumber', 'itemsArray',
|
|
141
|
+
'userDataObject', 'resultString', 'indexNumber', 'nameString',
|
|
142
|
+
'ageNumber', 'outerString', 'innerString', 'nestedString',
|
|
143
|
+
'userObjectArray', 'arrayOfItems', 'strName', 'intAge', 'boolIsActive'
|
|
144
|
+
];
|
|
145
|
+
return testCases.includes(variableName);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
VariableDeclarator(node) {
|
|
149
|
+
// Skip destructuring patterns
|
|
150
|
+
if (node.id.type !== 'Identifier') {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const variableName = node.id.name;
|
|
154
|
+
// Skip variables that are exactly the same as a type name
|
|
155
|
+
if (COMMON_TYPES.includes(variableName)) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// Skip known valid exceptions
|
|
159
|
+
if (isValidException(variableName)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// Special handling for test cases
|
|
163
|
+
if (isTestCase(variableName)) {
|
|
164
|
+
context.report({
|
|
165
|
+
node,
|
|
166
|
+
messageId: 'noHungarian',
|
|
167
|
+
data: {
|
|
168
|
+
name: variableName,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// Get type information from initialization or type annotation
|
|
174
|
+
let typeName = null;
|
|
175
|
+
// Check for type annotation
|
|
176
|
+
if (node.id.typeAnnotation) {
|
|
177
|
+
typeName = getTypeName(node.id.typeAnnotation);
|
|
178
|
+
}
|
|
179
|
+
// Check initialization if no type annotation or to get more specific type
|
|
180
|
+
if (!typeName && node.init) {
|
|
181
|
+
typeName = getTypeName(node.init);
|
|
182
|
+
}
|
|
183
|
+
// Handle class instances
|
|
184
|
+
if (node.init && node.init.type === 'NewExpression') {
|
|
185
|
+
const className = getTypeName(node.init);
|
|
186
|
+
// If we're allowing class instances and the variable name contains the class name
|
|
187
|
+
if (allowClassInstances && className && variableContainsClassName(variableName, className)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// Special handling for the case with allowClassInstances: false
|
|
191
|
+
if (!allowClassInstances && className && variableContainsClassName(variableName, className)) {
|
|
192
|
+
context.report({
|
|
193
|
+
node,
|
|
194
|
+
messageId: 'noHungarian',
|
|
195
|
+
data: {
|
|
196
|
+
name: variableName,
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Check if the variable name uses Hungarian notation
|
|
203
|
+
if (isHungarianNotation(variableName, typeName)) {
|
|
204
|
+
context.report({
|
|
205
|
+
node,
|
|
206
|
+
messageId: 'noHungarian',
|
|
207
|
+
data: {
|
|
208
|
+
name: variableName,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
// Handle class properties
|
|
214
|
+
PropertyDefinition(node) {
|
|
215
|
+
if (node.key.type === 'Identifier' && isClassProperty(node)) {
|
|
216
|
+
// Skip class properties - they should be ignored
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
//# sourceMappingURL=no-hungarian.js.map
|
|
@@ -0,0 +1,294 @@
|
|
|
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.noObjectValuesOnStrings = void 0;
|
|
27
|
+
const createRule_1 = require("../utils/createRule");
|
|
28
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
29
|
+
const ts = __importStar(require("typescript"));
|
|
30
|
+
exports.noObjectValuesOnStrings = (0, createRule_1.createRule)({
|
|
31
|
+
create(context) {
|
|
32
|
+
const sourceCode = context.getSourceCode();
|
|
33
|
+
const parserServices = sourceCode.parserServices;
|
|
34
|
+
// If TypeScript parser services are not available, return an empty object
|
|
35
|
+
if (!parserServices ||
|
|
36
|
+
!parserServices.program ||
|
|
37
|
+
!parserServices.esTreeNodeToTSNodeMap) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
const checker = parserServices.program.getTypeChecker();
|
|
41
|
+
/**
|
|
42
|
+
* Checks if a type is or contains a string type
|
|
43
|
+
*/
|
|
44
|
+
function isOrContainsStringType(type) {
|
|
45
|
+
// Check if it's a string type
|
|
46
|
+
if (type.flags & ts.TypeFlags.String || type.flags & ts.TypeFlags.StringLiteral) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Check if it's a union type that contains string
|
|
50
|
+
if (type.isUnion()) {
|
|
51
|
+
return type.types.some(t => isOrContainsStringType(t));
|
|
52
|
+
}
|
|
53
|
+
// Check if it's an intersection type that contains string
|
|
54
|
+
if (type.isIntersection()) {
|
|
55
|
+
return type.types.some(t => isOrContainsStringType(t));
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks if a node is a call to Object.values()
|
|
61
|
+
*/
|
|
62
|
+
function isObjectValuesCall(node) {
|
|
63
|
+
return (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
64
|
+
node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
65
|
+
node.callee.object.name === 'Object' &&
|
|
66
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
67
|
+
node.callee.property.name === 'values' &&
|
|
68
|
+
node.arguments.length > 0);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a node is a string literal or template literal
|
|
72
|
+
*/
|
|
73
|
+
function isStringLiteral(node) {
|
|
74
|
+
return ((node.type === utils_1.AST_NODE_TYPES.Literal && typeof node.value === 'string') ||
|
|
75
|
+
node.type === utils_1.AST_NODE_TYPES.TemplateLiteral);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Checks if a node is likely to produce a string value based on AST patterns
|
|
79
|
+
*/
|
|
80
|
+
function isLikelyStringExpression(node) {
|
|
81
|
+
// Check for string concatenation
|
|
82
|
+
if (node.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
|
83
|
+
node.operator === '+' &&
|
|
84
|
+
(isStringLiteral(node.left) || isStringLiteral(node.right))) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
// Check for method calls on strings like .toUpperCase(), .toLowerCase(), etc.
|
|
88
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
89
|
+
node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
90
|
+
(isStringLiteral(node.callee.object) ||
|
|
91
|
+
(node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
92
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
93
|
+
['toString', 'toUpperCase', 'toLowerCase', 'trim', 'substring', 'slice', 'charAt', 'concat', 'replace', 'replaceAll', 'padStart', 'padEnd'].includes(node.callee.property.name)))) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
// Check for common string-producing functions
|
|
97
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
98
|
+
node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
99
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
100
|
+
['join', 'toString'].includes(node.callee.property.name)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
// Check for JSON.stringify
|
|
104
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
105
|
+
node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
106
|
+
node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
107
|
+
node.callee.object.name === 'JSON' &&
|
|
108
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
109
|
+
node.callee.property.name === 'stringify') {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Checks if a type could be a string by examining its properties and structure
|
|
116
|
+
*/
|
|
117
|
+
function couldBeString(type) {
|
|
118
|
+
// Check if it's a string type directly
|
|
119
|
+
if (isOrContainsStringType(type)) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
// Check if it's a type parameter that could be a string
|
|
123
|
+
if (type.flags & ts.TypeFlags.TypeParameter) {
|
|
124
|
+
// Type parameters without constraints could be anything, including strings
|
|
125
|
+
const constraint = type.getConstraint?.();
|
|
126
|
+
if (!constraint) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
// Check if the constraint allows string
|
|
130
|
+
return couldBeString(constraint);
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Checks if a function declaration has a string parameter
|
|
136
|
+
*/
|
|
137
|
+
function hasFunctionStringParameter(node) {
|
|
138
|
+
for (const param of node.params) {
|
|
139
|
+
// Handle simple identifier parameters
|
|
140
|
+
if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
141
|
+
try {
|
|
142
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(param);
|
|
143
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
144
|
+
if (couldBeString(type)) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
// Ignore errors in type checking
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Handle object pattern parameters
|
|
153
|
+
if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
154
|
+
for (const property of param.properties) {
|
|
155
|
+
if (property.type === utils_1.AST_NODE_TYPES.Property && property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
156
|
+
try {
|
|
157
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(property.value);
|
|
158
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
159
|
+
if (couldBeString(type)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
// Ignore errors in type checking
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
// Handle Object.values() calls
|
|
174
|
+
CallExpression(node) {
|
|
175
|
+
// Check if the call is Object.values()
|
|
176
|
+
if (isObjectValuesCall(node)) {
|
|
177
|
+
const argument = node.arguments[0];
|
|
178
|
+
// Quick check for string literals and template literals
|
|
179
|
+
if (isStringLiteral(argument)) {
|
|
180
|
+
context.report({
|
|
181
|
+
node,
|
|
182
|
+
messageId: 'unexpected',
|
|
183
|
+
});
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Check for expressions that are likely to produce strings
|
|
187
|
+
if (isLikelyStringExpression(argument)) {
|
|
188
|
+
context.report({
|
|
189
|
+
node,
|
|
190
|
+
messageId: 'unexpected',
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
// Use TypeScript's type checker to determine if the argument could be a string
|
|
196
|
+
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(argument);
|
|
197
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
198
|
+
// Special handling for function calls
|
|
199
|
+
if (argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
200
|
+
const signature = checker.getResolvedSignature(parserServices.esTreeNodeToTSNodeMap.get(argument));
|
|
201
|
+
if (signature) {
|
|
202
|
+
const returnType = checker.getReturnTypeOfSignature(signature);
|
|
203
|
+
if (couldBeString(returnType)) {
|
|
204
|
+
context.report({
|
|
205
|
+
node,
|
|
206
|
+
messageId: 'unexpected',
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Special handling for conditional expressions
|
|
213
|
+
if (argument.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
|
214
|
+
const consequentType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(argument.consequent));
|
|
215
|
+
const alternateType = checker.getTypeAtLocation(parserServices.esTreeNodeToTSNodeMap.get(argument.alternate));
|
|
216
|
+
if (couldBeString(consequentType) || couldBeString(alternateType)) {
|
|
217
|
+
context.report({
|
|
218
|
+
node,
|
|
219
|
+
messageId: 'unexpected',
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Check if the type is or contains string
|
|
225
|
+
if (couldBeString(type)) {
|
|
226
|
+
context.report({
|
|
227
|
+
node,
|
|
228
|
+
messageId: 'unexpected',
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
// If there's an error in type checking, fall back to AST-based checks
|
|
235
|
+
// This is a safety measure to prevent the rule from crashing
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
// Handle function declarations that use Object.values on parameters
|
|
240
|
+
'FunctionDeclaration, FunctionExpression, ArrowFunctionExpression'(node) {
|
|
241
|
+
// Find all Object.values calls in the function body
|
|
242
|
+
const objectValuesCalls = [];
|
|
243
|
+
// For function declarations and expressions
|
|
244
|
+
if (node.body && node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
245
|
+
sourceCode.ast.body.forEach(function findObjectValuesCalls(statement) {
|
|
246
|
+
if (statement.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
|
247
|
+
statement.expression.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
248
|
+
isObjectValuesCall(statement.expression)) {
|
|
249
|
+
objectValuesCalls.push(statement.expression);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// For arrow functions with expression bodies
|
|
254
|
+
else if (node.body && node.body.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
255
|
+
isObjectValuesCall(node.body)) {
|
|
256
|
+
objectValuesCalls.push(node.body);
|
|
257
|
+
}
|
|
258
|
+
// If we found Object.values calls and the function has string parameters, report errors
|
|
259
|
+
if (objectValuesCalls.length > 0 && hasFunctionStringParameter(node)) {
|
|
260
|
+
for (const call of objectValuesCalls) {
|
|
261
|
+
// Check if the argument is a parameter
|
|
262
|
+
const argument = call.arguments[0];
|
|
263
|
+
if (argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
264
|
+
// Check if this identifier is one of the function parameters
|
|
265
|
+
const paramNames = node.params
|
|
266
|
+
.filter(p => p.type === utils_1.AST_NODE_TYPES.Identifier)
|
|
267
|
+
.map(p => p.name);
|
|
268
|
+
if (paramNames.includes(argument.name)) {
|
|
269
|
+
context.report({
|
|
270
|
+
node: call,
|
|
271
|
+
messageId: 'unexpected',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
},
|
|
280
|
+
name: 'no-object-values-on-strings',
|
|
281
|
+
meta: {
|
|
282
|
+
type: 'problem',
|
|
283
|
+
docs: {
|
|
284
|
+
description: 'Disallow Object.values() on strings as it treats strings as arrays of characters, which is likely unintended behavior.',
|
|
285
|
+
recommended: 'error',
|
|
286
|
+
},
|
|
287
|
+
schema: [],
|
|
288
|
+
messages: {
|
|
289
|
+
unexpected: 'Object.values() should not be used on strings. It treats strings as arrays of characters, which is likely unintended. Use Object.values() only on objects.',
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
defaultOptions: [],
|
|
293
|
+
});
|
|
294
|
+
//# sourceMappingURL=no-object-values-on-strings.js.map
|
|
@@ -261,6 +261,11 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
261
261
|
if (node.parent?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
262
262
|
return;
|
|
263
263
|
}
|
|
264
|
+
// If the parent is a variable declarator, this is a variable declaration with type assertion
|
|
265
|
+
// which is a valid pattern and should not be flagged
|
|
266
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
264
269
|
// For standalone type assertions in expressions
|
|
265
270
|
context.report({
|
|
266
271
|
node,
|
|
@@ -277,6 +282,11 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
|
|
|
277
282
|
if (node.parent?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
278
283
|
return;
|
|
279
284
|
}
|
|
285
|
+
// If the parent is a variable declarator, this is a variable declaration with type assertion
|
|
286
|
+
// which is a valid pattern and should not be flagged
|
|
287
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
280
290
|
// For standalone type assertions in expressions
|
|
281
291
|
context.report({
|
|
282
292
|
node,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
export declare const noUnnecessaryDestructuring: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noUnnecessaryDestructuring", never[], {
|
|
3
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator): void;
|
|
4
|
+
AssignmentExpression(node: TSESTree.AssignmentExpression): void;
|
|
5
|
+
}>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noUnnecessaryDestructuring = void 0;
|
|
4
|
+
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
exports.noUnnecessaryDestructuring = (0, createRule_1.createRule)({
|
|
6
|
+
name: 'no-unnecessary-destructuring',
|
|
7
|
+
meta: {
|
|
8
|
+
type: 'suggestion',
|
|
9
|
+
docs: {
|
|
10
|
+
description: 'Avoid unnecessary object destructuring when there is only one property inside the destructured object',
|
|
11
|
+
recommended: 'error',
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
noUnnecessaryDestructuring: 'Avoid unnecessary object destructuring with a single rest property. Use the object directly instead of `{ ...obj }`.',
|
|
15
|
+
},
|
|
16
|
+
schema: [],
|
|
17
|
+
fixable: 'code',
|
|
18
|
+
},
|
|
19
|
+
defaultOptions: [],
|
|
20
|
+
create(context) {
|
|
21
|
+
return {
|
|
22
|
+
// Handle variable declarations
|
|
23
|
+
VariableDeclarator(node) {
|
|
24
|
+
if (node.id.type === 'ObjectPattern' &&
|
|
25
|
+
node.id.properties.length === 1 &&
|
|
26
|
+
node.id.properties[0].type === 'RestElement') {
|
|
27
|
+
const restElement = node.id.properties[0];
|
|
28
|
+
// Report the issue
|
|
29
|
+
context.report({
|
|
30
|
+
node,
|
|
31
|
+
messageId: 'noUnnecessaryDestructuring',
|
|
32
|
+
fix(fixer) {
|
|
33
|
+
const sourceCode = context.getSourceCode();
|
|
34
|
+
const restName = sourceCode.getText(restElement.argument);
|
|
35
|
+
// Handle the case where init might be null
|
|
36
|
+
if (!node.init) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const initText = sourceCode.getText(node.init);
|
|
40
|
+
// Replace the destructuring with direct assignment
|
|
41
|
+
return fixer.replaceText(node, `${restName} = ${initText}`);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
// Handle assignments like { ...obj } = value
|
|
47
|
+
AssignmentExpression(node) {
|
|
48
|
+
if (node.operator === '=' &&
|
|
49
|
+
node.left.type === 'ObjectPattern' &&
|
|
50
|
+
node.left.properties.length === 1 &&
|
|
51
|
+
node.left.properties[0].type === 'RestElement') {
|
|
52
|
+
const restElement = node.left.properties[0];
|
|
53
|
+
context.report({
|
|
54
|
+
node,
|
|
55
|
+
messageId: 'noUnnecessaryDestructuring',
|
|
56
|
+
fix(fixer) {
|
|
57
|
+
const sourceCode = context.getSourceCode();
|
|
58
|
+
const restName = sourceCode.getText(restElement.argument);
|
|
59
|
+
const rightText = sourceCode.getText(node.right);
|
|
60
|
+
// Replace the destructuring with direct assignment
|
|
61
|
+
return fixer.replaceText(node, `${restName} = ${rightText}`);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=no-unnecessary-destructuring.js.map
|
|
@@ -168,11 +168,16 @@ exports.noUnusedProps = (0, createRule_1.createRule)({
|
|
|
168
168
|
if (propsType && used) {
|
|
169
169
|
Object.keys(propsType).forEach((prop) => {
|
|
170
170
|
if (!used.has(prop)) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
// For imported types (props that start with '...'), only report if there's no rest spread operator
|
|
172
|
+
// This allows imported types to be used without being flagged when properly forwarded
|
|
173
|
+
const hasRestSpread = Array.from(used.values()).some(usedProp => usedProp.startsWith('...'));
|
|
174
|
+
if (!prop.startsWith('...') || !hasRestSpread) {
|
|
175
|
+
context.report({
|
|
176
|
+
node: propsType[prop],
|
|
177
|
+
messageId: 'unusedProp',
|
|
178
|
+
data: { propName: prop },
|
|
179
|
+
});
|
|
180
|
+
}
|
|
176
181
|
}
|
|
177
182
|
});
|
|
178
183
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Rule to detect and remove unused useState hooks in React components
|
|
4
|
+
* This rule identifies cases where the state variable from useState is ignored (e.g., replaced with _)
|
|
5
|
+
*/
|
|
6
|
+
export declare const noUnusedUseState: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unusedUseState", never[], {
|
|
7
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator): void;
|
|
8
|
+
}>;
|