@blumintinc/eslint-plugin-blumint 1.12.5 → 1.13.0

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 (142) hide show
  1. package/README.md +140 -99
  2. package/lib/index.js +111 -20
  3. package/lib/rules/array-methods-this-context.js +20 -2
  4. package/lib/rules/avoid-utils-directory.js +11 -2
  5. package/lib/rules/dynamic-https-errors.d.ts +3 -1
  6. package/lib/rules/dynamic-https-errors.js +48 -10
  7. package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
  8. package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +51 -95
  9. package/lib/rules/enforce-assert-throws.d.ts +3 -1
  10. package/lib/rules/enforce-assert-throws.js +493 -51
  11. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
  12. package/lib/rules/enforce-boolean-naming-prefixes.js +832 -39
  13. package/lib/rules/enforce-callable-types.js +4 -4
  14. package/lib/rules/enforce-callback-memo.js +143 -18
  15. package/lib/rules/enforce-centralized-mock-firestore.js +4 -1
  16. package/lib/rules/enforce-console-error.d.ts +3 -0
  17. package/lib/rules/enforce-console-error.js +250 -0
  18. package/lib/rules/enforce-css-media-queries.js +24 -46
  19. package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
  20. package/lib/rules/enforce-dynamic-imports.js +1 -1
  21. package/lib/rules/enforce-exported-function-types.js +3 -3
  22. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
  23. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +212 -0
  24. package/lib/rules/enforce-firestore-doc-ref-generic.js +335 -0
  25. package/lib/rules/enforce-firestore-facade.js +347 -157
  26. package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
  27. package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
  28. package/lib/rules/enforce-global-constants.d.ts +4 -1
  29. package/lib/rules/enforce-global-constants.js +240 -12
  30. package/lib/rules/enforce-memoize-async.d.ts +2 -1
  31. package/lib/rules/enforce-memoize-async.js +96 -27
  32. package/lib/rules/enforce-memoize-getters.d.ts +3 -0
  33. package/lib/rules/enforce-memoize-getters.js +123 -0
  34. package/lib/rules/enforce-microdiff.js +2 -1
  35. package/lib/rules/enforce-positive-naming.js +5 -7
  36. package/lib/rules/enforce-props-argument-name.d.ts +2 -7
  37. package/lib/rules/enforce-props-argument-name.js +165 -151
  38. package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
  39. package/lib/rules/enforce-props-naming-consistency.js +145 -0
  40. package/lib/rules/enforce-querykey-ts.d.ts +9 -0
  41. package/lib/rules/enforce-querykey-ts.js +274 -0
  42. package/lib/rules/enforce-singular-type-names.js +9 -5
  43. package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
  44. package/lib/rules/enforce-typescript-markdown-code-blocks.js +96 -0
  45. package/lib/rules/enforce-verb-noun-naming.js +85 -10
  46. package/lib/rules/extract-global-constants.js +57 -45
  47. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
  48. package/lib/rules/fast-deep-equal-over-microdiff.js +356 -78
  49. package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
  50. package/lib/rules/firestore-transaction-reads-before-writes.js +231 -0
  51. package/lib/rules/generic-starts-with-t.js +8 -2
  52. package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
  53. package/lib/rules/memoize-root-level-hocs.js +242 -0
  54. package/lib/rules/no-array-length-in-deps.d.ts +12 -0
  55. package/lib/rules/no-array-length-in-deps.js +308 -0
  56. package/lib/rules/no-async-array-filter.js +5 -2
  57. package/lib/rules/no-complex-cloud-params.js +15 -2
  58. package/lib/rules/no-compositing-layer-props.js +4 -2
  59. package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
  60. package/lib/rules/no-entire-object-hook-deps.js +317 -63
  61. package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
  62. package/lib/rules/no-excessive-parent-chain.js +273 -0
  63. package/lib/rules/no-firestore-object-arrays.js +345 -30
  64. package/lib/rules/no-hungarian.js +13 -13
  65. package/lib/rules/no-margin-properties.d.ts +7 -1
  66. package/lib/rules/no-margin-properties.js +16 -3
  67. package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
  68. package/lib/rules/no-misleading-boolean-prefixes.js +327 -0
  69. package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
  70. package/lib/rules/no-overridable-method-calls-in-constructor.js +260 -0
  71. package/lib/rules/no-passthrough-getters.d.ts +4 -0
  72. package/lib/rules/no-passthrough-getters.js +170 -0
  73. package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
  74. package/lib/rules/no-redundant-usecallback-wrapper.js +315 -0
  75. package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
  76. package/lib/rules/no-restricted-properties-fix.js +172 -0
  77. package/lib/rules/no-separate-loading-state.d.ts +8 -0
  78. package/lib/rules/no-separate-loading-state.js +142 -0
  79. package/lib/rules/no-stale-state-across-await.d.ts +1 -0
  80. package/lib/rules/no-stale-state-across-await.js +185 -0
  81. package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
  82. package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
  83. package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
  84. package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
  85. package/lib/rules/no-undefined-null-passthrough.js +166 -19
  86. package/lib/rules/no-unnecessary-verb-suffix.js +0 -2
  87. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  88. package/lib/rules/no-unused-props.d.ts +3 -0
  89. package/lib/rules/no-unused-props.js +213 -78
  90. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  91. package/lib/rules/optimize-object-boolean-conditions.js +234 -0
  92. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  93. package/lib/rules/parallelize-async-operations.js +375 -0
  94. package/lib/rules/prefer-batch-operations.js +199 -33
  95. package/lib/rules/prefer-block-comments-for-declarations.js +13 -3
  96. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  97. package/lib/rules/prefer-document-flattening.js +239 -0
  98. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  99. package/lib/rules/prefer-field-paths-in-transforms.js +250 -0
  100. package/lib/rules/prefer-fragment-component.js +3 -3
  101. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  102. package/lib/rules/prefer-global-router-state-key.js +230 -31
  103. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  104. package/lib/rules/prefer-next-dynamic.js +366 -0
  105. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  106. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +430 -0
  107. package/lib/rules/prefer-nullish-coalescing-override.d.ts +6 -0
  108. package/lib/rules/prefer-nullish-coalescing-override.js +182 -0
  109. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  110. package/lib/rules/prefer-params-over-parent-id.js +228 -0
  111. package/lib/rules/prefer-settings-object.js +23 -15
  112. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  113. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  114. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  115. package/lib/rules/prefer-url-tostring-over-tojson.js +166 -0
  116. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  117. package/lib/rules/prefer-use-deep-compare-memo.js +428 -0
  118. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +24 -1
  119. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  120. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  121. package/lib/rules/react-usememo-should-be-component.js +144 -78
  122. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  123. package/lib/rules/require-image-optimized.js +1 -1
  124. package/lib/rules/require-memo.js +11 -1
  125. package/lib/rules/require-usememo-object-literals.js +7 -1
  126. package/lib/rules/semantic-function-prefixes.js +38 -18
  127. package/lib/rules/sync-onwrite-name-func.js +5 -1
  128. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  129. package/lib/rules/test-file-location-enforcement.js +57 -0
  130. package/lib/rules/use-custom-link.js +6 -1
  131. package/lib/rules/use-custom-router.js +6 -1
  132. package/lib/rules/use-latest-callback.d.ts +3 -0
  133. package/lib/rules/use-latest-callback.js +270 -0
  134. package/lib/utils/ASTHelpers.js +10 -0
  135. package/lib/utils/createRule.js +1 -1
  136. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  137. package/lib/utils/ruleTester.d.ts +1 -0
  138. package/lib/utils/ruleTester.js +4 -1
  139. package/package.json +21 -14
  140. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
  141. package/lib/rules/no-restricted-imports-dynamic.d.ts +0 -25
  142. package/lib/rules/no-restricted-imports-dynamic.js +0 -213
