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