@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
@@ -33,6 +33,106 @@ function isPromiseAll(node) {
33
33
  callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
34
34
  callee.property.name === 'all');
35
35
  }
36
+ function isSetterCall(call) {
37
+ if (call.callee.type !== utils_1.AST_NODE_TYPES.MemberExpression)
38
+ return false;
39
+ if (call.callee.property.type !== utils_1.AST_NODE_TYPES.Identifier)
40
+ return false;
41
+ return SETTER_METHODS.has(call.callee.property.name);
42
+ }
43
+ function getAllCallsFromExpression(expr) {
44
+ const calls = [];
45
+ function traverse(node) {
46
+ switch (node.type) {
47
+ case utils_1.AST_NODE_TYPES.CallExpression:
48
+ calls.push(node);
49
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
50
+ const obj = node.callee.object;
51
+ if (obj.type !== utils_1.AST_NODE_TYPES.Identifier &&
52
+ obj.type !== utils_1.AST_NODE_TYPES.ThisExpression &&
53
+ obj.type !== utils_1.AST_NODE_TYPES.Literal) {
54
+ traverse(obj);
55
+ }
56
+ }
57
+ for (const arg of node.arguments) {
58
+ if (!arg)
59
+ continue;
60
+ if (arg.type === utils_1.AST_NODE_TYPES.SpreadElement) {
61
+ traverse(arg.argument);
62
+ }
63
+ else {
64
+ traverse(arg);
65
+ }
66
+ }
67
+ break;
68
+ case utils_1.AST_NODE_TYPES.AwaitExpression:
69
+ traverse(node.argument);
70
+ break;
71
+ case utils_1.AST_NODE_TYPES.ArrayExpression:
72
+ for (const el of node.elements) {
73
+ if (!el)
74
+ continue;
75
+ if (el.type === utils_1.AST_NODE_TYPES.SpreadElement) {
76
+ traverse(el.argument);
77
+ }
78
+ else {
79
+ traverse(el);
80
+ }
81
+ }
82
+ break;
83
+ case utils_1.AST_NODE_TYPES.ObjectExpression:
84
+ for (const prop of node.properties) {
85
+ if (prop.type === utils_1.AST_NODE_TYPES.SpreadElement) {
86
+ traverse(prop.argument);
87
+ }
88
+ else if (prop.type === utils_1.AST_NODE_TYPES.Property) {
89
+ const val = prop.value;
90
+ if (val.type !== utils_1.AST_NODE_TYPES.Identifier &&
91
+ val.type !== utils_1.AST_NODE_TYPES.Literal &&
92
+ val.type !== utils_1.AST_NODE_TYPES.ThisExpression) {
93
+ traverse(val);
94
+ }
95
+ }
96
+ }
97
+ break;
98
+ case utils_1.AST_NODE_TYPES.SequenceExpression:
99
+ for (const expr of node.expressions) {
100
+ traverse(expr);
101
+ }
102
+ break;
103
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
104
+ traverse(node.argument);
105
+ break;
106
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
107
+ traverse(node.left);
108
+ traverse(node.right);
109
+ break;
110
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
111
+ traverse(node.test);
112
+ traverse(node.consequent);
113
+ traverse(node.alternate);
114
+ break;
115
+ case utils_1.AST_NODE_TYPES.MemberExpression:
116
+ if (node.object.type !== utils_1.AST_NODE_TYPES.Identifier &&
117
+ node.object.type !== utils_1.AST_NODE_TYPES.ThisExpression &&
118
+ node.object.type !== utils_1.AST_NODE_TYPES.Literal) {
119
+ traverse(node.object);
120
+ }
121
+ break;
122
+ // For function expressions, we don't traverse inside them
123
+ case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
124
+ case utils_1.AST_NODE_TYPES.FunctionExpression:
125
+ case utils_1.AST_NODE_TYPES.Identifier:
126
+ case utils_1.AST_NODE_TYPES.Literal:
127
+ case utils_1.AST_NODE_TYPES.ThisExpression:
128
+ break;
129
+ default:
130
+ break;
131
+ }
132
+ }
133
+ traverse(expr);
134
+ return calls;
135
+ }
36
136
  function findLoopNode(node) {
37
137
  let current = node;
38
138
  let loopNode = null;
@@ -48,7 +148,52 @@ function findLoopNode(node) {
48
148
  case utils_1.AST_NODE_TYPES.CallExpression:
49
149
  // Check for Promise.all
50
150
  if (isPromiseAll(current)) {
51
- return { node: current, isArrayMethod: 'map' };
151
+ // Check if Promise.all contains mixed operation types
152
+ if (current.arguments.length > 0) {
153
+ let hasSetterCalls = false;
154
+ let hasNonSetterCalls = false;
155
+ // Handle direct array expressions
156
+ if (current.arguments[0].type === utils_1.AST_NODE_TYPES.ArrayExpression) {
157
+ const arrayElements = current.arguments[0].elements;
158
+ // Analyze each element in the Promise.all array
159
+ for (const element of arrayElements) {
160
+ if (!element)
161
+ continue;
162
+ // Skip spread elements for now
163
+ if (element.type === utils_1.AST_NODE_TYPES.SpreadElement)
164
+ continue;
165
+ // Check all possible calls in this element
166
+ const allCalls = getAllCallsFromExpression(element);
167
+ let hasSetterInElement = false;
168
+ let hasNonSetterInElement = false;
169
+ for (const call of allCalls) {
170
+ if (isSetterCall(call)) {
171
+ hasSetterInElement = true;
172
+ }
173
+ else {
174
+ hasNonSetterInElement = true;
175
+ }
176
+ }
177
+ if (hasSetterInElement) {
178
+ hasSetterCalls = true;
179
+ }
180
+ if (hasNonSetterInElement) {
181
+ hasNonSetterCalls = true;
182
+ }
183
+ }
184
+ }
185
+ else {
186
+ // For non-array expressions (e.g., variables, method calls),
187
+ // we can't easily analyze the contents, so assume mixed operations
188
+ hasNonSetterCalls = true;
189
+ }
190
+ // If we have both setter calls and non-setter calls, don't flag
191
+ // This handles the case where different operation types are mixed
192
+ if (hasSetterCalls && hasNonSetterCalls) {
193
+ return undefined;
194
+ }
195
+ }
196
+ return { node: current, isArrayMethod: 'map', isPromiseAll: true };
52
197
  }
53
198
  // Check for array methods
54
199
  const { isValid, methodName: currentMethodName } = isArrayMethod(current);
@@ -79,6 +224,27 @@ function findLoopNode(node) {
79
224
  }
80
225
  return undefined;
81
226
  }
227
+ function describeLoopContext(loopInfo) {
228
+ if (loopInfo.isPromiseAll)
229
+ return 'Promise.all()';
230
+ if (loopInfo.isArrayMethod) {
231
+ return `${loopInfo.isArrayMethod}() callback`;
232
+ }
233
+ switch (loopInfo.node.type) {
234
+ case utils_1.AST_NODE_TYPES.ForStatement:
235
+ return 'for loop';
236
+ case utils_1.AST_NODE_TYPES.ForInStatement:
237
+ return 'for...in loop';
238
+ case utils_1.AST_NODE_TYPES.ForOfStatement:
239
+ return 'for...of loop';
240
+ case utils_1.AST_NODE_TYPES.WhileStatement:
241
+ return 'while loop';
242
+ case utils_1.AST_NODE_TYPES.DoWhileStatement:
243
+ return 'do...while loop';
244
+ default:
245
+ return 'this control flow';
246
+ }
247
+ }
82
248
  function isFirestoreSetterInstance(node) {
83
249
  // Check if it's a DocSetter instance
84
250
  if (node.type === utils_1.AST_NODE_TYPES.NewExpression) {
@@ -144,6 +310,7 @@ function isSetterMethodCall(node) {
144
310
  return { isValid: true, methodName, setterInstance };
145
311
  }
146
312
  const loopSetterCalls = new Map();
313
+ const reportedLoops = new Set();
147
314
  exports.preferBatchOperations = (0, createRule_1.createRule)({
148
315
  name: 'prefer-batch-operations',
149
316
  meta: {
@@ -155,8 +322,7 @@ exports.preferBatchOperations = (0, createRule_1.createRule)({
155
322
  fixable: 'code',
156
323
  schema: [],
157
324
  messages: {
158
- preferSetAll: 'Use setAll() instead of multiple set() calls for better performance',
159
- preferOverwriteAll: 'Use overwriteAll() instead of multiple overwrite() calls for better performance',
325
+ preferBatch: 'DocSetter.{{setterMethod}} is invoked repeatedly inside {{contextDescription}}, which issues separate Firestore writes per document and can leave partial updates when later calls fail. Batch the documents with DocSetter.{{batchMethod}} so the writes stay grouped and latency stays predictable.',
160
326
  },
161
327
  },
162
328
  defaultOptions: [],
@@ -165,6 +331,7 @@ exports.preferBatchOperations = (0, createRule_1.createRule)({
165
331
  'Program:exit'() {
166
332
  // Clear the maps for the next file
167
333
  loopSetterCalls.clear();
334
+ reportedLoops.clear();
168
335
  },
169
336
  CallExpression(node) {
170
337
  const { isValid, methodName, setterInstance } = isSetterMethodCall(node);
@@ -174,6 +341,13 @@ exports.preferBatchOperations = (0, createRule_1.createRule)({
174
341
  const loopInfo = findLoopNode(node);
175
342
  if (!loopInfo)
176
343
  return;
344
+ const batchMethod = methodName === 'set' ? 'setAll()' : 'overwriteAll()';
345
+ const messageData = {
346
+ setterMethod: `${methodName}()`,
347
+ batchMethod,
348
+ contextDescription: describeLoopContext(loopInfo),
349
+ };
350
+ const messageId = 'preferBatch';
177
351
  // Get or create the setter calls map for this loop
178
352
  let setterCalls = loopSetterCalls.get(loopInfo.node);
179
353
  if (!setterCalls) {
@@ -183,48 +357,67 @@ exports.preferBatchOperations = (0, createRule_1.createRule)({
183
357
  // Track setter instance and method calls for this loop
184
358
  const key = setterInstance;
185
359
  const existing = setterCalls.get(key);
360
+ // Determine if this is a traditional loop
361
+ const isTraditionalLoop = !loopInfo.isArrayMethod &&
362
+ !loopInfo.isPromiseAll &&
363
+ (loopInfo.node.type === utils_1.AST_NODE_TYPES.ForStatement ||
364
+ loopInfo.node.type === utils_1.AST_NODE_TYPES.ForInStatement ||
365
+ loopInfo.node.type === utils_1.AST_NODE_TYPES.ForOfStatement ||
366
+ loopInfo.node.type === utils_1.AST_NODE_TYPES.WhileStatement ||
367
+ loopInfo.node.type === utils_1.AST_NODE_TYPES.DoWhileStatement);
186
368
  if (existing) {
187
369
  // If we see a different method on the same setter instance, don't report
188
370
  if (existing.methodName !== methodName)
189
371
  return;
190
372
  existing.count++;
373
+ // For Promise.all contexts, report only once per loop context, on the second occurrence
374
+ if (loopInfo.isPromiseAll &&
375
+ existing.count === 2 &&
376
+ !reportedLoops.has(loopInfo.node)) {
377
+ reportedLoops.add(loopInfo.node);
378
+ context.report({
379
+ node: existing.firstNode,
380
+ messageId,
381
+ data: messageData,
382
+ fix: () => null, // We can't provide a fix because we don't know the array structure
383
+ });
384
+ }
191
385
  }
192
386
  else {
193
- setterCalls.set(key, { methodName, count: 1 });
194
- }
195
- // Report on the first occurrence of a repeated call
196
- // For Promise.all and array methods, report on the first occurrence
197
- // For regular loops, report on the first occurrence too since we know it's in a loop
198
- const shouldReport = loopInfo.isArrayMethod
199
- ? ['forEach', 'reduce', 'filter', 'map'].includes(loopInfo.isArrayMethod)
200
- : setterCalls.get(key).count === 1;
201
- // Don't report if we have multiple different setter instances in a loop
202
- // Only check this for regular loops, not array methods or Promise.all
203
- if (shouldReport && !loopInfo.isArrayMethod) {
204
- const setterInstances = new Set(Array.from(setterCalls.keys()));
205
- if (setterInstances.size > 1) {
206
- // This is a valid use case when using multiple setters in a loop
207
- // For example: userSetter.set(doc.user) and orderSetter.set(doc.order)
208
- // Each setter operates on a different collection, so they can't be batched together
209
- // We only want to report when using the same setter instance multiple times
210
- // For example: userSetter.set(doc.user) multiple times should use userSetter.setAll()
211
- if (loopInfo.node.type.startsWith('For') ||
212
- loopInfo.node.type.startsWith('While') ||
213
- loopInfo.node.type.startsWith('Do')) {
387
+ setterCalls.set(key, { methodName, count: 1, firstNode: node });
388
+ // Check if we now have multiple different setter instances in a traditional loop
389
+ if (isTraditionalLoop) {
390
+ const setterInstances = new Set(Array.from(setterCalls.keys()));
391
+ if (setterInstances.size > 1) {
392
+ // This is a valid use case when using multiple setters in a loop
393
+ // For example: userSetter.set(doc.user) and orderSetter.set(doc.order)
394
+ // Each setter operates on a different collection, so they can't be batched together
395
+ // Don't report anything for this loop
214
396
  return;
215
397
  }
398
+ // For traditional loops, report on the first occurrence since we know it's in a loop
399
+ if (!reportedLoops.has(loopInfo.node)) {
400
+ reportedLoops.add(loopInfo.node);
401
+ context.report({
402
+ node,
403
+ messageId,
404
+ data: messageData,
405
+ fix: () => null, // We can't provide a fix because we don't know the array structure
406
+ });
407
+ }
408
+ }
409
+ // For array methods, report on the first occurrence
410
+ else if (loopInfo.isArrayMethod) {
411
+ if (!reportedLoops.has(loopInfo.node)) {
412
+ reportedLoops.add(loopInfo.node);
413
+ context.report({
414
+ node,
415
+ messageId,
416
+ data: messageData,
417
+ fix: () => null, // We can't provide a fix because we don't know the array structure
418
+ });
419
+ }
216
420
  }
217
- }
218
- // Report on the first occurrence of a repeated call
219
- // For Promise.all and array methods, report on the first occurrence
220
- // For regular loops, report on the first occurrence too since we know it's in a loop
221
- if (shouldReport) {
222
- const messageId = methodName === 'set' ? 'preferSetAll' : 'preferOverwriteAll';
223
- context.report({
224
- node,
225
- messageId,
226
- fix: () => null, // We can't provide a fix because we don't know the array structure
227
- });
228
421
  }
229
422
  },
230
423
  };
@@ -16,13 +16,23 @@ exports.preferBlockCommentsForDeclarations = (0, createRule_1.createRule)({
16
16
  if (comment.type !== 'Line') {
17
17
  return false;
18
18
  }
19
- // Ignore ESLint directive comments
19
+ // Ignore ESLint & TypeScript directive comments
20
20
  const commentText = comment.value.trim();
21
- if (commentText.startsWith('eslint-disable') ||
21
+ if (
22
+ // ESLint
23
+ commentText.startsWith('eslint-disable') ||
22
24
  commentText.startsWith('eslint-enable') ||
23
25
  commentText.startsWith('eslint-env') ||
26
+ commentText.startsWith('eslint ') ||
24
27
  commentText.startsWith('global ') ||
25
- commentText.startsWith('globals ')) {
28
+ commentText.startsWith('globals ') ||
29
+ commentText.startsWith('exported ') ||
30
+ // TypeScript line directives (keep as line comments)
31
+ /^@ts-(ignore|expect-error|check|nocheck)\b/.test(commentText) ||
32
+ // TypeScript triple-slash directives (value of a 'Line' comment that started with '///')
33
+ commentText.startsWith('/ <reference') ||
34
+ commentText.startsWith('/ <amd-') ||
35
+ commentText.startsWith('/ <jsxImportSource')) {
26
36
  return false;
27
37
  }
28
38
  // Check if the comment is directly before the node
@@ -55,17 +65,22 @@ exports.preferBlockCommentsForDeclarations = (0, createRule_1.createRule)({
55
65
  if (isInsideFunctionBody(node)) {
56
66
  return;
57
67
  }
58
- const sourceCode = context.getSourceCode();
68
+ const sourceCode = context.sourceCode;
59
69
  const comments = sourceCode.getCommentsBefore(node);
60
70
  // Find the closest comment to the node
61
71
  const lastComment = comments[comments.length - 1];
62
72
  if (lastComment && isLineCommentBeforeDeclaration(lastComment, node)) {
73
+ const commentText = lastComment.value.trim();
74
+ const commentLabel = commentText || 'declaration comment';
63
75
  context.report({
64
76
  loc: lastComment.loc,
65
77
  messageId: 'preferBlockComment',
78
+ data: { commentText: commentLabel },
66
79
  fix: (fixer) => {
67
- const commentText = lastComment.value.trim();
68
- return fixer.replaceText(lastComment, `/** ${commentText} */`);
80
+ if (commentLabel.includes('*/')) {
81
+ return null;
82
+ }
83
+ return fixer.replaceText(lastComment, `/** ${commentLabel} */`);
69
84
  },
70
85
  });
71
86
  }
@@ -119,7 +134,7 @@ exports.preferBlockCommentsForDeclarations = (0, createRule_1.createRule)({
119
134
  fixable: 'code',
120
135
  schema: [],
121
136
  messages: {
122
- preferBlockComment: 'Use block comments (/** */) instead of line comments (//) for declarations to improve IDE support.',
137
+ preferBlockComment: 'Line comment "{{commentText}}" sits on a declaration, and IDE hovers and TypeScript docs ignore it as documentation. Rewrite it as a block comment (/** ... */) so the text stays attached to the declaration and tooling surfaces it.',
123
138
  },
124
139
  },
125
140
  defaultOptions: [],
@@ -14,7 +14,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
14
14
  fixable: 'code',
15
15
  schema: [],
16
16
  messages: {
17
- preferCloneDeep: 'Use cloneDeep from functions/src/util/cloneDeep.ts instead of nested spread operators for deep object copying',
17
+ preferCloneDeep: 'Nested spread copies only clone one level, so inner objects still point at the original and later mutations leak back. Use cloneDeep from functions/src/util/cloneDeep.ts and pass overrides as the second argument so the base object is deeply cloned before applying updates.',
18
18
  },
19
19
  },
20
20
  defaultOptions: [],
@@ -74,7 +74,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
74
74
  !hasSymbol);
75
75
  }
76
76
  function generateCloneDeepFix(node) {
77
- const sourceCode = context.getSourceCode();
77
+ const sourceCode = context.sourceCode;
78
78
  // Find the base object (first spread element)
79
79
  let baseObj = null;
80
80
  // Extract the base object from the first spread element
@@ -248,7 +248,7 @@ exports.preferCloneDeep = (0, createRule_1.createRule)({
248
248
  }
249
249
  // Helper function to extract nested overrides without spread elements
250
250
  function extractNestedOverrides(node) {
251
- const sourceCode = context.getSourceCode();
251
+ const sourceCode = context.sourceCode;
252
252
  const overrides = [];
253
253
  for (const prop of node.properties) {
254
254
  if (prop.type === utils_1.AST_NODE_TYPES.Property) {
@@ -1,8 +1,9 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
1
2
  type Options = [
2
3
  {
3
4
  object?: boolean;
4
5
  enforceForRenamedProperties?: boolean;
5
6
  }
6
7
  ];
7
- export declare const preferDestructuringNoClass: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferDestructuring", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
8
+ export declare const preferDestructuringNoClass: TSESLint.RuleModule<"preferDestructuring", Options, TSESLint.RuleListener>;
8
9
  export {};