@blumintinc/eslint-plugin-blumint 1.13.0 → 1.14.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 (186) hide show
  1. package/README.md +170 -139
  2. package/lib/index.js +190 -1
  3. package/lib/rules/array-methods-this-context.js +1 -1
  4. package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
  5. package/lib/rules/consistent-callback-naming.js +1 -1
  6. package/lib/rules/dynamic-https-errors.js +3 -4
  7. package/lib/rules/enforce-assert-safe-object-key.js +11 -11
  8. package/lib/rules/enforce-boolean-naming-prefixes.js +11 -23
  9. package/lib/rules/enforce-callback-memo.js +1 -1
  10. package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
  11. package/lib/rules/enforce-console-error.js +35 -7
  12. package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
  13. package/lib/rules/enforce-dynamic-file-naming.js +53 -28
  14. package/lib/rules/enforce-early-destructuring.d.ts +2 -0
  15. package/lib/rules/enforce-early-destructuring.js +980 -0
  16. package/lib/rules/enforce-empty-object-check.d.ts +11 -0
  17. package/lib/rules/enforce-empty-object-check.js +502 -0
  18. package/lib/rules/enforce-exported-function-types.js +1 -1
  19. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +100 -50
  20. package/lib/rules/enforce-firestore-doc-ref-generic.js +15 -3
  21. package/lib/rules/enforce-firestore-facade.js +4 -2
  22. package/lib/rules/enforce-firestore-set-merge.js +3 -4
  23. package/lib/rules/enforce-global-constants.js +1 -1
  24. package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
  25. package/lib/rules/enforce-memoize-async.js +2 -6
  26. package/lib/rules/enforce-memoize-getters.js +39 -15
  27. package/lib/rules/enforce-microdiff.js +1 -1
  28. package/lib/rules/enforce-object-literal-as-const.js +4 -4
  29. package/lib/rules/enforce-positive-naming.js +4 -0
  30. package/lib/rules/enforce-props-argument-name.js +2 -2
  31. package/lib/rules/enforce-safe-stringify.js +1 -1
  32. package/lib/rules/enforce-serializable-params.js +3 -3
  33. package/lib/rules/enforce-singular-type-names.js +1 -1
  34. package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
  35. package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
  36. package/lib/rules/enforce-storage-context.d.ts +9 -0
  37. package/lib/rules/enforce-storage-context.js +646 -0
  38. package/lib/rules/enforce-timestamp-now.js +23 -7
  39. package/lib/rules/enforce-transform-memoization.d.ts +4 -0
  40. package/lib/rules/enforce-transform-memoization.js +416 -0
  41. package/lib/rules/enforce-typescript-markdown-code-blocks.js +7 -3
  42. package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
  43. package/lib/rules/enforce-unique-cursor-headers.js +365 -0
  44. package/lib/rules/enforce-verb-noun-naming.js +5 -4
  45. package/lib/rules/ensure-pointer-events-none.js +28 -2
  46. package/lib/rules/export-if-in-doubt.js +27 -4
  47. package/lib/rules/extract-global-constants.js +40 -13
  48. package/lib/rules/fast-deep-equal-over-microdiff.js +29 -39
  49. package/lib/rules/firestore-transaction-reads-before-writes.js +50 -4
  50. package/lib/rules/flatten-push-calls.d.ts +2 -0
  51. package/lib/rules/flatten-push-calls.js +428 -0
  52. package/lib/rules/global-const-style.js +50 -19
  53. package/lib/rules/jsdoc-above-field.d.ts +11 -0
  54. package/lib/rules/jsdoc-above-field.js +208 -0
  55. package/lib/rules/key-only-outermost-element.js +21 -3
  56. package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
  57. package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
  58. package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
  59. package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
  60. package/lib/rules/memo-nested-react-components.d.ts +8 -0
  61. package/lib/rules/memo-nested-react-components.js +410 -0
  62. package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
  63. package/lib/rules/no-always-true-false-conditions.js +68 -8
  64. package/lib/rules/no-array-length-in-deps.js +9 -3
  65. package/lib/rules/no-async-foreach.js +210 -12
  66. package/lib/rules/no-circular-references.js +18 -17
  67. package/lib/rules/no-class-instance-destructuring.js +55 -12
  68. package/lib/rules/no-complex-cloud-params.js +7 -3
  69. package/lib/rules/no-compositing-layer-props.js +2 -2
  70. package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
  71. package/lib/rules/no-console-error.d.ts +9 -0
  72. package/lib/rules/no-console-error.js +525 -0
  73. package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
  74. package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
  75. package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
  76. package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
  77. package/lib/rules/no-entire-object-hook-deps.js +4 -6
  78. package/lib/rules/no-excessive-parent-chain.js +1 -1
  79. package/lib/rules/no-explicit-return-type.d.ts +2 -1
  80. package/lib/rules/no-explicit-return-type.js +215 -26
  81. package/lib/rules/no-filter-without-return.js +5 -1
  82. package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
  83. package/lib/rules/no-firestore-jest-mock.js +126 -8
  84. package/lib/rules/no-firestore-object-arrays.js +67 -12
  85. package/lib/rules/no-handler-suffix.d.ts +12 -0
  86. package/lib/rules/no-handler-suffix.js +305 -0
  87. package/lib/rules/no-hungarian.js +1 -1
  88. package/lib/rules/no-inline-component-prop.d.ts +10 -0
  89. package/lib/rules/no-inline-component-prop.js +456 -0
  90. package/lib/rules/no-jsx-in-hooks.js +6 -1
  91. package/lib/rules/no-jsx-whitespace-literal.js +8 -2
  92. package/lib/rules/no-margin-properties.js +6 -6
  93. package/lib/rules/no-memoize-on-static.js +9 -1
  94. package/lib/rules/no-misleading-boolean-prefixes.js +7 -3
  95. package/lib/rules/no-misused-switch-case.js +22 -1
  96. package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
  97. package/lib/rules/no-mixed-firestore-transactions.js +296 -34
  98. package/lib/rules/no-mock-firebase-admin.js +5 -2
  99. package/lib/rules/no-object-values-on-strings.js +10 -2
  100. package/lib/rules/no-overridable-method-calls-in-constructor.js +29 -17
  101. package/lib/rules/no-passthrough-getters.js +38 -2
  102. package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
  103. package/lib/rules/no-redundant-annotation-assertion.js +402 -0
  104. package/lib/rules/no-redundant-param-types.js +16 -6
  105. package/lib/rules/no-redundant-this-params.d.ts +3 -0
  106. package/lib/rules/no-redundant-this-params.js +459 -0
  107. package/lib/rules/no-redundant-usecallback-wrapper.js +16 -3
  108. package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
  109. package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
  110. package/lib/rules/no-restricted-properties-fix.js +11 -10
  111. package/lib/rules/no-separate-loading-state.js +7 -16
  112. package/lib/rules/no-stale-state-across-await.js +1 -1
  113. package/lib/rules/no-type-assertion-returns.js +43 -49
  114. package/lib/rules/no-undefined-null-passthrough.js +61 -31
  115. package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
  116. package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
  117. package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
  118. package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
  119. package/lib/rules/no-unnecessary-destructuring.js +14 -4
  120. package/lib/rules/no-unnecessary-verb-suffix.js +2 -1
  121. package/lib/rules/no-unpinned-dependencies.js +36 -5
  122. package/lib/rules/no-unused-props.d.ts +2 -2
  123. package/lib/rules/no-unused-props.js +295 -91
  124. package/lib/rules/no-unused-usestate.js +6 -2
  125. package/lib/rules/no-useless-fragment.js +28 -2
  126. package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
  127. package/lib/rules/no-useless-usememo-primitives.js +393 -0
  128. package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
  129. package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
  130. package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
  131. package/lib/rules/omit-index-html.d.ts +2 -1
  132. package/lib/rules/omit-index-html.js +62 -7
  133. package/lib/rules/optimize-object-boolean-conditions.js +6 -4
  134. package/lib/rules/parallelize-async-operations.js +24 -5
  135. package/lib/rules/prefer-batch-operations.d.ts +1 -3
  136. package/lib/rules/prefer-batch-operations.js +32 -5
  137. package/lib/rules/prefer-block-comments-for-declarations.js +9 -4
  138. package/lib/rules/prefer-clone-deep.js +3 -3
  139. package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
  140. package/lib/rules/prefer-destructuring-no-class.js +244 -59
  141. package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
  142. package/lib/rules/prefer-docsetter-setall.js +243 -0
  143. package/lib/rules/prefer-field-paths-in-transforms.js +4 -3
  144. package/lib/rules/prefer-fragment-component.js +1 -1
  145. package/lib/rules/prefer-fragment-shorthand.js +2 -1
  146. package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
  147. package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
  148. package/lib/rules/prefer-global-router-state-key.js +88 -13
  149. package/lib/rules/prefer-memoized-props.d.ts +3 -0
  150. package/lib/rules/prefer-memoized-props.js +445 -0
  151. package/lib/rules/prefer-next-dynamic.js +60 -69
  152. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +9 -4
  153. package/lib/rules/prefer-nullish-coalescing-override.d.ts +2 -1
  154. package/lib/rules/prefer-nullish-coalescing-override.js +11 -5
  155. package/lib/rules/prefer-params-over-parent-id.js +220 -171
  156. package/lib/rules/prefer-settings-object.js +2 -2
  157. package/lib/rules/prefer-type-over-interface.js +7 -2
  158. package/lib/rules/prefer-url-tostring-over-tojson.js +6 -3
  159. package/lib/rules/prefer-use-deep-compare-memo.js +8 -5
  160. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
  161. package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
  162. package/lib/rules/prevent-children-clobber.d.ts +2 -0
  163. package/lib/rules/prevent-children-clobber.js +536 -0
  164. package/lib/rules/react-memoize-literals.d.ts +4 -0
  165. package/lib/rules/react-memoize-literals.js +495 -0
  166. package/lib/rules/react-usememo-should-be-component.js +2 -2
  167. package/lib/rules/require-hooks-default-params.js +17 -16
  168. package/lib/rules/require-https-error-cause.d.ts +4 -0
  169. package/lib/rules/require-https-error-cause.js +136 -0
  170. package/lib/rules/require-https-error.js +43 -21
  171. package/lib/rules/require-image-optimized.js +1 -1
  172. package/lib/rules/require-memo.js +1 -1
  173. package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
  174. package/lib/rules/require-memoize-jsx-returners.js +485 -0
  175. package/lib/rules/require-usememo-object-literals.js +2 -3
  176. package/lib/rules/test-file-location-enforcement.js +1 -1
  177. package/lib/rules/use-latest-callback.js +6 -5
  178. package/lib/rules/vertically-group-related-functions.d.ts +16 -0
  179. package/lib/rules/vertically-group-related-functions.js +480 -0
  180. package/lib/utils/ASTHelpers.d.ts +13 -1
  181. package/lib/utils/ASTHelpers.js +24 -0
  182. package/lib/utils/getMethodName.d.ts +27 -0
  183. package/lib/utils/getMethodName.js +35 -0
  184. package/lib/utils/tsTypeClassifier.d.ts +30 -0
  185. package/lib/utils/tsTypeClassifier.js +149 -0
  186. package/package.json +3 -1
