@blumintinc/eslint-plugin-blumint 1.13.0 → 1.15.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 (199) hide show
  1. package/README.md +169 -139
  2. package/lib/index.js +200 -9
  3. package/lib/rules/array-methods-this-context.js +1 -1
  4. package/lib/rules/avoid-utils-directory.js +0 -4
  5. package/lib/rules/class-methods-read-top-to-bottom.js +24 -5
  6. package/lib/rules/consistent-callback-naming.js +43 -4
  7. package/lib/rules/dynamic-https-errors.d.ts +1 -1
  8. package/lib/rules/dynamic-https-errors.js +134 -44
  9. package/lib/rules/enforce-assert-safe-object-key.js +11 -11
  10. package/lib/rules/enforce-boolean-naming-prefixes.js +11 -235
  11. package/lib/rules/enforce-callback-memo.js +1 -1
  12. package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
  13. package/lib/rules/enforce-console-error.js +35 -7
  14. package/lib/rules/enforce-date-ttime.d.ts +1 -0
  15. package/lib/rules/enforce-date-ttime.js +156 -0
  16. package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
  17. package/lib/rules/enforce-dynamic-file-naming.js +53 -28
  18. package/lib/rules/enforce-dynamic-firebase-imports.d.ts +2 -1
  19. package/lib/rules/enforce-dynamic-firebase-imports.js +3 -2
  20. package/lib/rules/enforce-early-destructuring.d.ts +2 -0
  21. package/lib/rules/enforce-early-destructuring.js +980 -0
  22. package/lib/rules/enforce-empty-object-check.d.ts +11 -0
  23. package/lib/rules/enforce-empty-object-check.js +502 -0
  24. package/lib/rules/enforce-exported-function-types.js +1 -1
  25. package/lib/rules/enforce-f-extension-for-entry-points.d.ts +8 -0
  26. package/lib/rules/enforce-f-extension-for-entry-points.js +283 -0
  27. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +100 -50
  28. package/lib/rules/enforce-firestore-doc-ref-generic.js +15 -3
  29. package/lib/rules/enforce-firestore-facade.js +4 -2
  30. package/lib/rules/enforce-firestore-set-merge.js +3 -4
  31. package/lib/rules/enforce-global-constants.js +4 -4
  32. package/lib/rules/enforce-id-capitalization.js +1 -1
  33. package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
  34. package/lib/rules/enforce-memoize-async.js +71 -21
  35. package/lib/rules/enforce-memoize-getters.js +39 -15
  36. package/lib/rules/enforce-microdiff.js +1 -1
  37. package/lib/rules/enforce-object-literal-as-const.js +4 -4
  38. package/lib/rules/enforce-positive-naming.js +4 -0
  39. package/lib/rules/enforce-props-argument-name.js +44 -18
  40. package/lib/rules/enforce-safe-stringify.js +1 -1
  41. package/lib/rules/enforce-serializable-params.js +3 -3
  42. package/lib/rules/enforce-singular-type-names.js +1 -1
  43. package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
  44. package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
  45. package/lib/rules/enforce-storage-context.d.ts +9 -0
  46. package/lib/rules/enforce-storage-context.js +646 -0
  47. package/lib/rules/enforce-timestamp-now.js +23 -7
  48. package/lib/rules/enforce-transform-memoization.d.ts +4 -0
  49. package/lib/rules/enforce-transform-memoization.js +416 -0
  50. package/lib/rules/enforce-typescript-markdown-code-blocks.js +7 -3
  51. package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
  52. package/lib/rules/enforce-unique-cursor-headers.js +365 -0
  53. package/lib/rules/enforce-verb-noun-naming.js +3818 -4644
  54. package/lib/rules/ensure-pointer-events-none.js +28 -2
  55. package/lib/rules/export-if-in-doubt.js +27 -4
  56. package/lib/rules/extract-global-constants.js +40 -13
  57. package/lib/rules/fast-deep-equal-over-microdiff.js +29 -39
  58. package/lib/rules/firestore-transaction-reads-before-writes.js +50 -4
  59. package/lib/rules/flatten-push-calls.d.ts +2 -0
  60. package/lib/rules/flatten-push-calls.js +428 -0
  61. package/lib/rules/global-const-style.js +66 -23
  62. package/lib/rules/jsdoc-above-field.d.ts +11 -0
  63. package/lib/rules/jsdoc-above-field.js +208 -0
  64. package/lib/rules/key-only-outermost-element.js +21 -3
  65. package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
  66. package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
  67. package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
  68. package/lib/rules/memo-compare-deeply-complex-props.js +801 -0
  69. package/lib/rules/memo-nested-react-components.d.ts +8 -0
  70. package/lib/rules/memo-nested-react-components.js +415 -0
  71. package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
  72. package/lib/rules/no-always-true-false-conditions.js +68 -8
  73. package/lib/rules/no-array-length-in-deps.js +9 -3
  74. package/lib/rules/no-async-foreach.js +215 -12
  75. package/lib/rules/no-circular-references.d.ts +2 -1
  76. package/lib/rules/no-circular-references.js +31 -32
  77. package/lib/rules/no-class-instance-destructuring.js +55 -12
  78. package/lib/rules/no-complex-cloud-params.js +7 -3
  79. package/lib/rules/no-compositing-layer-props.js +2 -2
  80. package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
  81. package/lib/rules/no-console-error.d.ts +9 -0
  82. package/lib/rules/no-console-error.js +527 -0
  83. package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
  84. package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
  85. package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
  86. package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
  87. package/lib/rules/no-entire-object-hook-deps.js +52 -7
  88. package/lib/rules/no-excessive-parent-chain.js +4 -1
  89. package/lib/rules/no-explicit-return-type.d.ts +2 -1
  90. package/lib/rules/no-explicit-return-type.js +215 -26
  91. package/lib/rules/no-filter-without-return.js +5 -1
  92. package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
  93. package/lib/rules/no-firestore-jest-mock.js +126 -8
  94. package/lib/rules/no-firestore-object-arrays.js +67 -12
  95. package/lib/rules/no-handler-suffix.d.ts +12 -0
  96. package/lib/rules/no-handler-suffix.js +305 -0
  97. package/lib/rules/no-hungarian.js +1 -1
  98. package/lib/rules/no-inline-component-prop.d.ts +10 -0
  99. package/lib/rules/no-inline-component-prop.js +465 -0
  100. package/lib/rules/no-jsx-in-hooks.js +6 -1
  101. package/lib/rules/no-jsx-whitespace-literal.js +8 -2
  102. package/lib/rules/no-margin-properties.js +6 -6
  103. package/lib/rules/no-memoize-on-static.js +9 -1
  104. package/lib/rules/no-misleading-boolean-prefixes.js +7 -3
  105. package/lib/rules/no-misused-switch-case.js +22 -1
  106. package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
  107. package/lib/rules/no-mixed-firestore-transactions.js +296 -34
  108. package/lib/rules/no-mock-firebase-admin.js +5 -2
  109. package/lib/rules/no-object-values-on-strings.js +10 -2
  110. package/lib/rules/no-overridable-method-calls-in-constructor.js +29 -17
  111. package/lib/rules/no-passthrough-getters.d.ts +2 -2
  112. package/lib/rules/no-passthrough-getters.js +120 -2
  113. package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
  114. package/lib/rules/no-redundant-annotation-assertion.js +402 -0
  115. package/lib/rules/no-redundant-param-types.js +16 -6
  116. package/lib/rules/no-redundant-this-params.d.ts +3 -0
  117. package/lib/rules/no-redundant-this-params.js +508 -0
  118. package/lib/rules/no-redundant-usecallback-wrapper.js +16 -3
  119. package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
  120. package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
  121. package/lib/rules/no-restricted-properties-fix.js +11 -10
  122. package/lib/rules/no-separate-loading-state.js +7 -16
  123. package/lib/rules/no-stale-state-across-await.js +1 -1
  124. package/lib/rules/no-type-assertion-returns.js +43 -49
  125. package/lib/rules/no-undefined-null-passthrough.js +61 -31
  126. package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
  127. package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
  128. package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
  129. package/lib/rules/no-unnecessary-destructuring-rename.js +344 -0
  130. package/lib/rules/no-unnecessary-destructuring.js +14 -4
  131. package/lib/rules/no-unnecessary-verb-suffix.js +2 -1
  132. package/lib/rules/no-unpinned-dependencies.js +36 -5
  133. package/lib/rules/no-unused-props.d.ts +2 -2
  134. package/lib/rules/no-unused-props.js +295 -91
  135. package/lib/rules/no-unused-usestate.js +6 -2
  136. package/lib/rules/no-useless-fragment.js +28 -2
  137. package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
  138. package/lib/rules/no-useless-usememo-primitives.js +393 -0
  139. package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
  140. package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
  141. package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
  142. package/lib/rules/omit-index-html.d.ts +2 -1
  143. package/lib/rules/omit-index-html.js +62 -7
  144. package/lib/rules/optimize-object-boolean-conditions.js +6 -4
  145. package/lib/rules/parallelize-async-operations.js +143 -59
  146. package/lib/rules/prefer-batch-operations.d.ts +1 -3
  147. package/lib/rules/prefer-batch-operations.js +32 -5
  148. package/lib/rules/prefer-block-comments-for-declarations.js +9 -4
  149. package/lib/rules/prefer-clone-deep.js +3 -3
  150. package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
  151. package/lib/rules/prefer-destructuring-no-class.js +244 -59
  152. package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
  153. package/lib/rules/prefer-docsetter-setall.js +243 -0
  154. package/lib/rules/prefer-field-paths-in-transforms.js +4 -3
  155. package/lib/rules/prefer-fragment-component.js +1 -1
  156. package/lib/rules/prefer-fragment-shorthand.js +2 -1
  157. package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
  158. package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
  159. package/lib/rules/prefer-global-router-state-key.js +88 -13
  160. package/lib/rules/prefer-memoized-props.d.ts +3 -0
  161. package/lib/rules/prefer-memoized-props.js +445 -0
  162. package/lib/rules/prefer-next-dynamic.js +60 -69
  163. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +118 -8
  164. package/lib/rules/prefer-nullish-coalescing-override.d.ts +2 -1
  165. package/lib/rules/prefer-nullish-coalescing-override.js +11 -5
  166. package/lib/rules/prefer-params-over-parent-id.js +221 -172
  167. package/lib/rules/prefer-settings-object.js +27 -10
  168. package/lib/rules/prefer-type-alias-over-typeof-constant.js +9 -0
  169. package/lib/rules/prefer-type-over-interface.js +7 -2
  170. package/lib/rules/prefer-url-tostring-over-tojson.js +6 -3
  171. package/lib/rules/prefer-use-deep-compare-memo.js +8 -5
  172. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
  173. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +1 -1
  174. package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
  175. package/lib/rules/prevent-children-clobber.d.ts +2 -0
  176. package/lib/rules/prevent-children-clobber.js +540 -0
  177. package/lib/rules/react-memoize-literals.d.ts +4 -0
  178. package/lib/rules/react-memoize-literals.js +614 -0
  179. package/lib/rules/react-usememo-should-be-component.js +2 -2
  180. package/lib/rules/require-hooks-default-params.js +17 -16
  181. package/lib/rules/require-https-error-cause.d.ts +4 -0
  182. package/lib/rules/require-https-error-cause.js +155 -0
  183. package/lib/rules/require-https-error.js +43 -21
  184. package/lib/rules/require-image-optimized.js +1 -1
  185. package/lib/rules/require-memo.js +18 -10
  186. package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
  187. package/lib/rules/require-memoize-jsx-returners.js +485 -0
  188. package/lib/rules/require-usememo-object-literals.js +2 -3
  189. package/lib/rules/test-file-location-enforcement.js +1 -1
  190. package/lib/rules/use-latest-callback.js +6 -5
  191. package/lib/rules/vertically-group-related-functions.d.ts +16 -0
  192. package/lib/rules/vertically-group-related-functions.js +480 -0
  193. package/lib/utils/ASTHelpers.d.ts +47 -2
  194. package/lib/utils/ASTHelpers.js +370 -112
  195. package/lib/utils/getMethodName.d.ts +27 -0
  196. package/lib/utils/getMethodName.js +35 -0
  197. package/lib/utils/tsTypeClassifier.d.ts +30 -0
  198. package/lib/utils/tsTypeClassifier.js +149 -0
  199. package/package.json +3 -1
