@blumintinc/eslint-plugin-blumint 1.12.6 → 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 (258) hide show
  1. package/README.md +171 -99
  2. package/lib/index.js +300 -17
  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/class-methods-read-top-to-bottom.js +24 -5
  6. package/lib/rules/consistent-callback-naming.js +1 -1
  7. package/lib/rules/dynamic-https-errors.d.ts +3 -1
  8. package/lib/rules/dynamic-https-errors.js +48 -11
  9. package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
  10. package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +52 -96
  11. package/lib/rules/enforce-assert-throws.d.ts +3 -1
  12. package/lib/rules/enforce-assert-throws.js +493 -51
  13. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
  14. package/lib/rules/enforce-boolean-naming-prefixes.js +820 -39
  15. package/lib/rules/enforce-callable-types.js +4 -4
  16. package/lib/rules/enforce-callback-memo.js +143 -18
  17. package/lib/rules/enforce-centralized-mock-firestore.js +5 -2
  18. package/lib/rules/enforce-console-error.d.ts +3 -0
  19. package/lib/rules/enforce-console-error.js +278 -0
  20. package/lib/rules/enforce-css-media-queries.js +24 -46
  21. package/lib/rules/enforce-dynamic-file-naming.d.ts +2 -0
  22. package/lib/rules/enforce-dynamic-file-naming.js +53 -28
  23. package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
  24. package/lib/rules/enforce-dynamic-imports.js +1 -1
  25. package/lib/rules/enforce-early-destructuring.d.ts +2 -0
  26. package/lib/rules/enforce-early-destructuring.js +980 -0
  27. package/lib/rules/enforce-empty-object-check.d.ts +11 -0
  28. package/lib/rules/enforce-empty-object-check.js +502 -0
  29. package/lib/rules/enforce-exported-function-types.js +4 -4
  30. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
  31. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +262 -0
  32. package/lib/rules/enforce-firestore-doc-ref-generic.js +350 -3
  33. package/lib/rules/enforce-firestore-facade.js +349 -157
  34. package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
  35. package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
  36. package/lib/rules/enforce-firestore-set-merge.js +3 -4
  37. package/lib/rules/enforce-global-constants.d.ts +4 -1
  38. package/lib/rules/enforce-global-constants.js +240 -12
  39. package/lib/rules/enforce-identifiable-firestore-type.js +108 -58
  40. package/lib/rules/enforce-memoize-async.d.ts +2 -1
  41. package/lib/rules/enforce-memoize-async.js +92 -27
  42. package/lib/rules/enforce-memoize-getters.d.ts +3 -0
  43. package/lib/rules/enforce-memoize-getters.js +147 -0
  44. package/lib/rules/enforce-microdiff.js +3 -2
  45. package/lib/rules/enforce-object-literal-as-const.js +4 -4
  46. package/lib/rules/enforce-positive-naming.js +9 -7
  47. package/lib/rules/enforce-props-argument-name.d.ts +2 -7
  48. package/lib/rules/enforce-props-argument-name.js +165 -151
  49. package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
  50. package/lib/rules/enforce-props-naming-consistency.js +145 -0
  51. package/lib/rules/enforce-querykey-ts.d.ts +9 -0
  52. package/lib/rules/enforce-querykey-ts.js +274 -0
  53. package/lib/rules/enforce-safe-stringify.js +1 -1
  54. package/lib/rules/enforce-serializable-params.js +3 -3
  55. package/lib/rules/enforce-singular-type-names.js +10 -6
  56. package/lib/rules/enforce-stable-hash-spread-props.d.ts +13 -0
  57. package/lib/rules/enforce-stable-hash-spread-props.js +342 -0
  58. package/lib/rules/enforce-storage-context.d.ts +9 -0
  59. package/lib/rules/enforce-storage-context.js +646 -0
  60. package/lib/rules/enforce-timestamp-now.js +23 -7
  61. package/lib/rules/enforce-transform-memoization.d.ts +4 -0
  62. package/lib/rules/enforce-transform-memoization.js +416 -0
  63. package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
  64. package/lib/rules/enforce-typescript-markdown-code-blocks.js +100 -0
  65. package/lib/rules/enforce-unique-cursor-headers.d.ts +16 -0
  66. package/lib/rules/enforce-unique-cursor-headers.js +365 -0
  67. package/lib/rules/enforce-verb-noun-naming.js +90 -14
  68. package/lib/rules/ensure-pointer-events-none.js +28 -2
  69. package/lib/rules/export-if-in-doubt.js +27 -4
  70. package/lib/rules/extract-global-constants.js +92 -53
  71. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
  72. package/lib/rules/fast-deep-equal-over-microdiff.js +351 -83
  73. package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
  74. package/lib/rules/firestore-transaction-reads-before-writes.js +277 -0
  75. package/lib/rules/flatten-push-calls.d.ts +2 -0
  76. package/lib/rules/flatten-push-calls.js +428 -0
  77. package/lib/rules/generic-starts-with-t.js +8 -2
  78. package/lib/rules/global-const-style.js +50 -19
  79. package/lib/rules/jsdoc-above-field.d.ts +11 -0
  80. package/lib/rules/jsdoc-above-field.js +208 -0
  81. package/lib/rules/key-only-outermost-element.js +21 -3
  82. package/lib/rules/logical-top-to-bottom-grouping.d.ts +5 -0
  83. package/lib/rules/logical-top-to-bottom-grouping.js +1258 -0
  84. package/lib/rules/memo-compare-deeply-complex-props.d.ts +3 -0
  85. package/lib/rules/memo-compare-deeply-complex-props.js +788 -0
  86. package/lib/rules/memo-nested-react-components.d.ts +8 -0
  87. package/lib/rules/memo-nested-react-components.js +410 -0
  88. package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
  89. package/lib/rules/memoize-root-level-hocs.js +242 -0
  90. package/lib/rules/no-always-true-false-conditions.d.ts +2 -1
  91. package/lib/rules/no-always-true-false-conditions.js +68 -8
  92. package/lib/rules/no-array-length-in-deps.d.ts +12 -0
  93. package/lib/rules/no-array-length-in-deps.js +314 -0
  94. package/lib/rules/no-async-array-filter.js +5 -2
  95. package/lib/rules/no-async-foreach.js +210 -12
  96. package/lib/rules/no-circular-references.js +18 -17
  97. package/lib/rules/no-class-instance-destructuring.js +55 -12
  98. package/lib/rules/no-complex-cloud-params.js +22 -5
  99. package/lib/rules/no-compositing-layer-props.js +6 -4
  100. package/lib/rules/no-conditional-literals-in-jsx.js +37 -12
  101. package/lib/rules/no-console-error.d.ts +9 -0
  102. package/lib/rules/no-console-error.js +525 -0
  103. package/lib/rules/no-curly-brackets-around-commented-properties.d.ts +2 -0
  104. package/lib/rules/no-curly-brackets-around-commented-properties.js +258 -0
  105. package/lib/rules/no-empty-dependency-use-callbacks.d.ts +11 -0
  106. package/lib/rules/no-empty-dependency-use-callbacks.js +576 -0
  107. package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
  108. package/lib/rules/no-entire-object-hook-deps.js +320 -68
  109. package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
  110. package/lib/rules/no-excessive-parent-chain.js +273 -0
  111. package/lib/rules/no-explicit-return-type.d.ts +2 -1
  112. package/lib/rules/no-explicit-return-type.js +215 -26
  113. package/lib/rules/no-filter-without-return.js +5 -1
  114. package/lib/rules/no-firestore-jest-mock.d.ts +2 -1
  115. package/lib/rules/no-firestore-jest-mock.js +126 -8
  116. package/lib/rules/no-firestore-object-arrays.js +398 -28
  117. package/lib/rules/no-handler-suffix.d.ts +12 -0
  118. package/lib/rules/no-handler-suffix.js +305 -0
  119. package/lib/rules/no-hungarian.js +14 -14
  120. package/lib/rules/no-inline-component-prop.d.ts +10 -0
  121. package/lib/rules/no-inline-component-prop.js +456 -0
  122. package/lib/rules/no-jsx-in-hooks.js +6 -1
  123. package/lib/rules/no-jsx-whitespace-literal.js +8 -2
  124. package/lib/rules/no-margin-properties.d.ts +7 -1
  125. package/lib/rules/no-margin-properties.js +22 -9
  126. package/lib/rules/no-memoize-on-static.js +9 -1
  127. package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
  128. package/lib/rules/no-misleading-boolean-prefixes.js +331 -0
  129. package/lib/rules/no-misused-switch-case.js +22 -1
  130. package/lib/rules/no-mixed-firestore-transactions.d.ts +3 -1
  131. package/lib/rules/no-mixed-firestore-transactions.js +296 -34
  132. package/lib/rules/no-mock-firebase-admin.js +5 -2
  133. package/lib/rules/no-object-values-on-strings.js +10 -2
  134. package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
  135. package/lib/rules/no-overridable-method-calls-in-constructor.js +272 -0
  136. package/lib/rules/no-passthrough-getters.d.ts +4 -0
  137. package/lib/rules/no-passthrough-getters.js +206 -0
  138. package/lib/rules/no-redundant-annotation-assertion.d.ts +2 -0
  139. package/lib/rules/no-redundant-annotation-assertion.js +402 -0
  140. package/lib/rules/no-redundant-param-types.js +16 -6
  141. package/lib/rules/no-redundant-this-params.d.ts +3 -0
  142. package/lib/rules/no-redundant-this-params.js +459 -0
  143. package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
  144. package/lib/rules/no-redundant-usecallback-wrapper.js +328 -0
  145. package/lib/rules/no-res-error-status-in-onrequest.d.ts +4 -0
  146. package/lib/rules/no-res-error-status-in-onrequest.js +521 -0
  147. package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
  148. package/lib/rules/no-restricted-properties-fix.js +173 -0
  149. package/lib/rules/no-separate-loading-state.d.ts +8 -0
  150. package/lib/rules/no-separate-loading-state.js +133 -0
  151. package/lib/rules/no-stale-state-across-await.d.ts +1 -0
  152. package/lib/rules/no-stale-state-across-await.js +185 -0
  153. package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
  154. package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
  155. package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
  156. package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
  157. package/lib/rules/no-type-assertion-returns.js +49 -49
  158. package/lib/rules/no-undefined-null-passthrough.js +217 -40
  159. package/lib/rules/no-unmemoized-memo-without-props.d.ts +8 -0
  160. package/lib/rules/no-unmemoized-memo-without-props.js +426 -0
  161. package/lib/rules/no-unnecessary-destructuring-rename.d.ts +2 -0
  162. package/lib/rules/no-unnecessary-destructuring-rename.js +347 -0
  163. package/lib/rules/no-unnecessary-destructuring.js +14 -4
  164. package/lib/rules/no-unnecessary-verb-suffix.js +2 -3
  165. package/lib/rules/no-unpinned-dependencies.js +36 -5
  166. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  167. package/lib/rules/no-unused-props.d.ts +5 -2
  168. package/lib/rules/no-unused-props.js +451 -112
  169. package/lib/rules/no-unused-usestate.js +6 -2
  170. package/lib/rules/no-useless-fragment.js +28 -2
  171. package/lib/rules/no-useless-usememo-primitives.d.ts +9 -0
  172. package/lib/rules/no-useless-usememo-primitives.js +393 -0
  173. package/lib/rules/no-usememo-for-pass-by-value.d.ts +13 -0
  174. package/lib/rules/no-usememo-for-pass-by-value.js +757 -0
  175. package/lib/rules/no-uuidv4-base62-as-key.js +18 -13
  176. package/lib/rules/omit-index-html.d.ts +2 -1
  177. package/lib/rules/omit-index-html.js +62 -7
  178. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  179. package/lib/rules/optimize-object-boolean-conditions.js +236 -0
  180. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  181. package/lib/rules/parallelize-async-operations.js +394 -0
  182. package/lib/rules/prefer-batch-operations.d.ts +1 -3
  183. package/lib/rules/prefer-batch-operations.js +228 -35
  184. package/lib/rules/prefer-block-comments-for-declarations.js +22 -7
  185. package/lib/rules/prefer-clone-deep.js +3 -3
  186. package/lib/rules/prefer-destructuring-no-class.d.ts +2 -1
  187. package/lib/rules/prefer-destructuring-no-class.js +244 -59
  188. package/lib/rules/prefer-docsetter-setall.d.ts +2 -0
  189. package/lib/rules/prefer-docsetter-setall.js +243 -0
  190. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  191. package/lib/rules/prefer-document-flattening.js +239 -0
  192. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  193. package/lib/rules/prefer-field-paths-in-transforms.js +251 -0
  194. package/lib/rules/prefer-fragment-component.js +4 -4
  195. package/lib/rules/prefer-fragment-shorthand.js +2 -1
  196. package/lib/rules/prefer-getter-over-parameterless-method.d.ts +13 -0
  197. package/lib/rules/prefer-getter-over-parameterless-method.js +648 -0
  198. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  199. package/lib/rules/prefer-global-router-state-key.js +305 -31
  200. package/lib/rules/prefer-memoized-props.d.ts +3 -0
  201. package/lib/rules/prefer-memoized-props.js +445 -0
  202. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  203. package/lib/rules/prefer-next-dynamic.js +357 -0
  204. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  205. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +435 -0
  206. package/lib/rules/prefer-nullish-coalescing-override.d.ts +7 -0
  207. package/lib/rules/prefer-nullish-coalescing-override.js +188 -0
  208. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  209. package/lib/rules/prefer-params-over-parent-id.js +277 -0
  210. package/lib/rules/prefer-settings-object.js +23 -15
  211. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  212. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  213. package/lib/rules/prefer-type-over-interface.js +7 -2
  214. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  215. package/lib/rules/prefer-url-tostring-over-tojson.js +169 -0
  216. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  217. package/lib/rules/prefer-use-deep-compare-memo.js +431 -0
  218. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +25 -2
  219. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  220. package/lib/rules/prefer-utility-function-over-private-static.js +34 -2
  221. package/lib/rules/prevent-children-clobber.d.ts +2 -0
  222. package/lib/rules/prevent-children-clobber.js +536 -0
  223. package/lib/rules/react-memoize-literals.d.ts +4 -0
  224. package/lib/rules/react-memoize-literals.js +495 -0
  225. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  226. package/lib/rules/react-usememo-should-be-component.js +144 -78
  227. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  228. package/lib/rules/require-hooks-default-params.js +17 -16
  229. package/lib/rules/require-https-error-cause.d.ts +4 -0
  230. package/lib/rules/require-https-error-cause.js +136 -0
  231. package/lib/rules/require-https-error.js +43 -21
  232. package/lib/rules/require-image-optimized.js +2 -2
  233. package/lib/rules/require-memo.js +12 -2
  234. package/lib/rules/require-memoize-jsx-returners.d.ts +3 -0
  235. package/lib/rules/require-memoize-jsx-returners.js +485 -0
  236. package/lib/rules/require-usememo-object-literals.js +9 -4
  237. package/lib/rules/semantic-function-prefixes.js +38 -18
  238. package/lib/rules/sync-onwrite-name-func.js +5 -1
  239. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  240. package/lib/rules/test-file-location-enforcement.js +57 -0
  241. package/lib/rules/use-custom-link.js +6 -1
  242. package/lib/rules/use-custom-router.js +6 -1
  243. package/lib/rules/use-latest-callback.d.ts +3 -0
  244. package/lib/rules/use-latest-callback.js +271 -0
  245. package/lib/rules/vertically-group-related-functions.d.ts +16 -0
  246. package/lib/rules/vertically-group-related-functions.js +480 -0
  247. package/lib/utils/ASTHelpers.d.ts +13 -1
  248. package/lib/utils/ASTHelpers.js +34 -0
  249. package/lib/utils/createRule.js +1 -1
  250. package/lib/utils/getMethodName.d.ts +27 -0
  251. package/lib/utils/getMethodName.js +35 -0
  252. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  253. package/lib/utils/ruleTester.d.ts +1 -0
  254. package/lib/utils/ruleTester.js +4 -1
  255. package/lib/utils/tsTypeClassifier.d.ts +30 -0
  256. package/lib/utils/tsTypeClassifier.js +149 -0
  257. package/package.json +23 -14
  258. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noExcessiveParentChain = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ // Maximum number of consecutive .parent calls allowed before warning