@@ -3,60 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enforcePropsArgumentName = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
5
  const createRule_1 = require("../utils/createRule");
6
- const defaultOptions = {
7
- enforceDestructuring: false,
8
- ignoreExternalInterfaces: true,
9
- };
10
6
  exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
11
7
  name: 'enforce-props-argument-name',
12
8
  meta: {
13
9
  type: 'suggestion',
14
10
  docs: {
15
- description: 'Enforce using "Props" suffix in type names for parameter objects',
11
+ description: 'Authoritative rule: parameters with types ending in "Props" should be named "props" (or prefixed variants when multiple Props params exist)',
16
12
  recommended: 'error',
17
13
  },
18
14
  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
- ],
15
+ schema: [],
33
16
  messages: {
34
- usePropsForType: 'Use "Props" suffix in type name instead of "{{ typeSuffix }}"',
17
+ usePropsParameterName: 'Parameter with type "{{ typeName }}" should be named "props"',
18
+ usePropsParameterNameWithPrefix: 'Parameter with type "{{ typeName }}" should be named "{{ suggestedName }}"',
35
19
  },
36
20
  },
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
- }
21
+ defaultOptions: [],
22
+ create(context) {
60
23
  // Extract type name from a type annotation
61
24
  function getTypeName(typeAnnotation) {
62
25
  if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
@@ -67,137 +30,188 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
67
30
  }
68
31
  return null;
69
32
  }