@@ -97,13 +97,40 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
97
97
  },
98
98
  ],
99
99
  messages: {
100
- noTypeAssertionReturns: 'Type assertions in return statements are not allowed. Type the variable explicitly before returning it.',
101
- useExplicitVariable: 'Explicit return type annotations are not allowed. Type the variable explicitly before returning it.',
100
+ noTypeAssertionReturns: `What's wrong: Return value uses a type assertion to "{{assertedType}}", which bypasses TypeScript checks. ` +
101
+ `→ Why it matters: Casts can hide missing or invalid data and let unsafe values reach callers without compile-time errors. ` +
102
+ `→ How to fix: Create a typed variable or add a narrowing step before returning so the value is validated by the type system instead of forced with a cast.`,
103
+ useExplicitVariable: `What's wrong: Return type "{{returnType}}" is declared while returning an unchecked expression, so TypeScript trusts the annotation instead of validating the actual value. ` +
104
+ `→ Why it matters: This hides mismatches between implementation and contract, letting incorrect return values propagate without type errors. ` +
105
+ `→ How to fix: Assign the expression to a typed variable or narrow it first, then return that variable so TypeScript can verify it against the declared return type.`,
102
106
  },
103
107
  },
104
108
  defaultOptions: [defaultOptions],
105
109
  create(context, [options]) {
106
110
  const mergedOptions = { ...defaultOptions, ...options };
111
+ const sourceCode = context.getSourceCode();
112
+ function getTypeText(typeNode, fallback) {
113
+ const text = typeNode ? sourceCode.getText(typeNode).trim() : '';
114
+ return text || fallback;
115
+ }
116
+ function reportTypeAssertion(node) {
117
+ context.report({
118
+ node,
119
+ messageId: 'noTypeAssertionReturns',
120
+ data: {
121
+ assertedType: getTypeText(node.typeAnnotation, 'the asserted type'),
122
+ },
123
+ });
124
+ }
125
+ function reportReturnTypeAnnotation(typeAnnotation) {
126
+ context.report({
127
+ node: typeAnnotation,
128
+ messageId: 'useExplicitVariable',
129
+ data: {
130
+ returnType: getTypeText(typeAnnotation.typeAnnotation, 'the declared return type'),
131
+ },
132
+ });
133
+ }
107
134
  /**
108
135
  * Checks if a node is inside a JSX attribute or object property (which could be JSX props)
109
136
  */
@@ -202,7 +229,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
202
229
  /**
203
230
  * Check if the current file is a .f.ts file
204
231
  */
205
- function isFunctionFile(context) {
232
+ function isFunctionFile() {
206
233
  const filename = context.getFilename();
207
234
  return filename.endsWith('.f.ts');
208
235
  }
@@ -213,7 +240,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
213
240
  if (!node.returnType)
214
241
  return;
215
242
  // Skip checking return types in .f.ts files
216
- if (isFunctionFile(context)) {
243
+ if (isFunctionFile()) {
217
244
  return;
218
245
  }
219
246
  // Allow type predicates if configured
@@ -224,10 +251,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
224
251
  // If type predicates are not allowed, report them
225
252
  if (!mergedOptions.allowTypePredicates &&
226
253
  isTypePredicate(node.returnType)) {
227
- context.report({
228
- node: node.returnType,
229
- messageId: 'useExplicitVariable',
230
- });
254
+ reportReturnTypeAnnotation(node.returnType);
231
255
  return;
232
256
  }
233
257
  // Check if the function has a return statement with a direct value (not a variable)
@@ -244,10 +268,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
244
268
  if (statement.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
245
269
  statement.argument.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
246
270
  statement.argument.type === utils_1.AST_NODE_TYPES.CallExpression) {
247
- context.report({
248
- node: node.returnType,
249
- messageId: 'useExplicitVariable',
250
- });
271
+ reportReturnTypeAnnotation(node.returnType);
251
272
  break;
252
273
  }
253
274
  }
@@ -267,23 +288,14 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
267
288
  }
268
289
  // For nested type assertions, only report the outermost one
269
290
  if (node.argument.expression.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
270
- context.report({
271
- node: node.argument,
272
- messageId: 'noTypeAssertionReturns',
273
- });
291
+ reportTypeAssertion(node.argument);
274
292
  return;
275
293
  }
276
- context.report({
277
- node: node.argument,
278
- messageId: 'noTypeAssertionReturns',
279
- });
294
+ reportTypeAssertion(node.argument);
280
295
  }