7
+ const DEFAULT_MAX_PARENT_CHAIN_LENGTH = 2;
8
+ // Handler types that this rule applies to
9
+ const HANDLER_TYPES = new Set([
10
+ 'DocumentChangeHandler',
11
+ 'DocumentChangeHandlerTransaction',
12
+ 'RealtimeDbChangeHandler',
13
+ 'RealtimeDbChangeHandlerTransaction',
14
+ ]);
15
+ exports.noExcessiveParentChain = (0, createRule_1.createRule)({
16
+ name: 'no-excessive-parent-chain',
17
+ meta: {
18
+ type: 'suggestion',
19
+ hasSuggestions: true,
20
+ docs: {
21
+ description: 'Discourage excessive use of the ref.parent property chain in Firestore and RealtimeDB change handlers',
22
+ recommended: 'error',
23
+ },
24
+ schema: [
25
+ {
26
+ type: 'object',
27
+ properties: {
28
+ max: {
29
+ type: 'integer',
30
+ minimum: 1,
31
+ },
32
+ },
33
+ additionalProperties: false,
34
+ },
35
+ ],
36
+ messages: {
37
+ excessiveParentChain: 'Found {{count}} consecutive ref.parent hops in this handler. Long parent chains break when Firestore/RealtimeDB paths change and bypass the typed params the trigger already provides. Read path components from event.params (for example, params.userId) instead of walking ref.parent repeatedly.',
38
+ },
39
+ },
40
+ defaultOptions: [{}],
41
+ create(context) {
42
+ const maxParentChainLength = context.options[0]?.max ?? DEFAULT_MAX_PARENT_CHAIN_LENGTH;
43
+ // Track variables that contain event data
44
+ const eventDataVariables = new Map();
45
+ const eventIdentifiers = new Set();
46
+ const HANDLER_PARAM_SOURCE = '__handler_param__';
47
+ const recordEventIdentifier = (name) => {
48
+ eventIdentifiers.add(name);
49
+ };
50
+ const getRootIdentifier = (node) => {
51
+ let current = node;
52
+ while (current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
53
+ current = current.object;
54
+ }
55
+ return current.type === utils_1.AST_NODE_TYPES.Identifier ? current.name : null;
56
+ };
57
+ const hasRefProperty = (node) => {
58
+ let current = node;
59
+ while (current) {
60
+ if (current.property.type === utils_1.AST_NODE_TYPES.Identifier &&
61
+ current.property.name === 'ref') {
62
+ return true;
63
+ }
64
+ current =
65
+ current.object.type === utils_1.AST_NODE_TYPES.MemberExpression
66
+ ? current.object
67
+ : null;
68
+ }
69
+ return false;
70
+ };
71
+ const registerHandlerParams = (node) => {
72
+ for (const param of node.params) {
73
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
74
+ recordEventIdentifier(param.name);
75
+ }
76
+ if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
77
+ for (const prop of param.properties) {
78
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
79
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
80
+ prop.value.type === utils_1.AST_NODE_TYPES.Identifier &&
81
+ prop.key.name === 'data') {
82
+ eventDataVariables.set(prop.value.name, HANDLER_PARAM_SOURCE);
83
+ recordEventIdentifier(HANDLER_PARAM_SOURCE);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ };
89
+ // Check if a function is one of our handler types
90
+ function isHandlerFunction(node) {
91
+ if (node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
92
+ node.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
93
+ return false;
94
+ }
95
+ // Check if the function is assigned to a variable with a type annotation
96
+ const parent = node.parent;
97
+ if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
98
+ parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
99
+ // Check for type annotation
100
+ if (parent.id.typeAnnotation?.typeAnnotation) {
101
+ const typeNode = parent.id.typeAnnotation.typeAnnotation;
102
+ // Check if it's a reference to one of our handler types
103
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
104
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
105
+ HANDLER_TYPES.has(typeNode.typeName.name)) {
106
+ return true;
107
+ }
108
+ // Check for generic types that might be handlers
109
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
110
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
111
+ // Try to resolve the type name to see if it's one of our handler types
112
+ const scope = context.getScope();
113
+ const typeName = typeNode.typeName.name;
114
+ const variable = scope.variables.find((v) => v.name === typeName);
115
+ if (variable && variable.defs.length > 0) {
116
+ const def = variable.defs[0];
117
+ if (def.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration &&
118
+ def.node.typeAnnotation.type ===
119
+ utils_1.AST_NODE_TYPES.TSTypeReference &&
120
+ def.node.typeAnnotation.typeName.type ===
121
+ utils_1.AST_NODE_TYPES.Identifier &&
122
+ HANDLER_TYPES.has(def.node.typeAnnotation.typeName.name)) {
123
+ return true;
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ // Check if the function is exported with a type annotation
130
+ if (parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
131
+ parent.declaration?.type === utils_1.AST_NODE_TYPES.VariableDeclaration &&
132
+ parent.declaration.declarations[0]?.id.type ===
133
+ utils_1.AST_NODE_TYPES.Identifier &&
134
+ parent.declaration.declarations[0].id.typeAnnotation?.typeAnnotation) {
135
+ const typeNode = parent.declaration.declarations[0].id.typeAnnotation.typeAnnotation;
136
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
137
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
138
+ HANDLER_TYPES.has(typeNode.typeName.name)) {
139
+ return true;
140
+ }
141
+ }
142
+ return false;
143
+ }
144
+ // Count consecutive .parent calls in a member expression chain
145
+ function countParentChain(node) {
146
+ let count = 1; // Start with 1 for the current .parent
147
+ let current = node.object;
148
+ // Traverse the chain of member expressions
149
+ while (current.type === utils_1.AST_NODE_TYPES.MemberExpression &&
150
+ current.property.type === utils_1.AST_NODE_TYPES.Identifier &&
151
+ current.property.name === 'parent') {
152
+ count++;
153
+ current = current.object;
154
+ }
155
+ return count;
156
+ }
157
+ // This function has been removed as it's no longer needed
158
+ return {
159
+ // Register handler parameters to capture destructured event data
160
+ 'ArrowFunctionExpression, FunctionExpression'(node) {
161
+ if (isHandlerFunction(node)) {
162
+ registerHandlerParams(node);
163
+ }
164
+ },
165
+ // Track variable assignments that contain event data
166
+ VariableDeclarator(node) {
167
+ if (node.id.type === utils_1.AST_NODE_TYPES.Identifier && node.init) {
168
+ // Track direct event.data assignments
169
+ if (node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
170
+ node.init.property.type === utils_1.AST_NODE_TYPES.Identifier &&
171
+ node.init.property.name === 'data' &&
172
+ node.init.object.type === utils_1.AST_NODE_TYPES.Identifier) {
173
+ // Store the variable name and the source object (event)
174
+ eventDataVariables.set(node.id.name, node.init.object.name);
175
+ recordEventIdentifier(node.init.object.name);
176
+ }
177
+ // Track assignments from other tracked variables
178
+ if (node.init.type === utils_1.AST_NODE_TYPES.Identifier &&
179
+ eventDataVariables.has(node.init.name)) {
180
+ // Store the variable name with the same source as the original variable
181
+ eventDataVariables.set(node.id.name, eventDataVariables.get(node.init.name) || '');
182
+ const source = eventDataVariables.get(node.init.name);
183
+ if (source) {
184
+ recordEventIdentifier(source);
185
+ }
186
+ }
187
+ // Track assignments from event data properties
188
+ if (node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
189
+ node.init.object.type === utils_1.AST_NODE_TYPES.Identifier &&
190
+ eventDataVariables.has(node.init.object.name)) {
191
+ // Store the variable name with the same source as the original variable
192
+ eventDataVariables.set(node.id.name, eventDataVariables.get(node.init.object.name) || '');
193
+ const source = eventDataVariables.get(node.init.object.name);
194
+ if (source) {
195
+ recordEventIdentifier(source);
196
+ }
197
+ }
198
+ }
199
+ // Also track destructuring assignments
200
+ if (node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern &&
201
+ node.init?.type === utils_1.AST_NODE_TYPES.Identifier) {
202
+ const eventSource = eventDataVariables.get(node.init.name) ||
203
+ (eventIdentifiers.has(node.init.name) ? node.init.name : null);
204
+ for (const property of node.id.properties) {
205
+ if (property.type === utils_1.AST_NODE_TYPES.Property &&
206
+ property.key.type === utils_1.AST_NODE_TYPES.Identifier &&
207
+ property.value.type === utils_1.AST_NODE_TYPES.Identifier &&
208
+ eventSource) {
209
+ const targetName = property.value.name;
210
+ eventDataVariables.set(targetName, eventSource);
211
+ recordEventIdentifier(eventSource);
212
+ }
213
+ }
214
+ }
215
+ },
216
+ // Check for excessive parent chains in member expressions
217
+ MemberExpression(node) {
218
+ // Only check for .parent chains
219
+ if (node.property.type !== utils_1.AST_NODE_TYPES.Identifier ||
220
+ node.property.name !== 'parent') {
221
+ return;
222
+ }
223
+ // Count the number of consecutive .parent calls
224
+ const parentCount = countParentChain(node);
225
+ if (parentCount <= maxParentChainLength) {
226
+ return;
227
+ }
228
+ // Check if we're in a handler function
229
+ let current = node;
230
+ let inHandler = false;
231
+ while (current) {
232
+ if (current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
233
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
234
+ if (isHandlerFunction(current)) {
235
+ inHandler = true;
236
+ break;
237
+ }
238
+ }
239
+ current = current.parent;
240
+ }
241
+ if (!inHandler) {
242
+ return;
243
+ }
244
+ // Only report when the chain originates from tracked event data and contains a ref segment
245
+ const rootIdentifier = getRootIdentifier(node);
246
+ if (!rootIdentifier) {
247
+ return;
248
+ }
249
+ if (!hasRefProperty(node) ||
250
+ (!eventDataVariables.has(rootIdentifier) &&
251
+ !eventIdentifiers.has(rootIdentifier))) {
252
+ return;
253
+ }
254
+ context.report({
255
+ node,
256
+ messageId: 'excessiveParentChain',
257
+ data: {
258
+ count: parentCount,
259
+ },
260
+ suggest: [
261
+ {
262
+ messageId: 'excessiveParentChain',
263
+ fix(fixer) {
264
+ return fixer.replaceText(node, 'event.params');
265
+ },
266
+ },
267
+ ],
268
+ });
269
+ },
270
+ };
271
+ },
272
+ });
273
+ //# sourceMappingURL=no-excessive-parent-chain.js.map
@@ -1,4 +1,5 @@
1
1
  import { TSESLint } from '@typescript-eslint/utils';
2
+ type MessageIds = 'noExplicitReturnTypeInferable' | 'noExplicitReturnTypeNonInferable';
2
3
  type Options = [
3
4
  {
4
5
  allowRecursiveFunctions?: boolean;
@@ -9,5 +10,5 @@ type Options = [
9
10
  allowFirestoreFunctionFiles?: boolean;
10
11
  }
11
12
  ];
12
- export declare const noExplicitReturnType: TSESLint.RuleModule<'noExplicitReturnType', Options>;
13
+ export declare const noExplicitReturnType: TSESLint.RuleModule<MessageIds, Options>;
13
14
  export {};
@@ -11,6 +11,98 @@ const defaultOptions = {
11
11
  allowDtsFiles: true,
12
12
  allowFirestoreFunctionFiles: true,
13
13
  };
14
+ function getNameFromIdentifierOrLiteral(key) {
15
+ if (key.type === utils_1.AST_NODE_TYPES.Identifier) {
16
+ return key.name;
17
+ }
18
+ if (typeof key.value === 'string') {
19
+ return key.value;
20
+ }
21
+ return undefined;
22
+ }
23
+ function describeClassMethod(node) {
24
+ if (!node.computed &&
25
+ (node.key.type === utils_1.AST_NODE_TYPES.Identifier ||
26
+ (node.key.type === utils_1.AST_NODE_TYPES.Literal &&
27
+ typeof node.key.value === 'string'))) {
28
+ const name = getNameFromIdentifierOrLiteral(node.key);
29
+ if (name) {
30
+ return `class method "${name}"`;
31
+ }
32
+ }
33
+ return 'class method';
34
+ }
35
+ function describeMethodSignature(node) {
36
+ if (!node.computed &&
37
+ (node.key.type === utils_1.AST_NODE_TYPES.Identifier ||
38
+ (node.key.type === utils_1.AST_NODE_TYPES.Literal &&
39
+ typeof node.key.value === 'string'))) {
40
+ const name = getNameFromIdentifierOrLiteral(node.key);
41
+ if (name) {
42
+ return `interface method "${name}"`;
43
+ }
44
+ }
45
+ return 'interface method';
46
+ }
47
+ function describeFunctionDeclaration(node) {
48
+ if (node.id?.name) {
49
+ return `function "${node.id.name}"`;
50
+ }
51
+ return 'function';
52
+ }
53
+ function describeTSDeclareFunction(node) {
54
+ if (node.id?.name) {
55
+ return `function "${node.id.name}"`;
56
+ }
57
+ return 'function';
58
+ }
59
+ function describeFunctionExpression(node) {
60
+ if (node.id?.name) {
61
+ return `function "${node.id.name}"`;
62
+ }
63
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
64
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
65
+ return `function "${node.parent.id.name}"`;
66
+ }
67
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.Property &&
68
+ !node.parent.computed &&
69
+ (node.parent.key.type === utils_1.AST_NODE_TYPES.Identifier ||
70
+ (node.parent.key.type === utils_1.AST_NODE_TYPES.Literal &&
71
+ typeof node.parent.key.value === 'string'))) {
72
+ const name = getNameFromIdentifierOrLiteral(node.parent.key);
73
+ if (name) {
74
+ return `object method "${name}"`;
75
+ }
76
+ }
77
+ return 'function expression';
78
+ }
79
+ function describeArrowFunction(node) {
80
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
81
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
82
+ return `arrow function "${node.parent.id.name}"`;
83
+ }
84
+ return 'arrow function';
85
+ }
86
+ function describeFunctionKind(node) {
87
+ switch (node.type) {
88
+ case utils_1.AST_NODE_TYPES.MethodDefinition:
89
+ return describeClassMethod(node);
90
+ case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
91
+ return describeClassMethod(node);
92
+ case utils_1.AST_NODE_TYPES.TSMethodSignature:
93
+ return describeMethodSignature(node);
94
+ case utils_1.AST_NODE_TYPES.TSDeclareFunction:
95
+ return describeTSDeclareFunction(node);
96
+ case utils_1.AST_NODE_TYPES.FunctionDeclaration:
97
+ return describeFunctionDeclaration(node);
98
+ case utils_1.AST_NODE_TYPES.FunctionExpression:
99
+ return describeFunctionExpression(node);
100
+ case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
101
+ return describeArrowFunction(node);
102
+ default:
103
+ return 'function';
104
+ }
105
+ }
14
106
  function isRecursiveFunction(node) {
15
107
  const functionName = node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration
16
108
  ? node.id?.name
@@ -63,16 +155,52 @@ function isOverloadedFunction(node) {
63
155
  const interfaceBody = node.parent;
64
156
  if (interfaceBody.type !== utils_1.AST_NODE_TYPES.TSInterfaceBody)
65
157
  return false;
66
- const methodName = node.key.type === utils_1.AST_NODE_TYPES.Identifier ? node.key.name : undefined;
158
+ if (node.computed)
159
+ return false;
160
+ const methodName = node.key.type === utils_1.AST_NODE_TYPES.Identifier ||
161
+ node.key.type === utils_1.AST_NODE_TYPES.Literal
162
+ ? getNameFromIdentifierOrLiteral(node.key)
163
+ : undefined;
67
164
  if (!methodName)
68
165
  return false;
69
166
  return (interfaceBody.body.filter((member) => member.type === utils_1.AST_NODE_TYPES.TSMethodSignature &&
70
- member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
71
- member.key.name === methodName).length > 1);
167
+ !member.computed &&
168
+ (member.key.type === utils_1.AST_NODE_TYPES.Identifier ||
169
+ member.key.type === utils_1.AST_NODE_TYPES.Literal) &&
170
+ getNameFromIdentifierOrLiteral(member.key) === methodName).length > 1);
171
+ }
172
+ return false;
173
+ }
174
+ function isOverloadedTsDeclareFunction(node) {
175
+ const functionName = node.id?.name;
176
+ if (!functionName)
177
+ return false;
178
+ let container = node.parent;
179
+ while (container) {
180
+ if (container.type === utils_1.AST_NODE_TYPES.Program ||
181
+ container.type === utils_1.AST_NODE_TYPES.TSModuleBlock) {
182
+ const declarations = container.body
183
+ .map((statement) => {
184
+ if (statement.type === utils_1.AST_NODE_TYPES.TSDeclareFunction) {
185
+ return statement;
186
+ }
187
+ if (statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
188
+ statement.declaration?.type === utils_1.AST_NODE_TYPES.TSDeclareFunction) {
189
+ return statement.declaration;
190
+ }
191
+ return undefined;
192
+ })
193
+ .filter((value) => Boolean(value?.id?.name))
194
+ .filter((decl) => decl.id.name === functionName);
195
+ return declarations.length > 1;
196
+ }
197
+ container = container.parent;
72
198
  }
73
199
  return false;
74
200
  }
