@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,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceIdCapitalization = void 0;
|
|
4
|
+
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
/**
|
|
6
|
+
* This rule ensures consistency in user-facing text by enforcing the use of "ID"
|
|
7
|
+
* instead of "id" when referring to identifiers in UI labels, instructions,
|
|
8
|
+
* error messages, and other visible strings.
|
|
9
|
+
*/
|
|
10
|
+
exports.enforceIdCapitalization = (0, createRule_1.createRule)({
|
|
11
|
+
name: 'enforce-id-capitalization',
|
|
12
|
+
meta: {
|
|
13
|
+
type: 'suggestion',
|
|
14
|
+
docs: {
|
|
15
|
+
description: 'Enforce the use of "ID" instead of "id" in user-facing text',
|
|
16
|
+
recommended: 'error',
|
|
17
|
+
},
|
|
18
|
+
fixable: 'code',
|
|
19
|
+
schema: [],
|
|
20
|
+
messages: {
|
|
21
|
+
enforceIdCapitalization: 'Use "ID" instead of "id" in user-facing text for better readability',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultOptions: [],
|
|
25
|
+
create(context) {
|
|
26
|
+
// Regular expression to match standalone "id" surrounded by whitespace or punctuation
|
|
27
|
+
// This ensures we only match "id" as a word, not as part of another word
|
|
28
|
+
const idRegex = /(^|\s|[.,;:!?'"()\[\]{}])id(\s|$|[.,;:!?'"()\[\]{}])/g;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a string contains "id" as a standalone word and report if found
|
|
31
|
+
*/
|
|
32
|
+
function checkForIdInString(node, value) {
|
|
33
|
+
if (typeof value !== 'string')
|
|
34
|
+
return;
|
|
35
|
+
// Reset the regex lastIndex to ensure consistent behavior
|
|
36
|
+
idRegex.lastIndex = 0;
|
|
37
|
+
// Check if the string contains "id" as a standalone word
|
|
38
|
+
if (idRegex.test(value)) {
|
|
39
|
+
// Reset the regex lastIndex again before replacing
|
|
40
|
+
idRegex.lastIndex = 0;
|
|
41
|
+
const fixedText = value.replace(idRegex, (_match, prefix, suffix) => {
|
|
42
|
+
return `${prefix}ID${suffix}`;
|
|
43
|
+
});
|
|
44
|
+
context.report({
|
|
45
|
+
node,
|
|
46
|
+
messageId: 'enforceIdCapitalization',
|
|
47
|
+
fix: (fixer) => {
|
|
48
|
+
if (node.type === 'Literal') {
|
|
49
|
+
return fixer.replaceText(node, JSON.stringify(fixedText));
|
|
50
|
+
}
|
|
51
|
+
else if (node.type === 'JSXText') {
|
|
52
|
+
return fixer.replaceText(node, fixedText);
|
|
53
|
+
}
|
|
54
|
+
else if (node.type === 'TemplateElement') {
|
|
55
|
+
return fixer.replaceText(node, fixedText);
|
|
56
|
+
}
|
|
57
|
+
return fixer.replaceText(node, JSON.stringify(fixedText));
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
// Check string literals
|
|
64
|
+
Literal(node) {
|
|
65
|
+
if (typeof node.value === 'string') {
|
|
66
|
+
checkForIdInString(node, node.value);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
// Check JSX text
|
|
70
|
+
JSXText(node) {
|
|
71
|
+
checkForIdInString(node, node.value);
|
|
72
|
+
},
|
|
73
|
+
// We don't need a separate handler for CallExpression since we already handle Literals
|
|
74
|
+
// The Literal handler will catch the string arguments in t("user.profile.id")
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=enforce-id-capitalization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const enforceMuiRoundedIcons: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"enforceRoundedVariant", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enforceMuiRoundedIcons = void 0;
|
|
4
|
+
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
6
|
+
exports.enforceMuiRoundedIcons = (0, createRule_1.createRule)({
|
|
7
|
+
name: 'enforce-mui-rounded-icons',
|
|
8
|
+
meta: {
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
docs: {
|
|
11
|
+
description: 'Enforce the use of -Rounded variant for MUI icons',
|
|
12
|
+
recommended: 'error',
|
|
13
|
+
},
|
|
14
|
+
fixable: 'code',
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
enforceRoundedVariant: 'Use the -Rounded variant for MUI icons (e.g., LogoutRounded instead of Logout)',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
return {
|
|
23
|
+
ImportDeclaration(node) {
|
|
24
|
+
// Only check imports from @mui/icons-material
|
|
25
|
+
if (node.source.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
26
|
+
typeof node.source.value === 'string' &&
|
|
27
|
+
node.source.value.startsWith('@mui/icons-material/')) {
|
|
28
|
+
const iconPath = node.source.value;
|
|
29
|
+
// Skip if already using -Rounded variant
|
|
30
|
+
if (iconPath.endsWith('Rounded')) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Extract the icon name from the path
|
|
34
|
+
const iconName = iconPath.split('/').pop();
|
|
35
|
+
// Skip if the icon name is not a string (shouldn't happen)
|
|
36
|
+
if (!iconName) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Create the rounded variant name
|
|
40
|
+
const roundedVariant = `${iconName}Rounded`;
|
|
41
|
+
context.report({
|
|
42
|
+
node,
|
|
43
|
+
messageId: 'enforceRoundedVariant',
|
|
44
|
+
fix: (fixer) => {
|
|
45
|
+
// Replace the import path with the rounded variant
|
|
46
|
+
return fixer.replaceText(node.source, `'@mui/icons-material/${roundedVariant}'`);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=enforce-mui-rounded-icons.js.map
|
|
@@ -125,6 +125,21 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
125
125
|
},
|
|
126
126
|
defaultOptions: [],
|
|
127
127
|
create(context) {
|
|
128
|
+
// Get the filename from the context
|
|
129
|
+
const filename = context.getFilename();
|
|
130
|
+
// Skip checking for files that should be ignored
|
|
131
|
+
// 1. Files that are not .ts or .tsx
|
|
132
|
+
// 2. Files starting with .
|
|
133
|
+
// 3. Files containing .config
|
|
134
|
+
// 4. Files containing rc suffix
|
|
135
|
+
if ((!filename.endsWith('.ts') && !filename.endsWith('.tsx')) ||
|
|
136
|
+
filename.split('/').pop()?.startsWith('.') ||
|
|
137
|
+
filename.includes('.config') ||
|
|
138
|
+
filename.includes('rc.') ||
|
|
139
|
+
filename.endsWith('rc')) {
|
|
140
|
+
// Return empty object to skip all checks for this file
|
|
141
|
+
return {};
|
|
142
|
+
}
|
|
128
143
|
/**
|
|
129
144
|
* Check if a name has negative connotations
|
|
130
145
|
*/
|
|
@@ -196,6 +211,15 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
196
211
|
}
|
|
197
212
|
return { isNegative: false, alternatives: [] };
|
|
198
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Safely formats alternatives for display
|
|
216
|
+
*/
|
|
217
|
+
function formatAlternatives(alternatives) {
|
|
218
|
+
if (Array.isArray(alternatives)) {
|
|
219
|
+
return alternatives.join(', ');
|
|
220
|
+
}
|
|
221
|
+
return String(alternatives);
|
|
222
|
+
}
|
|
199
223
|
/**
|
|
200
224
|
* Check variable declarations for negative naming
|
|
201
225
|
*/
|
|
@@ -210,7 +234,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
210
234
|
messageId: 'avoidNegativeNaming',
|
|
211
235
|
data: {
|
|
212
236
|
name: variableName,
|
|
213
|
-
alternatives: alternatives
|
|
237
|
+
alternatives: formatAlternatives(alternatives),
|
|
214
238
|
},
|
|
215
239
|
});
|
|
216
240
|
}
|
|
@@ -246,7 +270,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
246
270
|
messageId: 'avoidNegativeNaming',
|
|
247
271
|
data: {
|
|
248
272
|
name: functionName,
|
|
249
|
-
alternatives: alternatives
|
|
273
|
+
alternatives: formatAlternatives(alternatives),
|
|
250
274
|
},
|
|
251
275
|
});
|
|
252
276
|
}
|
|
@@ -265,7 +289,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
265
289
|
messageId: 'avoidNegativeNaming',
|
|
266
290
|
data: {
|
|
267
291
|
name: methodName,
|
|
268
|
-
alternatives: alternatives
|
|
292
|
+
alternatives: formatAlternatives(alternatives),
|
|
269
293
|
},
|
|
270
294
|
});
|
|
271
295
|
}
|
|
@@ -284,7 +308,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
284
308
|
messageId: 'avoidNegativeNaming',
|
|
285
309
|
data: {
|
|
286
310
|
name: propertyName,
|
|
287
|
-
alternatives: alternatives
|
|
311
|
+
alternatives: formatAlternatives(alternatives),
|
|
288
312
|
},
|
|
289
313
|
});
|
|
290
314
|
}
|
|
@@ -303,7 +327,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
303
327
|
messageId: 'avoidNegativeNaming',
|
|
304
328
|
data: {
|
|
305
329
|
name: propertyName,
|
|
306
|
-
alternatives: alternatives
|
|
330
|
+
alternatives: formatAlternatives(alternatives),
|
|
307
331
|
},
|
|
308
332
|
});
|
|
309
333
|
}
|
|
@@ -322,7 +346,7 @@ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
|
|
|
322
346
|
messageId: 'avoidNegativeNaming',
|
|
323
347
|
data: {
|
|
324
348
|
name: paramName,
|
|
325
|
-
alternatives: alternatives
|
|
349
|
+
alternatives: formatAlternatives(alternatives),
|
|
326
350
|
},
|
|
327
351
|
});
|
|
328
352
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
type MessageIds = 'reactNodeShouldBeLowercase' | 'componentTypeShouldBeUppercase';
|
|
2
|
+
export declare const enforceReactTypeNaming: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
+
export {};
|
|
@@ -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
|
|
@@ -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
|
|
@@ -4651,10 +4651,13 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
|
|
|
4651
4651
|
}
|
|
4652
4652
|
}
|
|
4653
4653
|
return false;
|
|
4654
|
-
})()
|
|
4654
|
+
})(),
|
|
4655
|
+
// 5. Component name ends with "Unmemoized" (common React pattern)
|
|
4656
|
+
functionName && functionName.endsWith('Unmemoized')
|
|
4655
4657
|
];
|
|
4656
4658
|
// Consider it a React component if it matches at least 2 indicators
|
|
4657
|
-
|
|
4659
|
+
// OR if it has the Unmemoized suffix (strong indicator of a React component)
|
|
4660
|
+
return indicators.filter(Boolean).length >= 2 || (!!functionName && functionName.endsWith('Unmemoized'));
|
|
4658
4661
|
}
|
|
4659
4662
|
return {
|
|
4660
4663
|
FunctionDeclaration(node) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ensurePointerEventsNone: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missingPointerEventsNone", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|