281
296
  // Check for type assertions using angle bracket syntax
282
297
  if (node.argument.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
283
- context.report({
284
- node: node.argument,
285
- messageId: 'noTypeAssertionReturns',
286
- });
298
+ reportTypeAssertion(node.argument);
287
299
  }
288
300
  },
289
301
  // Check functions with explicit return types
@@ -301,7 +313,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
301
313
  // Check for explicit return type
302
314
  if (node.returnType) {
303
315
  // Skip checking return types in .f.ts files
304
- if (isFunctionFile(context)) {
316
+ if (isFunctionFile()) {
305
317
  return;
306
318
  }
307
319
  // Allow type predicates if configured
@@ -312,19 +324,13 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
312
324
  // If type predicates are not allowed, report them
313
325
  if (!mergedOptions.allowTypePredicates &&
314
326
  isTypePredicate(node.returnType)) {
315
- context.report({
316
- node: node.returnType,
317
- messageId: 'useExplicitVariable',
318
- });
327
+ reportReturnTypeAnnotation(node.returnType);
319
328
  return;
320
329
  }
321
330
  // If returning an object literal with explicit return type, report it
322
331
  if (node.body.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
323
332
  node.body.type === utils_1.AST_NODE_TYPES.ArrayExpression) {
324
- context.report({
325
- node: node.returnType,
326
- messageId: 'useExplicitVariable',
327
- });
333
+ reportReturnTypeAnnotation(node.returnType);
328
334
  }
329
335
  }
330
336
  // Check for type assertions in the body
@@ -333,16 +339,10 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
333
339
  if (mergedOptions.allowAsConst && isAsConstAssertion(node.body)) {
334
340
  return;
335
341
  }
336
- context.report({
337
- node: node.body,
338
- messageId: 'noTypeAssertionReturns',
339
- });
342
+ reportTypeAssertion(node.body);
340
343
  }