75
201
  function isInterfaceOrAbstractMethodSignature(node) {
202
+ if (node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition)
203
+ return true;
76
204
  if (node.type === utils_1.AST_NODE_TYPES.TSMethodSignature)
77
205
  return true;
78
206
  if (node.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
@@ -133,7 +261,8 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
133
261
  },
134
262
  ],
135
263
  messages: {
136
- noExplicitReturnType: 'Explicit return type is not allowed. Let TypeScript infer it.',
264
+ noExplicitReturnTypeInferable: "What's wrong: {{functionKind}} has an explicit return type annotation. \u2192 Why it matters: it must be updated manually and can drift from what the implementation actually returns, hiding bugs behind a stale type. \u2192 How to fix: remove the return type annotation so TypeScript infers it from the implementation.",
265
+ noExplicitReturnTypeNonInferable: "What's wrong: {{functionKind}} has an explicit return type annotation but no implementation body. \u2192 Why it matters: TypeScript cannot infer the return type here; removing it widens the return type to `any`. \u2192 How to fix: keep the annotation, or provide an implementation body where inference can succeed.",
137
266
  },
138
267
  },
139
268
  defaultOptions: [defaultOptions],
@@ -141,58 +270,77 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
141
270
  const mergedOptions = { ...defaultOptions, ...options };
