@blumintinc/eslint-plugin-blumint 1.12.6 → 1.13.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 (141) hide show
  1. package/README.md +140 -99
  2. package/lib/index.js +111 -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/dynamic-https-errors.d.ts +3 -1
  6. package/lib/rules/dynamic-https-errors.js +48 -10
  7. package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
  8. package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +51 -95
  9. package/lib/rules/enforce-assert-throws.d.ts +3 -1
  10. package/lib/rules/enforce-assert-throws.js +493 -51
  11. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
  12. package/lib/rules/enforce-boolean-naming-prefixes.js +832 -39
  13. package/lib/rules/enforce-callable-types.js +4 -4
  14. package/lib/rules/enforce-callback-memo.js +143 -18
  15. package/lib/rules/enforce-centralized-mock-firestore.js +4 -1
  16. package/lib/rules/enforce-console-error.d.ts +3 -0
  17. package/lib/rules/enforce-console-error.js +250 -0
  18. package/lib/rules/enforce-css-media-queries.js +24 -46
  19. package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
  20. package/lib/rules/enforce-dynamic-imports.js +1 -1
  21. package/lib/rules/enforce-exported-function-types.js +3 -3
  22. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
  23. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +212 -0
  24. package/lib/rules/enforce-firestore-doc-ref-generic.js +335 -0
  25. package/lib/rules/enforce-firestore-facade.js +347 -157
  26. package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
  27. package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
  28. package/lib/rules/enforce-global-constants.d.ts +4 -1
  29. package/lib/rules/enforce-global-constants.js +240 -12
  30. package/lib/rules/enforce-memoize-async.d.ts +2 -1
  31. package/lib/rules/enforce-memoize-async.js +96 -27
  32. package/lib/rules/enforce-memoize-getters.d.ts +3 -0
  33. package/lib/rules/enforce-memoize-getters.js +123 -0
  34. package/lib/rules/enforce-microdiff.js +2 -1
  35. package/lib/rules/enforce-positive-naming.js +5 -7
  36. package/lib/rules/enforce-props-argument-name.d.ts +2 -7
  37. package/lib/rules/enforce-props-argument-name.js +165 -151
  38. package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
  39. package/lib/rules/enforce-props-naming-consistency.js +145 -0
  40. package/lib/rules/enforce-querykey-ts.d.ts +9 -0
  41. package/lib/rules/enforce-querykey-ts.js +274 -0
  42. package/lib/rules/enforce-singular-type-names.js +9 -5
  43. package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
  44. package/lib/rules/enforce-typescript-markdown-code-blocks.js +96 -0
  45. package/lib/rules/enforce-verb-noun-naming.js +85 -10
  46. package/lib/rules/extract-global-constants.js +57 -45
  47. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
  48. package/lib/rules/fast-deep-equal-over-microdiff.js +356 -78
  49. package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
  50. package/lib/rules/firestore-transaction-reads-before-writes.js +231 -0
  51. package/lib/rules/generic-starts-with-t.js +8 -2
  52. package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
  53. package/lib/rules/memoize-root-level-hocs.js +242 -0
  54. package/lib/rules/no-array-length-in-deps.d.ts +12 -0
  55. package/lib/rules/no-array-length-in-deps.js +308 -0
  56. package/lib/rules/no-async-array-filter.js +5 -2
  57. package/lib/rules/no-complex-cloud-params.js +15 -2
  58. package/lib/rules/no-compositing-layer-props.js +4 -2
  59. package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
  60. package/lib/rules/no-entire-object-hook-deps.js +317 -63
  61. package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
  62. package/lib/rules/no-excessive-parent-chain.js +273 -0
  63. package/lib/rules/no-firestore-object-arrays.js +345 -30
  64. package/lib/rules/no-hungarian.js +13 -13
  65. package/lib/rules/no-margin-properties.d.ts +7 -1
  66. package/lib/rules/no-margin-properties.js +16 -3
  67. package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
  68. package/lib/rules/no-misleading-boolean-prefixes.js +327 -0
  69. package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
  70. package/lib/rules/no-overridable-method-calls-in-constructor.js +260 -0
  71. package/lib/rules/no-passthrough-getters.d.ts +4 -0
  72. package/lib/rules/no-passthrough-getters.js +170 -0
  73. package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
  74. package/lib/rules/no-redundant-usecallback-wrapper.js +315 -0
  75. package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
  76. package/lib/rules/no-restricted-properties-fix.js +172 -0
  77. package/lib/rules/no-separate-loading-state.d.ts +8 -0
  78. package/lib/rules/no-separate-loading-state.js +142 -0
  79. package/lib/rules/no-stale-state-across-await.d.ts +1 -0
  80. package/lib/rules/no-stale-state-across-await.js +185 -0
  81. package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
  82. package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
  83. package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
  84. package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
  85. package/lib/rules/no-type-assertion-returns.js +6 -0
  86. package/lib/rules/no-undefined-null-passthrough.js +166 -19
  87. package/lib/rules/no-unnecessary-verb-suffix.js +0 -2
  88. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  89. package/lib/rules/no-unused-props.d.ts +3 -0
  90. package/lib/rules/no-unused-props.js +213 -78
  91. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  92. package/lib/rules/optimize-object-boolean-conditions.js +234 -0
  93. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  94. package/lib/rules/parallelize-async-operations.js +375 -0
  95. package/lib/rules/prefer-batch-operations.js +199 -33
  96. package/lib/rules/prefer-block-comments-for-declarations.js +13 -3
  97. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  98. package/lib/rules/prefer-document-flattening.js +239 -0
  99. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  100. package/lib/rules/prefer-field-paths-in-transforms.js +250 -0
  101. package/lib/rules/prefer-fragment-component.js +3 -3
  102. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  103. package/lib/rules/prefer-global-router-state-key.js +230 -31
  104. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  105. package/lib/rules/prefer-next-dynamic.js +366 -0
  106. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  107. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +430 -0
  108. package/lib/rules/prefer-nullish-coalescing-override.d.ts +6 -0
  109. package/lib/rules/prefer-nullish-coalescing-override.js +182 -0
  110. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  111. package/lib/rules/prefer-params-over-parent-id.js +228 -0
  112. package/lib/rules/prefer-settings-object.js +23 -15
  113. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  114. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  115. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  116. package/lib/rules/prefer-url-tostring-over-tojson.js +166 -0
  117. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  118. package/lib/rules/prefer-use-deep-compare-memo.js +428 -0
  119. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +24 -1
  120. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  121. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  122. package/lib/rules/react-usememo-should-be-component.js +144 -78
  123. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  124. package/lib/rules/require-image-optimized.js +1 -1
  125. package/lib/rules/require-memo.js +11 -1
  126. package/lib/rules/require-usememo-object-literals.js +7 -1
  127. package/lib/rules/semantic-function-prefixes.js +38 -18
  128. package/lib/rules/sync-onwrite-name-func.js +5 -1
  129. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  130. package/lib/rules/test-file-location-enforcement.js +57 -0
  131. package/lib/rules/use-custom-link.js +6 -1
  132. package/lib/rules/use-custom-router.js +6 -1
  133. package/lib/rules/use-latest-callback.d.ts +3 -0
  134. package/lib/rules/use-latest-callback.js +270 -0
  135. package/lib/utils/ASTHelpers.js +10 -0
  136. package/lib/utils/createRule.js +1 -1
  137. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  138. package/lib/utils/ruleTester.d.ts +1 -0
  139. package/lib/utils/ruleTester.js +4 -1
  140. package/package.json +21 -14
  141. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
