@blumintinc/eslint-plugin-blumint 1.12.5 → 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 (142) hide show
  1. package/README.md +140 -99
  2. package/lib/index.js +111 -20
  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-undefined-null-passthrough.js +166 -19
  86. package/lib/rules/no-unnecessary-verb-suffix.js +0 -2
  87. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  88. package/lib/rules/no-unused-props.d.ts +3 -0
  89. package/lib/rules/no-unused-props.js +213 -78
  90. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  91. package/lib/rules/optimize-object-boolean-conditions.js +234 -0
  92. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  93. package/lib/rules/parallelize-async-operations.js +375 -0
  94. package/lib/rules/prefer-batch-operations.js +199 -33
  95. package/lib/rules/prefer-block-comments-for-declarations.js +13 -3
  96. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  97. package/lib/rules/prefer-document-flattening.js +239 -0
  98. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  99. package/lib/rules/prefer-field-paths-in-transforms.js +250 -0
  100. package/lib/rules/prefer-fragment-component.js +3 -3
  101. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  102. package/lib/rules/prefer-global-router-state-key.js +230 -31
  103. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  104. package/lib/rules/prefer-next-dynamic.js +366 -0
  105. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  106. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +430 -0
  107. package/lib/rules/prefer-nullish-coalescing-override.d.ts +6 -0
  108. package/lib/rules/prefer-nullish-coalescing-override.js +182 -0
  109. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  110. package/lib/rules/prefer-params-over-parent-id.js +228 -0
  111. package/lib/rules/prefer-settings-object.js +23 -15
  112. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  113. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  114. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  115. package/lib/rules/prefer-url-tostring-over-tojson.js +166 -0
  116. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  117. package/lib/rules/prefer-use-deep-compare-memo.js +428 -0
  118. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +24 -1
  119. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  120. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  121. package/lib/rules/react-usememo-should-be-component.js +144 -78
  122. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  123. package/lib/rules/require-image-optimized.js +1 -1
  124. package/lib/rules/require-memo.js +11 -1
  125. package/lib/rules/require-usememo-object-literals.js +7 -1
  126. package/lib/rules/semantic-function-prefixes.js +38 -18
  127. package/lib/rules/sync-onwrite-name-func.js +5 -1
  128. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  129. package/lib/rules/test-file-location-enforcement.js +57 -0
  130. package/lib/rules/use-custom-link.js +6 -1
  131. package/lib/rules/use-custom-router.js +6 -1
  132. package/lib/rules/use-latest-callback.d.ts +3 -0
  133. package/lib/rules/use-latest-callback.js +270 -0
  134. package/lib/utils/ASTHelpers.js +10 -0
  135. package/lib/utils/createRule.js +1 -1
  136. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  137. package/lib/utils/ruleTester.d.ts +1 -0
  138. package/lib/utils/ruleTester.js +4 -1
  139. package/package.json +21 -14
  140. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
  141. package/lib/rules/no-restricted-imports-dynamic.d.ts +0 -25
  142. package/lib/rules/no-restricted-imports-dynamic.js +0 -213
@@ -3,21 +3,196 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enforceAssertThrows = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
5
  const createRule_1 = require("../utils/createRule");