142
271
  const filename = context.getFilename();
143
272
  if ((mergedOptions.allowDtsFiles && filename.endsWith('.d.ts')) ||
144
- (mergedOptions.allowFirestoreFunctionFiles && filename.endsWith('.f.ts'))) {
273
+ (mergedOptions.allowFirestoreFunctionFiles &&
274
+ filename.endsWith('.f.ts'))) {
145
275
  return {};
146
276
  }
147
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
277
  function fixReturnType(fixer, node) {
149
- const returnType = node.returnType || node.value?.returnType;
278
+ // Some nodes expose returnType directly while others nest it under value.
279
+ const returnType = 'returnType' in node
280
+ ? node.returnType
281
+ : 'value' in node
282
+ ? node.value.returnType
283
+ : null;
150
284
  if (!returnType)
151
285
  return null;
152
- // Create a fix that removes the return type annotation
153
286
  return fixer.remove(returnType);
154
287
  }
155
288
  return {
156
289
  FunctionDeclaration(node) {
157
- if (!node.returnType)
290
+ const returnType = node.returnType;
291
+ if (!returnType)
158
292
  return;
159
293
  if (isTypeGuardFunction(node) ||
160
294
  (mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node))) {
161
295
  return;
162
296
  }
297
+ const isInferable = Boolean(node.body);
163
298
  context.report({
164
- node: node.returnType,
165
- messageId: 'noExplicitReturnType',
166
- fix: (fixer) => fixReturnType(fixer, node),
299
+ node: returnType,
300
+ messageId: isInferable
301
+ ? 'noExplicitReturnTypeInferable'
302
+ : 'noExplicitReturnTypeNonInferable',
303
+ data: { functionKind: describeFunctionKind(node) },
304
+ ...(isInferable
305
+ ? { fix: (fixer) => fixReturnType(fixer, node) }
306
+ : {}),
167
307
  });
168
308
  },