@@ -0,0 +1,347 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noUnnecessaryDestructuringRename = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const BINDABLE_IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
7
+ const RESERVED_BINDINGS = new Set([
8
+ 'break',
9
+ 'case',
10
+ 'catch',
11
+ 'class',
12
+ 'const',
13
+ 'continue',
14
+ 'debugger',
15
+ 'default',
16
+ 'delete',
17
+ 'do',
18
+ 'else',
19
+ 'enum',
20
+ 'export',
21
+ 'extends',
22
+ 'false',
23
+ 'finally',
24
+ 'for',
25
+ 'function',
26
+ 'if',
27
+ 'import',
28
+ 'in',
29
+ 'instanceof',
30
+ 'new',
31
+ 'null',
32
+ 'return',
33
+ 'super',
34
+ 'switch',
35
+ 'this',
36
+ 'throw',
37
+ 'true',
38
+ 'try',
39
+ 'typeof',
40
+ 'var',
41
+ 'void',
42
+ 'while',
43
+ 'with',
44
+ 'yield',
45
+ 'let',
46
+ 'await',
47
+ 'static',
48
+ 'implements',
49
+ 'interface',
50
+ 'package',
51
+ 'private',
52
+ 'protected',
53
+ 'public',
54
+ ]);
55
+ function isBindableIdentifier(name) {
56
+ return BINDABLE_IDENTIFIER_PATTERN.test(name) && !RESERVED_BINDINGS.has(name);
57
+ }
58
+ function hasIdentifierKey(property) {
59
+ return !property.computed && property.key.type === utils_1.AST_NODE_TYPES.Identifier;
60
+ }
61
+ function extractAliasFromIdentifierValue(value, originalName) {
62
+ if (value.type !== utils_1.AST_NODE_TYPES.Identifier) {
63
+ return null;
64
+ }
65
+ if (value.name === originalName) {
66
+ return null;
67
+ }
68
+ return value;
69
+ }
70
+ function extractAliasFromAssignmentPattern(value, originalName) {
71
+ if (value.type !== utils_1.AST_NODE_TYPES.AssignmentPattern) {
72
+ return null;
73
+ }
74
+ if (value.left.type !== utils_1.AST_NODE_TYPES.Identifier) {
75
+ return null;
76
+ }
77
+ if (value.left.name === originalName) {
78
+ return null;
79
+ }
80
+ return value.left;
81
+ }
82
+ function getRenamedPropertyInfo(property) {
83
+ if (!hasIdentifierKey(property)) {
84
+ return null;
85
+ }
86
+ const originalName = property.key.name;
87
+ const identifierAlias = extractAliasFromIdentifierValue(property.value, originalName);
88
+ if (identifierAlias) {
89
+ return { originalName, aliasIdentifier: identifierAlias };
90
+ }
91
+ const assignmentAlias = extractAliasFromAssignmentPattern(property.value, originalName);
92
+ if (assignmentAlias) {
93
+ return { originalName, aliasIdentifier: assignmentAlias };
94
+ }
95
+ return null;
96
+ }
97
+ exports.noUnnecessaryDestructuringRename = (0, createRule_1.createRule)({
98
+ name: 'no-unnecessary-destructuring-rename',
99
+ meta: {
100
+ type: 'suggestion',
101
+ docs: {
102
+ description: 'Disallow destructuring renames that are only used to assign back to the original property name',
103
+ recommended: 'error',
104
+ },
105
+ fixable: 'code',
106
+ schema: [],
107
+ messages: {
108
+ unnecessaryDestructuringRename: 'Renaming "{{originalName}}" to "{{aliasName}}" only to write it back as "{{originalName}}" adds indirection without value. Keep the original name in the destructuring or reference the property directly to reduce cognitive overhead.',
109
+ },
110
+ },
111
+ defaultOptions: [],
112
+ create(context) {
113
+ const sourceCode = context.getSourceCode();
114
+ const candidates = [];
115
+ const getDeclaredVariables = (node) => {
116
+ const sourceCodeWithDeclarations = sourceCode;
117
+ if (typeof sourceCodeWithDeclarations.getDeclaredVariables === 'function') {
118
+ return sourceCodeWithDeclarations.getDeclaredVariables(node);
119
+ }
120
+ return context.getDeclaredVariables(node);
121
+ };
122
+ function collectDeclaredVariablesUpTree(node) {
123
+ const variables = [];
124
+ let currentNode = node;
125
+ while (currentNode) {
126
+ for (const variable of getDeclaredVariables(currentNode)) {
127
+ if (!variables.includes(variable)) {
128
+ variables.push(variable);
129
+ }
130
+ }
131
+ currentNode = currentNode.parent ?? null;
132
+ }
133
+ return variables;
134
+ }
135
+ function captureCandidates(pattern) {
136
+ const declaredVariables = collectDeclaredVariablesUpTree(pattern);
137
+ for (const property of pattern.properties) {
138
+ if (property.type !== utils_1.AST_NODE_TYPES.Property) {
139
+ continue;
140
+ }
141
+ const renameInfo = getRenamedPropertyInfo(property);
142
+ if (!renameInfo) {
143
+ continue;
144
+ }
145
+ if (!isBindableIdentifier(renameInfo.originalName)) {
146
+ continue;
147
+ }
148
+ const variable = declaredVariables.find((declaredVar) => declaredVar.identifiers.includes(renameInfo.aliasIdentifier));
149
+ if (!variable) {
150
+ continue;
151
+ }
152
+ candidates.push({
153
+ propertyNode: property,
154
+ originalName: renameInfo.originalName,
155
+ aliasIdentifier: renameInfo.aliasIdentifier,
156
+ variable,
157
+ });
158
+ }
159
+ }
160
+ function findMatchingReference(reference, originalName) {
161
+ const identifier = reference.identifier;
162
+ const parent = identifier.parent;
163
+ if (!parent ||
164
+ parent.type !== utils_1.AST_NODE_TYPES.Property ||
165
+ parent.value !== identifier ||
166
+ parent.computed ||
167
+ parent.key.type !== utils_1.AST_NODE_TYPES.Identifier ||
168
+ parent.key.name !== originalName) {
169
+ return null;
170
+ }
171
+ const grandParent = parent.parent;
172
+ if (!grandParent ||
173
+ grandParent.type !== utils_1.AST_NODE_TYPES.ObjectExpression) {
174
+ return null;
175
+ }
176
+ return parent;
177
+ }
178
+ function scopeHasNameInChain(scope, stopScope, name) {
179
+ let currentScope = scope;
180
+ while (currentScope) {
181
+ if (currentScope.set.has(name)) {
182
+ return true;
183
+ }
184
+ if (currentScope === stopScope) {
185
+ break;
186
+ }
187
+ currentScope = currentScope.upper;
188
+ }
189
+ return false;
190
+ }
191
+ function referencesNameWithoutLocalBinding(scope, name) {
192
+ const scopesToCheck = [scope];
193
+ while (scopesToCheck.length > 0) {
194
+ const currentScope = scopesToCheck.pop();
195
+ if (!currentScope.set.has(name)) {
196
+ const hasReference = currentScope.references.some((reference) => reference.identifier.name === name) ||
197
+ currentScope.through.some((reference) => reference.identifier.name === name);
198
+ if (hasReference) {
199
+ return true;
200
+ }
201
+ }
202
+ for (const childScope of currentScope.childScopes) {
203
+ if (!childScope.set.has(name)) {
204
+ scopesToCheck.push(childScope);
205
+ }
206
+ }
207
+ }
208
+ return false;
209
+ }
210
+ function isSafeToInlineOriginal(reference, variable, originalName) {
211
+ const declarationScope = variable.scope;
212
+ const referenceScope = reference.from ?? declarationScope;
213
+ if (declarationScope.set.has(originalName)) {
214
+ return false;
215
+ }
216
+ if (scopeHasNameInChain(referenceScope, declarationScope, originalName)) {
217
+ return false;
218
+ }
219
+ return !referencesNameWithoutLocalBinding(declarationScope, originalName);
220
+ }
221
+ function getMatchingReferences(variable, originalName) {
222
+ const matchingReferences = [];
223
+ for (const reference of variable.references) {
224
+ const property = findMatchingReference(reference, originalName);
225
+ if (property) {
226
+ matchingReferences.push({ reference, property });
227
+ }
228
+ }
229
+ return matchingReferences;
230
+ }
231
+ function hasOtherUsages(variable, matchedReference) {
232
+ return variable.references.some((ref) => {
233
+ if (ref === matchedReference) {
234
+ return false;
235
+ }
236
+ // Ignore the initializer write introduced by the destructuring itself.
237
+ if (ref.init && ref.isWrite() && !ref.isRead()) {
238
+ return false;
239
+ }
240
+ return (ref.isRead() ||
241
+ ref.isWrite() ||
242
+ // Type references indicate another use even if not a runtime read.
243
+ ref.isTypeReference ===
244
+ true);
245
+ });
246
+ }
247
+ function resolveValidCandidates() {
248
+ const resolvedCandidates = [];
249
+ for (const candidate of candidates) {
250
+ const { variable, originalName, aliasIdentifier, propertyNode } = candidate;
251
+ const matchingReferences = getMatchingReferences(variable, originalName);
252
+ if (matchingReferences.length !== 1) {
253
+ continue;
254
+ }
255
+ const [{ reference: matchedReference, property: targetProperty }] = matchingReferences;
256
+ if (!isSafeToInlineOriginal(matchedReference, variable, originalName)) {
257
+ continue;
258
+ }
259
+ if (hasOtherUsages(variable, matchedReference)) {
260
+ continue;
261
+ }
262
+ resolvedCandidates.push({
263
+ propertyNode,
264
+ originalName,
265
+ aliasIdentifier,
266
+ variable,
267
+ targetProperty,
268
+ reference: matchedReference,
269
+ });
270
+ }
271
+ return resolvedCandidates;
272
+ }
273
+ function removeScopeConflicts(resolvedCandidates) {
274
+ const countsByScope = new Map();
275
+ for (const candidate of resolvedCandidates) {
276
+ const scope = candidate.variable.scope;
277
+ const scopeCounts = countsByScope.get(scope) ?? new Map();
278
+ scopeCounts.set(candidate.originalName, (scopeCounts.get(candidate.originalName) ?? 0) + 1);
279
+ countsByScope.set(scope, scopeCounts);
280
+ }
281
+ return resolvedCandidates.filter((candidate) => {
282
+ const scopeCounts = countsByScope.get(candidate.variable.scope);
283
+ return (scopeCounts?.get(candidate.originalName) ?? 0) === 1;
284
+ });
285
+ }
286
+ function groupCandidatesByPattern(resolvedCandidates) {
287
+ const candidatesByPattern = new Map();
288
+ for (const candidate of resolvedCandidates) {
289
+ const pattern = candidate.propertyNode.parent;
290
+ const existing = candidatesByPattern.get(pattern);
291
+ if (existing) {
292
+ existing.push(candidate);
293
+ }
294
+ else {
295
+ candidatesByPattern.set(pattern, [candidate]);
296
+ }
297
+ }
298
+ return candidatesByPattern;
299
+ }
300
+ function generateFixes(fixer, patternCandidates) {
301
+ const fixes = [];
302
+ for (const groupCandidate of patternCandidates) {
303
+ const { propertyNode: groupedProperty, targetProperty: groupedTarget, originalName: groupedOriginal, } = groupCandidate;
304
+ const destructReplacement = groupedProperty.value.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
305
+ groupedProperty.value.left.type === utils_1.AST_NODE_TYPES.Identifier
306
+ ? `${groupedOriginal} = ${sourceCode.getText(groupedProperty.value.right)}`
307
+ : groupedOriginal;
308
+ fixes.push(fixer.replaceText(groupedProperty, destructReplacement));
309
+ // findMatchingReference constrains groupedTarget.value to the matched identifier,
310
+ // so replacing it directly avoids extra defensive branching in the fixer.
311
+ fixes.push(fixer.replaceText(groupedTarget.value, groupedOriginal));
312
+ }
313
+ return fixes;
314
+ }
315
+ function reportAndFixCandidates(candidatesByPattern) {
316
+ for (const patternCandidates of candidatesByPattern.values()) {
317
+ patternCandidates.forEach((candidate, index) => {
318
+ const { propertyNode, originalName, aliasIdentifier } = candidate;
319
+ const isFixCarrier = index === 0;
320
+ context.report({
321
+ node: propertyNode,
322
+ messageId: 'unnecessaryDestructuringRename',
323
+ data: {
324
+ originalName,
325
+ aliasName: aliasIdentifier.name,
326
+ },
327
+ fix: !isFixCarrier
328
+ ? undefined
329
+ : (fixer) => generateFixes(fixer, patternCandidates),
330
+ });
331
+ });
332
+ }
333
+ }
334
+ return {
335
+ ObjectPattern(node) {
336
+ captureCandidates(node);
337
+ },
338
+ 'Program:exit'() {
339
+ const resolvedCandidates = resolveValidCandidates();
340
+ const conflictFreeCandidates = removeScopeConflicts(resolvedCandidates);
341
+ const candidatesByPattern = groupCandidatesByPattern(conflictFreeCandidates);
342
+ reportAndFixCandidates(candidatesByPattern);
343
+ },
344
+ };
345
+ },
346
+ });
347
+ //# sourceMappingURL=no-unnecessary-destructuring-rename.js.map
@@ -7,11 +7,11 @@ exports.noUnnecessaryDestructuring = (0, createRule_1.createRule)({
7
7
  meta: {
8
8
  type: 'suggestion',
9
9
  docs: {
10
- description: 'Avoid unnecessary object destructuring when there is only one property inside the destructured object',
10
+ description: 'Avoid object patterns that only spread an existing object, since they clone the whole value without selecting properties',
11
11
  recommended: 'error',
12
12
  },
13
13
  messages: {
14
- noUnnecessaryDestructuring: 'Avoid unnecessary object destructuring with a single rest property. Use the object directly instead of `{ ...obj }`.',
14
+ noUnnecessaryDestructuring: 'Destructuring only the rest of "{{source}}" into "{{target}}" just clones the entire object. The shallow copy adds allocations and hides that you keep every property unchanged. Assign the object directly instead, for example `{{target}} = {{source}}`.',
15
15
  },
16
16
  schema: [],
17
17
  fixable: 'code',
@@ -24,13 +24,19 @@ exports.noUnnecessaryDestructuring = (0, createRule_1.createRule)({
24
24
  if (node.id.type === 'ObjectPattern' &&
25
25
  node.id.properties.length === 1 &&
26
26
  node.id.properties[0].type === 'RestElement') {
27
+ const sourceCode = context.getSourceCode();
27
28
  const restElement = node.id.properties[0];
28
29
  // Report the issue
29
30
  context.report({
30
31
  node,
31
32
  messageId: 'noUnnecessaryDestructuring',
33
+ data: {
34
+ target: sourceCode.getText(restElement.argument),
35
+ source: node.init
36
+ ? sourceCode.getText(node.init)
37
+ : 'the source object',
38
+ },
32
39
  fix(fixer) {
33
- const sourceCode = context.getSourceCode();
34
40
  const restName = sourceCode.getText(restElement.argument);
35
41
  // Handle the case where init might be null
36
42
  if (!node.init) {
@@ -49,12 +55,16 @@ exports.noUnnecessaryDestructuring = (0, createRule_1.createRule)({
49
55
  node.left.type === 'ObjectPattern' &&
50
56
  node.left.properties.length === 1 &&
51
57
  node.left.properties[0].type === 'RestElement') {
58
+ const sourceCode = context.getSourceCode();
52
59
  const restElement = node.left.properties[0];
53
60
  context.report({
54
61
  node,
55
62
  messageId: 'noUnnecessaryDestructuring',
63
+ data: {
64
+ target: sourceCode.getText(restElement.argument),
65
+ source: sourceCode.getText(node.right),
66
+ },
56
67
  fix(fixer) {
57
- const sourceCode = context.getSourceCode();
58
68
  const restName = sourceCode.getText(restElement.argument);
59
69
  const rightText = sourceCode.getText(node.right);
60
70
  // Replace the destructuring with direct assignment
@@ -74,7 +74,7 @@ exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
74
74
  fixable: 'code',
75
75
  schema: [],
76
76
  messages: {
77
- unnecessaryVerbSuffix: 'Unnecessary verb suffix "{{suffix}}" in function name. Consider using "{{suggestion}}" instead.',
77
+ unnecessaryVerbSuffix: 'Function name "{{name}}" ends with verb suffix "{{suffix}}" that does not add meaning beyond its parameters. Redundant verb-preposition endings make call sites harder to scan and hide the primary action. Rename to "{{suggestion}}" so the name stays action-oriented while arguments express the relationship.',
78
78
  },
79
79
  },
80
80
  defaultOptions: [],
@@ -98,6 +98,7 @@ exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
98
98
  node,
99
99
  messageId: 'unnecessaryVerbSuffix',
100
100
  data: {
101
+ name,
101
102
  suffix,
102
103
  suggestion,
103
104
  },
@@ -2,6 +2,33 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noUnpinnedDependencies = void 0;
4
4
  const createRule_1 = require("../utils/createRule");
5
+ const PINNED_SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-(?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
6
+ const RANGE_OPERATOR_PATTERN = /[~^]/;
7
+ const LEADING_RANGE_PATTERN = /^[~^]/;
8
+ const DEFAULT_PINNED_VERSION = '1.2.3';
9
+ const BASIC_SEMVER_PATTERN = /\d+\.\d+\.\d+/;
10
+ /**
11
+ * Computes a pinned-version recommendation from a raw dependency version.
12
+ * Strips a leading range token (^ or ~), validates pinned candidates, and
13
+ * falls back to the first semver-like substring or a default placeholder
14
+ * when no safe candidate is present.
15
+ *
16
+ * @param version Dependency version string from package.json.
17
+ * @returns Object describing whether a safe fix is possible and the
18
+ * suggested pinned version.
19
+ */
20
+ function computeVersionSuggestion(version) {
21
+ const startsWithRange = LEADING_RANGE_PATTERN.test(version);
22
+ const pinnedCandidate = startsWithRange
23
+ ? version.replace(LEADING_RANGE_PATTERN, '')
24
+ : version;
25
+ const firstSemverInString = version.match(BASIC_SEMVER_PATTERN)?.[0];
26
+ const isSimplePinned = startsWithRange && PINNED_SEMVER_PATTERN.test(pinnedCandidate);
27
+ const suggestedVersion = isSimplePinned && startsWithRange
28
+ ? pinnedCandidate
29
+ : firstSemverInString ?? DEFAULT_PINNED_VERSION;
30
+ return { isSimplePinned, suggestedVersion, pinnedCandidate };
31
+ }
5
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
33
  exports.noUnpinnedDependencies = (0, createRule_1.createRule)({
7
34
  meta: {
@@ -13,7 +40,7 @@ exports.noUnpinnedDependencies = (0, createRule_1.createRule)({
13
40
  fixable: 'code',
14
41
  schema: [],
15
42
  messages: {
16
- unexpected: "Dependency '{{ propertyName }}' should be pinned to a specific version, but '{{ version }}' was found.",
43
+ unexpected: "Dependency '{{ propertyName }}' is declared with the range '{{ version }}'. Ranges let package managers pull newer releases outside code review, which breaks reproducible installs and can hide breaking changes. Pin to a single exact version without range operators (for example '{{ suggestedVersion }}') so dependency updates stay intentional and auditable; complex ranges must be pinned manually.",
17
44
  },
18
45
  },
19
46
  defaultOptions: [],
@@ -42,19 +69,23 @@ exports.noUnpinnedDependencies = (0, createRule_1.createRule)({
42
69
  const version = node.value;
43
70
  const propertyName = property.key.name ||
44
71
  property.key.value;
45
- // Check if the version string starts with a caret (^) or tilde (~), indicating a non-pinned version
72
+ // Flag caret/tilde range operators anywhere; only auto-fix simple leading ranges
46
73
  if (typeof version === 'string' &&
47
- (version.includes('^') || version.includes('~'))) {
74
+ RANGE_OPERATOR_PATTERN.test(version)) {
75
+ const { isSimplePinned, suggestedVersion, pinnedCandidate } = computeVersionSuggestion(version);
48
76
  context.report({
49
77
  node: node,
50
78
  messageId: 'unexpected',
51
79
  data: {
52
80
  propertyName,
53
81
  version,
82
+ suggestedVersion,
54
83
  },
55
84
  fix: function (fixer) {
56
- const fixed = version.replace('^', '').replace('~', '');
57
- return fixer.replaceTextRange(node.range, `"${fixed}"`);
85
+ if (!isSimplePinned) {
86
+ return null;
87
+ }
88
+ return fixer.replaceTextRange(node.range, `"${pinnedCandidate}"`);
58
89
  },
59
90
  });
60
91
  }
@@ -1,5 +1,5 @@
1
- import { TSESTree } from '@typescript-eslint/utils';
2
- export declare const noUnusedProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unusedProp", never[], {
1
+ import { TSESLint, TSESTree } from '@typescript-eslint/utils';
2
+ export declare const noUnusedProps: TSESLint.RuleModule<"unusedProp", never[], {
3
3
  TSTypeAliasDeclaration(node: TSESTree.TSTypeAliasDeclaration): void;
4
4
  VariableDeclaration(node: TSESTree.VariableDeclaration): void;
5
5
  'VariableDeclaration:exit'(node: never): void;