@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.
Files changed (70) hide show
  1. package/README.md +83 -61
  2. package/lib/index.js +85 -1
  3. package/lib/rules/enforce-assert-throws.js +48 -3
  4. package/lib/rules/enforce-assertSafe-object-key.d.ts +2 -0
  5. package/lib/rules/enforce-assertSafe-object-key.js +284 -0
  6. package/lib/rules/enforce-centralized-mock-firestore.js +192 -14
  7. package/lib/rules/enforce-css-media-queries.d.ts +5 -0
  8. package/lib/rules/enforce-css-media-queries.js +87 -0
  9. package/lib/rules/enforce-dynamic-imports.d.ts +9 -0
  10. package/lib/rules/enforce-dynamic-imports.js +84 -0
  11. package/lib/rules/enforce-firestore-facade.js +8 -0
  12. package/lib/rules/enforce-id-capitalization.d.ts +6 -0
  13. package/lib/rules/enforce-id-capitalization.js +78 -0
  14. package/lib/rules/enforce-microdiff.d.ts +3 -0
  15. package/lib/rules/enforce-microdiff.js +379 -0
  16. package/lib/rules/enforce-mui-rounded-icons.d.ts +1 -0
  17. package/lib/rules/enforce-mui-rounded-icons.js +54 -0
  18. package/lib/rules/enforce-object-literal-as-const.d.ts +4 -0
  19. package/lib/rules/enforce-object-literal-as-const.js +88 -0
  20. package/lib/rules/enforce-positive-naming.d.ts +1 -0
  21. package/lib/rules/enforce-positive-naming.js +387 -0
  22. package/lib/rules/enforce-props-argument-name.d.ts +8 -0
  23. package/lib/rules/enforce-props-argument-name.js +182 -0
  24. package/lib/rules/enforce-react-type-naming.d.ts +3 -0
  25. package/lib/rules/enforce-react-type-naming.js +191 -0
  26. package/lib/rules/enforce-render-hits-memoization.js +155 -26
  27. package/lib/rules/enforce-singular-type-names.d.ts +2 -0
  28. package/lib/rules/enforce-singular-type-names.js +112 -0
  29. package/lib/rules/enforce-timestamp-now.d.ts +1 -0
  30. package/lib/rules/enforce-timestamp-now.js +223 -0
  31. package/lib/rules/enforce-verb-noun-naming.js +5 -2
  32. package/lib/rules/ensure-pointer-events-none.d.ts +1 -0
  33. package/lib/rules/ensure-pointer-events-none.js +211 -0
  34. package/lib/rules/extract-global-constants.d.ts +1 -1
  35. package/lib/rules/extract-global-constants.js +109 -1
  36. package/lib/rules/global-const-style.js +3 -2
  37. package/lib/rules/key-only-outermost-element.d.ts +3 -0
  38. package/lib/rules/key-only-outermost-element.js +152 -0
  39. package/lib/rules/no-always-true-false-conditions.d.ts +3 -0
  40. package/lib/rules/no-always-true-false-conditions.js +1221 -0
  41. package/lib/rules/no-circular-references.d.ts +1 -0
  42. package/lib/rules/no-circular-references.js +523 -0
  43. package/lib/rules/no-firestore-jest-mock.js +27 -4
  44. package/lib/rules/no-hungarian.d.ts +5 -0
  45. package/lib/rules/no-hungarian.js +223 -0
  46. package/lib/rules/no-mock-firebase-admin.js +20 -7
  47. package/lib/rules/no-object-values-on-strings.d.ts +2 -0
  48. package/lib/rules/no-object-values-on-strings.js +294 -0
  49. package/lib/rules/no-type-assertion-returns.d.ts +9 -0
  50. package/lib/rules/no-type-assertion-returns.js +299 -0
  51. package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
  52. package/lib/rules/no-unnecessary-destructuring.js +69 -0
  53. package/lib/rules/no-unnecessary-verb-suffix.d.ts +1 -0
  54. package/lib/rules/no-unnecessary-verb-suffix.js +203 -0
  55. package/lib/rules/no-unused-props.js +10 -5
  56. package/lib/rules/no-unused-usestate.d.ts +8 -0
  57. package/lib/rules/no-unused-usestate.js +84 -0
  58. package/lib/rules/omit-index-html.d.ts +7 -0
  59. package/lib/rules/omit-index-html.js +91 -0
  60. package/lib/rules/prefer-clone-deep.js +294 -22
  61. package/lib/rules/prefer-fragment-component.js +265 -34
  62. package/lib/rules/prefer-global-router-state-key.d.ts +5 -0
  63. package/lib/rules/prefer-global-router-state-key.js +89 -0
  64. package/lib/rules/prefer-usememo-over-useeffect-usestate.d.ts +5 -0
  65. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -0
  66. package/lib/rules/prefer-utility-function-over-private-static.d.ts +1 -0
  67. package/lib/rules/prefer-utility-function-over-private-static.js +77 -0
  68. package/lib/rules/react-usememo-should-be-component.d.ts +1 -0
  69. package/lib/rules/react-usememo-should-be-component.js +623 -0
  70. package/package.json +10 -7
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceObjectLiteralAsConst = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ exports.enforceObjectLiteralAsConst = (0, createRule_1.createRule)({
6
+ name: 'enforce-object-literal-as-const',
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Enforce that object literals returned from functions should be marked with `as const` to ensure type safety and immutability.',
11
+ recommended: 'error',
12
+ },
13
+ fixable: 'code',
14
+ messages: {
15
+ enforceAsConst: 'Object literals returned from functions should be marked with `as const` to ensure type safety and immutability',
16
+ },
17
+ schema: [],
18
+ },
19
+ defaultOptions: [],
20
+ create(context) {
21
+ return {
22
+ ReturnStatement(node) {
23
+ // Skip if there's no argument in the return statement
24
+ if (!node.argument) {
25
+ return;
26
+ }
27
+ // Check if the return statement is inside a function
28
+ const sourceCode = context.getSourceCode();
29
+ // Use context.getAncestors() for now, but note it's deprecated
30
+ // We'll need to update this when upgrading to ESLint v9
31
+ const ancestors = context.getAncestors();
32
+ const isInFunction = ancestors.some((ancestor) => ancestor.type === 'FunctionDeclaration' ||
33
+ ancestor.type === 'ArrowFunctionExpression' ||
34
+ ancestor.type === 'FunctionExpression' ||
35
+ ancestor.type === 'MethodDefinition');
36
+ if (!isInFunction) {
37
+ return;
38
+ }
39
+ // Check if the return value is an object or array literal
40
+ const { argument } = node;
41
+ // Skip if the return value already has 'as const' assertion
42
+ if (argument.type === 'TSAsExpression') {
43
+ const tsAsExpression = argument;
44
+ // Check if the type annotation is 'const'
45
+ if (tsAsExpression.typeAnnotation.type === 'TSTypeReference' &&
46
+ tsAsExpression.typeAnnotation.typeName.type === 'Identifier' &&
47
+ tsAsExpression.typeAnnotation.typeName.name === 'const') {
48
+ return;
49
+ }
50
+ // If it has another type assertion but not 'as const', we still need to check
51
+ // if the expression is an object/array literal
52
+ if (tsAsExpression.expression.type !== 'ObjectExpression' &&
53
+ tsAsExpression.expression.type !== 'ArrayExpression') {
54
+ return;
55
+ }
56
+ }
57
+ else if (argument.type !== 'ObjectExpression' &&
58
+ argument.type !== 'ArrayExpression') {
59
+ // Skip if not an object/array literal and not a type assertion
60
+ return;
61
+ }
62
+ // Skip if the return value uses spread operator
63
+ if ((argument.type === 'ObjectExpression' &&
64
+ argument.properties.some((prop) => prop.type === 'SpreadElement')) ||
65
+ (argument.type === 'ArrayExpression' &&
66
+ argument.elements.some((elem) => elem !== null && elem.type === 'SpreadElement'))) {
67
+ return;
68
+ }
69
+ // Report the issue and provide a fix
70
+ context.report({
71
+ node,
72
+ messageId: 'enforceAsConst',
73
+ fix(fixer) {
74
+ const text = sourceCode.getText(argument);
75
+ // If it's already a type assertion but not 'as const'
76
+ if (argument.type === 'TSAsExpression') {
77
+ // Get the expression part (before the 'as')
78
+ const expressionText = sourceCode.getText(argument.expression);
79
+ return fixer.replaceText(argument, `${expressionText} as const`);
80
+ }
81
+ return fixer.replaceText(argument, `${text} as const`);
82
+ },
83
+ });
84
+ },
85
+ };
86
+ },
87
+ });
88
+ //# sourceMappingURL=enforce-object-literal-as-const.js.map
@@ -0,0 +1 @@
1
+ export declare const enforcePositiveNaming: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"avoidNegativeNaming", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,387 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforcePositiveNaming = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ // Common negative prefixes and words to detect
7
+ const NEGATIVE_PREFIXES = ['not', 'no', 'non', 'un', 'in', 'dis'];
8
+ const NEGATIVE_WORDS = [
9
+ 'invalid',
10
+ 'disabled',
11
+ 'incomplete',
12
+ 'unpaid',
13
+ 'inactive',
14
+ 'disallowed',
15
+ 'unauthorized',
16
+ 'unverified',
17
+ 'prevent',
18
+ 'avoid',
19
+ 'block',
20
+ 'deny',
21
+ 'reject',
22
+ 'exclude',
23
+ 'fail',
24
+ 'missing',
25
+ 'forbidden',
26
+ 'impossible',
27
+ 'error',
28
+ 'broken',
29
+ 'banned',
30
+ 'restricted',
31
+ 'limitation',
32
+ 'limitation',
33
+ 'blocked',
34
+ 'prohibited',
35
+ ];
36
+ // Whitelist of acceptable negative terms (technical terms, common patterns)
37
+ const ALLOWED_NEGATIVE_TERMS = new Set([
38
+ 'isNaN',
39
+ 'isNull',
40
+ 'isUndefined',
41
+ 'isEmpty',
42
+ 'isOffline',
43
+ 'isMissing',
44
+ 'isOutOfStock',
45
+ 'isOutOfBounds',
46
+ 'isOffPeak',
47
+ 'isNoticeable',
48
+ 'hasNotification',
49
+ 'isNoteworthy',
50
+ 'isNone',
51
+ 'isNegative',
52
+ 'isNeutral',
53
+ 'isNotification',
54
+ 'isNote',
55
+ 'hasNote',
56
+ ]);
57
+ // Map of negative terms to suggested positive alternatives
58
+ const POSITIVE_ALTERNATIVES = {
59
+ // Prefixes
60
+ 'isNot': ['is'],
61
+ 'isUn': ['is'],
62
+ 'isDis': ['is'],
63
+ 'isIn': ['is'],
64
+ 'isNon': ['is'],
65
+ 'hasNo': ['has'],
66
+ 'hasNot': ['has'],
67
+ 'canNot': ['can'],
68
+ 'shouldNot': ['should'],
69
+ 'willNot': ['will'],
70
+ 'doesNot': ['does'],
71
+ // Words
72
+ 'isInvalid': ['isValid'],
73
+ 'isDisabled': ['isEnabled'],
74
+ 'isIncomplete': ['isComplete'],
75
+ 'isUnpaid': ['isPaid', 'hasPaid'],
76
+ 'isInactive': ['isActive'],
77
+ 'isDisallowed': ['isAllowed'],
78
+ 'isUnauthorized': ['isAuthorized'],
79
+ 'isUnverified': ['isVerified'],
80
+ 'isImpossible': ['isPossible'],
81
+ 'isError': ['isSuccess', 'isValid'],
82
+ 'isBroken': ['isWorking', 'isFunctional'],
83
+ 'isBanned': ['isAllowed', 'isPermitted'],
84
+ 'isRestricted': ['isAllowed', 'isAccessible'],
85
+ 'isBlocked': ['isAllowed', 'isAccessible'],
86
+ 'isProhibited': ['isAllowed', 'isPermitted'],
87
+ 'prevent': ['allow', 'enable'],
88
+ 'avoid': ['use', 'prefer'],
89
+ 'block': ['allow', 'permit'],
90
+ 'deny': ['allow', 'grant'],
91
+ 'reject': ['accept', 'approve'],
92
+ 'exclude': ['include'],
93
+ 'fail': ['succeed', 'pass'],
94
+ 'missing': ['present', 'available'],
95
+ 'forbidden': ['allowed', 'permitted'],
96
+ 'disabled': ['enabled'],
97
+ 'incomplete': ['complete'],
98
+ 'invalid': ['valid'],
99
+ 'disallowed': ['allowed'],
100
+ 'unauthorized': ['authorized'],
101
+ 'unverified': ['verified'],
102
+ 'inactive': ['active'],
103
+ 'disabledFeatures': ['enabledFeatures'],
104
+ 'inactiveUsers': ['activeUsers'],
105
+ 'disableFeature': ['enableFeature'],
106
+ 'disableAccount': ['enableAccount'],
107
+ 'blockUser': ['allowUser'],
108
+ 'preventAccess': ['allowAccess', 'enableAccess'],
109
+ 'restrictAccess': ['allowAccess', 'grantAccess'],
110
+ 'limitations': ['capabilities', 'features'],
111
+ 'limitation': ['capability', 'feature'],
112
+ };
113
+ exports.enforcePositiveNaming = (0, createRule_1.createRule)({
114
+ name: 'enforce-positive-naming',
115
+ meta: {
116
+ type: 'suggestion',
117
+ docs: {
118
+ description: 'Enforce positive variable naming patterns and avoid negative naming',
119
+ recommended: 'error',
120
+ },
121
+ schema: [],
122
+ messages: {
123
+ avoidNegativeNaming: 'Avoid negative naming "{{name}}". Consider using a positive alternative like: {{alternatives}}',
124
+ },
125
+ },
126
+ defaultOptions: [],
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
+ }
143
+ /**
144
+ * Check if a name has negative connotations
145
+ */
146
+ function hasNegativeNaming(name) {
147
+ // Skip checking if the name is in the whitelist
148
+ if (ALLOWED_NEGATIVE_TERMS.has(name)) {
149
+ return { isNegative: false, alternatives: [] };
150
+ }
151
+ // Check for exact matches in our alternatives map first
152
+ if (POSITIVE_ALTERNATIVES[name]) {
153
+ return { isNegative: true, alternatives: POSITIVE_ALTERNATIVES[name] };
154
+ }
155
+ // Check for negative prefixes
156
+ for (const prefix of NEGATIVE_PREFIXES) {
157
+ // Check for patterns like isNot, hasNo, canNot, etc.
158
+ const prefixPatterns = [
159
+ { pattern: new RegExp(`^is${prefix}`, 'i'), key: `is${prefix.charAt(0).toUpperCase() + prefix.slice(1)}` },
160
+ { pattern: new RegExp(`^has${prefix}`, 'i'), key: `has${prefix.charAt(0).toUpperCase() + prefix.slice(1)}` },
161
+ { pattern: new RegExp(`^can${prefix}`, 'i'), key: `can${prefix.charAt(0).toUpperCase() + prefix.slice(1)}` },
162
+ { pattern: new RegExp(`^should${prefix}`, 'i'), key: `should${prefix.charAt(0).toUpperCase() + prefix.slice(1)}` },
163
+ { pattern: new RegExp(`^will${prefix}`, 'i'), key: `will${prefix.charAt(0).toUpperCase() + prefix.slice(1)}` },
164
+ { pattern: new RegExp(`^does${prefix}`, 'i'), key: `does${prefix.charAt(0).toUpperCase() + prefix.slice(1)}` },
165
+ ];
166
+ for (const { pattern, key } of prefixPatterns) {
167
+ if (pattern.test(name)) {
168
+ // If we have a direct match for this pattern (like isNotVerified -> isVerified)
169
+ const directMatch = POSITIVE_ALTERNATIVES[name];
170
+ if (directMatch) {
171
+ return { isNegative: true, alternatives: directMatch };
172
+ }
173
+ const alternatives = POSITIVE_ALTERNATIVES[key] || [];
174
+ if (alternatives.length > 0) {
175
+ // Suggest the positive version with the rest of the name
176
+ const restOfName = name.replace(pattern, '');
177
+ const suggestedAlternatives = alternatives.map(alt => `${alt}${restOfName.charAt(0).toUpperCase() + restOfName.slice(1)}`);
178
+ return { isNegative: true, alternatives: suggestedAlternatives };
179
+ }
180
+ return { isNegative: true, alternatives: ['a positive alternative'] };
181
+ }
182
+ }
183
+ }
184
+ // Check for exact negative words
185
+ for (const word of NEGATIVE_WORDS) {
186
+ if (name.toLowerCase() === word.toLowerCase()) {
187
+ const alternatives = POSITIVE_ALTERNATIVES[word] || [];
188
+ return {
189
+ isNegative: true,
190
+ alternatives: alternatives.length ? alternatives : ['a positive alternative']
191
+ };
192
+ }
193
+ }
194
+ // Check for negative words in camelCase/PascalCase
195
+ for (const word of NEGATIVE_WORDS) {
196
+ // Match the word at the beginning or after a capital letter
197
+ const pattern = new RegExp(`(^|[A-Z])${word}($|[A-Z])`, 'i');
198
+ if (pattern.test(name)) {
199
+ // For compound names like isInvalid, check if we have a specific suggestion
200
+ const exactMatch = `is${word.charAt(0).toUpperCase() + word.slice(1)}`;
201
+ if (POSITIVE_ALTERNATIVES[exactMatch]) {
202
+ return { isNegative: true, alternatives: POSITIVE_ALTERNATIVES[exactMatch] };
203
+ }
204
+ // Otherwise suggest general alternatives for the negative word
205
+ const alternatives = POSITIVE_ALTERNATIVES[word] || [];
206
+ return {
207
+ isNegative: true,
208
+ alternatives: alternatives.length ? alternatives : ['a positive alternative']
209
+ };
210
+ }
211
+ }
212
+ return { isNegative: false, alternatives: [] };
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
+ }
223
+ /**
224
+ * Check variable declarations for negative naming
225
+ */
226
+ function checkVariableDeclaration(node) {
227
+ if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier)
228
+ return;
229
+ const variableName = node.id.name;
230
+ const { isNegative, alternatives } = hasNegativeNaming(variableName);
231
+ if (isNegative) {
232
+ context.report({
233
+ node: node.id,
234
+ messageId: 'avoidNegativeNaming',
235
+ data: {
236
+ name: variableName,
237
+ alternatives: formatAlternatives(alternatives),
238
+ },
239
+ });
240
+ }
241
+ }
242
+ /**
243
+ * Check function declarations for negative naming
244
+ */
245
+ function checkFunctionDeclaration(node) {
246
+ // Skip anonymous functions
247
+ if (!node.id && node.parent?.type !== utils_1.AST_NODE_TYPES.VariableDeclarator) {
248
+ return;
249
+ }
250
+ // Get function name from either the function declaration or variable declarator
251
+ let functionName = '';
252
+ if (node.id) {
253
+ functionName = node.id.name;
254
+ }
255
+ else if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
256
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
257
+ functionName = node.parent.id.name;
258
+ }
259
+ else if (node.parent?.type === utils_1.AST_NODE_TYPES.Property &&
260
+ node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
261
+ // Handle object method shorthand
262
+ functionName = node.parent.key.name;
263
+ }
264
+ if (!functionName)
265
+ return;
266
+ const { isNegative, alternatives } = hasNegativeNaming(functionName);
267
+ if (isNegative) {
268
+ context.report({
269
+ node: node.id || node,
270
+ messageId: 'avoidNegativeNaming',
271
+ data: {
272
+ name: functionName,
273
+ alternatives: formatAlternatives(alternatives),
274
+ },
275
+ });
276
+ }
277
+ }
278
+ /**
279
+ * Check method definitions for negative naming
280
+ */
281
+ function checkMethodDefinition(node) {
282
+ if (node.key.type !== utils_1.AST_NODE_TYPES.Identifier)
283
+ return;
284
+ const methodName = node.key.name;
285
+ const { isNegative, alternatives } = hasNegativeNaming(methodName);
286
+ if (isNegative) {
287
+ context.report({
288
+ node: node.key,
289
+ messageId: 'avoidNegativeNaming',
290
+ data: {
291
+ name: methodName,
292
+ alternatives: formatAlternatives(alternatives),
293
+ },
294
+ });
295
+ }
296
+ }
297
+ /**
298
+ * Check property definitions for negative naming
299
+ */
300
+ function checkProperty(node) {
301
+ if (node.key.type !== utils_1.AST_NODE_TYPES.Identifier)
302
+ return;
303
+ const propertyName = node.key.name;
304
+ const { isNegative, alternatives } = hasNegativeNaming(propertyName);
305
+ if (isNegative) {
306
+ context.report({
307
+ node: node.key,
308
+ messageId: 'avoidNegativeNaming',
309
+ data: {
310
+ name: propertyName,
311
+ alternatives: formatAlternatives(alternatives),
312
+ },
313
+ });
314
+ }
315
+ }
316
+ /**
317
+ * Check TSPropertySignature for negative naming (in interfaces)
318
+ */
319
+ function checkPropertySignature(node) {
320
+ if (node.key.type !== utils_1.AST_NODE_TYPES.Identifier)
321
+ return;
322
+ const propertyName = node.key.name;
323
+ const { isNegative, alternatives } = hasNegativeNaming(propertyName);
324
+ if (isNegative) {
325
+ context.report({
326
+ node: node.key,
327
+ messageId: 'avoidNegativeNaming',
328
+ data: {
329
+ name: propertyName,
330
+ alternatives: formatAlternatives(alternatives),
331
+ },
332
+ });
333
+ }
334
+ }
335
+ /**
336
+ * Check parameter names for negative naming
337
+ */
338
+ function checkParameter(node) {
339
+ if (node.type !== utils_1.AST_NODE_TYPES.Identifier)
340
+ return;
341
+ const paramName = node.name;
342
+ const { isNegative, alternatives } = hasNegativeNaming(paramName);
343
+ if (isNegative) {
344
+ context.report({
345
+ node,
346
+ messageId: 'avoidNegativeNaming',
347
+ data: {
348
+ name: paramName,
349
+ alternatives: formatAlternatives(alternatives),
350
+ },
351
+ });
352
+ }
353
+ }
354
+ return {
355
+ VariableDeclarator: checkVariableDeclaration,
356
+ FunctionDeclaration: checkFunctionDeclaration,
357
+ // Only check function expressions when they're not part of a variable declaration
358
+ // to avoid duplicate errors
359
+ FunctionExpression(node) {
360
+ if (node.parent?.type !== utils_1.AST_NODE_TYPES.VariableDeclarator) {
361
+ checkFunctionDeclaration(node);
362
+ }
363
+ },
364
+ // Only check arrow function expressions when they're not part of a variable declaration
365
+ // to avoid duplicate errors
366
+ ArrowFunctionExpression(node) {
367
+ if (node.parent?.type !== utils_1.AST_NODE_TYPES.VariableDeclarator) {
368
+ checkFunctionDeclaration(node);
369
+ }
370
+ },
371
+ MethodDefinition: checkMethodDefinition,
372
+ Property: checkProperty,
373
+ TSPropertySignature: checkPropertySignature,
374
+ Identifier(node) {
375
+ // Check parameter names in function declarations
376
+ if (node.parent &&
377
+ (node.parent.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
378
+ node.parent.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
379
+ node.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) &&
380
+ node.parent.params.includes(node)) {
381
+ checkParameter(node);
382
+ }
383
+ },
384
+ };
385
+ },
386
+ });
387
+ //# sourceMappingURL=enforce-positive-naming.js.map
@@ -0,0 +1,8 @@
1
+ type Options = [
2
+ {
3
+ enforceDestructuring?: boolean;
4
+ ignoreExternalInterfaces?: boolean;
5
+ }
6
+ ];
7
+ export declare const enforcePropsArgumentName: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"usePropsForType", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
8
+ export {};
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforcePropsArgumentName = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const defaultOptions = {
7
+ enforceDestructuring: false,
8
+ ignoreExternalInterfaces: true,
9
+ };
10
+ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
11
+ name: 'enforce-props-argument-name',
12
+ meta: {
13
+ type: 'suggestion',
14
+ docs: {
15
+ description: 'Enforce using "Props" suffix in type names for parameter objects',
16
+ recommended: 'error',
17
+ },
18
+ fixable: 'code',
19
+ schema: [
20
+ {
21
+ type: 'object',
22
+ properties: {
23
+ enforceDestructuring: {
24
+ type: 'boolean',
25
+ },
26
+ ignoreExternalInterfaces: {
27
+ type: 'boolean',
28
+ },
29
+ },
30
+ additionalProperties: false,
31
+ },
32
+ ],
33
+ messages: {
34
+ usePropsForType: 'Use "Props" suffix in type name instead of "{{ typeSuffix }}"',
35
+ },
36
+ },
37
+ defaultOptions: [defaultOptions],
38
+ create(context, [options]) {
39
+ const finalOptions = { ...defaultOptions, ...options };
40
+ // Check if a node is from an external library
41
+ function isFromExternalLibrary(node) {
42
+ if (!finalOptions.ignoreExternalInterfaces) {
43
+ return false;
44
+ }
45
+ let current = node;
46
+ while (current.parent) {
47
+ if (current.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) {
48
+ // Check if the interface extends something from an external library
49
+ const interfaceDecl = current;
50
+ if (interfaceDecl.extends && interfaceDecl.extends.length > 0) {
51
+ // Check if any of the extended interfaces are from external libraries
52
+ // This is a simplified check - a more robust implementation would trace imports
53
+ return true;
54
+ }
55
+ }
56
+ current = current.parent;
57
+ }
58
+ return false;
59
+ }
60
+ // Extract type name from a type annotation
61
+ function getTypeName(typeAnnotation) {
62
+ if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
63
+ const typeName = typeAnnotation.typeName;
64
+ if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
65
+ return typeName.name;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ // Check if a type name has a non-"Props" suffix
71
+ function hasNonPropsSuffix(typeName) {
72
+ if (typeName.endsWith('Props')) {
73
+ return null;
74
+ }
75
+ const suffixes = ['Config', 'Settings', 'Options', 'Params', 'Parameters', 'Args', 'Arguments'];
76
+ for (const suffix of suffixes) {
77
+ if (typeName.endsWith(suffix)) {
78
+ return suffix;
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+ // Fix type name
84
+ function fixTypeName(fixer, node, oldName, suffix) {
85
+ if (node.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
86
+ node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
87
+ const baseName = oldName.slice(0, -suffix.length);
88
+ return fixer.replaceText(node.typeName, `${baseName}Props`);
89
+ }
90
+ return null;
91
+ }
92
+ // Check function parameters
93
+ function checkFunctionParams(node) {
94
+ if (node.params.length !== 1) {
95
+ return; // Only check functions with a single parameter
96
+ }
97
+ const param = node.params[0];
98
+ // Skip primitive parameters
99
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation && (param.typeAnnotation.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSStringKeyword ||
100
+ param.typeAnnotation.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSNumberKeyword ||
101
+ param.typeAnnotation.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword)) {
102
+ return;
103
+ }
104
+ // Check if the parameter has a type annotation
105
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
106
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
107
+ if (typeName) {
108
+ const nonPropsSuffix = hasNonPropsSuffix(typeName);
109
+ if (nonPropsSuffix) {
110
+ context.report({
111
+ node: param.typeAnnotation.typeAnnotation,
112
+ messageId: 'usePropsForType',
113
+ data: { typeSuffix: nonPropsSuffix },
114
+ fix: (fixer) => fixTypeName(fixer, param.typeAnnotation.typeAnnotation, typeName, nonPropsSuffix),
115
+ });
116
+ }
117
+ }
118
+ }
119
+ }
120
+ // Check class constructor parameters
121
+ function checkClassConstructor(node) {
122
+ if (node.kind !== 'constructor') {
123
+ return;
124
+ }
125
+ const constructor = node.value;
126
+ if (constructor.params.length !== 1) {
127
+ return; // Only check constructors with a single parameter
128
+ }
129
+ const param = constructor.params[0];
130
+ // Skip primitive parameters
131
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation && (param.typeAnnotation.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSStringKeyword ||
132
+ param.typeAnnotation.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSNumberKeyword ||
133
+ param.typeAnnotation.typeAnnotation.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword)) {
134
+ return;
135
+ }
136
+ // Check if the parameter has a type annotation
137
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier && param.typeAnnotation && param.typeAnnotation.typeAnnotation) {
138
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
139
+ if (typeName) {
140
+ const nonPropsSuffix = hasNonPropsSuffix(typeName);
141
+ if (nonPropsSuffix) {
142
+ context.report({
143
+ node: param.typeAnnotation.typeAnnotation,
144
+ messageId: 'usePropsForType',
145
+ data: { typeSuffix: nonPropsSuffix },
146
+ fix: (fixer) => fixTypeName(fixer, param.typeAnnotation.typeAnnotation, typeName, nonPropsSuffix),
147
+ });
148
+ }
149
+ }
150
+ }
151
+ }
152
+ // Check type definitions
153
+ function checkTypeDefinition(node) {
154
+ if (!node.id || !node.id.name) {
155
+ return;
156
+ }
157
+ const typeName = node.id.name;
158
+ const nonPropsSuffix = hasNonPropsSuffix(typeName);
159
+ if (nonPropsSuffix && !isFromExternalLibrary(node)) {
160
+ const baseName = typeName.slice(0, -nonPropsSuffix.length);
161
+ const newTypeName = `${baseName}Props`;
162
+ context.report({
163
+ node: node.id,
164
+ messageId: 'usePropsForType',
165
+ data: { typeSuffix: nonPropsSuffix },
166
+ fix: (fixer) => {
167
+ return fixer.replaceText(node.id, newTypeName);
168
+ },
169
+ });
170
+ }
171
+ }
172
+ return {
173
+ FunctionDeclaration: checkFunctionParams,
174
+ FunctionExpression: checkFunctionParams,
175
+ ArrowFunctionExpression: checkFunctionParams,
176
+ TSMethodSignature: checkFunctionParams,
177
+ MethodDefinition: checkClassConstructor,
178
+ TSTypeAliasDeclaration: checkTypeDefinition,
179
+ };
180
+ },
181
+ });
182
+ //# sourceMappingURL=enforce-props-argument-name.js.map
@@ -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 {};