169
309
  FunctionExpression(node) {
170
- if (!node.returnType)
310
+ const returnType = node.returnType;
311
+ if (!returnType)
312
+ return;
313
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
171
314
  return;
315
+ }
172
316
  if (isTypeGuardFunction(node) ||
173
317
  (mergedOptions.allowRecursiveFunctions && isRecursiveFunction(node))) {
174
318
  return;
175
319
  }
176
320
  context.report({
177
- node: node.returnType,
178
- messageId: 'noExplicitReturnType',
321
+ node: returnType,
322
+ messageId: 'noExplicitReturnTypeInferable',
323
+ data: { functionKind: describeFunctionKind(node) },
179
324
  fix: (fixer) => fixReturnType(fixer, node),
180
325
  });
181
326
  },
182
327
  ArrowFunctionExpression(node) {
183
- if (!node.returnType)
328
+ const returnType = node.returnType;
329
+ if (!returnType)
184
330
  return;
185
331
  if (isTypeGuardFunction(node)) {
186
332
  return;
187
333
  }
188
334
  context.report({
189
- node: node.returnType,
190
- messageId: 'noExplicitReturnType',
335
+ node: returnType,
336
+ messageId: 'noExplicitReturnTypeInferable',
337
+ data: { functionKind: describeFunctionKind(node) },
191
338
  fix: (fixer) => fixReturnType(fixer, node),
192
339
  });
193
340
  },
