@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,393 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noUselessUsememoPrimitives = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const tsTypeClassifier_1 = require("../utils/tsTypeClassifier");
7
+ const DEFAULT_OPTIONS = {
8
+ ignoreCallExpressions: true,
9
+ ignoreSymbol: true,
10
+ tsOnly: false,
11
+ };
12
+ const NON_DETERMINISTIC_MEMBERS = new Set([
13
+ 'Date.now',
14
+ 'Math.random',
15
+ 'performance.now',
16
+ 'crypto.randomUUID',
17
+ 'crypto.getRandomValues',
18
+ ]);
19
+ const NON_DETERMINISTIC_CONSTRUCTORS = new Set(['Date']);
20
+ const COMPARISON_OPERATORS = new Set([
21
+ '==',
22
+ '===',
23
+ '!=',
24
+ '!==',
25
+ '<',
26
+ '<=',
27
+ '>',
28
+ '>=',
29
+ 'instanceof',
30
+ 'in',
31
+ ]);
32
+ function isStringLikeWithoutTypes(expr) {
33
+ switch (expr.type) {
34
+ case utils_1.AST_NODE_TYPES.Literal:
35
+ return typeof expr.value === 'string';
36
+ case utils_1.AST_NODE_TYPES.TemplateLiteral:
37
+ return true;
38
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
39
+ return expr.operator === 'typeof';
40
+ case utils_1.AST_NODE_TYPES.BinaryExpression:
41
+ return (expr.operator === '+' &&
42
+ (isStringLikeWithoutTypes(expr.left) ||
43
+ isStringLikeWithoutTypes(expr.right)));
44
+ default:
45
+ return false;
46
+ }
47
+ }
48
+ function isUseMemoCallee(callee) {
49
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
50
+ return callee.name === 'useMemo';
51
+ }
52
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
53
+ !callee.computed &&
54
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
55
+ return callee.property.name === 'useMemo';
56
+ }
57
+ return false;
58
+ }
59
+ function getReturnedExpression(callback) {
60
+ if (callback.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
61
+ return callback.body;
62
+ }
63
+ if (callback.body.body.length !== 1) {
64
+ return null;
65
+ }
66
+ const soleStatement = callback.body.body[0];
67
+ if (soleStatement.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
68
+ soleStatement.argument &&
69
+ soleStatement.argument.type !== utils_1.AST_NODE_TYPES.SequenceExpression) {
70
+ return soleStatement.argument;
71
+ }
72
+ return null;
73
+ }
74
+ function walkExpression(expr, predicate, maxDepth = 100) {
75
+ const stack = [
76
+ { node: expr, depth: 0 },
77
+ ];
78
+ while (stack.length > 0) {
79
+ const { node: current, depth } = stack.pop();
80
+ if (depth > maxDepth)
81
+ continue;
82
+ if (predicate(current)) {
83
+ return true;
84
+ }
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ for (const key of Object.keys(current)) {
87
+ if (key === 'parent')
88
+ continue;
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ const value = current[key];
91
+ if (!value)
92
+ continue;
93
+ if (Array.isArray(value)) {
94
+ for (const child of value) {
95
+ if (child && typeof child === 'object' && 'type' in child) {
96
+ stack.push({ node: child, depth: depth + 1 });
97
+ }
98
+ }
99
+ }
100
+ else if (typeof value === 'object' && 'type' in value) {
101
+ stack.push({ node: value, depth: depth + 1 });
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+ function containsCallExpression(expr) {
108
+ return walkExpression(expr, (node) => [
109
+ utils_1.AST_NODE_TYPES.CallExpression,
110
+ utils_1.AST_NODE_TYPES.NewExpression,
111
+ utils_1.AST_NODE_TYPES.TaggedTemplateExpression,
112
+ ].includes(node.type));
113
+ }
114
+ function isNonDeterministicCall(node) {
115
+ const callee = node.callee;
116
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression && !callee.computed) {
117
+ const object = callee.object;
118
+ const property = callee.property;
119
+ if (object.type === utils_1.AST_NODE_TYPES.Identifier &&
120
+ property.type === utils_1.AST_NODE_TYPES.Identifier) {
121
+ const key = `${object.name}.${property.name}`;
122
+ if (NON_DETERMINISTIC_MEMBERS.has(key)) {
123
+ return true;
124
+ }
125
+ }
126
+ }
127
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier && callee.name === 'Date') {
128
+ return true;
129
+ }
130
+ return false;
131
+ }
132
+ function isNonDeterministicInvocation(expr) {
133
+ return walkExpression(expr, (node) => {
134
+ if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
135
+ return isNonDeterministicCall(node);
136
+ }
137
+ if (node.type === utils_1.AST_NODE_TYPES.NewExpression) {
138
+ if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
139
+ NON_DETERMINISTIC_CONSTRUCTORS.has(node.callee.name)) {
140
+ return true;
141
+ }
142
+ }
143
+ return false;
144
+ });
145
+ }
146
+ function hasUnsafeSideEffects(expr) {
147
+ return walkExpression(expr, (node) => [
148
+ utils_1.AST_NODE_TYPES.AssignmentExpression,
149
+ utils_1.AST_NODE_TYPES.AwaitExpression,
150
+ utils_1.AST_NODE_TYPES.UpdateExpression,
151
+ utils_1.AST_NODE_TYPES.YieldExpression,
152
+ utils_1.AST_NODE_TYPES.SequenceExpression,
153
+ ].includes(node.type) ||
154
+ (node.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
155
+ node.operator === 'delete'));
156
+ }
157
+ function describePrimitiveExpression(expr) {
158
+ switch (expr.type) {
159
+ case utils_1.AST_NODE_TYPES.Literal: {
160
+ if ('regex' in expr && expr.regex) {
161
+ return 'RegExp object';
162
+ }
163
+ if ('bigint' in expr && expr.bigint !== undefined) {
164
+ return 'bigint value';
165
+ }
166
+ if (expr.value === null)
167
+ return 'null value';
168
+ if (typeof expr.value === 'boolean')
169
+ return 'boolean value';
170
+ if (typeof expr.value === 'number')
171
+ return 'number value';
172
+ if (typeof expr.value === 'string')
173
+ return 'string value';
174
+ /* istanbul ignore next -- defensive fallback for uncommon literals */
175
+ return 'primitive value';
176
+ }
177
+ case utils_1.AST_NODE_TYPES.TemplateLiteral:
178
+ return 'string value';
179
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
180
+ if (expr.operator === '!')
181
+ return 'boolean condition';
182
+ if (expr.operator === 'void')
183
+ return 'undefined value';
184
+ if (expr.operator === 'typeof')
185
+ return 'string value';
186
+ /* istanbul ignore next -- other unary operators are treated as primitives */
187
+ return 'primitive value';
188
+ case utils_1.AST_NODE_TYPES.BinaryExpression:
189
+ if (COMPARISON_OPERATORS.has(expr.operator)) {
190
+ return 'boolean condition';
191
+ }
192
+ if (expr.operator === '+' && isStringLikeWithoutTypes(expr)) {
193
+ return 'string value';
194
+ }
195
+ return 'number value';
196
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
197
+ return 'primitive value';
198
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
199
+ return 'primitive value';
200
+ case utils_1.AST_NODE_TYPES.Identifier:
201
+ if (expr.name === 'undefined')
202
+ return 'undefined value';
203
+ if (expr.name === 'Infinity' || expr.name === 'NaN')
204
+ return 'number value';
205
+ return 'primitive value';
206
+ default:
207
+ /* istanbul ignore next -- unreachable with current node set */
208
+ return 'primitive value';
209
+ }
210
+ }
211
+ function isPrimitiveExpressionWithoutTypes(expr) {
212
+ switch (expr.type) {
213
+ case utils_1.AST_NODE_TYPES.Literal: {
214
+ if ('regex' in expr && expr.regex) {
215
+ return { primitive: false, kind: describePrimitiveExpression(expr) };
216
+ }
217
+ return { primitive: true, kind: describePrimitiveExpression(expr) };
218
+ }
219
+ case utils_1.AST_NODE_TYPES.Identifier: {
220
+ const identifier = expr;
221
+ if (identifier.name === 'undefined' ||
222
+ identifier.name === 'Infinity' ||
223
+ identifier.name === 'NaN') {
224
+ return { primitive: true, kind: describePrimitiveExpression(expr) };
225
+ }
226
+ return { primitive: false, kind: 'primitive value' };
227
+ }
228
+ case utils_1.AST_NODE_TYPES.TemplateLiteral:
229
+ return { primitive: true, kind: describePrimitiveExpression(expr) };
230
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
231
+ return { primitive: true, kind: describePrimitiveExpression(expr) };
232
+ case utils_1.AST_NODE_TYPES.BinaryExpression: {
233
+ const primitive = COMPARISON_OPERATORS.has(expr.operator) ||
234
+ (isPrimitiveExpressionWithoutTypes(expr.left)
235
+ .primitive &&
236
+ isPrimitiveExpressionWithoutTypes(expr.right)
237
+ .primitive);
238
+ return {
239
+ primitive,
240
+ kind: describePrimitiveExpression(expr),
241
+ };
242
+ }
243
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
244
+ return {
245
+ primitive: isPrimitiveExpressionWithoutTypes(expr.left)
246
+ .primitive &&
247
+ isPrimitiveExpressionWithoutTypes(expr.right)
248
+ .primitive,
249
+ kind: describePrimitiveExpression(expr),
250
+ };
251
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
252
+ return {
253
+ primitive: isPrimitiveExpressionWithoutTypes(expr.consequent).primitive &&
254
+ isPrimitiveExpressionWithoutTypes(expr.alternate).primitive,
255
+ kind: describePrimitiveExpression(expr),
256
+ };
257
+ case utils_1.AST_NODE_TYPES.ChainExpression:
258
+ return isPrimitiveExpressionWithoutTypes(expr.expression);
259
+ case utils_1.AST_NODE_TYPES.TSAsExpression:
260
+ case utils_1.AST_NODE_TYPES.TSTypeAssertion:
261
+ case utils_1.AST_NODE_TYPES.TSNonNullExpression:
262
+ return isPrimitiveExpressionWithoutTypes(expr.expression);
263
+ default:
264
+ return { primitive: false, kind: 'primitive value' };
265
+ }
266
+ }
267
+ exports.noUselessUsememoPrimitives = (0, createRule_1.createRule)({
268
+ name: 'no-useless-usememo-primitives',
269
+ meta: {
270
+ type: 'suggestion',
271
+ docs: {
272
+ description: 'Disallow useless useMemo with primitive values.',
273
+ recommended: 'error',
274
+ },
275
+ fixable: 'code',
276
+ schema: [
277
+ {
278
+ type: 'object',
279
+ properties: {
280
+ ignoreCallExpressions: { type: 'boolean', default: true },
281
+ ignoreSymbol: { type: 'boolean', default: true },
282
+ tsOnly: { type: 'boolean', default: false },
283
+ },
284
+ additionalProperties: false,
285
+ },
286
+ ],
287
+ messages: {
288
+ uselessUseMemoPrimitive: 'useMemo wraps a primitive {{valueKind}}. → Primitives are pass-by-value and have no identity to preserve, so memoization provides zero referential-stability benefit and only adds unnecessary hook overhead. → Remove useMemo and inline the expression directly.',
289
+ },
290
+ },
291
+ defaultOptions: [DEFAULT_OPTIONS],
292
+ create(context) {
293
+ const options = { ...DEFAULT_OPTIONS, ...context.options[0] };
294
+ const sourceCode = context.sourceCode;
295
+ const services = sourceCode.parserServices;
296
+ const parserServices = services &&
297
+ 'hasFullTypeInformation' in services &&
298
+ services.hasFullTypeInformation
299
+ ? services
300
+ : null;
301
+ if (options.tsOnly && !parserServices) {
302
+ return {};
303
+ }
304
+ let tsModule = null;
305
+ let checker = null;
306
+ if (parserServices) {
307
+ try {
308
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
309
+ const ts = require('typescript');
310
+ tsModule = ts;
311
+ checker = parserServices.program.getTypeChecker();
312
+ }
313
+ catch {
314
+ /* istanbul ignore next -- TypeScript not available, falls back to heuristic path */
315
+ }
316
+ }
317
+ function classifyExpressionTypeInternal(expr) {
318
+ if (!checker || !tsModule || !parserServices) {
319
+ return { status: 'unknown', kind: 'unknown value' };
320
+ }
321
+ return (0, tsTypeClassifier_1.classifyExpressionType)(expr, {
322
+ checker,
323
+ tsModule,
324
+ parserServices,
325
+ options,
326
+ });
327
+ }
328
+ return {
329
+ CallExpression(node) {
330
+ if (!isUseMemoCallee(node.callee)) {
331
+ return;
332
+ }
333
+ if (node.arguments.length === 0) {
334
+ return;
335
+ }
336
+ const callback = node.arguments[0];
337
+ if (callback.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
338
+ callback.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
339
+ return;
340
+ }
341
+ if (callback.async) {
342
+ return;
343
+ }
344
+ if (callback.generator) {
345
+ return;
346
+ }
347
+ const returnedExpression = getReturnedExpression(callback);
348
+ if (!returnedExpression) {
349
+ return;
350
+ }
351
+ if (hasUnsafeSideEffects(returnedExpression)) {
352
+ return;
353
+ }
354
+ if (isNonDeterministicInvocation(returnedExpression)) {
355
+ return;
356
+ }
357
+ if (options.ignoreCallExpressions &&
358
+ containsCallExpression(returnedExpression)) {
359
+ return;
360
+ }
361
+ const typeEvaluation = classifyExpressionTypeInternal(returnedExpression);
362
+ let isPrimitive = false;
363
+ let valueKind = typeEvaluation.kind;
364
+ if (typeEvaluation.status === 'primitive') {
365
+ isPrimitive = true;
366
+ }
367
+ else if (typeEvaluation.status !== 'non-primitive') {
368
+ const heuristic = isPrimitiveExpressionWithoutTypes(returnedExpression);
369
+ if (heuristic.primitive) {
370
+ isPrimitive = true;
371
+ valueKind = heuristic.kind;
372
+ }
373
+ }
374
+ if (!isPrimitive) {
375
+ return;
376
+ }
377
+ context.report({
378
+ node,
379
+ messageId: 'uselessUseMemoPrimitive',
380
+ data: {
381
+ valueKind,
382
+ },
383
+ fix(fixer) {
384
+ const replacement = `(${sourceCode.getText(returnedExpression)})`;
385
+ return fixer.replaceText(node, replacement);
386
+ },
387
+ });
388
+ },
389
+ };
390
+ },
391
+ });
392
+ exports.default = exports.noUselessUsememoPrimitives;
393
+ //# sourceMappingURL=no-useless-usememo-primitives.js.map
@@ -0,0 +1,13 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ type Options = [
3
+ {
4
+ /**
5
+ * Patterns (regex strings) that describe call expressions considered "expensive"
6
+ * and therefore allowed even when they return pass-by-value types.
7
+ */
8
+ allowExpensiveCalleePatterns?: string[];
9
+ }
10
+ ];
11
+ type MessageIds = 'primitiveMemo' | 'invalidRegex';
12
+ export declare const noUsememoForPassByValue: TSESLint.RuleModule<MessageIds, Options, TSESLint.RuleListener>;
13
+ export default noUsememoForPassByValue;