70
- // Check if a type name has a non-"Props" suffix
71
- function hasNonPropsSuffix(typeName) {
72
- if (typeName.endsWith('Props')) {
73
- return null;
33
+ // Check if a type name ends with "Props"
34
+ function endsWithProps(typeName) {
35
+ return typeName.endsWith('Props');
36
+ }
37
+ // Generate suggested parameter name for Props types
38
+ function getSuggestedParameterName(typeName, allParams, currentParam) {
39
+ const currentId = getIdFromParam(currentParam);
40
+ const currentName = currentId?.name;
41
+ const existingNames = new Set(allParams
42
+ .map((p) => getIdFromParam(p))
43
+ .filter((id) => !!id && id.name !== currentName)
44
+ .map((id) => id.name));
45
+ if (typeName === 'Props') {
46
+ let candidate = 'props';
47
+ let suffix = 2;
48
+ while (existingNames.has(candidate)) {
49
+ candidate = `props${suffix++}`;
50
+ }
51
+ return candidate;
74
52
  }
75
- const suffixes = [
76
- 'Config',
77
- 'Settings',
78
- 'Options',
79
- 'Params',
80
- 'Parameters',
81
- 'Args',
82
- 'Arguments',
83
- ];
84
- for (const suffix of suffixes) {
85
- if (typeName.endsWith(suffix)) {
86
- return suffix;
53
+ // Count how many parameters have Props types
54
+ const propsParams = allParams.filter((param) => {
55
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
56
+ param.typeAnnotation &&
57
+ param.typeAnnotation.typeAnnotation) {
58
+ const paramTypeName = getTypeName(param.typeAnnotation.typeAnnotation);
59
+ return paramTypeName && endsWithProps(paramTypeName);
87
60
  }
61
+ return false;
62
+ });
63
+ // If there's only one Props parameter, suggest "props"
64
+ if (propsParams.length <= 1) {
65
+ let candidate = 'props';
66
+ let suffix = 2;
67
+ while (existingNames.has(candidate)) {
68
+ candidate = `props${suffix++}`;
69
+ }
70
+ return candidate;
88
71
  }
89
- return null;
90
- }
91
- // Fix type name
92
- function fixTypeName(fixer, node, oldName, suffix) {
93
- if (node.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
94
- node.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
95
- const baseName = oldName.slice(0, -suffix.length);
96
- return fixer.replaceText(node.typeName, `${baseName}Props`);
72
+ // If there are multiple Props parameters, use prefixed names
73
+ // For types like "UserProps", suggest "userProps"
74
+ const baseName = typeName.slice(0, -5); // Remove "Props"
75
+ const camelCaseBase = baseName.charAt(0).toLowerCase() + baseName.slice(1);
76
+ let candidate = `${camelCaseBase}Props`;
77
+ let i = 2;
78
+ while (existingNames.has(candidate)) {
79
+ candidate = `${camelCaseBase}Props${i++}`;
97
80
  }
98
- return null;
81
+ return candidate;
99
82
  }
100
- // Check function parameters
101
- function checkFunctionParams(node) {
102
- if (node.params.length !== 1) {
103
- return; // Only check functions with a single parameter
83
+ // Check if parameter is destructured
84
+ function isDestructuredParameter(param) {
85
+ if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
86
+ param.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
87
+ return true;
104
88
  }
105
- const param = node.params[0];
106
- // Skip primitive parameters
107
- if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
108
- param.typeAnnotation &&
109
- (param.typeAnnotation.typeAnnotation.type ===
110
- utils_1.AST_NODE_TYPES.TSStringKeyword ||
111
- param.typeAnnotation.typeAnnotation.type ===
112
- utils_1.AST_NODE_TYPES.TSNumberKeyword ||
113
- param.typeAnnotation.typeAnnotation.type ===
114
- utils_1.AST_NODE_TYPES.TSBooleanKeyword)) {
115
- return;
89
+ if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
90
+ (param.left.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
91
+ param.left.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
92
+ return true;
116
93
  }
117
- // Check if the parameter has a type annotation
118
- if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
119
- param.typeAnnotation &&
120
- param.typeAnnotation.typeAnnotation) {
121
- const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
122
- if (typeName) {
123
- const nonPropsSuffix = hasNonPropsSuffix(typeName);
124
- if (nonPropsSuffix) {
125
- context.report({
126
- node: param.typeAnnotation.typeAnnotation,
127
- messageId: 'usePropsForType',
128
- data: { typeSuffix: nonPropsSuffix },
129
- fix: (fixer) => fixTypeName(fixer, param.typeAnnotation.typeAnnotation, typeName, nonPropsSuffix),
130
- });
131
- }
94
+ if (param.type === utils_1.AST_NODE_TYPES.TSParameterProperty) {
95
+ const inner = param.parameter;
96
+ if (inner.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
97
+ inner.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
98
+ return true;
99
+ }
100
+ if (inner.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
101
+ (inner.left.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
102
+ inner.left.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
103
+ return true;
132
104
  }
133
105
  }
106
+ return false;
134
107
  }
135
- // Check class constructor parameters
136
- function checkClassConstructor(node) {
137
- if (node.kind !== 'constructor') {
138
- return;
108
+ function getIdFromParam(param) {
109
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier)
110
+ return param;
111
+ if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
112
+ param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
113
+ return param.left;
139
114
  }
140
- const constructor = node.value;
141
- if (constructor.params.length !== 1) {
142
- return; // Only check constructors with a single parameter
143
- }
144
- const param = constructor.params[0];
145
- // Skip primitive parameters
146
- if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
147
- param.typeAnnotation &&
148
- (param.typeAnnotation.typeAnnotation.type ===
149
- utils_1.AST_NODE_TYPES.TSStringKeyword ||
150
- param.typeAnnotation.typeAnnotation.type ===
151
- utils_1.AST_NODE_TYPES.TSNumberKeyword ||
152
- param.typeAnnotation.typeAnnotation.type ===
153
- utils_1.AST_NODE_TYPES.TSBooleanKeyword)) {
154
- return;
115
+ if (param.type === utils_1.AST_NODE_TYPES.TSParameterProperty) {
116
+ const inner = param.parameter;
117
+ if (inner.type === utils_1.AST_NODE_TYPES.Identifier)
118
+ return inner;
119
+ if (inner.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
120
+ inner.left.type === utils_1.AST_NODE_TYPES.Identifier) {
121
+ return inner.left;
122
+ }
155
123
  }
156
- // Check if the parameter has a type annotation
157
- if (param.type === utils_1.AST_NODE_TYPES.Identifier &&
158
- param.typeAnnotation &&
159
- param.typeAnnotation.typeAnnotation) {
160
- const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
161
- if (typeName) {
162
- const nonPropsSuffix = hasNonPropsSuffix(typeName);
163
- if (nonPropsSuffix) {
164
- context.report({
165
- node: param.typeAnnotation.typeAnnotation,
166
- messageId: 'usePropsForType',
167
- data: { typeSuffix: nonPropsSuffix },
168
- fix: (fixer) => fixTypeName(fixer, param.typeAnnotation.typeAnnotation, typeName, nonPropsSuffix),
169
- });
170
- }
124
+ if (param.type === utils_1.AST_NODE_TYPES.RestElement) {
125
+ if (param.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
126
+ return param.argument;
171
127
  }
172
128
  }
129
+ return null;
173
130
  }
174
- // Check type definitions
175
- function checkTypeDefinition(node) {
176
- if (!node.id || !node.id.name) {
131
+ // Check function parameters
132
+ function checkFunctionParams(node) {
133
+ // Skip function expressions that are part of method definitions
134
+ // to avoid duplicate processing
135
+ if (node.type === utils_1.AST_NODE_TYPES.FunctionExpression &&
136
+ node.parent &&
137
+ node.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
177
138
  return;
178
139
  }
179
- const typeName = node.id.name;
180
- const nonPropsSuffix = hasNonPropsSuffix(typeName);
181
- if (nonPropsSuffix && !isFromExternalLibrary(node)) {
182
- const baseName = typeName.slice(0, -nonPropsSuffix.length);
183
- const newTypeName = `${baseName}Props`;
184
- context.report({
185
- node: node.id,
186
- messageId: 'usePropsForType',
187
- data: { typeSuffix: nonPropsSuffix },
188
- fix: (fixer) => {
189
- return fixer.replaceText(node.id, newTypeName);
190
- },
191
- });
192
- }
140
+ const sourceCode = context.getSourceCode();
141
+ node.params.forEach((param) => {
142
+ if (isDestructuredParameter(param)) {
143
+ return;
144
+ }
145
+ const id = getIdFromParam(param);
146
+ if (id && id.typeAnnotation && id.typeAnnotation.typeAnnotation) {
147
+ const typeName = getTypeName(id.typeAnnotation.typeAnnotation);
148
+ if (typeName && endsWithProps(typeName)) {
149
+ const suggestedName = getSuggestedParameterName(typeName, node.params, param);
150
+ if (id.name !== suggestedName) {
151
+ const messageId = suggestedName === 'props'
152
+ ? 'usePropsParameterName'
153
+ : 'usePropsParameterNameWithPrefix';
154
+ context.report({
155
+ node: id,
156
+ messageId,
157
+ data: {
158
+ typeName,
159
+ suggestedName,
160
+ },
161
+ fix: (fixer) => {
162
+ const token = sourceCode.getFirstToken(id);
163
+ if (!token)
164
+ return null;
165
+ return fixer.replaceTextRange([token.range[0], token.range[1]], suggestedName);
166
+ },
167
+ });
168
+ }
169
+ }
170
+ }
171
+ });
172
+ }
173
+ // Check class method parameters (including constructors)
174
+ function checkClassMethod(node) {
175
+ const method = node.value;
176
+ const sourceCode = context.getSourceCode();
177
+ method.params.forEach((param) => {
178
+ if (isDestructuredParameter(param)) {
179
+ return;
180
+ }
181
+ const id = getIdFromParam(param);
182
+ if (id && id.typeAnnotation && id.typeAnnotation.typeAnnotation) {
183
+ const typeName = getTypeName(id.typeAnnotation.typeAnnotation);
184
+ if (typeName && endsWithProps(typeName)) {
185
+ const suggestedName = getSuggestedParameterName(typeName, method.params, param);
186
+ if (id.name !== suggestedName) {
187
+ const messageId = suggestedName === 'props'
188
+ ? 'usePropsParameterName'
189
+ : 'usePropsParameterNameWithPrefix';
190
+ context.report({
191
+ node: id,
192
+ messageId,
193
+ data: {
194
+ typeName,
195
+ suggestedName,
196
+ },
197
+ fix: (fixer) => {
198
+ const token = sourceCode.getFirstToken(id);
199
+ if (!token)
200
+ return null;
201
+ return fixer.replaceTextRange([token.range[0], token.range[1]], suggestedName);
202
+ },
203
+ });
204
+ }
205
+ }
206
+ }
207
+ });
193
208
  }
194
209
  return {
195
210
  FunctionDeclaration: checkFunctionParams,
196
211
  FunctionExpression: checkFunctionParams,
197
212
  ArrowFunctionExpression: checkFunctionParams,
198
213
  TSMethodSignature: checkFunctionParams,
199
- MethodDefinition: checkClassConstructor,
200
- TSTypeAliasDeclaration: checkTypeDefinition,
214
+ MethodDefinition: checkClassMethod,
201
215
  };
202
216
  },
203
217
  });
@@ -0,0 +1,2 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const enforcePropsNamingConsistency: TSESLint.RuleModule<"usePropsName", [], TSESLint.RuleListener>;
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforcePropsNamingConsistency = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.enforcePropsNamingConsistency = (0, createRule_1.createRule)({
7
+ name: 'enforce-props-naming-consistency',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Prefer naming single "Props"-typed parameters as "props"; enforcement defers to enforce-props-argument-name for multi-Props cases',
12
+ recommended: 'warn',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ usePropsName: 'Parameter with a type ending in "Props" should be named "props" instead of "{{ paramName }}"',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ // Extract type name from a type annotation
23
+ function getTypeName(typeAnnotation) {
24
+ if (typeAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
25
+ const typeName = typeAnnotation.typeName;
26
+ if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
27
+ return typeName.name;
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+ // Check if a parameter should be named "props"
33
+ function shouldBeNamedProps(param) {
34
+ // Only check non-destructured parameters
35
+ if (param.type !== utils_1.AST_NODE_TYPES.Identifier) {
36
+ return false;
37
+ }
38
+ // Check if the parameter has a type annotation
39
+ if (!param.typeAnnotation || !param.typeAnnotation.typeAnnotation) {
40
+ return false;
41
+ }
42
+ // Get the type name
43
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
44
+ if (!typeName) {
45
+ return false;
46
+ }
47
+ // Check if the type name ends with "Props"
48
+ return typeName.endsWith('Props');
49
+ }
50
+ // Check if a parameter name is already prefixed with "props"
51
+ function isPropsNameWithPrefix(paramName) {
52
+ return paramName.endsWith('Props') || paramName === 'props';
53
+ }
54
+ // Fix parameter name to "props"
55
+ function fixParameterName(fixer, param) {
56
+ return fixer.replaceText(param, 'props');
57
+ }
58
+ // Check function parameters
59
+ function checkFunctionParams(node) {
60
+ // Skip functions with multiple parameters that have Props types
61
+ const propsTypeParams = node.params.filter((param) => {
62
+ if (param.type !== utils_1.AST_NODE_TYPES.Identifier)
63
+ return false;
64
+ if (!param.typeAnnotation || !param.typeAnnotation.typeAnnotation)
65
+ return false;
66
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
67
+ return typeName && typeName.endsWith('Props');
68
+ });
69
+ if (propsTypeParams.length > 1) {
70
+ return; // Skip functions with multiple Props parameters
71
+ }
72
+ for (const param of node.params) {
73
+ if (shouldBeNamedProps(param) &&
74
+ param.type === utils_1.AST_NODE_TYPES.Identifier &&
75
+ !isPropsNameWithPrefix(param.name)) {
76
+ context.report({
77
+ node: param,
78
+ messageId: 'usePropsName',
79
+ data: { paramName: param.name },
80
+ fix: (fixer) => fixParameterName(fixer, param),
81
+ });
82
+ }
83
+ }
84
+ }
85
+ // Check class constructor parameters
86
+ function checkClassConstructor(node) {
87
+ if (node.kind !== 'constructor') {
88
+ return;
89
+ }
90
+ const constructor = node.value;
91
+ // Skip constructors with multiple parameters that have Props types
92
+ const propsTypeParams = constructor.params.filter((param) => {
93
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
94
+ if (!param.typeAnnotation || !param.typeAnnotation.typeAnnotation)
95
+ return false;
96
+ const typeName = getTypeName(param.typeAnnotation.typeAnnotation);
97
+ return typeName && typeName.endsWith('Props');
98
+ }
99
+ else if (param.type === utils_1.AST_NODE_TYPES.TSParameterProperty &&
100
+ param.parameter.type === utils_1.AST_NODE_TYPES.Identifier) {
101
+ if (!param.parameter.typeAnnotation ||
102
+ !param.parameter.typeAnnotation.typeAnnotation)
103
+ return false;
104
+ const typeName = getTypeName(param.parameter.typeAnnotation.typeAnnotation);
105
+ return typeName && typeName.endsWith('Props');
106
+ }
107
+ return false;
108
+ });
109
+ if (propsTypeParams.length > 1) {
110
+ return; // Skip constructors with multiple Props parameters
111
+ }
112
+ for (const param of constructor.params) {
113
+ if (shouldBeNamedProps(param) &&
114
+ param.type === utils_1.AST_NODE_TYPES.Identifier &&
115
+ !isPropsNameWithPrefix(param.name)) {
116
+ context.report({
117
+ node: param,
118
+ messageId: 'usePropsName',
119
+ data: { paramName: param.name },
120
+ fix: (fixer) => fixParameterName(fixer, param),
121
+ });
122
+ }
123
+ else if (param.type === utils_1.AST_NODE_TYPES.TSParameterProperty &&
124
+ param.parameter.type === utils_1.AST_NODE_TYPES.Identifier &&
125
+ shouldBeNamedProps(param.parameter) &&
126
+ !isPropsNameWithPrefix(param.parameter.name)) {
127
+ context.report({
128
+ node: param.parameter,
129
+ messageId: 'usePropsName',
130
+ data: { paramName: param.parameter.name },
131
+ fix: (fixer) => fixParameterName(fixer, param.parameter),
132
+ });
133
+ }
134
+ }
135
+ }
136
+ return {
137
+ FunctionDeclaration: checkFunctionParams,
138
+ FunctionExpression: checkFunctionParams,
139
+ ArrowFunctionExpression: checkFunctionParams,
140
+ TSMethodSignature: checkFunctionParams,
141
+ MethodDefinition: checkClassConstructor,
142
+ };
143
+ },
144
+ });
145
+ //# sourceMappingURL=enforce-props-naming-consistency.js.map
@@ -0,0 +1,9 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ type MessageIds = 'enforceQueryKeyImport' | 'enforceQueryKeyConstant';
3
+ /**
4
+ * Rule to enforce the use of centralized router state key constants imported from
5
+ * `src/util/routing/queryKeys.ts` instead of arbitrary string literals when calling
6
+ * router methods that accept key parameters.
7
+ */
8
+ export declare const enforceQueryKeyTs: TSESLint.RuleModule<MessageIds, [], TSESLint.RuleListener>;
9
+ export {};