194
341
  TSMethodSignature(node) {
195
- if (!node.returnType)
342
+ const returnType = node.returnType;
343
+ if (!returnType)
196
344
  return;
197
345
  if (mergedOptions.allowInterfaceMethodSignatures) {
198
346
  return;
@@ -202,23 +350,64 @@ exports.noExplicitReturnType = (0, createRule_1.createRule)({
202
350
  return;
203
351
  }
204
352
  context.report({
205
- node: node.returnType,
206
- messageId: 'noExplicitReturnType',
207
- fix: (fixer) => fixReturnType(fixer, node),
353
+ node: returnType,
354
+ messageId: 'noExplicitReturnTypeNonInferable',
355
+ data: { functionKind: describeFunctionKind(node) },
208
356
  });
209
357
  },
210
358
  MethodDefinition(node) {
211
- if (!node.value.returnType)
359
+ const returnType = node.value.returnType;
360
+ if (!returnType)
212
361
  return;
213
362
  if (isTypeGuardFunction(node.value) ||
214
363
  (mergedOptions.allowAbstractMethodSignatures &&
215
364
  isInterfaceOrAbstractMethodSignature(node))) {
216
365
  return;
217
366
  }
367
+ const isInferable = Boolean(node.value.body);
218
368
  context.report({
219
- node: node.value.returnType,
220
- messageId: 'noExplicitReturnType',
221
- fix: (fixer) => fixReturnType(fixer, node),
369
+ node: returnType,
370
+ messageId: isInferable
371
+ ? 'noExplicitReturnTypeInferable'
372
+ : 'noExplicitReturnTypeNonInferable',
373
+ data: { functionKind: describeFunctionKind(node) },
374
+ ...(isInferable
375
+ ? { fix: (fixer) => fixReturnType(fixer, node) }
376
+ : {}),
377
+ });
378
+ },
379
+ TSAbstractMethodDefinition(node) {
380
+ const returnType = node.value.returnType;
381
+ if (!returnType)
382
+ return;
383
+ if (isTypeGuardFunction(node.value) ||
384
+ (mergedOptions.allowAbstractMethodSignatures &&
385
+ isInterfaceOrAbstractMethodSignature(node))) {
386
+ return;
387
+ }
388
+ // Abstract methods never have bodies; they are always non-inferable and
389
+ // intentionally have no fixer.
390
+ context.report({
391
+ node: returnType,
392
+ messageId: 'noExplicitReturnTypeNonInferable',
393
+ data: { functionKind: describeFunctionKind(node) },
394
+ });
395
+ },
396
+ TSDeclareFunction(node) {
397
+ const returnType = node.returnType;
398
+ if (!returnType)
399
+ return;
400
+ if (isTypeGuardFunction(node)) {
401
+ return;
402
+ }
403
+ if (mergedOptions.allowOverloadedFunctions &&
404
+ isOverloadedTsDeclareFunction(node)) {
405
+ return;
406
+ }
407
+ context.report({
408
+ node: returnType,
409
+ messageId: 'noExplicitReturnTypeNonInferable',
410
+ data: { functionKind: describeFunctionKind(node) },
222
411
  });
223
412
  },
224
413
  };
@@ -15,9 +15,13 @@ exports.noFilterWithoutReturn = (0, createRule_1.createRule)({
15
15
  return;
16
16
  }
17
17
  if (!ASTHelpers_1.ASTHelpers.hasReturnStatement(body)) {
18
+ const filterCall = context.getSourceCode().getText(node.callee);
18
19
  context.report({
19
20
  node,
20
21
  messageId: 'unexpected',
22
+ data: {
23
+ filterCall,
24
+ },
21
25
  });
22
26
  }
23
27
  }
@@ -33,7 +37,7 @@ exports.noFilterWithoutReturn = (0, createRule_1.createRule)({
33
37
  },
34
38
  schema: [],
35
39
  messages: {
36
- unexpected: 'Array.filter callbacks with block statements must contain a return statement. Instead of `array.filter(x => { doSomething(x); })`, use `array.filter(x => { doSomething(x); return someCondition; })` or use implicit return `array.filter(x => someCondition)`.',
40
+ unexpected: 'Callback for {{filterCall}} uses braces but never returns a value, so filter receives undefined for every element and silently drops them all. Return the predicate result from inside the block (e.g., "return matches(item);") or use a concise arrow like {{filterCall}}((item) => matches(item)) to make the keep/remove condition explicit.',
37
41
  },
38
42
  },
39
43
  defaultOptions: [],
@@ -1 +1,2 @@
1
- export declare const noFirestoreJestMock: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noFirestoreJestMock", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
1
+ import type { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const noFirestoreJestMock: TSESLint.RuleModule<"noFirestoreJestMock", [], TSESLint.RuleListener>;