@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,327 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noMisleadingBooleanPrefixes = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const DEFAULT_PREFIXES = ['is', 'has', 'should'];
7
+ function unwrapTypeNode(node) {
8
+ let current = node;
9
+ while (current.type === 'TSParenthesizedType' &&
10
+ current.typeAnnotation) {
11
+ current = current.typeAnnotation;
12
+ }
13
+ return current;
14
+ }
15
+ function isUppercaseLetter(char) {
16
+ return char >= 'A' && char <= 'Z';
17
+ }
18
+ function isDigit(char) {
19
+ return char >= '0' && char <= '9';
20
+ }
21
+ function startsWithBooleanPrefix(name, prefixes) {
22
+ if (!name)
23
+ return false;
24
+ const lower = name.toLowerCase();
25
+ for (const prefix of prefixes) {
26
+ const p = prefix.toLowerCase();
27
+ if (lower.startsWith(p)) {
28
+ if (name.length === p.length)
29
+ return true; // edge: exact match like "is"
30
+ const next = name[p.length];
31
+ if (isUppercaseLetter(next) || next === '_' || isDigit(next)) {
32
+ return true;
33
+ }
34
+ }
35
+ }
36
+ return false;
37
+ }
38
+ function isTsBooleanLike(typeNode) {
39
+ if (!typeNode)
40
+ return false;
41
+ typeNode = unwrapTypeNode(typeNode);
42
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword)
43
+ return true;
44
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
45
+ typeof typeNode.literal.value === 'boolean') {
46
+ return true;
47
+ }
48
+ const isBooleanishUnionMember = (t) => {
49
+ const unwrapped = unwrapTypeNode(t);
50
+ return (unwrapped.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword ||
51
+ (unwrapped.type === utils_1.AST_NODE_TYPES.TSLiteralType &&
52
+ typeof unwrapped.literal.value ===
53
+ 'boolean') ||
54
+ unwrapped.type === utils_1.AST_NODE_TYPES.TSUndefinedKeyword ||
55
+ unwrapped.type === utils_1.AST_NODE_TYPES.TSNullKeyword ||
56
+ unwrapped.type === utils_1.AST_NODE_TYPES.TSVoidKeyword);
57
+ };
58
+ // Allow unions like boolean | undefined | null | void
59
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType) {
60
+ return typeNode.types.every((t) => isBooleanishUnionMember(t));
61
+ }
62
+ // Promise<boolean> (or Promise<boolean | undefined | null>)
63
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
64
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
65
+ typeNode.typeName.name === 'Promise' &&
66
+ typeNode.typeParameters?.params?.length) {
67
+ const inner = typeNode.typeParameters.params[0];
68
+ if (!inner)
69
+ return false;
70
+ const resolvedInner = unwrapTypeNode(inner);
71
+ if (isBooleanishUnionMember(resolvedInner))
72
+ return true;
73
+ const innerType = resolvedInner;
74
+ if (innerType.type === utils_1.AST_NODE_TYPES.TSUnionType &&
75
+ Array.isArray(innerType.types) &&
76
+ innerType.types.every((t) => isBooleanishUnionMember(t))) {
77
+ return true;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ function isExpressionBooleanLike(expr) {
83
+ switch (expr.type) {
84
+ case utils_1.AST_NODE_TYPES.Literal:
85
+ return typeof expr.value === 'boolean' ? true : 'non';
86
+ case utils_1.AST_NODE_TYPES.TemplateLiteral:
87
+ return 'non';
88
+ case utils_1.AST_NODE_TYPES.ObjectExpression:
89
+ case utils_1.AST_NODE_TYPES.ArrayExpression:
90
+ case utils_1.AST_NODE_TYPES.NewExpression:
91
+ case utils_1.AST_NODE_TYPES.ClassExpression:
92
+ case utils_1.AST_NODE_TYPES.FunctionExpression:
93
+ case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
94
+ return 'non';
95
+ case utils_1.AST_NODE_TYPES.UnaryExpression:
96
+ if (expr.operator === '!')
97
+ return true; // !x or !!x
98
+ if (expr.operator === 'void')
99
+ return 'non';
100
+ return 'unknown';
101
+ case utils_1.AST_NODE_TYPES.BinaryExpression: {
102
+ const cmp = ['===', '!==', '==', '!=', '>', '<', '>=', '<='];
103
+ return cmp.includes(expr.operator) ? true : 'unknown';
104
+ }
105
+ case utils_1.AST_NODE_TYPES.LogicalExpression:
106
+ // && and || often return non-boolean operands; don't infer true
107
+ return 'unknown';
108
+ case utils_1.AST_NODE_TYPES.MemberExpression: {
109
+ if (expr.property.type === utils_1.AST_NODE_TYPES.Identifier &&
110
+ expr.property.name === 'length') {
111
+ return 'non';
112
+ }
113
+ return 'unknown';
114
+ }
115
+ case utils_1.AST_NODE_TYPES.ConditionalExpression: {
116
+ const cons = isExpressionBooleanLike(expr.consequent);
117
+ const alt = isExpressionBooleanLike(expr.alternate);
118
+ if (cons === true && alt === true)
119
+ return true;
120
+ if (cons === 'non' || alt === 'non')
121
+ return 'non';
122
+ return 'unknown';
123
+ }
124
+ case utils_1.AST_NODE_TYPES.CallExpression: {
125
+ if (expr.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
126
+ expr.callee.name === 'Boolean') {
127
+ return true;
128
+ }
129
+ return 'unknown';
130
+ }
131
+ case utils_1.AST_NODE_TYPES.Identifier:
132
+ if (expr.name === 'undefined')
133
+ return 'non';
134
+ return 'unknown';
135
+ case utils_1.AST_NODE_TYPES.AwaitExpression:
136
+ return 'unknown';
137
+ default:
138
+ return 'unknown';
139
+ }
140
+ }
141
+ function getReturnTypeNode(node) {
142
+ if (node.returnType?.typeAnnotation)
143
+ return node.returnType.typeAnnotation;
144
+ return undefined;
145
+ }
146
+ function hasTypePredicate(node) {
147
+ return (node.returnType?.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypePredicate);
148
+ }
149
+ function collectReturnExpressions(fn) {
150
+ const results = [];
151
+ const visited = new Set();
152
+ function visit(n) {
153
+ if (!n || visited.has(n))
154
+ return;
155
+ visited.add(n);
156
+ // Do not traverse into nested functions/classes
157
+ if (n.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
158
+ n.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
159
+ n.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
160
+ n.type === utils_1.AST_NODE_TYPES.ClassDeclaration ||
161
+ n.type === utils_1.AST_NODE_TYPES.ClassExpression) {
162
+ if (n === fn) {
163
+ // traverse this function's body
164
+ // continue
165
+ }
166
+ else {
167
+ return;
168
+ }
169
+ }
170
+ if (n.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
171
+ if (n.argument && n.argument.type) {
172
+ results.push(n.argument);
173
+ }
174
+ else {
175
+ // return; without value
176
+ // Represent as Identifier 'undefined' to treat as non-boolean
177
+ // We won't push undefined, but handle later by a flag
178
+ results.noValueReturn = true;
179
+ }
180
+ }
181
+ for (const key of Object.keys(n)) {
182
+ if (key === 'parent' || key === 'range' || key === 'loc')
183
+ continue;
184
+ const value = n[key];
185
+ if (!value)
186
+ continue;
187
+ if (Array.isArray(value)) {
188
+ for (const child of value) {
189
+ if (child && typeof child === 'object' && 'type' in child)
190
+ visit(child);
191
+ }
192
+ }
193
+ else if (value && typeof value === 'object' && 'type' in value) {
194
+ visit(value);
195
+ }
196
+ }
197
+ }
198
+ if (fn.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression && fn.expression) {
199
+ // expression-bodied arrow function: synthesize a return
200
+ const bodyExpr = fn.body;
201
+ results.push(bodyExpr);
202
+ return results;
203
+ }
204
+ if (fn.body && fn.body.type === utils_1.AST_NODE_TYPES.BlockStatement)
205
+ visit(fn.body);
206
+ return results;
207
+ }
208
+ exports.noMisleadingBooleanPrefixes = (0, createRule_1.createRule)({
209
+ name: 'no-misleading-boolean-prefixes',
210
+ meta: {
211
+ type: 'problem',
212
+ docs: {
213
+ description: 'Reserve boolean-style prefixes (is/has/should) for functions that actually return boolean values.',
214
+ recommended: 'error',
215
+ },
216
+ schema: [
217
+ {
218
+ type: 'object',
219
+ properties: {
220
+ prefixes: {
221
+ type: 'array',
222
+ items: { type: 'string' },
223
+ },
224
+ },
225
+ additionalProperties: false,
226
+ },
227
+ ],
228
+ messages: {
229
+ nonBooleanReturn: 'Function "{{name}}" uses a boolean-style prefix but does not return a boolean value.',
230
+ },
231
+ },
232
+ defaultOptions: [{ prefixes: DEFAULT_PREFIXES }],
233
+ create(context, [options]) {
234
+ const prefixes = (options && options.prefixes) || DEFAULT_PREFIXES;
235
+ function shouldCheckName(name) {
236
+ return startsWithBooleanPrefix(name, prefixes);
237
+ }
238
+ function report(node, name) {
239
+ context.report({ node, messageId: 'nonBooleanReturn', data: { name } });
240
+ }
241
+ function checkFunctionLike(node, name, reportNode) {
242
+ if (!shouldCheckName(name))
243
+ return;
244
+ // Type predicate allows boolean-like
245
+ if (hasTypePredicate(node))
246
+ return;
247
+ const typeNode = getReturnTypeNode(node);
248
+ if (typeNode) {
249
+ if (isTsBooleanLike(typeNode))
250
+ return;
251
+ // Explicit non-boolean annotation
252
+ report(reportNode, name);
253
+ return;
254
+ }
255
+ const returns = collectReturnExpressions(node);
256
+ const noValueReturn = returns.noValueReturn === true;
257
+ if (noValueReturn) {
258
+ report(reportNode, name);
259
+ return;
260
+ }
261
+ if (returns.length === 0) {
262
+ // No returns implies void
263
+ report(reportNode, name);
264
+ return;
265
+ }
266
+ for (const expr of returns) {
267
+ const kind = isExpressionBooleanLike(expr);
268
+ if (kind === 'non') {
269
+ report(reportNode, name);
270
+ return;
271
+ }
272
+ }
273
+ // If we can't determine it's non-boolean, do not report to avoid false positives
274
+ }
275
+ return {
276
+ FunctionDeclaration(node) {
277
+ if (!node.id)
278
+ return;
279
+ checkFunctionLike(node, node.id.name, node.id);
280
+ },
281
+ FunctionExpression(node) {
282
+ // Prefer variable declarator or method/property name
283
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
284
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
285
+ checkFunctionLike(node, node.parent.id.name, node.parent.id);
286
+ return;
287
+ }
288
+ // If part of a property or method, let dedicated visitors handle it to avoid duplicates
289
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.Property)
290
+ return;
291
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.MethodDefinition)
292
+ return;
293
+ if (node.id) {
294
+ checkFunctionLike(node, node.id.name, node.id);
295
+ }
296
+ },
297
+ ArrowFunctionExpression(node) {
298
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
299
+ node.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
300
+ checkFunctionLike(node, node.parent.id.name, node.parent.id);
301
+ return;
302
+ }
303
+ // If part of a property, let the Property visitor handle it to avoid duplicates
304
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.Property)
305
+ return;
306
+ },
307
+ MethodDefinition(node) {
308
+ if (node.key.type !== utils_1.AST_NODE_TYPES.Identifier)
309
+ return;
310
+ const name = node.key.name;
311
+ const fn = node.value;
312
+ if (fn.type === utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression)
313
+ return;
314
+ checkFunctionLike(fn, name, node.key);
315
+ },
316
+ Property(node) {
317
+ if (node.key.type !== utils_1.AST_NODE_TYPES.Identifier)
318
+ return;
319
+ if (node.value.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
320
+ node.value.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
321
+ checkFunctionLike(node.value, node.key.name, node.key);
322
+ }
323
+ },
324
+ };
325
+ },
326
+ });
327
+ //# sourceMappingURL=no-misleading-boolean-prefixes.js.map
@@ -0,0 +1 @@
1
+ export declare const noOverridableMethodCallsInConstructor: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noOverridableMethodCallsInConstructor", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noOverridableMethodCallsInConstructor = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.noOverridableMethodCallsInConstructor = (0, createRule_1.createRule)({
7
+ name: 'no-overridable-method-calls-in-constructor',
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Disallow calling overridable methods in constructors to prevent unexpected behavior',
12
+ recommended: 'error',
13
+ },
14
+ fixable: undefined,
15
+ schema: [],
16
+ messages: {
17
+ noOverridableMethodCallsInConstructor: 'Avoid calling overridable methods in constructors. This can lead to unexpected behavior when the method is overridden in a derived class.',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ /**
23
+ * Checks if a method is overridable (not private and not static)
24
+ */
25
+ function isOverridableMethod(node) {
26
+ // Private methods are not overridable
27
+ if (node.accessibility === 'private') {
28
+ return false;
29
+ }
30
+ // ES private methods (#foo) are non-overridable
31
+ if (node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
32
+ return false;
33
+ }
34
+ // Static methods are not overridable in the same way as instance methods
35
+ if (node.static) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ /**
41
+ * Checks if a method is abstract
42
+ */
43
+ function isAbstractMethod(_node) {
44
+ return false;
45
+ }
46
+ /**
47
+ * Collects all method names from a class, categorized by whether they are overridable
48
+ */
49
+ function collectMethodNames(classNode) {
50
+ const overridableMethods = new Set();
51
+ const abstractMethods = new Set();
52
+ for (const member of classNode.body.body) {
53
+ if (member.type === utils_1.AST_NODE_TYPES.MethodDefinition) {
54
+ const methodName = getMethodName(member.key);
55
+ if (methodName) {
56
+ if (isAbstractMethod(member)) {
57
+ abstractMethods.add(methodName);
58
+ }
59
+ else if (isOverridableMethod(member)) {
60
+ overridableMethods.add(methodName);
61
+ }
62
+ }
63
+ }
64
+ else if (member.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition) {
65
+ const methodName = getMethodName(member.key);
66
+ if (methodName) {
67
+ abstractMethods.add(methodName);
68
+ }
69
+ }
70
+ }
71
+ return { overridableMethods, abstractMethods };
72
+ }
73
+ /**
74
+ * Gets the method name from a property key
75
+ */
76
+ function getMethodName(key) {
77
+ switch (key.type) {
78
+ case utils_1.AST_NODE_TYPES.Identifier:
79
+ return key.name;
80
+ case utils_1.AST_NODE_TYPES.Literal:
81
+ return typeof key.value === 'string'
82
+ ? key.value
83
+ : String(key.value);
84
+ default:
85
+ return null; // Computed property names are not handled
86
+ }
87
+ }
88
+ /**
89
+ * Checks if a call expression is calling a method on 'this' or 'super'
90
+ */
91
+ function isThisOrSuperMethodCall(node) {
92
+ if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
93
+ !node.callee.computed // Ignore computed properties like this[methodName]()
94
+ ) {
95
+ const object = node.callee.object;
96
+ const property = node.callee.property;
97
+ // Check if it's a call on 'this'
98
+ if (object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
99
+ property.type === utils_1.AST_NODE_TYPES.Identifier) {
100
+ return {
101
+ isMethodCall: true,
102
+ methodName: property.name,
103
+ isSuper: false,
104
+ };
105
+ }
106
+ // Check if it's a call on 'super'
107
+ if (object.type === utils_1.AST_NODE_TYPES.Super &&
108
+ property.type === utils_1.AST_NODE_TYPES.Identifier) {
109
+ return {
110
+ isMethodCall: true,
111
+ methodName: property.name,
112
+ isSuper: true,
113
+ };
114
+ }
115
+ // Check for method chaining: this.method().anotherMethod()
116
+ // In this case, object is a CallExpression
117
+ if (object.type === utils_1.AST_NODE_TYPES.CallExpression) {
118
+ const nestedCall = isThisOrSuperMethodCall(object);
119
+ if (nestedCall.isMethodCall) {
120
+ return nestedCall; // Return the nested method call info
121
+ }
122
+ }
123
+ }
124
+ return { isMethodCall: false, methodName: null, isSuper: false };
125
+ }
126
+ /**
127
+ * Checks if a member expression is accessing a property on 'this' or 'super'
128
+ */
129
+ function isThisOrSuperPropertyAccess(node) {
130
+ if (!node.computed) {
131
+ // Ignore computed properties like this[propertyName]
132
+ const object = node.object;
133
+ const property = node.property;
134
+ // Check if it's access on 'this'
135
+ if (object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
136
+ property.type === utils_1.AST_NODE_TYPES.Identifier) {
137
+ // Skip 'constructor' property as it's not an overridable method
138
+ if (property.name === 'constructor') {
139
+ return {
140
+ isPropertyAccess: false,
141
+ propertyName: null,
142
+ isSuper: false,
143
+ };
144
+ }
145
+ return {
146
+ isPropertyAccess: true,
147
+ propertyName: property.name,
148
+ isSuper: false,
149
+ };
150
+ }
151
+ // Check if it's access on 'super'
152
+ if (object.type === utils_1.AST_NODE_TYPES.Super &&
153
+ property.type === utils_1.AST_NODE_TYPES.Identifier) {
154
+ return {
155
+ isPropertyAccess: true,
156
+ propertyName: property.name,
157
+ isSuper: true,
158
+ };
159
+ }
160
+ }
161
+ return { isPropertyAccess: false, propertyName: null, isSuper: false };
162
+ }
163
+ return {
164
+ // Process class declarations and expressions
165
+ 'ClassDeclaration, ClassExpression'(node) {
166
+ const { overridableMethods, abstractMethods } = collectMethodNames(node);
167
+ // Find the constructor method
168
+ const ctorMethod = node.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
169
+ member.kind === 'constructor');
170
+ if (!ctorMethod || !ctorMethod.value.body) {
171
+ return; // No constructor or constructor has no body
172
+ }
173
+ // Function to recursively check for method calls in the constructor
174
+ function checkForMethodCalls(bodyNode) {
175
+ if (!bodyNode || typeof bodyNode !== 'object') {
176
+ return;
177
+ }
178
+ // Check for method calls
179
+ if (bodyNode.type === utils_1.AST_NODE_TYPES.CallExpression) {
180
+ const { isMethodCall, methodName, isSuper } = isThisOrSuperMethodCall(bodyNode);
181
+ if (isMethodCall && methodName) {
182
+ // Abstract methods are always a problem when called from constructor
183
+ // This includes super calls to abstract methods
184
+ if (abstractMethods.has(methodName)) {
185
+ context.report({
186
+ node: bodyNode,
187
+ messageId: 'noOverridableMethodCallsInConstructor',
188
+ });
189
+ }
190
+ // Overridable methods are a problem when called on 'this'
191
+ else if (overridableMethods.has(methodName) && !isSuper) {
192
+ context.report({
193
+ node: bodyNode,
194
+ messageId: 'noOverridableMethodCallsInConstructor',
195
+ });
196
+ }
197
+ }
198
+ }
199
+ // Check for property access (getters/setters) - only if not part of a call expression
200
+ else if (bodyNode.type === utils_1.AST_NODE_TYPES.MemberExpression) {
201
+ // Check if this MemberExpression is the callee of a CallExpression
202
+ // If so, skip it as it will be handled by the CallExpression check
203
+ const parent = bodyNode.parent;
204
+ if (parent &&
205
+ parent.type === utils_1.AST_NODE_TYPES.CallExpression &&
206
+ parent.callee === bodyNode) {
207
+ return; // Skip this MemberExpression as it's part of a method call
208
+ }
209
+ const { isPropertyAccess, propertyName, isSuper } = isThisOrSuperPropertyAccess(bodyNode);
210
+ if (isPropertyAccess && propertyName) {
211
+ // Abstract methods are always a problem when accessed from constructor
212
+ if (abstractMethods.has(propertyName)) {
213
+ context.report({
214
+ node: bodyNode,
215
+ messageId: 'noOverridableMethodCallsInConstructor',
216
+ });
217
+ }
218
+ // Overridable methods are a problem when accessed on 'this'
219
+ else if (overridableMethods.has(propertyName) && !isSuper) {
220
+ context.report({
221
+ node: bodyNode,
222
+ messageId: 'noOverridableMethodCallsInConstructor',
223
+ });
224
+ }
225
+ }
226
+ }
227
+ // Avoid circular references by not checking 'parent' property
228
+ const keysToSkip = new Set(['parent', 'loc', 'range']);
229
+ // Don't traverse into function expressions or arrow functions as they create new scopes
230
+ if (bodyNode.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
231
+ bodyNode.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
232
+ bodyNode.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
233
+ return;
234
+ }
235
+ // Recursively check all child nodes
236
+ for (const key in bodyNode) {
237
+ if (keysToSkip.has(key))
238
+ continue;
239
+ const child = bodyNode[key];
240
+ if (child && typeof child === 'object') {
241
+ if ('type' in child) {
242
+ checkForMethodCalls(child);
243
+ }
244
+ else if (Array.isArray(child)) {
245
+ child.forEach((item) => {
246
+ if (item && typeof item === 'object' && 'type' in item) {
247
+ checkForMethodCalls(item);
248
+ }
249
+ });
250
+ }
251
+ }
252
+ }
253
+ }
254
+ // Start checking from the constructor body
255
+ checkForMethodCalls(ctorMethod.value.body);
256
+ },
257
+ };
258
+ },
259
+ });
260
+ //# sourceMappingURL=no-overridable-method-calls-in-constructor.js.map
@@ -0,0 +1,4 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ export declare const noPassthroughGetters: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"noPassthroughGetter", never[], {
3
+ MethodDefinition(node: TSESTree.MethodDefinition): void;
4
+ }>;