6
+ function isCallbackFunctionNode(node) {
7
+ if (node.type !== utils_1.AST_NODE_TYPES.FunctionExpression &&
8
+ node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
9
+ return false;
10
+ }
11
+ const parent = node.parent;
12
+ if (!parent)
13
+ return false;
14
+ if (parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
15
+ const grandParent = parent.parent;
16
+ if (grandParent &&
17
+ (grandParent.type === utils_1.AST_NODE_TYPES.ConditionalExpression ||
18
+ grandParent.type === utils_1.AST_NODE_TYPES.LogicalExpression)) {
19
+ return true;
20
+ }
21
+ return parent.arguments.includes(node) || parent.callee === node;
22
+ }
23
+ if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression ||
24
+ parent.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
25
+ return true;
26
+ }
27
+ return false;
28
+ }
29
+ function isTypeLikeNode(node) {
30
+ return node.type.startsWith('TS') || node.type.endsWith('TypeAnnotation');
31
+ }
6
32
  exports.enforceAssertThrows = (0, createRule_1.createRule)({
7
33
  name: 'enforce-assert-throws',
8
34
  meta: {
9
35
  type: 'problem',
10
36
  docs: {
11
- description: 'Enforce that functions with assert- prefix must throw an error or call process.exit(1)',
37
+ description: 'Enforce that functions with an assert prefix must throw an error or call process.exit(1), and functions that call assert-prefixed methods should themselves be assert-prefixed',
12
38
  recommended: 'error',
13
39
  },
14
40
  schema: [],
15
41
  messages: {
16
- assertShouldThrow: 'Functions with assert- prefix must throw an error or call process.exit(1). Either rename the function or add a throw/exit statement.',
42
+ assertShouldThrow: 'Assert helper "{{functionName}}" does not throw, exit, or delegate to another assert helper. Assert-prefixed functions are fail-fast guards; letting them return normally hides failed checks and allows callers to continue with invalid state. Throw an error, call process.exit(1), or rename the function if it should not halt execution.',
43
+ shouldBeAssertPrefixed: 'Function "{{functionName}}" calls an assert-prefixed helper but is not assert-prefixed itself. The assert- prefix signals that the function terminates on failed checks; without it, callers may assume it returns safely and keep running after an assertion aborts. Rename the function to start with "assert" or avoid calling assert helpers from non-assert functions.',
17
44
  },
18
45
  },
19
46
  defaultOptions: [],
20
47
  create(context) {
48
+ function callsAssertMethod(node) {
49
+ let hasAssertCall = false;
50
+ function walk(node) {
51
+ if (hasAssertCall)
52
+ return; // Early exit if we found an assert call
53
+ if (isTypeLikeNode(node))
54
+ return;
55
+ if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
56
+ const callee = node.callee;
57
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
58
+ if (callee.name.toLowerCase().startsWith('assert')) {
59
+ hasAssertCall = true;
60
+ return;
61
+ }
62
+ }
63
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
64
+ const property = callee.property;
65
+ if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
66
+ if (property.name.toLowerCase().startsWith('assert')) {
67
+ hasAssertCall = true;
68
+ return;
69
+ }
70
+ }
71
+ }
72
+ }
73
+ // Handle nested functions - only skip if it's a standalone function, not a callback
74
+ if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
75
+ node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
76
+ node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
77
+ if (node !== currentFunction) {
78
+ // Allow traversing into callback functions
79
+ if (!isCallbackFunctionNode(node)) {
80
+ return;
81
+ }
82
+ }
83
+ }
84
+ // Handle specific node types that contain other nodes
85
+ switch (node.type) {
86
+ case utils_1.AST_NODE_TYPES.BlockStatement:
87
+ node.body.forEach((stmt) => walk(stmt));
88
+ break;
89
+ case utils_1.AST_NODE_TYPES.IfStatement:
90
+ walk(node.consequent);
91
+ if (node.alternate) {
92
+ walk(node.alternate);
93
+ }
94
+ break;
95
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
96
+ walk(node.consequent);
97
+ walk(node.alternate);
98
+ break;
99
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
100
+ walk(node.left);
101
+ walk(node.right);
102
+ break;
103
+ case utils_1.AST_NODE_TYPES.ExpressionStatement:
104
+ walk(node.expression);
105
+ break;
106
+ case utils_1.AST_NODE_TYPES.ReturnStatement:
107
+ if (node.argument) {
108
+ walk(node.argument);
109
+ }
110
+ break;
111
+ case utils_1.AST_NODE_TYPES.AwaitExpression:
112
+ walk(node.argument);
113
+ break;
114
+ case utils_1.AST_NODE_TYPES.VariableDeclaration:
115
+ node.declarations.forEach((decl) => walk(decl));
116
+ break;
117
+ case utils_1.AST_NODE_TYPES.VariableDeclarator:
118
+ if (node.init) {
119
+ walk(node.init);
120
+ }
121
+ break;
122
+ case utils_1.AST_NODE_TYPES.CallExpression:
123
+ // Walk through arguments to find callback functions
124
+ node.arguments.forEach((arg) => walk(arg));
125
+ break;
126
+ case utils_1.AST_NODE_TYPES.FunctionExpression:
127
+ case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
128
+ // For callback functions, walk their body
129
+ if (isCallbackFunctionNode(node)) {
130
+ if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
131
+ walk(node.body);
132
+ }
133
+ else {
134
+ // Arrow function with expression body
135
+ walk(node.body);
136
+ }
137
+ }
138
+ break;
139
+ case utils_1.AST_NODE_TYPES.ForStatement:
140
+ case utils_1.AST_NODE_TYPES.ForInStatement:
141
+ case utils_1.AST_NODE_TYPES.ForOfStatement:
142
+ case utils_1.AST_NODE_TYPES.WhileStatement:
143
+ case utils_1.AST_NODE_TYPES.DoWhileStatement:
144
+ walk(node.body);
145
+ break;
146
+ case utils_1.AST_NODE_TYPES.SwitchStatement:
147
+ node.cases.forEach((caseNode) => walk(caseNode));
148
+ break;
149
+ case utils_1.AST_NODE_TYPES.SwitchCase:
150
+ node.consequent.forEach((stmt) => walk(stmt));
151
+ break;
152
+ case utils_1.AST_NODE_TYPES.LabeledStatement:
153
+ case utils_1.AST_NODE_TYPES.WithStatement:
154
+ walk(node.body);
155
+ break;
156
+ case utils_1.AST_NODE_TYPES.TryStatement:
157
+ walk(node.block);
158
+ if (node.handler) {
159
+ walk(node.handler);
160
+ }
161
+ if (node.finalizer) {
162
+ walk(node.finalizer);
163
+ }
164
+ break;
165
+ case utils_1.AST_NODE_TYPES.CatchClause:
166
+ walk(node.body);
167
+ break;
168
+ default:
169
+ // Handle other node types generically
170
+ for (const key of Object.keys(node)) {
171
+ if (key === 'parent' || key === 'range' || key === 'loc')
172
+ continue;
173
+ const value = node[key];
174
+ if (Array.isArray(value)) {
175
+ value.forEach((item) => {
176
+ if (item &&
177
+ typeof item === 'object' &&
178
+ 'type' in item &&
179
+ !isTypeLikeNode(item)) {
180
+ walk(item);
181
+ }
182
+ });
183
+ }
184
+ else if (value &&
185
+ typeof value === 'object' &&
186
+ 'type' in value &&
187
+ !isTypeLikeNode(value)) {
188
+ walk(value);
189
+ }
190
+ }
191
+ }
192
+ }
193
+ walk(node);
194
+ return hasAssertCall;
195
+ }
21
196
  function isAssertionCall(node) {
22
197
  if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
23
198
  const expression = node.expression;
@@ -62,9 +237,177 @@ exports.enforceAssertThrows = (0, createRule_1.createRule)({
62
237
  }
63
238
  return false;
64
239
  }
240
+ // Check if a call expression is calling an assert-prefixed method
241
+ function isCallingAssertMethod(node, functionBody) {
242
+ if (node.type === utils_1.AST_NODE_TYPES.ReturnStatement && node.argument) {
243
+ return isCallingAssertMethodInExpression(node.argument, functionBody || undefined);
244
+ }
245
+ else if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
246
+ return isCallingAssertMethodInExpression(node.expression, functionBody || undefined);
247
+ }
248
+ return false;
249
+ }
250
+ function isCallingAssertMethodInExpression(expression, functionBody) {
251
+ // Handle direct call: this.assertSomething()
252
+ if (expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
253
+ const callee = expression.callee;
254
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
255
+ const property = callee.property;
256
+ if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
257
+ return property.name.toLowerCase().startsWith('assert');
258
+ }
259
+ }
260
+ else if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
261
+ // Check if it's a direct assert function call
262
+ if (callee.name.toLowerCase().startsWith('assert')) {
263
+ return true;
264
+ }
265
+ // Check if it's a variable that was assigned an assert method
266
+ if (functionBody &&
267
+ isCallingVariableAssignedAssertMethod(expression, functionBody)) {
268
+ return true;
269
+ }
270
+ }
271
+ // Handle chained calls: this.assertA().then(() => this.assertB())
272
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
273
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
274
+ // Check if the method name is 'then' or other promise-related methods
275
+ if (['then', 'catch', 'finally'].includes(callee.property.name)) {
276
+ // Check if the first part of the chain is an assert call
277
+ if (callee.object.type === utils_1.AST_NODE_TYPES.CallExpression) {
278
+ const objectCallee = callee.object.callee;
279
+ if (objectCallee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
280
+ objectCallee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
281
+ if (objectCallee.property.name.toLowerCase().startsWith('assert')) {
282
+ return true;
283
+ }
284
+ }
285
+ }
286
+ // Check if any of the arguments to then/catch/finally are calling assert methods
287
+ if (expression.arguments.length > 0) {
288
+ for (const arg of expression.arguments) {
289
+ if (arg.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
290
+ arg.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
291
+ // Check the function body for assert calls
292
+ if (arg.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
293
+ for (const stmt of arg.body.body) {
294
+ if (isCallingAssertMethod(stmt)) {
295
+ return true;
296
+ }
297
+ }
298
+ }
299
+ else if (arg.body.type === utils_1.AST_NODE_TYPES.CallExpression) {
300
+ // Arrow function with expression body
301
+ const arrowCallee = arg.body.callee;
302
+ if (arrowCallee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
303
+ arrowCallee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
304
+ if (arrowCallee.property.name
305
+ .toLowerCase()
306
+ .startsWith('assert')) {
307
+ return true;
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
317
+ // Handle await expression: await this.assertSomething()
318
+ if (expression.type === utils_1.AST_NODE_TYPES.AwaitExpression) {
319
+ return isCallingAssertMethodInExpression(expression.argument, functionBody);
320
+ }
321
+ // Handle ternary expressions: condition ? this.assertA() : this.assertB()
322
+ if (expression.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
323
+ return (isCallingAssertMethodInExpression(expression.consequent, functionBody) ||
324
+ isCallingAssertMethodInExpression(expression.alternate, functionBody));
325
+ }
326
+ // Handle logical expressions: this.assertA() || this.assertB()
327
+ if (expression.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
328
+ return (isCallingAssertMethodInExpression(expression.left, functionBody) ||
329
+ isCallingAssertMethodInExpression(expression.right, functionBody));
330
+ }
331
+ return false;
332
+ }
333
+ // Special case for the method chaining pattern in the test
334
+ function hasPromiseChainWithAssertMethods(node) {
335
+ const walkChain = (callExpr) => {
336
+ let current = callExpr;
337
+ while (current) {
338
+ const callee = current.callee;
339
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
340
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
341
+ callee.property.name.toLowerCase().startsWith('assert')) {
342
+ return true;
343
+ }
344
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
345
+ callee.object.type === utils_1.AST_NODE_TYPES.CallExpression) {
346
+ // Walk further up any chained call (then/catch/finally or other links).
347
+ current = callee.object;
348
+ continue;
349
+ }
350
+ break;
351
+ }
352
+ return false;
353
+ };
354
+ if (node.type === utils_1.AST_NODE_TYPES.ReturnStatement && node.argument) {
355
+ if (node.argument.type === utils_1.AST_NODE_TYPES.CallExpression &&
356
+ walkChain(node.argument)) {
357
+ return true;
358
+ }
359
+ }
360
+ if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
361
+ for (const declarator of node.declarations) {
362
+ if (declarator.init &&
363
+ declarator.init.type === utils_1.AST_NODE_TYPES.AwaitExpression &&
364
+ declarator.init.argument.type === utils_1.AST_NODE_TYPES.CallExpression &&
365
+ walkChain(declarator.init.argument)) {
366
+ return true;
367
+ }
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+ // Check if a variable is assigned an assert method
373
+ function isVariableAssignedAssertMethod(node, variableName) {
374
+ if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
375
+ for (const declarator of node.declarations) {
376
+ if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
377
+ declarator.id.name === variableName &&
378
+ declarator.init) {
379
+ if (declarator.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
380
+ declarator.init.property.type === utils_1.AST_NODE_TYPES.Identifier) {
381
+ return declarator.init.property.name
382
+ .toLowerCase()
383
+ .startsWith('assert');
384
+ }
385
+ }
386
+ }
387
+ }
388
+ return false;
389
+ }
390
+ // Check if a call expression is calling a variable that was assigned an assert method
391
+ function isCallingVariableAssignedAssertMethod(expression, functionBody) {
392
+ if (expression.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
393
+ const variableName = expression.callee.name;
394
+ // Check if this variable was assigned an assert method in the function body
395
+ for (const stmt of functionBody.body) {
396
+ if (isVariableAssignedAssertMethod(stmt, variableName)) {
397
+ return true;
398
+ }
399
+ }
400
+ }
401
+ return false;
402
+ }
65
403
  function hasThrowStatement(node) {
66
404
  let hasThrow = false;
405
+ const functionBody = node.type === utils_1.AST_NODE_TYPES.BlockStatement ? node : null;
67
406
  function walk(node) {
407
+ if (hasThrow)
408
+ return; // Early exit if we already found a throw
409
+ if (isTypeLikeNode(node))
410
+ return;
68
411
  if (node.type === utils_1.AST_NODE_TYPES.ThrowStatement) {
69
412
  hasThrow = true;
70
413
  return;
@@ -81,58 +424,144 @@ exports.enforceAssertThrows = (0, createRule_1.createRule)({
81
424
  hasThrow = true;
82
425
  return;
83
426
  }
84
- // Don't check throw statements in nested functions
427
+ // Check for calls to other assert methods
428
+ if (isCallingAssertMethod(node, functionBody)) {
429
+ hasThrow = true;
430
+ return;
431
+ }
432
+ // Check for promise chains with assert methods
433
+ if (hasPromiseChainWithAssertMethods(node)) {
434
+ hasThrow = true;
435
+ return;
436
+ }
437
+ // Handle nested functions - only skip if it's a standalone function, not a callback
85
438
  if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
86
439
  node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
87
440
  node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
88
441
  if (node !== currentFunction) {
89
- return;
90
- }
91
- }
92
- // Check catch blocks for process.exit(1)
93
- if (node.type === utils_1.AST_NODE_TYPES.CatchClause) {
94
- walk(node.body);
95
- return;
96
- }
97
- // Handle TryStatement specially
98
- if (node.type === utils_1.AST_NODE_TYPES.TryStatement) {
99
- walk(node.block);
100
- if (node.handler) {
101
- walk(node.handler);
102
- }
103
- if (node.finalizer) {
104
- walk(node.finalizer);
442
+ // Allow traversing into callback functions
443
+ if (!isCallbackFunctionNode(node)) {
444
+ return;
445
+ }
105
446
  }
106
- return;
107
- }
108
- // Handle BlockStatement specially
109
- if (node.type === utils_1.AST_NODE_TYPES.BlockStatement) {
110
- node.body.forEach((stmt) => walk(stmt));
111
- return;
112
447
  }
113
- // Handle IfStatement specially
114
- if (node.type === utils_1.AST_NODE_TYPES.IfStatement) {
115
- walk(node.consequent);
116
- if (node.alternate) {
448
+ // Handle specific node types that need special traversal
449
+ switch (node.type) {
450
+ case utils_1.AST_NODE_TYPES.CatchClause:
451
+ walk(node.body);
452
+ return;
453
+ case utils_1.AST_NODE_TYPES.TryStatement:
454
+ walk(node.block);
455
+ if (node.handler) {
456
+ walk(node.handler);
457
+ }
458
+ if (node.finalizer) {
459
+ walk(node.finalizer);
460
+ }
461
+ return;
462
+ case utils_1.AST_NODE_TYPES.BlockStatement:
463
+ node.body.forEach((stmt) => walk(stmt));
464
+ return;
465
+ case utils_1.AST_NODE_TYPES.IfStatement:
466
+ walk(node.consequent);
467
+ if (node.alternate) {
468
+ walk(node.alternate);
469
+ }
470
+ return;
471
+ case utils_1.AST_NODE_TYPES.ConditionalExpression:
472
+ walk(node.consequent);
117
473
  walk(node.alternate);
118
- }
119
- return;
120
- }
121
- // Handle other node types
122
- for (const key of Object.keys(node)) {
123
- const value = node[key];
124
- if (Array.isArray(value)) {
125
- value.forEach((item) => {
126
- if (item && typeof item === 'object' && !('parent' in item)) {
127
- walk(item);
474
+ return;
475
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
476
+ walk(node.left);
477
+ walk(node.right);
478
+ return;
479
+ case utils_1.AST_NODE_TYPES.ForStatement:
480
+ walk(node.body);
481
+ return;
482
+ case utils_1.AST_NODE_TYPES.ForInStatement:
483
+ walk(node.body);
484
+ return;
485
+ case utils_1.AST_NODE_TYPES.ForOfStatement:
486
+ walk(node.body);
487
+ return;
488
+ case utils_1.AST_NODE_TYPES.WhileStatement:
489
+ walk(node.body);
490
+ return;
491
+ case utils_1.AST_NODE_TYPES.DoWhileStatement:
492
+ walk(node.body);
493
+ return;
494
+ case utils_1.AST_NODE_TYPES.SwitchStatement:
495
+ node.cases.forEach((caseNode) => walk(caseNode));
496
+ return;
497
+ case utils_1.AST_NODE_TYPES.SwitchCase:
498
+ node.consequent.forEach((stmt) => walk(stmt));
499
+ return;
500
+ case utils_1.AST_NODE_TYPES.LabeledStatement:
501
+ walk(node.body);
502
+ return;
503
+ case utils_1.AST_NODE_TYPES.WithStatement:
504
+ walk(node.body);
505
+ return;
506
+ case utils_1.AST_NODE_TYPES.ExpressionStatement:
507
+ walk(node.expression);
508
+ return;
509
+ case utils_1.AST_NODE_TYPES.ReturnStatement:
510
+ if (node.argument) {
511
+ walk(node.argument);
512
+ }
513
+ return;
514
+ case utils_1.AST_NODE_TYPES.CallExpression:
515
+ // Walk through arguments to find callback functions
516
+ node.arguments.forEach((arg) => walk(arg));
517
+ return;
518
+ case utils_1.AST_NODE_TYPES.FunctionExpression:
519
+ case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
520
+ // For callback functions, walk their body
521
+ if (isCallbackFunctionNode(node)) {
522
+ if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
523
+ walk(node.body);
128
524
  }
129
- });
130
- }
131
- else if (value &&
132
- typeof value === 'object' &&
133
- !('parent' in value)) {
134
- walk(value);
135
- }
525
+ else {
526
+ // Arrow function with expression body
527
+ walk(node.body);
528
+ }
529
+ }
530
+ return;
531
+ case utils_1.AST_NODE_TYPES.VariableDeclaration:
532
+ node.declarations.forEach((decl) => walk(decl));
533
+ return;
534
+ case utils_1.AST_NODE_TYPES.VariableDeclarator:
535
+ if (node.init) {
536
+ walk(node.init);
537
+ }
538
+ return;
539
+ case utils_1.AST_NODE_TYPES.AwaitExpression:
540
+ walk(node.argument);
541
+ return;
542
+ default:
543
+ // Handle other node types generically
544
+ for (const key of Object.keys(node)) {
545
+ if (key === 'parent' || key === 'range' || key === 'loc')
546
+ continue;
547
+ const value = node[key];
548
+ if (Array.isArray(value)) {
549
+ value.forEach((item) => {
550
+ if (item &&
551
+ typeof item === 'object' &&
552
+ 'type' in item &&
553
+ !isTypeLikeNode(item)) {
554
+ walk(item);
555
+ }
556
+ });
557
+ }
558
+ else if (value &&
559
+ typeof value === 'object' &&
560
+ 'type' in value &&
561
+ !isTypeLikeNode(value)) {
562
+ walk(value);
563
+ }
564
+ }
136
565
  }
137
566
  }
138
567
  walk(node);
@@ -157,19 +586,32 @@ exports.enforceAssertThrows = (0, createRule_1.createRule)({
157
586
  functionName = parent.id.name;
158
587
  }
159
588
  }
589
+ const displayName = functionName || 'this function';
590
+ currentFunction = node;
591
+ const functionBody = node.type === utils_1.AST_NODE_TYPES.MethodDefinition
592
+ ? node.value.body
593
+ : node.body;
160
594
  if (functionName.toLowerCase().startsWith('assert')) {
161
- currentFunction = node;
162
- const functionBody = node.type === utils_1.AST_NODE_TYPES.MethodDefinition
163
- ? node.value.body
164
- : node.body;
595
+ // Check that assert-prefixed functions throw or call other assert functions
165
596
  if (functionBody && !hasThrowStatement(functionBody)) {
166
597
  context.report({
167
598
  node,
168
599
  messageId: 'assertShouldThrow',
600
+ data: { functionName: displayName },
601
+ });
602
+ }
603
+ }
604
+ else if (functionName && functionBody) {
605
+ // Check that functions calling assert-prefixed methods are themselves prefixed with assert
606
+ if (callsAssertMethod(functionBody)) {
607
+ context.report({
608
+ node,
609
+ messageId: 'shouldBeAssertPrefixed',
610
+ data: { functionName: displayName },
169
611
  });
170
612
  }
171
- currentFunction = null;
172
613
  }
614
+ currentFunction = null;
173
615
  }
174
616
  return {
175
617
  FunctionDeclaration: checkFunction,
@@ -1,7 +1,9 @@
1
+ import type { TSESLint } from '@typescript-eslint/utils';
1
2
  type Options = [
2
3
  {
3
4
  prefixes?: string[];
5
+ ignoreOverriddenGetters?: boolean;
4
6
  }
5
7
  ];
6
- export declare const enforceBooleanNamingPrefixes: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"missingBooleanPrefix", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
8
+ export declare const enforceBooleanNamingPrefixes: TSESLint.RuleModule<"missingBooleanPrefix", Options, TSESLint.RuleListener>;
7
9
  export {};