@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.
Files changed (39) hide show
  1. package/README.md +84 -39
  2. package/lib/index.js +50 -2
  3. package/lib/rules/enforce-css-media-queries.d.ts +5 -0
  4. package/lib/rules/enforce-css-media-queries.js +87 -0
  5. package/lib/rules/enforce-dynamic-imports.d.ts +9 -0
  6. package/lib/rules/enforce-dynamic-imports.js +84 -0
  7. package/lib/rules/enforce-id-capitalization.d.ts +6 -0
  8. package/lib/rules/enforce-id-capitalization.js +78 -0
  9. package/lib/rules/enforce-mui-rounded-icons.d.ts +1 -0
  10. package/lib/rules/enforce-mui-rounded-icons.js +54 -0
  11. package/lib/rules/enforce-positive-naming.js +30 -6
  12. package/lib/rules/enforce-react-type-naming.d.ts +3 -0
  13. package/lib/rules/enforce-react-type-naming.js +191 -0
  14. package/lib/rules/enforce-singular-type-names.d.ts +2 -0
  15. package/lib/rules/enforce-singular-type-names.js +112 -0
  16. package/lib/rules/enforce-verb-noun-naming.js +5 -2
  17. package/lib/rules/ensure-pointer-events-none.d.ts +1 -0
  18. package/lib/rules/ensure-pointer-events-none.js +211 -0
  19. package/lib/rules/key-only-outermost-element.d.ts +3 -0
  20. package/lib/rules/key-only-outermost-element.js +152 -0
  21. package/lib/rules/no-always-true-false-conditions.js +19 -0
  22. package/lib/rules/no-circular-references.d.ts +1 -0
  23. package/lib/rules/no-circular-references.js +523 -0
  24. package/lib/rules/no-hungarian.d.ts +5 -0
  25. package/lib/rules/no-hungarian.js +223 -0
  26. package/lib/rules/no-object-values-on-strings.d.ts +2 -0
  27. package/lib/rules/no-object-values-on-strings.js +294 -0
  28. package/lib/rules/no-type-assertion-returns.js +10 -0
  29. package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
  30. package/lib/rules/no-unnecessary-destructuring.js +69 -0
  31. package/lib/rules/no-unused-props.js +10 -5
  32. package/lib/rules/no-unused-usestate.d.ts +8 -0
  33. package/lib/rules/no-unused-usestate.js +84 -0
  34. package/lib/rules/omit-index-html.d.ts +7 -0
  35. package/lib/rules/omit-index-html.js +91 -0
  36. package/lib/rules/prefer-usememo-over-useeffect-usestate.d.ts +5 -0
  37. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -0
  38. package/lib/rules/react-usememo-should-be-component.js +369 -2
  39. 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.join(', '),
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.join(', '),
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.join(', '),
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.join(', '),
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.join(', '),
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.join(', '),
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,2 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const enforceSingularTypeNames: TSESLint.RuleModule<'typeShouldBeSingular', never[]>;
@@ -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
- return indicators.filter(Boolean).length >= 2;
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>;