@@ -0,0 +1,430 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preferNullishCoalescingBooleanProps = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const BOOLEAN_PROP_REGEX = /^(is|has|should|can|will|do|does|did|was|were|enable|disable)/;
7
+ function isInJSXBooleanAttribute(node) {
8
+ const parent = node.parent;
9
+ if (parent?.type !== utils_1.AST_NODE_TYPES.JSXAttribute)
10
+ return false;
11
+ const attributeName = parent.name.name;
12
+ const booleanPropNames = [
13
+ 'disabled',
14
+ 'required',
15
+ 'checked',
16
+ 'selected',
17
+ 'readOnly',
18
+ 'autoFocus',
19
+ 'autoPlay',
20
+ 'controls',
21
+ 'default',
22
+ 'defer',
23
+ 'hidden',
24
+ 'isOpen',
25
+ 'loop',
26
+ 'multiple',
27
+ 'muted',
28
+ 'noValidate',
29
+ 'open',
30
+ 'scoped',
31
+ 'seamless',
32
+ 'itemScope',
33
+ 'allowFullScreen',
34
+ 'async',
35
+ 'autofocus',
36
+ 'autoplay',
37
+ 'formNoValidate',
38
+ 'spellcheck',
39
+ 'translate',
40
+ ];
41
+ return (typeof attributeName === 'string' &&
42
+ (booleanPropNames.includes(attributeName) ||
43
+ BOOLEAN_PROP_REGEX.test(attributeName)));
44
+ }
45
+ function isInConditionalContext(node) {
46
+ const parent = node.parent;
47
+ if (!parent)
48
+ return false;
49
+ return ((parent.type === utils_1.AST_NODE_TYPES.IfStatement && node === parent.test) ||
50
+ (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
51
+ node === parent.test) ||
52
+ (parent.type === utils_1.AST_NODE_TYPES.WhileStatement && node === parent.test) ||
53
+ (parent.type === utils_1.AST_NODE_TYPES.ForStatement && node === parent.test) ||
54
+ (parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement && node === parent.test) ||
55
+ (parent.type === utils_1.AST_NODE_TYPES.SwitchCase && node === parent.test));
56
+ }
57
+ /**
58
+ * Determines if a node is within a boolean context in JSX props or other boolean contexts
59
+ */
60
+ function isInBooleanContext(node) {
61
+ let current = node;
62
+ // Traverse up the AST to find if we're in a boolean context
63
+ while (current && current.parent) {
64
+ if (isInJSXBooleanAttribute(current))
65
+ return true;
66
+ if (isInConditionalContext(current))
67
+ return true;
68
+ // If we're in a logical expression that's part of a boolean context
69
+ if (current.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
70
+ (current.parent.operator === '&&' || current.parent.operator === '||')) {
71
+ // Continue up the tree to check if the parent logical expression is in a boolean context
72
+ current = current.parent;
73
+ continue;
74
+ }
75
+ // If we're in a unary expression with a boolean operator
76
+ if (current.parent.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
77
+ current.parent.operator === '!') {
78
+ return true;
79
+ }
80
+ // If we're in a conditional expression (ternary)
81
+ if (current.parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression &&
82
+ current === current.parent.test) {
83
+ return true;
84
+ }
85
+ // If we're in a variable declaration that has a boolean-like name
86
+ if (current.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
87
+ current.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
88
+ const variableName = current.parent.id.name;
89
+ if (/^(is|has|should|can|will|do|does|did|was|were)/.test(variableName)) {
90
+ return true;
91
+ }
92
+ }
93
+ // If we're in a while loop condition
94
+ if (current.parent.type === utils_1.AST_NODE_TYPES.WhileStatement &&
95
+ current === current.parent.test) {
96
+ return true;
97
+ }
98
+ // If we're in a for loop condition
99
+ if (current.parent.type === utils_1.AST_NODE_TYPES.ForStatement &&
100
+ current === current.parent.test) {
101
+ return true;
102
+ }
103
+ // If we're in a do-while loop condition
104
+ if (current.parent.type === utils_1.AST_NODE_TYPES.DoWhileStatement &&
105
+ current === current.parent.test) {
106
+ return true;
107
+ }
108
+ // If we're in a function return statement with a boolean-like function name
109
+ if (current.parent.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
110
+ // Find the function that contains this return statement
111
+ let functionNode = current.parent.parent;
112
+ let functionName = '';
113
+ // Handle different function types
114
+ if (functionNode &&
115
+ functionNode.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
116
+ functionNode.id) {
117
+ functionName = functionNode.id.name;
118
+ }
119
+ else if (functionNode &&
120
+ functionNode.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
121
+ // For function expressions, check the parent context
122
+ if (functionNode.parent &&
123
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
124
+ functionNode.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
125
+ functionName = functionNode.parent.id.name;
126
+ }
127
+ else if (functionNode.parent &&
128
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.Property &&
129
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
130
+ functionName = functionNode.parent.key.name;
131
+ }
132
+ else if (functionNode.parent &&
133
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
134
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
135
+ functionName = functionNode.parent.key.name;
136
+ }
137
+ }
138
+ else if (functionNode &&
139
+ functionNode.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
140
+ // For arrow functions, check the parent context
141
+ if (functionNode.parent &&
142
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
143
+ functionNode.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
144
+ functionName = functionNode.parent.id.name;
145
+ }
146
+ else if (functionNode.parent &&
147
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.Property &&
148
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
149
+ functionName = functionNode.parent.key.name;
150
+ }
151
+ else if (functionNode.parent &&
152
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
153
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
154
+ functionName = functionNode.parent.key.name;
155
+ }
156
+ }
157
+ else if (functionNode &&
158
+ functionNode.type === utils_1.AST_NODE_TYPES.BlockStatement) {
159
+ // Handle case where return is in a block statement
160
+ functionNode = functionNode.parent;
161
+ if (functionNode &&
162
+ functionNode.type === utils_1.AST_NODE_TYPES.FunctionDeclaration &&
163
+ functionNode.id) {
164
+ functionName = functionNode.id.name;
165
+ }
166
+ else if (functionNode &&
167
+ functionNode.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
168
+ if (functionNode.parent &&
169
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
170
+ functionNode.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
171
+ functionName = functionNode.parent.id.name;
172
+ }
173
+ else if (functionNode.parent &&
174
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.Property &&
175
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
176
+ functionName = functionNode.parent.key.name;
177
+ }
178
+ else if (functionNode.parent &&
179
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
180
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
181
+ functionName = functionNode.parent.key.name;
182
+ }
183
+ }
184
+ else if (functionNode &&
185
+ functionNode.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
186
+ if (functionNode.parent &&
187
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
188
+ functionNode.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
189
+ functionName = functionNode.parent.id.name;
190
+ }
191
+ else if (functionNode.parent &&
192
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.Property &&
193
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
194
+ functionName = functionNode.parent.key.name;
195
+ }
196
+ else if (functionNode.parent &&
197
+ functionNode.parent.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
198
+ functionNode.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
199
+ functionName = functionNode.parent.key.name;
200
+ }
201
+ }
202
+ }
203
+ if (functionName &&
204
+ /^(is|has|should|can|will|do|does|did|was|were|check|validate)/.test(functionName)) {
205
+ return true;
206
+ }
207
+ }
208
+ // If we're directly in an arrow function body (without explicit return) with boolean-like name
209
+ if (current.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
210
+ let functionName = '';
211
+ if (current.parent.parent &&
212
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
213
+ current.parent.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
214
+ functionName = current.parent.parent.id.name;
215
+ }
216
+ else if (current.parent.parent &&
217
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.Property &&
218
+ current.parent.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
219
+ functionName = current.parent.parent.key.name;
220
+ }
221
+ if (functionName &&
222
+ /^(is|has|should|can|will|do|does|did|was|were|check|validate)/.test(functionName)) {
223
+ return true;
224
+ }
225
+ }
226
+ // If we're in a conditional rendering context (JSX && operator)
227
+ if (current.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
228
+ current.parent.operator === '&&' &&
229
+ current.parent.parent &&
230
+ (current.parent.parent.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer ||
231
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.ReturnStatement)) {
232
+ return true;
233
+ }
234
+ // If we're the left side of a && operator that's used for conditional rendering
235
+ if (current.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
236
+ current.parent.operator === '&&' &&
237
+ current === current.parent.left &&
238
+ current.parent.parent &&
239
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
240
+ return true;
241
+ }
242
+ // If we're in a logical expression that will be used for conditional rendering
243
+ if (current.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
244
+ current.parent.operator === '&&' &&
245
+ current.parent.parent &&
246
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
247
+ current.parent.right &&
248
+ current.parent.right.type === utils_1.AST_NODE_TYPES.JSXElement) {
249
+ return true;
250
+ }
251
+ // If we're inside parentheses that are the left side of a && operator for conditional rendering
252
+ if (current.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
253
+ current.parent.parent &&
254
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
255
+ current.parent.parent.operator === '&&' &&
256
+ current.parent.parent.parent &&
257
+ current.parent.parent.parent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
258
+ current.parent.parent.right &&
259
+ current.parent.parent.right.type === utils_1.AST_NODE_TYPES.JSXElement) {
260
+ return true;
261
+ }
262
+ // Check if we're in a logical expression that's eventually used for conditional rendering
263
+ let tempParent = current.parent;
264
+ while (tempParent) {
265
+ if (tempParent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
266
+ tempParent.operator === '&&' &&
267
+ tempParent.right &&
268
+ tempParent.right.type === utils_1.AST_NODE_TYPES.JSXElement) {
269
+ return true;
270
+ }
271
+ tempParent = tempParent.parent;
272
+ }
273
+ // If we're in a switch case
274
+ if (current.parent.type === utils_1.AST_NODE_TYPES.SwitchCase &&
275
+ current === current.parent.test) {
276
+ return true;
277
+ }
278
+ // If we're in array method callbacks that expect boolean returns
279
+ if (current.parent.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
280
+ current.parent.parent &&
281
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
282
+ current.parent.parent.parent &&
283
+ current.parent.parent.parent.type === utils_1.AST_NODE_TYPES.CallExpression &&
284
+ current.parent.parent.parent.callee.type ===
285
+ utils_1.AST_NODE_TYPES.MemberExpression &&
286
+ current.parent.parent.parent.callee.property.type ===
287
+ utils_1.AST_NODE_TYPES.Identifier) {
288
+ const methodName = current.parent.parent.parent.callee.property.name;
289
+ if (['filter', 'some', 'every', 'find', 'findIndex'].includes(methodName)) {
290
+ return true;
291
+ }
292
+ }
293
+ // If we're directly in array method callbacks (arrow function body without return)
294
+ if (current.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
295
+ current.parent.parent &&
296
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.CallExpression &&
297
+ current.parent.parent.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
298
+ current.parent.parent.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
299
+ const methodName = current.parent.parent.callee.property.name;
300
+ if (['filter', 'some', 'every', 'find', 'findIndex'].includes(methodName)) {
301
+ return true;
302
+ }
303
+ }
304
+ // If we're in an object property with a boolean-like name
305
+ if (current.parent.type === utils_1.AST_NODE_TYPES.Property &&
306
+ current.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
307
+ const propertyName = current.parent.key.name;
308
+ if (/^(is|has|should|can|will|do|does|did|was|were|enable|disable|validate)/.test(propertyName)) {
309
+ return true;
310
+ }
311
+ }
312
+ // If we're in destructuring assignment with boolean-like name
313
+ if (current.parent.type === utils_1.AST_NODE_TYPES.AssignmentPattern &&
314
+ current.parent.parent &&
315
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.Property &&
316
+ current.parent.parent.key.type === utils_1.AST_NODE_TYPES.Identifier) {
317
+ const propertyName = current.parent.parent.key.name;
318
+ if (/^(is|has|should|can|will|do|does|did|was|were)/.test(propertyName)) {
319
+ return true;
320
+ }
321
+ }
322
+ // If we're in a function call argument for useState with boolean-like variable name
323
+ if (current.parent.type === utils_1.AST_NODE_TYPES.CallExpression &&
324
+ current.parent.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
325
+ current.parent.callee.name === 'useState' &&
326
+ current.parent.parent &&
327
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
328
+ current.parent.parent.id.type === utils_1.AST_NODE_TYPES.ArrayPattern &&
329
+ current.parent.parent.id.elements.length > 0 &&
330
+ current.parent.parent.id.elements[0] &&
331
+ current.parent.parent.id.elements[0].type === utils_1.AST_NODE_TYPES.Identifier) {
332
+ const variableName = current.parent.parent.id.elements[0].name;
333
+ if (/^(is|has|should|can|will|do|does|did|was|were|ready|valid|loading|error|complete|active|enabled|disabled|visible|hidden)/.test(variableName)) {
334
+ return true;
335
+ }
336
+ }
337
+ // If we're in an event handler (arrow function in JSX prop)
338
+ if (current.parent.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
339
+ current.parent.operator === '&&' &&
340
+ current.parent.parent &&
341
+ current.parent.parent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
342
+ current.parent.parent.parent &&
343
+ current.parent.parent.parent.type ===
344
+ utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
345
+ current.parent.parent.parent.parent &&
346
+ current.parent.parent.parent.parent.type === utils_1.AST_NODE_TYPES.JSXAttribute) {
347
+ return true;
348
+ }
349
+ // If we're in a logical expression inside an arrow function that's in a JSX attribute
350
+ let tempCurrent = current.parent;
351
+ while (tempCurrent) {
352
+ if (tempCurrent.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
353
+ tempCurrent.parent &&
354
+ tempCurrent.parent.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
355
+ tempCurrent.parent.parent &&
356
+ tempCurrent.parent.parent.type === utils_1.AST_NODE_TYPES.JSXAttribute) {
357
+ return true;
358
+ }
359
+ tempCurrent = tempCurrent.parent;
360
+ }
361
+ current = current.parent;
362
+ }
363
+ return false;
364
+ }
365
+ /**
366
+ * Checks if the left operand could be nullish (null or undefined)
367
+ */
368
+ function couldBeNullish(node) {
369
+ // For literals, check the actual value
370
+ if (node.type === utils_1.AST_NODE_TYPES.Literal) {
371
+ return node.value === null || node.value === undefined;
372
+ }
373
+ if (node.type === utils_1.AST_NODE_TYPES.Identifier && node.name === 'undefined') {
374
+ return true;
375
+ }
376
+ if (node.type === utils_1.AST_NODE_TYPES.NewExpression ||
377
+ node.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
378
+ node.type === utils_1.AST_NODE_TYPES.ObjectExpression ||
379
+ node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
380
+ node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
381
+ node.type === utils_1.AST_NODE_TYPES.ClassExpression ||
382
+ (node.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
383
+ node.expressions.length === 0)) {
384
+ return false;
385
+ }
386
+ // For other expressions, conservatively assume they could be nullish
387
+ return true;
388
+ }
389
+ exports.preferNullishCoalescingBooleanProps = (0, createRule_1.createRule)({
390
+ name: 'prefer-nullish-coalescing-boolean-props',
391
+ meta: {
392
+ type: 'suggestion',
393
+ docs: {
394
+ description: 'Prefer nullish coalescing over logical OR, but allow logical OR in boolean contexts',
395
+ recommended: 'error',
396
+ },
397
+ fixable: 'code',
398
+ messages: {
399
+ preferNullishCoalescing: 'Prefer using nullish coalescing operator (??) instead of logical OR operator (||) when checking for null/undefined',
400
+ },
401
+ schema: [],
402
+ },
403
+ defaultOptions: [],
404
+ create(context) {
405
+ return {
406
+ LogicalExpression(node) {
407
+ if (node.operator === '||') {
408
+ // If the node is in a boolean context, we allow logical OR
409
+ if (isInBooleanContext(node)) {
410
+ return;
411
+ }
412
+ // Check if this could benefit from nullish coalescing
413
+ // We only suggest nullish coalescing when the left operand could be nullish
414
+ if (couldBeNullish(node.left)) {
415
+ context.report({
416
+ node,
417
+ messageId: 'preferNullishCoalescing',
418
+ fix(fixer) {
419
+ return fixer.replaceText(node, `${context.getSourceCode().getText(node.left)} ?? ${context
420
+ .getSourceCode()
421
+ .getText(node.right)}`);
422
+ },
423
+ });
424
+ }
425
+ }
426
+ },
427
+ };
428
+ },
429
+ });
430
+ //# sourceMappingURL=prefer-nullish-coalescing-boolean-props.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * This rule overrides the behavior of @typescript-eslint/prefer-nullish-coalescing
3
+ * to only suggest using the nullish coalescing operator when checking for null/undefined,
4
+ * not when intentionally checking for all falsy values.
5
+ */
6
+ export declare const preferNullishCoalescingOverride: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferNullishCoalescing", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preferNullishCoalescingOverride = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ /**
7
+ * This rule overrides the behavior of @typescript-eslint/prefer-nullish-coalescing
8
+ * to only suggest using the nullish coalescing operator when checking for null/undefined,
9
+ * not when intentionally checking for all falsy values.
10
+ */
11
+ exports.preferNullishCoalescingOverride = (0, createRule_1.createRule)({
12
+ name: 'prefer-nullish-coalescing-override',
13
+ meta: {
14
+ type: 'suggestion',
15
+ docs: {
16
+ description: 'Enforce using nullish coalescing operator instead of logical OR operator, but only when appropriate',
17
+ recommended: 'warn',
18
+ },
19
+ fixable: 'code',
20
+ schema: [],
21
+ messages: {
22
+ preferNullishCoalescing: 'Prefer using nullish coalescing operator (`??`) instead of logical OR operator (`||`) when only checking for null/undefined.',
23
+ },
24
+ },
25
+ defaultOptions: [],
26
+ create(context) {
27
+ /**
28
+ * Checks if a node is in a boolean context where truthiness matters
29
+ */
30
+ function isInBooleanContext(node) {
31
+ const parent = node.parent;
32
+ if (!parent)
33
+ return false;
34
+ switch (parent.type) {
35
+ // Direct boolean contexts
36
+ case utils_1.AST_NODE_TYPES.IfStatement:
37
+ case utils_1.AST_NODE_TYPES.WhileStatement:
38
+ case utils_1.AST_NODE_TYPES.DoWhileStatement:
39
+ case utils_1.AST_NODE_TYPES.ForStatement:
40
+ return parent.test === node;
41
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
42
+ return parent.test === node;
43
+ // Logical expressions (&&, ||, !)
44
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
45
+ return true;
46
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
47
+ return parent.operator === '!';
48
+ // JSX expressions that expect boolean values
49
+ case utils_1.AST_NODE_TYPES.JSXExpressionContainer:
50
+ return isJSXBooleanContext(parent);
51
+ default:
52
+ return false;
53
+ }
54
+ }
55
+ /**
56
+ * Checks if a JSX expression container is in a boolean context
57
+ */
58
+ function isJSXBooleanContext(jsxContainer) {
59
+ const parent = jsxContainer.parent;
60
+ if (!parent)
61
+ return false;
62
+ // Check if used as a child expression (conditional rendering or child selection).
63
+ // Other guards (rightOperandSuggestsFalsyHandling, etc.) will prevent unsafe conversions.
64
+ if (parent.type === utils_1.AST_NODE_TYPES.JSXElement ||
65
+ parent.type === utils_1.AST_NODE_TYPES.JSXFragment) {
66
+ return true;
67
+ }
68
+ return false;
69
+ }
70
+ /**
71
+ * Checks if the right operand suggests we want to handle all falsy values
72
+ */
73
+ function rightOperandSuggestsFalsyHandling(right) {
74
+ // String literals suggest we want to handle empty strings too
75
+ if (right.type === utils_1.AST_NODE_TYPES.Literal &&
76
+ typeof right.value === 'string') {
77
+ return true;
78
+ }
79
+ // Number literals (especially 0) suggest we want to handle falsy numbers
80
+ if (right.type === utils_1.AST_NODE_TYPES.Literal &&
81
+ typeof right.value === 'number') {
82
+ return true;
83
+ }
84
+ // Boolean literals suggest boolean logic
85
+ if (right.type === utils_1.AST_NODE_TYPES.Literal &&
86
+ typeof right.value === 'boolean') {
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ /**
92
+ * Checks if the left operand is likely to be used for boolean logic
93
+ */
94
+ function leftOperandSuggestsBooleanLogic(left) {
95
+ // Variables with boolean-like names
96
+ if (left.type === utils_1.AST_NODE_TYPES.Identifier) {
97
+ const name = left.name.toLowerCase();
98
+ const booleanPrefixes = [
99
+ 'is',
100
+ 'has',
101
+ 'can',
102
+ 'should',
103
+ 'will',
104
+ 'was',
105
+ 'were',
106
+ 'are',
107
+ ];
108
+ const booleanSuffixes = [
109
+ 'enabled',
110
+ 'disabled',
111
+ 'active',
112
+ 'inactive',
113
+ 'valid',
114
+ 'invalid',
115
+ ];
116
+ return (booleanPrefixes.some((prefix) => name.startsWith(prefix)) ||
117
+ booleanSuffixes.some((suffix) => name.endsWith(suffix)));
118
+ }
119
+ // Binary expressions that result in booleans
120
+ if (left.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
121
+ const comparisonOperators = [
122
+ '===',
123
+ '!==',
124
+ '==',
125
+ '!=',
126
+ '<',
127
+ '>',
128
+ '<=',
129
+ '>=',
130
+ ];
131
+ return comparisonOperators.includes(left.operator);
132
+ }
133
+ return false;
134
+ }
135
+ /**
136
+ * Checks if this is a variable assignment context where falsy handling is expected
137
+ */
138
+ function isVariableAssignmentWithFalsyHandling(node) {
139
+ const parent = node.parent;
140
+ if (!parent)
141
+ return false;
142
+ // Variable declarations like: const name = username || 'Anonymous'
143
+ if (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
144
+ parent.init === node) {
145
+ return rightOperandSuggestsFalsyHandling(node.right);
146
+ }
147
+ // Assignment expressions like: this.name = username || 'Anonymous'
148
+ if (parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
149
+ parent.right === node) {
150
+ return rightOperandSuggestsFalsyHandling(node.right);
151
+ }
152
+ return false;
153
+ }
154
+ /**
155
+ * Checks if this logical OR should be converted to nullish coalescing
156
+ */
157
+ function shouldConvertToNullishCoalescing(_node) {
158
+ return false;
159
+ }
160
+ void isInBooleanContext;
161
+ void leftOperandSuggestsBooleanLogic;
162
+ void rightOperandSuggestsFalsyHandling;
163
+ void isVariableAssignmentWithFalsyHandling;
164
+ return {
165
+ LogicalExpression(node) {
166
+ if (shouldConvertToNullishCoalescing(node)) {
167
+ const sc = context.getSourceCode();
168
+ const leftText = sc.getText(node.left);
169
+ const rightText = sc.getText(node.right);
170
+ context.report({
171
+ node,
172
+ messageId: 'preferNullishCoalescing',
173
+ fix(fixer) {
174
+ return fixer.replaceText(node, `${leftText} ?? ${rightText}`);
175
+ },
176
+ });
177
+ }
178
+ },
179
+ };
180
+ },
181
+ });
182
+ //# sourceMappingURL=prefer-nullish-coalescing-override.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @fileoverview Enforce the use of event.params over .ref.parent.id in Firebase change handlers
3
+ * @author BluMint
4
+ */
5
+ export declare const preferParamsOverParentId: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"preferParams", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;