@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,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firestoreTransactionReadsBeforeWrites = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ // Define the operations that are considered reads and writes
7
+ const READ_OPERATIONS = new Set(['get']);
8
+ const WRITE_OPERATIONS = new Set(['set', 'update', 'delete']);
9
+ exports.firestoreTransactionReadsBeforeWrites = (0, createRule_1.createRule)({
10
+ name: 'firestore-transaction-reads-before-writes',
11
+ meta: {
12
+ type: 'problem',
13
+ docs: {
14
+ description: 'Enforce that all Firestore transaction read operations are performed before any write operations',
15
+ recommended: 'error',
16
+ },
17
+ schema: [],
18
+ messages: {
19
+ readsAfterWrites: 'Read operation "{{readMethod}}" runs after transaction writes ({{writeMethods}}). Firestore transactions must collect every read before any write to keep a consistent snapshot and avoid Firestore retries that reapply writes with stale data. Move this read before the first write in the same transaction callback.',
20
+ },
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ // Track transaction objects and their operations
25
+ const transactionScopes = new Map();
26
+ // Helper to check if a node is a transaction parameter
27
+ function isTransactionParameter(param) {
28
+ if (param.type !== utils_1.AST_NODE_TYPES.Identifier)
29
+ return false;
30
+ // Check for type annotation
31
+ const typeAnnotation = param.typeAnnotation;
32
+ if (!typeAnnotation ||
33
+ typeAnnotation.type !== utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
34
+ // If no type annotation, check the parameter name for common patterns
35
+ // Include 't' as a valid transaction parameter name
36
+ return /^(transaction|tx|t)$/i.test(param.name);
37
+ }
38
+ const type = typeAnnotation.typeAnnotation;
39
+ // Check for Transaction type
40
+ if (type.type === utils_1.AST_NODE_TYPES.TSTypeReference) {
41
+ const typeName = type.typeName;
42
+ // Check for FirebaseFirestore.Transaction
43
+ if (typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
44
+ return (typeName.left.type === utils_1.AST_NODE_TYPES.Identifier &&
45
+ typeName.left.name === 'FirebaseFirestore' &&
46
+ typeName.right.type === utils_1.AST_NODE_TYPES.Identifier &&
47
+ typeName.right.name === 'Transaction');
48
+ }
49
+ // Check for Transaction or FirestoreTransaction
50
+ if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
51
+ return /Transaction/i.test(typeName.name);
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+ // Helper to find the transaction scope for a node
57
+ function findTransactionScope(node) {
58
+ let current = node;
59
+ while (current) {
60
+ // Check if this node is a transaction scope
61
+ if (transactionScopes.has(current)) {
62
+ return current;
63
+ }
64
+ // Check if this is a function with a transaction parameter
65
+ if (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
66
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
67
+ current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
68
+ const params = current.params;
69
+ if (params.some(isTransactionParameter)) {
70
+ // If this function has a transaction parameter, track it
71
+ if (!transactionScopes.has(current.body)) {
72
+ const transactionParam = params.find(isTransactionParameter);
73
+ const transactionVariables = new Set();
74
+ if (transactionParam &&
75
+ transactionParam.type === utils_1.AST_NODE_TYPES.Identifier) {
76
+ transactionVariables.add(transactionParam.name);
77
+ }
78
+ transactionScopes.set(current.body, {
79
+ hasWriteOperation: false,
80
+ writeNodes: [],
81
+ transactionVariables,
82
+ });
83
+ }
84
+ return current.body;
85
+ }
86
+ }
87
+ current = current.parent;
88
+ }
89
+ return null;
90
+ }
91
+ // Helper to check if a node is a transaction method call
92
+ function isTransactionMethodCall(node, transactionScope) {
93
+ const callee = node.callee;
94
+ if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
95
+ return { isRead: false, isWrite: false, methodName: null };
96
+ }
97
+ const object = callee.object;
98
+ const property = callee.property;
99
+ // Check if the object is a transaction
100
+ if (object.type !== utils_1.AST_NODE_TYPES.Identifier) {
101
+ return { isRead: false, isWrite: false, methodName: null };
102
+ }
103
+ // If we have a transaction scope, check if the object name is a known transaction variable
104
+ if (transactionScope) {
105
+ const scopeInfo = transactionScopes.get(transactionScope);
106
+ if (scopeInfo && !scopeInfo.transactionVariables.has(object.name)) {
107
+ return { isRead: false, isWrite: false, methodName: null };
108
+ }
109
+ }
110
+ else {
111
+ // Fallback to general pattern matching
112
+ if (!/^(transaction|tx|t)$/i.test(object.name)) {
113
+ return { isRead: false, isWrite: false, methodName: null };
114
+ }
115
+ }
116
+ let methodName = null;
117
+ // Check if the property is a read or write operation
118
+ if (property.type === utils_1.AST_NODE_TYPES.Identifier && !callee.computed) {
119
+ // Normal property access: transaction.get()
120
+ methodName = property.name;
121
+ }
122
+ else if (callee.computed &&
123
+ property.type === utils_1.AST_NODE_TYPES.Literal &&
124
+ typeof property.value === 'string') {
125
+ // Computed property access with string literal: transaction['get']
126
+ methodName = property.value;
127
+ }
128
+ else if (callee.computed &&
129
+ property.type === utils_1.AST_NODE_TYPES.Identifier) {
130
+ // Computed property access with variable: transaction[methodName]
131
+ // This is tricky to analyze statically. For now, we'll be conservative
132
+ // and assume it could be any method. We'll handle this in the caller.
133
+ return { isRead: true, isWrite: true, methodName: null }; // Could be either - let caller decide
134
+ }
135
+ if (!methodName) {
136
+ return { isRead: false, isWrite: false, methodName: null };
137
+ }
138
+ const isRead = READ_OPERATIONS.has(methodName);
139
+ const isWrite = WRITE_OPERATIONS.has(methodName);
140
+ return { isRead, isWrite, methodName };
141
+ }
142
+ function formatMethodName(callExpression) {
143
+ const callee = callExpression.callee;
144
+ if (callee.type !== utils_1.AST_NODE_TYPES.MemberExpression) {
145
+ return 'this transaction read';
146
+ }
147
+ const objectName = callee.object.type === utils_1.AST_NODE_TYPES.Identifier
148
+ ? callee.object.name
149
+ : 'transaction';
150
+ if (callee.computed) {
151
+ if (callee.property.type === utils_1.AST_NODE_TYPES.Literal) {
152
+ return `${objectName}[${String(callee.property.value)}]`;
153
+ }
154
+ if (callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
155
+ return `${objectName}[${callee.property.name}]`;
156
+ }
157
+ return `${objectName}[computed]`;
158
+ }
159
+ if (callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
160
+ return `${objectName}.${callee.property.name}`;
161
+ }
162
+ return `${objectName}[unknown]`;
163
+ }
164
+ function formatWriteMethods(writeNodes) {
165
+ const methodNames = writeNodes
166
+ .map((node) => node.type === utils_1.AST_NODE_TYPES.CallExpression
167
+ ? formatMethodName(node)
168
+ : null)
169
+ .filter((name) => Boolean(name));
170
+ if (!methodNames.length) {
171
+ return 'earlier transaction writes';
172
+ }
173
+ const uniqueNames = [...new Set(methodNames)];
174
+ if (uniqueNames.length === 1) {
175
+ return uniqueNames[0];
176
+ }
177
+ if (uniqueNames.length === 2) {
178
+ return `${uniqueNames[0]} and ${uniqueNames[1]}`;
179
+ }
180
+ return `${uniqueNames
181
+ .slice(0, uniqueNames.length - 1)
182
+ .join(', ')} and ${uniqueNames[uniqueNames.length - 1]}`;
183
+ }
184
+ return {
185
+ // Track transaction scopes from runTransaction calls
186
+ 'CallExpression[callee.property.name="runTransaction"]'(node) {
187
+ const callback = node.arguments[0];
188
+ if (callback &&
189
+ (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
190
+ callback.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
191
+ const transactionParam = callback.params.find(isTransactionParameter);
192
+ const transactionVariables = new Set();
193
+ if (transactionParam &&
194
+ transactionParam.type === utils_1.AST_NODE_TYPES.Identifier) {
195
+ transactionVariables.add(transactionParam.name);
196
+ }
197
+ transactionScopes.set(callback.body, {
198
+ hasWriteOperation: false,
199
+ writeNodes: [],
200
+ transactionVariables,
201
+ });
202
+ }
203
+ },
204
+ // Clean up transaction scopes when exiting runTransaction calls
205
+ 'CallExpression[callee.property.name="runTransaction"]:exit'(node) {
206
+ const callback = node.arguments[0];
207
+ if (callback &&
208
+ (callback.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
209
+ callback.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
210
+ transactionScopes.delete(callback.body);
211
+ }
212
+ },
213
+ // Track variable assignments that reference the transaction
214
+ VariableDeclarator(node) {
215
+ if (node.id.type === utils_1.AST_NODE_TYPES.Identifier &&
216
+ node.init &&
217
+ node.init.type === utils_1.AST_NODE_TYPES.Identifier) {
218
+ const transactionScope = findTransactionScope(node);
219
+ if (transactionScope) {
220
+ const scopeInfo = transactionScopes.get(transactionScope);
221
+ if (scopeInfo &&
222
+ scopeInfo.transactionVariables.has(node.init.name)) {
223
+ scopeInfo.transactionVariables.add(node.id.name);
224
+ }
225
+ }
226
+ }
227
+ },
228
+ // Check all call expressions for transaction operations
229
+ CallExpression(node) {
230
+ const transactionScope = findTransactionScope(node);
231
+ const { isRead, isWrite } = isTransactionMethodCall(node, transactionScope);
232
+ if (!isRead && !isWrite) {
233
+ return;
234
+ }
235
+ if (!transactionScope) {
236
+ return;
237
+ }
238
+ const scopeInfo = transactionScopes.get(transactionScope);
239
+ if (!scopeInfo) {
240
+ return;
241
+ }
242
+ // Handle computed property access specially
243
+ if (isRead && isWrite) {
244
+ // This is a computed property access - we need to be more careful
245
+ // Only flag as a read if we already have writes in this scope
246
+ if (scopeInfo.hasWriteOperation) {
247
+ const readMethod = formatMethodName(node);
248
+ const writeMethods = formatWriteMethods(scopeInfo.writeNodes);
249
+ context.report({
250
+ node,
251
+ messageId: 'readsAfterWrites',
252
+ data: { readMethod, writeMethods },
253
+ });
254
+ }
255
+ // Don't mark as write since we don't know what method it is
256
+ return;
257
+ }
258
+ // If it's a write operation, mark the scope as having a write
259
+ if (isWrite) {
260
+ scopeInfo.hasWriteOperation = true;
261
+ scopeInfo.writeNodes.push(node);
262
+ }
263
+ // If it's a read operation and the scope already has a write, report an error
264
+ if (isRead && scopeInfo.hasWriteOperation) {
265
+ const readMethod = formatMethodName(node);
266
+ const writeMethods = formatWriteMethods(scopeInfo.writeNodes);
267
+ context.report({
268
+ node,
269
+ messageId: 'readsAfterWrites',
270
+ data: { readMethod, writeMethods },
271
+ });
272
+ }
273
+ },
274
+ };
275
+ },
276
+ });
277
+ //# sourceMappingURL=firestore-transaction-reads-before-writes.js.map
@@ -0,0 +1,2 @@
1
+ import { TSESLint } from '@typescript-eslint/utils';
2
+ export declare const flattenPushCalls: TSESLint.RuleModule<"flattenPushCalls", [], TSESLint.RuleListener>;