341
344
  else if (node.body.type === utils_1.AST_NODE_TYPES.TSTypeAssertion) {
342
- context.report({
343
- node: node.body,
344
- messageId: 'noTypeAssertionReturns',
345
- });
345
+ reportTypeAssertion(node.body);
346
346
  }
347
347
  }
348
348
  else if (node.returnType) {
@@ -365,10 +365,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
365
365
  return;
366
366
  }
367
367
  // For standalone type assertions in expressions
368
- context.report({
369
- node,
370
- messageId: 'noTypeAssertionReturns',
371
- });
368
+ reportTypeAssertion(node);
372
369
  },
373
370
  // Check for type assertions using angle bracket syntax
374
371
  TSTypeAssertion(node) {
@@ -377,10 +374,7 @@ exports.noTypeAssertionReturns = (0, createRule_1.createRule)({
377
374
  return;
378
375
  }
379
376
  // For standalone type assertions in expressions
380
- context.report({
381
- node,
382
- messageId: 'noTypeAssertionReturns',
383
- });
377
+ reportTypeAssertion(node);
384
378
  },
385
379
  };
386
380
  },
@@ -2,18 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noUndefinedNullPassthrough = void 0;
4
4
  const createRule_1 = require("../utils/createRule");
5
+ const utils_1 = require("@typescript-eslint/utils");
5
6
  exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
