@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.
- package/README.md +140 -99
- package/lib/index.js +111 -17
- package/lib/rules/array-methods-this-context.js +20 -2
- package/lib/rules/avoid-utils-directory.js +11 -2
- package/lib/rules/dynamic-https-errors.d.ts +3 -1
- package/lib/rules/dynamic-https-errors.js +48 -10
- package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
- package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +51 -95
- package/lib/rules/enforce-assert-throws.d.ts +3 -1
- package/lib/rules/enforce-assert-throws.js +493 -51
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
- package/lib/rules/enforce-boolean-naming-prefixes.js +832 -39
- package/lib/rules/enforce-callable-types.js +4 -4
- package/lib/rules/enforce-callback-memo.js +143 -18
- package/lib/rules/enforce-centralized-mock-firestore.js +4 -1
- package/lib/rules/enforce-console-error.d.ts +3 -0
- package/lib/rules/enforce-console-error.js +250 -0
- package/lib/rules/enforce-css-media-queries.js +24 -46
- package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
- package/lib/rules/enforce-dynamic-imports.js +1 -1
- package/lib/rules/enforce-exported-function-types.js +3 -3
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +212 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.js +335 -0
- package/lib/rules/enforce-firestore-facade.js +347 -157
- package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
- package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
- package/lib/rules/enforce-global-constants.d.ts +4 -1
- package/lib/rules/enforce-global-constants.js +240 -12
- package/lib/rules/enforce-memoize-async.d.ts +2 -1
- package/lib/rules/enforce-memoize-async.js +96 -27
- package/lib/rules/enforce-memoize-getters.d.ts +3 -0
- package/lib/rules/enforce-memoize-getters.js +123 -0
- package/lib/rules/enforce-microdiff.js +2 -1
- package/lib/rules/enforce-positive-naming.js +5 -7
- package/lib/rules/enforce-props-argument-name.d.ts +2 -7
- package/lib/rules/enforce-props-argument-name.js +165 -151
- package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
- package/lib/rules/enforce-props-naming-consistency.js +145 -0
- package/lib/rules/enforce-querykey-ts.d.ts +9 -0
- package/lib/rules/enforce-querykey-ts.js +274 -0
- package/lib/rules/enforce-singular-type-names.js +9 -5
- package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.js +96 -0
- package/lib/rules/enforce-verb-noun-naming.js +85 -10
- package/lib/rules/extract-global-constants.js +57 -45
- package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
- package/lib/rules/fast-deep-equal-over-microdiff.js +356 -78
- package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
- package/lib/rules/firestore-transaction-reads-before-writes.js +231 -0
- package/lib/rules/generic-starts-with-t.js +8 -2
- package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
- package/lib/rules/memoize-root-level-hocs.js +242 -0
- package/lib/rules/no-array-length-in-deps.d.ts +12 -0
- package/lib/rules/no-array-length-in-deps.js +308 -0
- package/lib/rules/no-async-array-filter.js +5 -2
- package/lib/rules/no-complex-cloud-params.js +15 -2
- package/lib/rules/no-compositing-layer-props.js +4 -2
- package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
- package/lib/rules/no-entire-object-hook-deps.js +317 -63
- package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
- package/lib/rules/no-excessive-parent-chain.js +273 -0
- package/lib/rules/no-firestore-object-arrays.js +345 -30
- package/lib/rules/no-hungarian.js +13 -13
- package/lib/rules/no-margin-properties.d.ts +7 -1
- package/lib/rules/no-margin-properties.js +16 -3
- package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
- package/lib/rules/no-misleading-boolean-prefixes.js +327 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.js +260 -0
- package/lib/rules/no-passthrough-getters.d.ts +4 -0
- package/lib/rules/no-passthrough-getters.js +170 -0
- package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
- package/lib/rules/no-redundant-usecallback-wrapper.js +315 -0
- package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
- package/lib/rules/no-restricted-properties-fix.js +172 -0
- package/lib/rules/no-separate-loading-state.d.ts +8 -0
- package/lib/rules/no-separate-loading-state.js +142 -0
- package/lib/rules/no-stale-state-across-await.d.ts +1 -0
- package/lib/rules/no-stale-state-across-await.js +185 -0
- package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
- package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
- package/lib/rules/no-type-assertion-returns.js +6 -0
- package/lib/rules/no-undefined-null-passthrough.js +166 -19
- package/lib/rules/no-unnecessary-verb-suffix.js +0 -2
- package/lib/rules/no-unsafe-firestore-spread.js +8 -5
- package/lib/rules/no-unused-props.d.ts +3 -0
- package/lib/rules/no-unused-props.js +213 -78
- package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
- package/lib/rules/optimize-object-boolean-conditions.js +234 -0
- package/lib/rules/parallelize-async-operations.d.ts +8 -0
- package/lib/rules/parallelize-async-operations.js +375 -0
- package/lib/rules/prefer-batch-operations.js +199 -33
- package/lib/rules/prefer-block-comments-for-declarations.js +13 -3
- package/lib/rules/prefer-document-flattening.d.ts +4 -0
- package/lib/rules/prefer-document-flattening.js +239 -0
- package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
- package/lib/rules/prefer-field-paths-in-transforms.js +250 -0
- package/lib/rules/prefer-fragment-component.js +3 -3
- package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
- package/lib/rules/prefer-global-router-state-key.js +230 -31
- package/lib/rules/prefer-next-dynamic.d.ts +9 -0
- package/lib/rules/prefer-next-dynamic.js +366 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +430 -0
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +6 -0
- package/lib/rules/prefer-nullish-coalescing-override.js +182 -0
- package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
- package/lib/rules/prefer-params-over-parent-id.js +228 -0
- package/lib/rules/prefer-settings-object.js +23 -15
- package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
- package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
- package/lib/rules/prefer-url-tostring-over-tojson.js +166 -0
- package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
- package/lib/rules/prefer-use-deep-compare-memo.js +428 -0
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +24 -1
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
- package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
- package/lib/rules/react-usememo-should-be-component.js +144 -78
- package/lib/rules/require-dynamic-firebase-imports.js +2 -1
- package/lib/rules/require-image-optimized.js +1 -1
- package/lib/rules/require-memo.js +11 -1
- package/lib/rules/require-usememo-object-literals.js +7 -1
- package/lib/rules/semantic-function-prefixes.js +38 -18
- package/lib/rules/sync-onwrite-name-func.js +5 -1
- package/lib/rules/test-file-location-enforcement.d.ts +1 -0
- package/lib/rules/test-file-location-enforcement.js +57 -0
- package/lib/rules/use-custom-link.js +6 -1
- package/lib/rules/use-custom-router.js +6 -1
- package/lib/rules/use-latest-callback.d.ts +3 -0
- package/lib/rules/use-latest-callback.js +270 -0
- package/lib/utils/ASTHelpers.js +10 -0
- package/lib/utils/createRule.js +1 -1
- package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
- package/lib/utils/ruleTester.d.ts +1 -0
- package/lib/utils/ruleTester.js +4 -1
- package/package.json +21 -14
- package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.enforceBooleanNamingPrefixes = void 0;
|
|
4
7
|
const utils_1 = require("@typescript-eslint/utils");
|
|
8
|
+
const pluralize_1 = __importDefault(require("pluralize"));
|
|
5
9
|
const createRule_1 = require("../utils/createRule");
|
|
6
|
-
// Default approved boolean prefixes
|
|
10
|
+
// Default approved boolean prefixes. Some less common prefixes (e.g., 'are',
|
|
11
|
+
// 'includes') stay allowed for flexibility even though the user-facing message
|
|
12
|
+
// highlights only the most common ones. Underscore-prefixed names are also
|
|
13
|
+
// intentionally allowed for private/internal fields.
|
|
7
14
|
const DEFAULT_BOOLEAN_PREFIXES = [
|
|
8
15
|
'is',
|
|
9
16
|
'has',
|
|
@@ -21,7 +28,12 @@ const DEFAULT_BOOLEAN_PREFIXES = [
|
|
|
21
28
|
'needs',
|
|
22
29
|
'asserts',
|
|
23
30
|
'includes',
|
|
31
|
+
'are', // Adding 'are' as an approved prefix (plural form of 'is')
|
|
24
32
|
];
|
|
33
|
+
const DEFAULT_OPTIONS = {
|
|
34
|
+
prefixes: DEFAULT_BOOLEAN_PREFIXES,
|
|
35
|
+
ignoreOverriddenGetters: false,
|
|
36
|
+
};
|
|
25
37
|
exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
26
38
|
name: 'enforce-boolean-naming-prefixes',
|
|
27
39
|
meta: {
|
|
@@ -41,28 +53,99 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
41
53
|
type: 'string',
|
|
42
54
|
},
|
|
43
55
|
},
|
|
56
|
+
ignoreOverriddenGetters: {
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
default: false,
|
|
59
|
+
},
|
|
44
60
|
},
|
|
45
61
|
additionalProperties: false,
|
|
46
62
|
},
|
|
47
63
|
],
|
|
48
64
|
messages: {
|
|
49
|
-
missingBooleanPrefix: 'Boolean {{type}} "{{name}}" should start with
|
|
65
|
+
missingBooleanPrefix: 'Boolean {{type}} "{{name}}" should start with a prefix like "is", "has", "can", or "should" (e.g., is{{capitalizedName}}, has{{capitalizedName}}). ' +
|
|
66
|
+
'Boolean prefixes make code self-documenting—readers immediately know the value is true/false without checking its declaration or type. ' +
|
|
67
|
+
'This improves readability at call sites: `if (user.{{name}})` is ambiguous, but `if (user.is{{capitalizedName}})` clearly signals a boolean check.',
|
|
50
68
|
},
|
|
51
69
|
},
|
|
52
|
-
defaultOptions: [
|
|
70
|
+
defaultOptions: [DEFAULT_OPTIONS],
|
|
53
71
|
create(context, [options]) {
|
|
54
|
-
const approvedPrefixes = options.prefixes ||
|
|
72
|
+
const approvedPrefixes = options.prefixes || DEFAULT_OPTIONS.prefixes;
|
|
73
|
+
const ignoreOverriddenGetters = options.ignoreOverriddenGetters ?? DEFAULT_OPTIONS.ignoreOverriddenGetters;
|
|
74
|
+
const importStatusCache = new Map();
|
|
75
|
+
const externalApiUsageCache = new Map();
|
|
76
|
+
function findVariableInScopes(name) {
|
|
77
|
+
let currentScope = context.getScope();
|
|
78
|
+
while (currentScope) {
|
|
79
|
+
const variable = currentScope.variables.find((v) => v.name === name);
|
|
80
|
+
if (variable)
|
|
81
|
+
return variable;
|
|
82
|
+
currentScope = currentScope.upper;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if a name starts with any of the approved prefixes, their plural forms,
|
|
88
|
+
* or if it starts with an underscore (which indicates a private/internal property)
|
|
89
|
+
*/
|
|
90
|
+
function hasApprovedPrefix(name, options) {
|
|
91
|
+
const treatLeadingUnderscoreAsApproved = options?.treatLeadingUnderscoreAsApproved ?? true;
|
|
92
|
+
const normalizedName = name.startsWith('_') ? name.slice(1) : name;
|
|
93
|
+
// Skip checking properties that start with an underscore (private/internal properties)
|
|
94
|
+
if (treatLeadingUnderscoreAsApproved && name.startsWith('_')) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return approvedPrefixes.some((prefix) => {
|
|
98
|
+
// Check for exact prefix match
|
|
99
|
+
if (normalizedName.toLowerCase().startsWith(prefix.toLowerCase())) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
// Check for plural form of the prefix
|
|
103
|
+
// Only apply pluralization to certain prefixes that have meaningful plural forms
|
|
104
|
+
if (['is', 'has', 'does', 'was', 'had', 'did'].includes(prefix)) {
|
|
105
|
+
const pluralPrefix = pluralize_1.default.plural(prefix);
|
|
106
|
+
return normalizedName
|
|
107
|
+
.toLowerCase()
|
|
108
|
+
.startsWith(pluralPrefix.toLowerCase());
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function nameSuggestsBoolean(name) {
|
|
114
|
+
const normalizedName = name.startsWith('_') ? name.slice(1) : name;
|
|
115
|
+
const lowerName = normalizedName.toLowerCase();
|
|
116
|
+
const suffixKeywords = [
|
|
117
|
+
'active',
|
|
118
|
+
'inactive',
|
|
119
|
+
'enabled',
|
|
120
|
+
'disabled',
|
|
121
|
+
'visible',
|
|
122
|
+
'ready',
|
|
123
|
+
'valid',
|
|
124
|
+
'verified',
|
|
125
|
+
'authenticated',
|
|
126
|
+
'authorized',
|
|
127
|
+
];
|
|
128
|
+
return (hasApprovedPrefix(normalizedName, {
|
|
129
|
+
treatLeadingUnderscoreAsApproved: false,
|
|
130
|
+
}) ||
|
|
131
|
+
suffixKeywords.some((keyword) => lowerName.endsWith(keyword)));
|
|
132
|
+
}
|
|
55
133
|
/**
|
|
56
|
-
*
|
|
134
|
+
* Capitalize the first letter of a name for use in suggested alternatives
|
|
57
135
|
*/
|
|
58
|
-
function
|
|
59
|
-
|
|
136
|
+
function capitalizeFirst(name) {
|
|
137
|
+
if (!name)
|
|
138
|
+
return '';
|
|
139
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
60
140
|
}
|
|
61
141
|
/**
|
|
62
142
|
* Format the list of approved prefixes for error messages
|
|
143
|
+
* Note: We exclude 'are' and 'includes' from the error message to maintain backward compatibility with tests
|
|
63
144
|
*/
|
|
64
145
|
function formatPrefixes() {
|
|
65
|
-
|
|
146
|
+
// Filter out 'are' and 'includes' from the displayed prefixes to maintain backward compatibility with tests
|
|
147
|
+
const displayPrefixes = approvedPrefixes.filter((prefix) => prefix !== 'are' && prefix !== 'includes');
|
|
148
|
+
return displayPrefixes.join(', ');
|
|
66
149
|
}
|
|
67
150
|
/**
|
|
68
151
|
* Check if a node is a TypeScript type predicate
|
|
@@ -117,7 +200,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
117
200
|
}
|
|
118
201
|
// Check for logical expressions that typically return boolean
|
|
119
202
|
if (node.init.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
|
120
|
-
['===', '!==', '==', '!=', '>', '<', '>=', '<='].includes(node.init.operator)) {
|
|
203
|
+
['===', '!==', '==', '!=', '>', '<', '>=', '<=', 'in', 'instanceof'].includes(node.init.operator)) {
|
|
121
204
|
return true;
|
|
122
205
|
}
|
|
123
206
|
// Check for logical expressions (&&)
|
|
@@ -126,37 +209,85 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
126
209
|
// Check if the right side is a method call that might return a non-boolean value
|
|
127
210
|
const rightSide = node.init.right;
|
|
128
211
|
if (rightSide.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
212
|
+
// If right side is a simple identifier call
|
|
213
|
+
if (rightSide.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
214
|
+
const calleeName = rightSide.callee.name;
|
|
215
|
+
const lowerCallee = calleeName.toLowerCase();
|
|
216
|
+
// For assert*-style utilities, only treat as boolean if we can confirm boolean return type
|
|
217
|
+
if (lowerCallee.startsWith('assert')) {
|
|
218
|
+
return identifierReturnsBoolean(calleeName);
|
|
219
|
+
}
|
|
220
|
+
// Otherwise, infer based on naming heuristics
|
|
221
|
+
const isBooleanCall = approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
222
|
+
lowerCallee.startsWith(prefix.toLowerCase()));
|
|
223
|
+
if (isBooleanCall ||
|
|
224
|
+
lowerCallee.includes('boolean') ||
|
|
225
|
+
lowerCallee.includes('enabled') ||
|
|
226
|
+
lowerCallee.includes('auth') ||
|
|
227
|
+
lowerCallee.includes('valid') ||
|
|
228
|
+
lowerCallee.includes('check')) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if (lowerCallee.startsWith('get') ||
|
|
232
|
+
lowerCallee.startsWith('fetch') ||
|
|
233
|
+
lowerCallee.startsWith('retrieve') ||
|
|
234
|
+
lowerCallee.startsWith('load') ||
|
|
235
|
+
lowerCallee.startsWith('read')) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
129
239
|
// If the method name doesn't suggest it returns a boolean, don't flag it
|
|
130
240
|
if (rightSide.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
131
241
|
rightSide.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
132
242
|
const methodName = rightSide.callee.property.name;
|
|
243
|
+
const lowerMethodName = methodName.toLowerCase();
|
|
244
|
+
// Ignore assert*-style methods which often return the input value
|
|
245
|
+
if (lowerMethodName.startsWith('assert')) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
133
248
|
// Check if the method name suggests it returns a boolean
|
|
134
|
-
const isBooleanMethod = approvedPrefixes.some((prefix) =>
|
|
249
|
+
const isBooleanMethod = approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
250
|
+
lowerMethodName.startsWith(prefix.toLowerCase()));
|
|
135
251
|
// If the method name suggests it returns a boolean (starts with a boolean prefix or contains 'boolean' or 'enabled'),
|
|
136
252
|
// then the variable should be treated as a boolean
|
|
137
253
|
if (isBooleanMethod ||
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
254
|
+
lowerMethodName.includes('boolean') ||
|
|
255
|
+
lowerMethodName.includes('enabled') ||
|
|
256
|
+
lowerMethodName.includes('auth') ||
|
|
257
|
+
lowerMethodName.includes('valid') ||
|
|
258
|
+
lowerMethodName.includes('check')) {
|
|
143
259
|
return true;
|
|
144
260
|
}
|
|
145
261
|
// For methods like getVolume(), getData(), etc., assume they return non-boolean values
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
262
|
+
if (lowerMethodName.startsWith('get') ||
|
|
263
|
+
lowerMethodName.startsWith('fetch') ||
|
|
264
|
+
lowerMethodName.startsWith('retrieve') ||
|
|
265
|
+
lowerMethodName.startsWith('load') ||
|
|
266
|
+
lowerMethodName.startsWith('read')) {
|
|
151
267
|
return false;
|
|
152
268
|
}
|
|
153
269
|
}
|
|
154
270
|
}
|
|
155
|
-
//
|
|
271
|
+
// Check if the right side is a property access that might return a non-boolean value
|
|
156
272
|
if (rightSide.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
157
273
|
rightSide.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
158
274
|
const propertyName = rightSide.property.name;
|
|
159
|
-
const
|
|
275
|
+
const lowerPropertyName = propertyName.toLowerCase();
|
|
276
|
+
// If the property name is 'parentElement', 'parentNode', etc., it's likely not a boolean
|
|
277
|
+
if (lowerPropertyName.includes('parent') ||
|
|
278
|
+
lowerPropertyName.includes('element') ||
|
|
279
|
+
lowerPropertyName.includes('node') ||
|
|
280
|
+
lowerPropertyName.includes('child') ||
|
|
281
|
+
lowerPropertyName.includes('sibling')) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
// Ignore assert*-style properties which often return the input value
|
|
285
|
+
if (lowerPropertyName.startsWith('assert')) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
// For property access like user.isAuthenticated, treat as boolean
|
|
289
|
+
const isBooleanProperty = approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
290
|
+
lowerPropertyName.startsWith(prefix.toLowerCase()));
|
|
160
291
|
if (isBooleanProperty) {
|
|
161
292
|
return true;
|
|
162
293
|
}
|
|
@@ -195,7 +326,13 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
195
326
|
if (leftSide.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
196
327
|
leftSide.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
197
328
|
const calleeName = leftSide.callee.name;
|
|
198
|
-
|
|
329
|
+
const lowerCallee = calleeName.toLowerCase();
|
|
330
|
+
// For assert*-style utilities, only treat as boolean if we can confirm boolean return type
|
|
331
|
+
if (lowerCallee.startsWith('assert')) {
|
|
332
|
+
return identifierReturnsBoolean(calleeName);
|
|
333
|
+
}
|
|
334
|
+
return approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
335
|
+
lowerCallee.startsWith(prefix.toLowerCase()));
|
|
199
336
|
}
|
|
200
337
|
// Default to false for other cases with || to avoid false positives
|
|
201
338
|
return false;
|
|
@@ -209,8 +346,14 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
209
346
|
if (node.init.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
210
347
|
node.init.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
211
348
|
const calleeName = node.init.callee.name;
|
|
349
|
+
const lowerCallee = calleeName.toLowerCase();
|
|
350
|
+
// For assert*-style utilities, only treat as boolean if we can confirm boolean return type
|
|
351
|
+
if (lowerCallee.startsWith('assert')) {
|
|
352
|
+
return identifierReturnsBoolean(calleeName);
|
|
353
|
+
}
|
|
212
354
|
// Check if the function name suggests it returns a boolean
|
|
213
|
-
return approvedPrefixes.some((prefix) =>
|
|
355
|
+
return approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
356
|
+
lowerCallee.startsWith(prefix.toLowerCase()));
|
|
214
357
|
}
|
|
215
358
|
}
|
|
216
359
|
return false;
|
|
@@ -234,7 +377,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
234
377
|
return true;
|
|
235
378
|
}
|
|
236
379
|
if (node.body.type === utils_1.AST_NODE_TYPES.BinaryExpression &&
|
|
237
|
-
['===', '!==', '==', '!=', '>', '<', '>=', '<='].includes(node.body.operator)) {
|
|
380
|
+
['===', '!==', '==', '!=', '>', '<', '>=', '<=', 'in', 'instanceof'].includes(node.body.operator)) {
|
|
238
381
|
return true;
|
|
239
382
|
}
|
|
240
383
|
if (node.body.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
|
@@ -257,6 +400,500 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
257
400
|
}
|
|
258
401
|
return false;
|
|
259
402
|
}
|
|
403
|
+
function identifierIsBoolean(identifier) {
|
|
404
|
+
if (nameSuggestsBoolean(identifier.name)) {
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
const variable = findVariableInScopes(identifier.name);
|
|
408
|
+
if (!variable)
|
|
409
|
+
return false;
|
|
410
|
+
for (const def of variable.defs) {
|
|
411
|
+
if (def.type === 'Variable' && def.node) {
|
|
412
|
+
const declarator = def.node;
|
|
413
|
+
if (declarator.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
414
|
+
(hasBooleanTypeAnnotation(declarator.id) ||
|
|
415
|
+
hasInitialBooleanValue(declarator))) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (def.type === 'Parameter' &&
|
|
420
|
+
def.name?.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
421
|
+
hasBooleanTypeAnnotation(def.name)) {
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
if (def.type === 'FunctionName' &&
|
|
425
|
+
def.node &&
|
|
426
|
+
returnsBooleanValue(def.node)) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
function callExpressionLooksBoolean(callExpression) {
|
|
433
|
+
if (callExpression.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
434
|
+
const calleeName = callExpression.callee.name;
|
|
435
|
+
const lowerCallee = calleeName.toLowerCase();
|
|
436
|
+
if (lowerCallee.startsWith('assert')) {
|
|
437
|
+
return identifierReturnsBoolean(calleeName)
|
|
438
|
+
? 'boolean'
|
|
439
|
+
: 'unknown';
|
|
440
|
+
}
|
|
441
|
+
const matchesPrefix = approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
442
|
+
lowerCallee.startsWith(prefix.toLowerCase()));
|
|
443
|
+
if (matchesPrefix ||
|
|
444
|
+
lowerCallee.includes('boolean') ||
|
|
445
|
+
lowerCallee.includes('enabled') ||
|
|
446
|
+
lowerCallee.includes('auth') ||
|
|
447
|
+
lowerCallee.includes('valid') ||
|
|
448
|
+
lowerCallee.includes('check')) {
|
|
449
|
+
return 'boolean';
|
|
450
|
+
}
|
|
451
|
+
if (lowerCallee.startsWith('get') ||
|
|
452
|
+
lowerCallee.startsWith('fetch') ||
|
|
453
|
+
lowerCallee.startsWith('retrieve') ||
|
|
454
|
+
lowerCallee.startsWith('load') ||
|
|
455
|
+
lowerCallee.startsWith('read')) {
|
|
456
|
+
return 'unknown';
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (callExpression.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
460
|
+
callExpression.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
461
|
+
const methodName = callExpression.callee.property.name;
|
|
462
|
+
const lowerMethodName = methodName.toLowerCase();
|
|
463
|
+
if (lowerMethodName.startsWith('assert')) {
|
|
464
|
+
return 'unknown';
|
|
465
|
+
}
|
|
466
|
+
const matchesPrefix = approvedPrefixes.some((prefix) => prefix !== 'asserts' &&
|
|
467
|
+
lowerMethodName.startsWith(prefix.toLowerCase()));
|
|
468
|
+
if (matchesPrefix ||
|
|
469
|
+
lowerMethodName.includes('boolean') ||
|
|
470
|
+
lowerMethodName.includes('enabled') ||
|
|
471
|
+
lowerMethodName.includes('auth') ||
|
|
472
|
+
lowerMethodName.includes('valid') ||
|
|
473
|
+
lowerMethodName.includes('check')) {
|
|
474
|
+
return 'boolean';
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return 'unknown';
|
|
478
|
+
}
|
|
479
|
+
function evaluateBooleanishExpression(expression) {
|
|
480
|
+
if (!expression)
|
|
481
|
+
return 'nonBoolean';
|
|
482
|
+
let currentExpression = expression;
|
|
483
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
|
|
484
|
+
currentExpression.type === utils_1.AST_NODE_TYPES.TSTypeAssertion ||
|
|
485
|
+
currentExpression.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression) {
|
|
486
|
+
if (currentExpression.typeAnnotation?.type ===
|
|
487
|
+
utils_1.AST_NODE_TYPES.TSBooleanKeyword) {
|
|
488
|
+
return 'boolean';
|
|
489
|
+
}
|
|
490
|
+
currentExpression = currentExpression.expression;
|
|
491
|
+
}
|
|
492
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.ChainExpression) {
|
|
493
|
+
currentExpression = currentExpression.expression;
|
|
494
|
+
}
|
|
495
|
+
if (currentExpression.type ===
|
|
496
|
+
'ParenthesizedExpression') {
|
|
497
|
+
return evaluateBooleanishExpression(currentExpression.expression);
|
|
498
|
+
}
|
|
499
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.TSNonNullExpression) {
|
|
500
|
+
return evaluateBooleanishExpression(currentExpression.expression);
|
|
501
|
+
}
|
|
502
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
503
|
+
typeof currentExpression.value === 'boolean') {
|
|
504
|
+
return 'boolean';
|
|
505
|
+
}
|
|
506
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
507
|
+
return identifierIsBoolean(currentExpression) ? 'boolean' : 'unknown';
|
|
508
|
+
}
|
|
509
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
510
|
+
currentExpression.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
511
|
+
return nameSuggestsBoolean(currentExpression.property.name)
|
|
512
|
+
? 'boolean'
|
|
513
|
+
: 'unknown';
|
|
514
|
+
}
|
|
515
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
516
|
+
return callExpressionLooksBoolean(currentExpression);
|
|
517
|
+
}
|
|
518
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
|
|
519
|
+
if ([
|
|
520
|
+
'===',
|
|
521
|
+
'!==',
|
|
522
|
+
'==',
|
|
523
|
+
'!=',
|
|
524
|
+
'>',
|
|
525
|
+
'<',
|
|
526
|
+
'>=',
|
|
527
|
+
'<=',
|
|
528
|
+
'in',
|
|
529
|
+
'instanceof',
|
|
530
|
+
].includes(currentExpression.operator)) {
|
|
531
|
+
return 'boolean';
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.UnaryExpression &&
|
|
535
|
+
currentExpression.operator === '!') {
|
|
536
|
+
return 'boolean';
|
|
537
|
+
}
|
|
538
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
|
|
539
|
+
const left = evaluateBooleanishExpression(currentExpression.left);
|
|
540
|
+
const right = evaluateBooleanishExpression(currentExpression.right);
|
|
541
|
+
if (left === 'nonBoolean' || right === 'nonBoolean') {
|
|
542
|
+
return 'nonBoolean';
|
|
543
|
+
}
|
|
544
|
+
if (left === 'boolean' && right === 'boolean') {
|
|
545
|
+
return 'boolean';
|
|
546
|
+
}
|
|
547
|
+
return 'unknown';
|
|
548
|
+
}
|
|
549
|
+
if (currentExpression.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
|
550
|
+
const consequent = evaluateBooleanishExpression(currentExpression.consequent);
|
|
551
|
+
const alternate = evaluateBooleanishExpression(currentExpression.alternate);
|
|
552
|
+
if (consequent === 'nonBoolean' || alternate === 'nonBoolean') {
|
|
553
|
+
return 'nonBoolean';
|
|
554
|
+
}
|
|
555
|
+
if (consequent === 'boolean' && alternate === 'boolean') {
|
|
556
|
+
return 'boolean';
|
|
557
|
+
}
|
|
558
|
+
return 'unknown';
|
|
559
|
+
}
|
|
560
|
+
return 'unknown';
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Attempt to resolve an identifier to a function declaration/expression and detect if it returns boolean
|
|
564
|
+
*/
|
|
565
|
+
function identifierReturnsBoolean(name) {
|
|
566
|
+
// Try to find the variable in all scopes, starting from current and going up
|
|
567
|
+
let currentScope = context.getScope();
|
|
568
|
+
let variable = undefined;
|
|
569
|
+
while (currentScope && !variable) {
|
|
570
|
+
variable = currentScope.variables.find((v) => v.name === name);
|
|
571
|
+
if (!variable) {
|
|
572
|
+
currentScope = currentScope.upper;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (!variable)
|
|
576
|
+
return false;
|
|
577
|
+
for (const def of variable.defs) {
|
|
578
|
+
// Function declaration
|
|
579
|
+
if (def.type === 'FunctionName' && def.node) {
|
|
580
|
+
const fn = def.node;
|
|
581
|
+
if (fn.returnType?.typeAnnotation &&
|
|
582
|
+
fn.returnType.typeAnnotation.type ===
|
|
583
|
+
utils_1.AST_NODE_TYPES.TSBooleanKeyword) {
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
// Variable with function expression or arrow function
|
|
588
|
+
if (def.type === 'Variable' && def.node) {
|
|
589
|
+
const varDecl = def.node;
|
|
590
|
+
const init = (varDecl && varDecl.init);
|
|
591
|
+
if (!init)
|
|
592
|
+
continue;
|
|
593
|
+
if ((init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
594
|
+
init.type === utils_1.AST_NODE_TYPES.FunctionExpression) &&
|
|
595
|
+
init.returnType?.typeAnnotation &&
|
|
596
|
+
init.returnType.typeAnnotation.type ===
|
|
597
|
+
utils_1.AST_NODE_TYPES.TSBooleanKeyword) {
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Check if a variable is used in a while loop condition and is likely a DOM element or tree node
|
|
606
|
+
* This helps identify variables like 'parent', 'element', 'node', etc. that are used
|
|
607
|
+
* in while loops for DOM/tree traversal and are not boolean values
|
|
608
|
+
*/
|
|
609
|
+
function isLikelyDomElementInWhileLoop(node) {
|
|
610
|
+
// Check if the variable name suggests it's a DOM element or tree node
|
|
611
|
+
const isTraversalName = node.name.toLowerCase().includes('element') ||
|
|
612
|
+
node.name.toLowerCase().includes('node') ||
|
|
613
|
+
node.name.toLowerCase().includes('parent') ||
|
|
614
|
+
node.name.toLowerCase().includes('child') ||
|
|
615
|
+
node.name.toLowerCase().includes('sibling') ||
|
|
616
|
+
node.name.toLowerCase().includes('ancestor') ||
|
|
617
|
+
node.name.toLowerCase().includes('descendant');
|
|
618
|
+
if (!isTraversalName) {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
// Must be used in a while loop to be considered for this exception
|
|
622
|
+
if (!isUsedInWhileLoop(node)) {
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
// Check if the variable is initialized with a traversal-related value
|
|
626
|
+
const variableDeclarator = node.parent;
|
|
627
|
+
if (variableDeclarator?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
628
|
+
variableDeclarator.init) {
|
|
629
|
+
const init = variableDeclarator.init;
|
|
630
|
+
// Check for logical expressions with traversal-related properties on the right side
|
|
631
|
+
if (init.type === utils_1.AST_NODE_TYPES.LogicalExpression &&
|
|
632
|
+
init.operator === '&&' &&
|
|
633
|
+
init.right.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
634
|
+
init.right.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
635
|
+
const propertyName = init.right.property
|
|
636
|
+
.name;
|
|
637
|
+
// DOM-specific properties - these are definitely DOM traversal
|
|
638
|
+
const isDomProperty = propertyName.toLowerCase().includes('element') ||
|
|
639
|
+
propertyName.toLowerCase().includes('node') ||
|
|
640
|
+
propertyName === 'parentElement' ||
|
|
641
|
+
propertyName === 'parentNode' ||
|
|
642
|
+
propertyName === 'firstChild' ||
|
|
643
|
+
propertyName === 'lastChild' ||
|
|
644
|
+
propertyName === 'nextSibling' ||
|
|
645
|
+
propertyName === 'previousSibling' ||
|
|
646
|
+
propertyName === 'firstElementChild' ||
|
|
647
|
+
propertyName === 'lastElementChild' ||
|
|
648
|
+
propertyName === 'nextElementSibling' ||
|
|
649
|
+
propertyName === 'previousElementSibling';
|
|
650
|
+
if (isDomProperty) {
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
// Tree-like properties - need additional confirmation
|
|
654
|
+
const isTreeProperty = propertyName === 'parent' ||
|
|
655
|
+
propertyName === 'child' ||
|
|
656
|
+
propertyName === 'root' ||
|
|
657
|
+
propertyName === 'left' ||
|
|
658
|
+
propertyName === 'right' ||
|
|
659
|
+
propertyName === 'next' ||
|
|
660
|
+
propertyName === 'prev' ||
|
|
661
|
+
propertyName === 'previous';
|
|
662
|
+
if (isTreeProperty) {
|
|
663
|
+
// For tree properties, check if there's a traversal pattern in the code
|
|
664
|
+
return hasTreeTraversalPattern(variableDeclarator);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
// Check for direct member expressions (without logical operators)
|
|
668
|
+
if (init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
669
|
+
init.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
670
|
+
const propertyName = init.property.name;
|
|
671
|
+
// DOM-specific properties
|
|
672
|
+
const isDomProperty = propertyName === 'parentElement' ||
|
|
673
|
+
propertyName === 'parentNode' ||
|
|
674
|
+
propertyName === 'firstChild' ||
|
|
675
|
+
propertyName === 'lastChild' ||
|
|
676
|
+
propertyName === 'nextSibling' ||
|
|
677
|
+
propertyName === 'previousSibling';
|
|
678
|
+
if (isDomProperty) {
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// Check for call expressions that return DOM elements
|
|
683
|
+
if (init.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
684
|
+
init.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
685
|
+
init.callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
686
|
+
const methodName = init.callee.property.name;
|
|
687
|
+
// DOM query methods
|
|
688
|
+
const isDomMethod = methodName === 'querySelector' ||
|
|
689
|
+
methodName === 'querySelectorAll' ||
|
|
690
|
+
methodName === 'getElementById' ||
|
|
691
|
+
methodName === 'getElementsByClassName' ||
|
|
692
|
+
methodName === 'getElementsByTagName';
|
|
693
|
+
if (isDomMethod) {
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Traverses an AST depth-first, skipping metadata keys, and stops early when the
|
|
702
|
+
* visitor returns true.
|
|
703
|
+
*/
|
|
704
|
+
const isTraversalMetadataKey = (key) => key === 'parent' || key === 'range' || key === 'loc';
|
|
705
|
+
function traverseAst(node, visitor) {
|
|
706
|
+
if (visitor(node))
|
|
707
|
+
return true;
|
|
708
|
+
for (const key in node) {
|
|
709
|
+
if (isTraversalMetadataKey(key))
|
|
710
|
+
continue;
|
|
711
|
+
const value = node[key];
|
|
712
|
+
if (Array.isArray(value)) {
|
|
713
|
+
for (const child of value) {
|
|
714
|
+
if (child &&
|
|
715
|
+
typeof child === 'object' &&
|
|
716
|
+
child.type) {
|
|
717
|
+
if (traverseAst(child, visitor)) {
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
else if (value &&
|
|
724
|
+
typeof value === 'object' &&
|
|
725
|
+
value.type) {
|
|
726
|
+
if (traverseAst(value, visitor)) {
|
|
727
|
+
return true;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
function collectReturnArguments(root) {
|
|
734
|
+
const results = [];
|
|
735
|
+
function walk(node) {
|
|
736
|
+
if (node.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
|
|
737
|
+
results.push(node.argument ?? null);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (node !== root &&
|
|
741
|
+
(node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
742
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
743
|
+
node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression)) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
for (const key in node) {
|
|
747
|
+
if (isTraversalMetadataKey(key))
|
|
748
|
+
continue;
|
|
749
|
+
const value = node[key];
|
|
750
|
+
if (Array.isArray(value)) {
|
|
751
|
+
for (const child of value) {
|
|
752
|
+
if (child &&
|
|
753
|
+
typeof child === 'object' &&
|
|
754
|
+
child.type) {
|
|
755
|
+
walk(child);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
else if (value &&
|
|
760
|
+
typeof value === 'object' &&
|
|
761
|
+
value.type) {
|
|
762
|
+
walk(value);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
walk(root);
|
|
767
|
+
return results;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Check if a variable is used in a while loop condition
|
|
771
|
+
* This searches the scope for while loops that use the variable in their condition
|
|
772
|
+
*/
|
|
773
|
+
function isUsedInWhileLoop(node) {
|
|
774
|
+
const variableName = node.name;
|
|
775
|
+
// Find the function or block scope containing this variable
|
|
776
|
+
let currentScope = node.parent;
|
|
777
|
+
while (currentScope &&
|
|
778
|
+
currentScope.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
779
|
+
currentScope = currentScope.parent;
|
|
780
|
+
}
|
|
781
|
+
if (!currentScope ||
|
|
782
|
+
currentScope.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
return traverseAst(currentScope, (searchNode) => {
|
|
786
|
+
return (searchNode.type === utils_1.AST_NODE_TYPES.WhileStatement &&
|
|
787
|
+
searchNode.test.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
788
|
+
searchNode.test.name === variableName);
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Check if a variable declarator has a tree traversal pattern
|
|
793
|
+
* This looks for patterns like reassignment to .parent, .parentElement, etc.
|
|
794
|
+
*/
|
|
795
|
+
function hasTreeTraversalPattern(declarator) {
|
|
796
|
+
if (declarator.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
const variableName = declarator.id.name;
|
|
800
|
+
// Find the function or block scope containing this variable
|
|
801
|
+
let currentScope = declarator.parent;
|
|
802
|
+
while (currentScope &&
|
|
803
|
+
currentScope.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
804
|
+
currentScope = currentScope.parent;
|
|
805
|
+
}
|
|
806
|
+
if (!currentScope ||
|
|
807
|
+
currentScope.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
return traverseAst(currentScope, (node) => {
|
|
811
|
+
if (node.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
812
|
+
node.left.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
813
|
+
node.left.name === variableName &&
|
|
814
|
+
node.right.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
815
|
+
node.right.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
816
|
+
const propertyName = node.right.property
|
|
817
|
+
.name;
|
|
818
|
+
// Check for DOM traversal properties
|
|
819
|
+
if (propertyName === 'parentElement' ||
|
|
820
|
+
propertyName === 'parentNode' ||
|
|
821
|
+
propertyName === 'nextSibling' ||
|
|
822
|
+
propertyName === 'previousSibling' ||
|
|
823
|
+
propertyName === 'firstChild' ||
|
|
824
|
+
propertyName === 'lastChild') {
|
|
825
|
+
return true;
|
|
826
|
+
}
|
|
827
|
+
// Check for generic tree traversal properties
|
|
828
|
+
if (propertyName === 'parent' ||
|
|
829
|
+
propertyName === 'child' ||
|
|
830
|
+
propertyName === 'left' ||
|
|
831
|
+
propertyName === 'right' ||
|
|
832
|
+
propertyName === 'next' ||
|
|
833
|
+
propertyName === 'prev' ||
|
|
834
|
+
propertyName === 'previous') {
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return false;
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Check if a variable is used in a while loop condition and is likely a boolean
|
|
843
|
+
* This helps identify variables that should be flagged as needing a boolean prefix
|
|
844
|
+
*/
|
|
845
|
+
function isLikelyBooleanInWhileLoop(node) {
|
|
846
|
+
// Check if the variable is initialized with a boolean-related value
|
|
847
|
+
const variableDeclarator = node.parent;
|
|
848
|
+
if (variableDeclarator?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
849
|
+
variableDeclarator.init) {
|
|
850
|
+
const init = variableDeclarator.init;
|
|
851
|
+
// Check for direct boolean initialization
|
|
852
|
+
if (init.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
853
|
+
typeof init.value === 'boolean') {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
// Check for property access with boolean-suggesting name
|
|
857
|
+
if (init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
858
|
+
init.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
859
|
+
const propertyName = init.property.name;
|
|
860
|
+
// If the property name suggests it's a boolean (starts with a boolean prefix)
|
|
861
|
+
const isBooleanProperty = approvedPrefixes.some((prefix) => propertyName.toLowerCase().startsWith(prefix.toLowerCase()));
|
|
862
|
+
if (isBooleanProperty) {
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
function getterReturnsBoolean(node) {
|
|
870
|
+
if (node.kind !== 'get') {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
const functionLike = node.value;
|
|
874
|
+
const returnAnnotation = functionLike?.returnType?.typeAnnotation ||
|
|
875
|
+
node.returnType
|
|
876
|
+
?.typeAnnotation;
|
|
877
|
+
if (returnAnnotation &&
|
|
878
|
+
returnAnnotation.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword) {
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
if (!functionLike?.body ||
|
|
882
|
+
functionLike.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
const returnArguments = collectReturnArguments(functionLike.body);
|
|
886
|
+
if (returnArguments.length === 0) {
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
for (const argument of returnArguments) {
|
|
890
|
+
const evaluation = evaluateBooleanishExpression(argument);
|
|
891
|
+
if (evaluation !== 'boolean') {
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
260
897
|
/**
|
|
261
898
|
* Check variable declarations for boolean naming
|
|
262
899
|
*/
|
|
@@ -267,8 +904,15 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
267
904
|
// Skip checking if it's a type predicate
|
|
268
905
|
if (isTypePredicate(node.id))
|
|
269
906
|
return;
|
|
907
|
+
// Skip checking if it's likely a DOM element used in a while loop
|
|
908
|
+
if (isLikelyDomElementInWhileLoop(node.id))
|
|
909
|
+
return;
|
|
270
910
|
// Check if it's a boolean variable
|
|
271
911
|
let isBooleanVar = hasBooleanTypeAnnotation(node.id) || hasInitialBooleanValue(node);
|
|
912
|
+
// Check if it's a boolean variable used in a while loop
|
|
913
|
+
if (!isBooleanVar && isLikelyBooleanInWhileLoop(node.id)) {
|
|
914
|
+
isBooleanVar = true;
|
|
915
|
+
}
|
|
272
916
|
// Check if it's an arrow function with boolean return type
|
|
273
917
|
if (node.init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
274
918
|
node.init.returnType?.typeAnnotation &&
|
|
@@ -283,6 +927,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
283
927
|
data: {
|
|
284
928
|
type: 'variable',
|
|
285
929
|
name: variableName,
|
|
930
|
+
capitalizedName: capitalizeFirst(variableName),
|
|
286
931
|
prefixes: formatPrefixes(),
|
|
287
932
|
},
|
|
288
933
|
});
|
|
@@ -327,6 +972,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
327
972
|
data: {
|
|
328
973
|
type: 'function',
|
|
329
974
|
name: functionName,
|
|
975
|
+
capitalizedName: capitalizeFirst(functionName),
|
|
330
976
|
prefixes: formatPrefixes(),
|
|
331
977
|
},
|
|
332
978
|
});
|
|
@@ -339,23 +985,34 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
339
985
|
if (node.key.type !== utils_1.AST_NODE_TYPES.Identifier)
|
|
340
986
|
return;
|
|
341
987
|
const methodName = node.key.name;
|
|
988
|
+
const isGetter = node.kind === 'get';
|
|
989
|
+
const returnAnnotation = node.value?.returnType?.typeAnnotation ||
|
|
990
|
+
node.returnType
|
|
991
|
+
?.typeAnnotation;
|
|
992
|
+
if (ignoreOverriddenGetters &&
|
|
993
|
+
isGetter &&
|
|
994
|
+
(node.override ||
|
|
995
|
+
node.abstract ||
|
|
996
|
+
!node.value?.body ||
|
|
997
|
+
node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition)) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
342
1000
|
// Skip checking if it's a type predicate
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
utils_1.AST_NODE_TYPES.TSTypePredicate) {
|
|
1001
|
+
if (returnAnnotation &&
|
|
1002
|
+
returnAnnotation.type === utils_1.AST_NODE_TYPES.TSTypePredicate) {
|
|
346
1003
|
return;
|
|
347
1004
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
node
|
|
351
|
-
|
|
352
|
-
!hasApprovedPrefix(methodName)) {
|
|
1005
|
+
const returnsBoolean = (returnAnnotation &&
|
|
1006
|
+
returnAnnotation.type === utils_1.AST_NODE_TYPES.TSBooleanKeyword) ||
|
|
1007
|
+
(isGetter && getterReturnsBoolean(node));
|
|
1008
|
+
if (returnsBoolean && !hasApprovedPrefix(methodName)) {
|
|
353
1009
|
context.report({
|
|
354
1010
|
node: node.key,
|
|
355
1011
|
messageId: 'missingBooleanPrefix',
|
|
356
1012
|
data: {
|
|
357
|
-
type: 'method',
|
|
1013
|
+
type: isGetter ? 'getter' : 'method',
|
|
358
1014
|
name: methodName,
|
|
1015
|
+
capitalizedName: capitalizeFirst(methodName),
|
|
359
1016
|
prefixes: formatPrefixes(),
|
|
360
1017
|
},
|
|
361
1018
|
});
|
|
@@ -388,6 +1045,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
388
1045
|
data: {
|
|
389
1046
|
type: 'property',
|
|
390
1047
|
name: propertyName,
|
|
1048
|
+
capitalizedName: capitalizeFirst(propertyName),
|
|
391
1049
|
prefixes: formatPrefixes(),
|
|
392
1050
|
},
|
|
393
1051
|
});
|
|
@@ -420,6 +1078,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
420
1078
|
data: {
|
|
421
1079
|
type: 'property',
|
|
422
1080
|
name: propertyName,
|
|
1081
|
+
capitalizedName: capitalizeFirst(propertyName),
|
|
423
1082
|
prefixes: formatPrefixes(),
|
|
424
1083
|
},
|
|
425
1084
|
});
|
|
@@ -429,12 +1088,96 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
429
1088
|
* Check if an identifier is imported from an external module
|
|
430
1089
|
*/
|
|
431
1090
|
function isImportedIdentifier(name) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
1091
|
+
if (importStatusCache.has(name)) {
|
|
1092
|
+
const cached = importStatusCache.get(name);
|
|
1093
|
+
if (cached !== undefined)
|
|
1094
|
+
return cached;
|
|
1095
|
+
}
|
|
1096
|
+
const variable = findVariableInScopes(name);
|
|
1097
|
+
if (!variable) {
|
|
1098
|
+
importStatusCache.set(name, false);
|
|
435
1099
|
return false;
|
|
436
|
-
|
|
437
|
-
|
|
1100
|
+
}
|
|
1101
|
+
const isImport = variable.defs.some((def) => def.type === 'ImportBinding');
|
|
1102
|
+
importStatusCache.set(name, isImport);
|
|
1103
|
+
return isImport;
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Check if a variable is used with an external API
|
|
1107
|
+
*/
|
|
1108
|
+
function isVariableUsedWithExternalApi(variableName) {
|
|
1109
|
+
if (externalApiUsageCache.has(variableName)) {
|
|
1110
|
+
const cached = externalApiUsageCache.get(variableName);
|
|
1111
|
+
if (cached !== undefined)
|
|
1112
|
+
return cached;
|
|
1113
|
+
}
|
|
1114
|
+
const variable = findVariableInScopes(variableName);
|
|
1115
|
+
if (!variable) {
|
|
1116
|
+
externalApiUsageCache.set(variableName, false);
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1119
|
+
for (const reference of variable.references) {
|
|
1120
|
+
if (reference.identifier === variable.identifiers[0])
|
|
1121
|
+
continue;
|
|
1122
|
+
const id = reference.identifier;
|
|
1123
|
+
const markAndReturnTrue = () => {
|
|
1124
|
+
externalApiUsageCache.set(variableName, true);
|
|
1125
|
+
return true;
|
|
1126
|
+
};
|
|
1127
|
+
if (id.parent?.type === utils_1.AST_NODE_TYPES.Property &&
|
|
1128
|
+
id.parent.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
|
1129
|
+
id.parent.parent.parent?.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
1130
|
+
id.parent.parent.parent.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
1131
|
+
isImportedIdentifier(id.parent.parent.parent.callee.name)) {
|
|
1132
|
+
return markAndReturnTrue();
|
|
1133
|
+
}
|
|
1134
|
+
if (id.parent?.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
1135
|
+
if (id.parent.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
1136
|
+
isImportedIdentifier(id.parent.callee.name)) {
|
|
1137
|
+
return markAndReturnTrue();
|
|
1138
|
+
}
|
|
1139
|
+
if (id.parent.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
1140
|
+
id.parent.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
1141
|
+
isImportedIdentifier(id.parent.callee.object.name)) {
|
|
1142
|
+
return markAndReturnTrue();
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
if (id.parent?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
1146
|
+
id.parent.parent?.type === utils_1.AST_NODE_TYPES.JSXAttribute &&
|
|
1147
|
+
id.parent.parent.parent?.type === utils_1.AST_NODE_TYPES.JSXOpeningElement &&
|
|
1148
|
+
id.parent.parent.parent.name.type === utils_1.AST_NODE_TYPES.JSXIdentifier &&
|
|
1149
|
+
isImportedIdentifier(id.parent.parent.parent.name.name)) {
|
|
1150
|
+
return markAndReturnTrue();
|
|
1151
|
+
}
|
|
1152
|
+
if (id.parent?.type === utils_1.AST_NODE_TYPES.JSXSpreadAttribute &&
|
|
1153
|
+
id.parent.parent?.type === utils_1.AST_NODE_TYPES.JSXOpeningElement &&
|
|
1154
|
+
id.parent.parent.name.type === utils_1.AST_NODE_TYPES.JSXIdentifier &&
|
|
1155
|
+
isImportedIdentifier(id.parent.parent.name.name)) {
|
|
1156
|
+
return markAndReturnTrue();
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
externalApiUsageCache.set(variableName, false);
|
|
1160
|
+
return false;
|
|
1161
|
+
}
|
|
1162
|
+
const HOOK_NAMES = new Set(['useMemo', 'useCallback', 'useState']);
|
|
1163
|
+
function findVariableFromReactHook(objectExpr) {
|
|
1164
|
+
let current = objectExpr;
|
|
1165
|
+
while (current) {
|
|
1166
|
+
if (current.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
|
|
1167
|
+
current = current.parent;
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
if (current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
|
|
1171
|
+
current.parent?.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
1172
|
+
current.parent.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
1173
|
+
HOOK_NAMES.has(current.parent.callee.name) &&
|
|
1174
|
+
current.parent.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
1175
|
+
current.parent.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
1176
|
+
return current.parent.parent.id.name;
|
|
1177
|
+
}
|
|
1178
|
+
current = current.parent;
|
|
1179
|
+
}
|
|
1180
|
+
return undefined;
|
|
438
1181
|
}
|
|
439
1182
|
/**
|
|
440
1183
|
* Check property definitions for boolean values
|
|
@@ -490,6 +1233,51 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
490
1233
|
}
|
|
491
1234
|
}
|
|
492
1235
|
}
|
|
1236
|
+
// Check if this property is in an object literal that's being assigned to a variable
|
|
1237
|
+
// This handles cases like const messageInputProps = { grow: true }
|
|
1238
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
|
1239
|
+
node.parent.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
1240
|
+
node.parent.parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
1241
|
+
const variableName = node.parent.parent.id.name;
|
|
1242
|
+
if (isVariableUsedWithExternalApi(variableName)) {
|
|
1243
|
+
isExternalApiCall = true;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
// Special case for useMemo and other React hooks
|
|
1247
|
+
// This handles cases like:
|
|
1248
|
+
// 1. const messageInputProps = useMemo(() => ({ grow: true }), [])
|
|
1249
|
+
// 2. const messageInputProps = useMemo(() => { return { grow: true }; }, [])
|
|
1250
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
1251
|
+
const hookVariable = findVariableFromReactHook(node.parent);
|
|
1252
|
+
if (hookVariable && isVariableUsedWithExternalApi(hookVariable)) {
|
|
1253
|
+
isExternalApiCall = true;
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
// Check if this property is in an object literal that's directly passed to an imported function
|
|
1257
|
+
// This handles cases like ExternalComponent({ grow: true })
|
|
1258
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
|
1259
|
+
node.parent.parent?.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
1260
|
+
node.parent.parent.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
1261
|
+
const calleeName = node.parent.parent.callee.name;
|
|
1262
|
+
if (isImportedIdentifier(calleeName)) {
|
|
1263
|
+
isExternalApiCall = true;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
// Check if this property is in an object literal that's directly passed as a JSX attribute
|
|
1267
|
+
// This handles cases like <ExternalComponent config={{ active: true }} />
|
|
1268
|
+
if (node.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression &&
|
|
1269
|
+
node.parent.parent?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
1270
|
+
node.parent.parent.parent?.type === utils_1.AST_NODE_TYPES.JSXAttribute &&
|
|
1271
|
+
node.parent.parent.parent.parent?.type ===
|
|
1272
|
+
utils_1.AST_NODE_TYPES.JSXOpeningElement) {
|
|
1273
|
+
const jsxOpeningElement = node.parent.parent.parent.parent;
|
|
1274
|
+
if (jsxOpeningElement.name.type === utils_1.AST_NODE_TYPES.JSXIdentifier) {
|
|
1275
|
+
const componentName = jsxOpeningElement.name.name;
|
|
1276
|
+
if (isImportedIdentifier(componentName)) {
|
|
1277
|
+
isExternalApiCall = true;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
493
1281
|
// Only report if it's not an external API call
|
|
494
1282
|
if (!isExternalApiCall) {
|
|
495
1283
|
context.report({
|
|
@@ -498,6 +1286,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
498
1286
|
data: {
|
|
499
1287
|
type: 'property',
|
|
500
1288
|
name: propertyName,
|
|
1289
|
+
capitalizedName: capitalizeFirst(propertyName),
|
|
501
1290
|
prefixes: formatPrefixes(),
|
|
502
1291
|
},
|
|
503
1292
|
});
|
|
@@ -541,6 +1330,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
541
1330
|
data: {
|
|
542
1331
|
type: 'property',
|
|
543
1332
|
name: propertyName,
|
|
1333
|
+
capitalizedName: capitalizeFirst(propertyName),
|
|
544
1334
|
prefixes: formatPrefixes(),
|
|
545
1335
|
},
|
|
546
1336
|
});
|
|
@@ -565,6 +1355,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
565
1355
|
data: {
|
|
566
1356
|
type: 'parameter',
|
|
567
1357
|
name: paramName,
|
|
1358
|
+
capitalizedName: capitalizeFirst(paramName),
|
|
568
1359
|
prefixes: formatPrefixes(),
|
|
569
1360
|
},
|
|
570
1361
|
});
|
|
@@ -587,6 +1378,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
587
1378
|
data: {
|
|
588
1379
|
type: 'property',
|
|
589
1380
|
name: propertyName,
|
|
1381
|
+
capitalizedName: capitalizeFirst(propertyName),
|
|
590
1382
|
prefixes: formatPrefixes(),
|
|
591
1383
|
},
|
|
592
1384
|
});
|
|
@@ -609,6 +1401,7 @@ exports.enforceBooleanNamingPrefixes = (0, createRule_1.createRule)({
|
|
|
609
1401
|
}
|
|
610
1402
|
},
|
|
611
1403
|
MethodDefinition: checkMethodDefinition,
|
|
1404
|
+
TSAbstractMethodDefinition: checkMethodDefinition,
|
|
612
1405
|
Property: checkProperty,
|
|
613
1406
|
ClassProperty: checkClassProperty,
|
|
614
1407
|
PropertyDefinition: checkClassPropertyDeclaration,
|