6
7
  create(context) {
7
8
  return {
8
9
  FunctionDeclaration(node) {
9
- // Skip functions with no parameters
10
- if (node.params.length === 0) {
11
- return;
12
- }
13
- // Skip React hooks (functions starting with 'use')
14
- if (node.id &&
15
- node.id.type === 'Identifier' &&
16
- node.id.name.startsWith('use')) {
10
+ if (shouldSkipFunction(node)) {
17
11
  return;
18
12
  }
19
13
  // For functions with exactly one parameter, check if it's passing through null/undefined
@@ -26,16 +20,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
26
20
  }
27
21
  },
28
22
  ArrowFunctionExpression(node) {
29
- // Skip functions with no parameters
30
- if (node.params.length === 0) {
31
- return;
32
- }
33
- // Skip if the function is part of a variable declaration that starts with 'use' (React hook)
34
- const parent = node.parent;
35
- if (parent &&
36
- parent.type === 'VariableDeclarator' &&
37
- parent.id.type === 'Identifier' &&
38
- parent.id.name.startsWith('use')) {
23
+ if (shouldSkipFunction(node)) {
39
24
  return;
40
25
  }
41
26
  // For arrow functions with block body
@@ -54,16 +39,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
54
39
  }
55
40
  },
56
41
  FunctionExpression(node) {
57
- // Skip functions with no parameters
58
- if (node.params.length === 0) {
59
- return;
60
- }
61
- // Skip if the function is part of a variable declaration that starts with 'use' (React hook)
62
- const parent = node.parent;
63
- if (parent &&
64
- parent.type === 'VariableDeclarator' &&
65
- parent.id.type === 'Identifier' &&
66
- parent.id.name.startsWith('use')) {
42
+ if (shouldSkipFunction(node)) {
67
43
  return;
68
44
  }
69
45
  // For functions with exactly one parameter, check if it's passing through null/undefined
@@ -86,7 +62,7 @@ exports.noUndefinedNullPassthrough = (0, createRule_1.createRule)({
86
62
  },
87
63
  schema: [],
88
64
  messages: {
89
- unexpected: 'Avoid functions that return undefined or null when their single argument is undefined or null. Move the null/undefined check to the caller instead.',
65
+ unexpected: 'Function returns null or undefined when "{{paramName}}" is nullish, which just passes the missing value to callers. This hides where validation belongs, delays failures until runtime as null values propagate unpredictably, and forces every caller to add defensive branching—making bugs and debugging more likely. Validate the argument before calling (throw an error if it is required) or return a concrete fallback (for example, an empty array or default object) so this function always provides a meaningful result.',
90
66
  },
91
67
  },
92
68
  defaultOptions: [],
@@ -126,6 +102,7 @@ function checkFunctionBody(body, param, context) {
126
102
  context.report({
127
103
  node: statement,
128
104
  messageId: 'unexpected',
105
+ data: { paramName },
129
106
  });
130
107
  }
131
108
  return;
@@ -142,6 +119,7 @@ function checkFunctionBody(body, param, context) {
142
119
  context.report({
143
120
  node: statement,
144
121
  messageId: 'unexpected',
122
+ data: { paramName },
145
123
  });
146
124
  }
147
125
  return;
@@ -254,6 +232,7 @@ function checkImplicitReturn(node, context) {
254
232
  context.report({
255
233
  node,
256
234
  messageId: 'unexpected',
235
+ data: { paramName },
257
236
  });
258
237
  }
259
238
  }
@@ -264,6 +243,7 @@ function checkImplicitReturn(node, context) {
264
243
  context.report({
265
244
  node,
266
245
  messageId: 'unexpected',
246
+ data: { paramName },
267
247
  });
268
248
  }
269
249
  }
@@ -272,6 +252,7 @@ function checkImplicitReturn(node, context) {
272
252
  context.report({
273
253
  node,
274
254
  messageId: 'unexpected',
255
+ data: { paramName },
275
256
  });
276
257
  }
277
258
  }
@@ -360,8 +341,8 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
360
341
  if (statement.type === 'IfStatement') {
361
342
  const test = statement.test;
362
343
  // Check if the test is checking any parameter for null/undefined
363
- const isParameterCheck = paramNames.some((paramName) => isNullUndefinedCheck(test, paramName));
364
- if (isParameterCheck) {
344
+ const matchedParam = paramNames.find((paramName) => isNullUndefinedCheck(test, paramName));
345
+ if (matchedParam) {
365
346
  // Check if the consequent is a block statement with a return
366
347
  if (statement.consequent.type === 'BlockStatement') {
367
348
  for (const consequentStmt of statement.consequent.body) {
@@ -374,6 +355,7 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
374
355
  context.report({
375
356
  node: statement,
376
357
  messageId: 'unexpected',
358
+ data: { paramName: matchedParam },
377
359
  });
378
360
  }
379
361
  return;
@@ -390,6 +372,7 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
390
372
  context.report({
391
373
  node: statement,
392
374
  messageId: 'unexpected',
375
+ data: { paramName: matchedParam },
393
376
  });
394
377
  }
395
378
  return;
@@ -398,4 +381,51 @@ function checkFunctionBodyForEarlyReturns(body, params, context) {
398
381
  }
399
382
  }
400
383
  }
384
+ function shouldSkipFunction(node) {
385
+ // Skip functions with no parameters
386
+ if (node.params.length === 0) {
387
+ return true;
388
+ }
389
+ // Skip functions with any parameter typed as 'unknown'
390
+ if (node.params.some(isUnknownParameter)) {
391
+ return true;
392
+ }
393
+ // Skip React hooks (functions starting with 'use')
394
+ let name;
395
+ if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
396
+ name = node.id?.name;
397
+ }
398
+ else {
399
+ const parent = node.parent;
400
+ if (parent &&
401
+ parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
402
+ parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
403
+ name = parent.id.name;
404
+ }
405
+ }
406
+ if (name?.startsWith('use')) {
407
+ return true;
408
+ }
409
+ return false;
410
+ }
411
+ /**
412
+ * Exempts type-narrowing functions from the rule by checking for 'unknown' parameters.
413
+ * These functions often narrow from 'unknown' and return null/undefined for invalid types,
414
+ * which is a valid pattern.
415
+ *
416
+ * TODO: Handle edge cases like rest parameters (...args: unknown[]),
417
+ * destructured parameters ({ x }: unknown), and array patterns ([a]: unknown).
418
+ */
419
+ function isUnknownParameter(param) {
420
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
421
+ return (param.typeAnnotation?.typeAnnotation.type ===
422
+ utils_1.AST_NODE_TYPES.TSUnknownKeyword);
423
+ }
424
+ if (param.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
425
+ param.left.type === utils_1.AST_NODE_TYPES.Identifier) {
426
+ return (param.left.typeAnnotation?.typeAnnotation.type ===
427
+ utils_1.AST_NODE_TYPES.TSUnknownKeyword);
428
+ }
429
+ return false;
430
+ }
401
431
  //# sourceMappingURL=no-undefined-null-passthrough.js.map
@@ -0,0 +1,8 @@
1
+ type Options = [
2
+ {
3
+ ignoreHooks?: string[];
4
+ ignoreHocs?: string[];
5
+ }
6
+ ];
7
+ export declare const noUnmemoizedMemoWithoutProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"unnecessaryMemoWithoutProps", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
8
+ export {};