@blumintinc/eslint-plugin-blumint 1.8.0 → 1.8.1
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 +84 -39
- package/lib/index.js +50 -2
- package/lib/rules/enforce-css-media-queries.d.ts +5 -0
- package/lib/rules/enforce-css-media-queries.js +87 -0
- package/lib/rules/enforce-dynamic-imports.d.ts +9 -0
- package/lib/rules/enforce-dynamic-imports.js +84 -0
- package/lib/rules/enforce-id-capitalization.d.ts +6 -0
- package/lib/rules/enforce-id-capitalization.js +78 -0
- package/lib/rules/enforce-mui-rounded-icons.d.ts +1 -0
- package/lib/rules/enforce-mui-rounded-icons.js +54 -0
- package/lib/rules/enforce-positive-naming.js +30 -6
- package/lib/rules/enforce-react-type-naming.d.ts +3 -0
- package/lib/rules/enforce-react-type-naming.js +191 -0
- package/lib/rules/enforce-singular-type-names.d.ts +2 -0
- package/lib/rules/enforce-singular-type-names.js +112 -0
- package/lib/rules/enforce-verb-noun-naming.js +5 -2
- package/lib/rules/ensure-pointer-events-none.d.ts +1 -0
- package/lib/rules/ensure-pointer-events-none.js +211 -0
- package/lib/rules/key-only-outermost-element.d.ts +3 -0
- package/lib/rules/key-only-outermost-element.js +152 -0
- package/lib/rules/no-always-true-false-conditions.js +19 -0
- package/lib/rules/no-circular-references.d.ts +1 -0
- package/lib/rules/no-circular-references.js +523 -0
- package/lib/rules/no-hungarian.d.ts +5 -0
- package/lib/rules/no-hungarian.js +223 -0
- package/lib/rules/no-object-values-on-strings.d.ts +2 -0
- package/lib/rules/no-object-values-on-strings.js +294 -0
- package/lib/rules/no-type-assertion-returns.js +10 -0
- package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
- package/lib/rules/no-unnecessary-destructuring.js +69 -0
- package/lib/rules/no-unused-props.js +10 -5
- package/lib/rules/no-unused-usestate.d.ts +8 -0
- package/lib/rules/no-unused-usestate.js +84 -0
- package/lib/rules/omit-index-html.d.ts +7 -0
- package/lib/rules/omit-index-html.js +91 -0
- package/lib/rules/prefer-usememo-over-useeffect-usestate.d.ts +5 -0
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -0
- package/lib/rules/react-usememo-should-be-component.js +369 -2
- package/package.json +7 -4
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensurePointerEventsNone = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule_1 = require("../utils/createRule");
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a string contains a pseudo-element selector (::before or ::after)
|
|
8
|
+
*/
|
|
9
|
+
function hasPseudoElementSelector(selector) {
|
|
10
|
+
return /::?(before|after)\b/i.test(selector);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a property name is position with absolute or fixed value
|
|
14
|
+
*/
|
|
15
|
+
function isAbsoluteOrFixedPosition(propertyName, propertyValue) {
|
|
16
|
+
if (propertyName !== 'position')
|
|
17
|
+
return false;
|
|
18
|
+
return propertyValue === 'absolute' || propertyValue === 'fixed';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a property is pointer-events with a value
|
|
22
|
+
*/
|
|
23
|
+
function isPointerEventsProperty(propertyName) {
|
|
24
|
+
return propertyName === 'pointerEvents' || propertyName === 'pointer-events';
|
|
25
|
+
}
|
|
26
|
+
exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
|
|
27
|
+
name: 'ensure-pointer-events-none',
|
|
28
|
+
meta: {
|
|
29
|
+
type: 'suggestion',
|
|
30
|
+
docs: {
|
|
31
|
+
description: 'Ensure pointer-events: none is added to non-interactive pseudo-elements',
|
|
32
|
+
recommended: 'error',
|
|
33
|
+
},
|
|
34
|
+
fixable: 'code',
|
|
35
|
+
schema: [],
|
|
36
|
+
messages: {
|
|
37
|
+
missingPointerEventsNone: 'Pseudo-elements (::before, ::after) with position: absolute or fixed should have pointer-events: none to prevent blocking interactions with underlying elements',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultOptions: [],
|
|
41
|
+
create(context) {
|
|
42
|
+
// Track style objects that have position: absolute or fixed
|
|
43
|
+
const absolutePositionedStyles = new Map();
|
|
44
|
+
// Track style objects that already have pointer-events defined
|
|
45
|
+
const stylesWithPointerEvents = new Map();
|
|
46
|
+
/**
|
|
47
|
+
* Process a CSS-in-JS style object to check for position: absolute/fixed and pointer-events
|
|
48
|
+
*/
|
|
49
|
+
function processStyleObject(node) {
|
|
50
|
+
let hasAbsolutePosition = false;
|
|
51
|
+
let pointerEventsValue;
|
|
52
|
+
// Check each property in the style object
|
|
53
|
+
for (const property of node.properties) {
|
|
54
|
+
if (property.type !== utils_1.AST_NODE_TYPES.Property)
|
|
55
|
+
continue;
|
|
56
|
+
let propertyName = '';
|
|
57
|
+
let propertyValue;
|
|
58
|
+
// Get property name
|
|
59
|
+
if (property.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
60
|
+
propertyName = property.key.name;
|
|
61
|
+
}
|
|
62
|
+
else if (property.key.type === utils_1.AST_NODE_TYPES.Literal && typeof property.key.value === 'string') {
|
|
63
|
+
propertyName = property.key.value;
|
|
64
|
+
}
|
|
65
|
+
// Get property value if it's a string literal
|
|
66
|
+
if (property.value.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
67
|
+
propertyValue = String(property.value.value);
|
|
68
|
+
}
|
|
69
|
+
else if (property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
70
|
+
propertyValue = property.value.name;
|
|
71
|
+
}
|
|
72
|
+
// Check if this is position: absolute/fixed
|
|
73
|
+
if (isAbsoluteOrFixedPosition(propertyName, propertyValue)) {
|
|
74
|
+
hasAbsolutePosition = true;
|
|
75
|
+
}
|
|
76
|
+
// Check if this is pointer-events property
|
|
77
|
+
if (isPointerEventsProperty(propertyName)) {
|
|
78
|
+
if (property.value.type === utils_1.AST_NODE_TYPES.Literal) {
|
|
79
|
+
pointerEventsValue = String(property.value.value);
|
|
80
|
+
}
|
|
81
|
+
else if (property.value.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
82
|
+
pointerEventsValue = property.value.name;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Store the results for this style object
|
|
87
|
+
absolutePositionedStyles.set(node, hasAbsolutePosition);
|
|
88
|
+
if (pointerEventsValue !== undefined) {
|
|
89
|
+
stylesWithPointerEvents.set(node, pointerEventsValue);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Check if a style object needs pointer-events: none
|
|
94
|
+
*/
|
|
95
|
+
function checkStyleObject(node, selector) {
|
|
96
|
+
const isPseudoElement = selector && hasPseudoElementSelector(selector);
|
|
97
|
+
const isAbsolutePositioned = absolutePositionedStyles.get(node) || false;
|
|
98
|
+
const pointerEventsValue = stylesWithPointerEvents.get(node);
|
|
99
|
+
// If this is a pseudo-element with absolute positioning but no pointer-events
|
|
100
|
+
if (isPseudoElement && isAbsolutePositioned && pointerEventsValue === undefined) {
|
|
101
|
+
context.report({
|
|
102
|
+
node,
|
|
103
|
+
messageId: 'missingPointerEventsNone',
|
|
104
|
+
fix(fixer) {
|
|
105
|
+
// Find the last property in the object
|
|
106
|
+
const sourceCode = context.getSourceCode();
|
|
107
|
+
const properties = node.properties;
|
|
108
|
+
if (properties.length === 0)
|
|
109
|
+
return null;
|
|
110
|
+
const lastProperty = properties[properties.length - 1];
|
|
111
|
+
const lastPropertyToken = sourceCode.getLastToken(lastProperty);
|
|
112
|
+
if (lastPropertyToken) {
|
|
113
|
+
return fixer.insertTextAfter(lastPropertyToken, `, pointerEvents: 'none'`);
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// If this is a pseudo-element with absolute positioning and pointer-events: auto
|
|
120
|
+
if (isPseudoElement && isAbsolutePositioned && pointerEventsValue === 'auto') {
|
|
121
|
+
// Don't report an error if pointer-events is explicitly set to 'auto'
|
|
122
|
+
// This is an intentional choice by the developer
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
// Check for pseudo-element selectors in styled-components and similar libraries
|
|
127
|
+
TaggedTemplateExpression(node) {
|
|
128
|
+
// Check if this is a styled-components template
|
|
129
|
+
const tag = node.tag;
|
|
130
|
+
let isStyledComponent = false;
|
|
131
|
+
if (tag.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
132
|
+
tag.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
133
|
+
tag.object.name === 'styled') {
|
|
134
|
+
isStyledComponent = true;
|
|
135
|
+
}
|
|
136
|
+
else if (tag.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
137
|
+
(tag.name === 'styled' || tag.name === 'css')) {
|
|
138
|
+
isStyledComponent = true;
|
|
139
|
+
}
|
|
140
|
+
if (isStyledComponent) {
|
|
141
|
+
// For styled-components, we need to check the template content
|
|
142
|
+
const template = node.quasi.quasis.map(q => q.value.raw).join('');
|
|
143
|
+
// Check if it contains a pseudo-element with position: absolute/fixed
|
|
144
|
+
if (hasPseudoElementSelector(template) &&
|
|
145
|
+
(template.includes('position: absolute') || template.includes('position: fixed'))) {
|
|
146
|
+
// Check if it's missing pointer-events: none
|
|
147
|
+
if (!template.includes('pointer-events: none') && !template.includes('pointer-events:none')) {
|
|
148
|
+
context.report({
|
|
149
|
+
node,
|
|
150
|
+
messageId: 'missingPointerEventsNone'
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
// Process style objects in JSX
|
|
157
|
+
JSXAttribute(node) {
|
|
158
|
+
if (node.name.type !== utils_1.AST_NODE_TYPES.JSXIdentifier || node.name.name !== 'style')
|
|
159
|
+
return;
|
|
160
|
+
if (node.value?.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer &&
|
|
161
|
+
node.value.expression.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
162
|
+
processStyleObject(node.value.expression);
|
|
163
|
+
checkStyleObject(node.value.expression);
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
// Process style objects in regular JavaScript/TypeScript
|
|
167
|
+
ObjectExpression(node) {
|
|
168
|
+
// Skip if parent is not a variable declaration or assignment
|
|
169
|
+
const parent = node.parent;
|
|
170
|
+
if (!parent)
|
|
171
|
+
return;
|
|
172
|
+
// Check if this might be a style object
|
|
173
|
+
let isStyleObject = false;
|
|
174
|
+
let selector;
|
|
175
|
+
if (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
176
|
+
parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
177
|
+
/style/i.test(parent.id.name)) {
|
|
178
|
+
isStyleObject = true;
|
|
179
|
+
}
|
|
180
|
+
else if (parent.type === utils_1.AST_NODE_TYPES.Property &&
|
|
181
|
+
parent.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
182
|
+
/style/i.test(parent.key.name)) {
|
|
183
|
+
isStyleObject = true;
|
|
184
|
+
}
|
|
185
|
+
else if (parent.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
186
|
+
// Check for CSS-in-JS libraries like emotion's css() function
|
|
187
|
+
const callee = parent.callee;
|
|
188
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier && callee.name === 'css') {
|
|
189
|
+
isStyleObject = true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (isStyleObject) {
|
|
193
|
+
processStyleObject(node);
|
|
194
|
+
checkStyleObject(node, selector);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
// Process CSS-in-JS libraries that use objects with selectors
|
|
198
|
+
Property(node) {
|
|
199
|
+
// Check for patterns like { '&::before': { ... } }
|
|
200
|
+
if (node.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
|
201
|
+
typeof node.key.value === 'string' &&
|
|
202
|
+
hasPseudoElementSelector(node.key.value) &&
|
|
203
|
+
node.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
204
|
+
processStyleObject(node.value);
|
|
205
|
+
checkStyleObject(node.value, node.key.value);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
//# sourceMappingURL=ensure-pointer-events-none.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
type MessageIds = 'keyOnlyOutermostElement' | 'fragmentShouldHaveKey';
|
|
2
|
+
export declare const keyOnlyOutermostElement: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
3
|
+
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.keyOnlyOutermostElement = void 0;
|
|
4
|
+
const createRule_1 = require("../utils/createRule");
|
|
5
|
+
exports.keyOnlyOutermostElement = (0, createRule_1.createRule)({
|
|
6
|
+
name: 'key-only-outermost-element',
|
|
7
|
+
meta: {
|
|
8
|
+
type: 'problem',
|
|
9
|
+
docs: {
|
|
10
|
+
description: 'Enforce that only the outermost element in list rendering has a key prop',
|
|
11
|
+
recommended: 'error',
|
|
12
|
+
},
|
|
13
|
+
messages: {
|
|
14
|
+
keyOnlyOutermostElement: 'Only the outermost element in a list rendering should have a key prop. Remove the key from this nested element.',
|
|
15
|
+
fragmentShouldHaveKey: 'Fragment used as the outermost element in a list should use <React.Fragment key={...}> instead of shorthand syntax.',
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
fixable: 'code',
|
|
19
|
+
},
|
|
20
|
+
defaultOptions: [],
|
|
21
|
+
create(context) {
|
|
22
|
+
// Track JSXElements that are direct children of a map callback
|
|
23
|
+
const mapCallbackElements = new Set();
|
|
24
|
+
// Track JSXFragments that are direct children of a map callback
|
|
25
|
+
const mapCallbackFragments = new Set();
|
|
26
|
+
// Track JSX attributes that have already been reported to avoid duplicate reports
|
|
27
|
+
const reportedAttributes = new Set();
|
|
28
|
+
// Helper function to process map calls
|
|
29
|
+
const processMapCall = (node) => {
|
|
30
|
+
// Get the callback function
|
|
31
|
+
const callback = node.arguments[0];
|
|
32
|
+
if (callback && (callback.type === 'ArrowFunctionExpression' ||
|
|
33
|
+
callback.type === 'FunctionExpression')) {
|
|
34
|
+
// Find the return statement or expression
|
|
35
|
+
let returnExpr = null;
|
|
36
|
+
if (callback.type === 'ArrowFunctionExpression' &&
|
|
37
|
+
callback.expression &&
|
|
38
|
+
callback.body.type !== 'BlockStatement') {
|
|
39
|
+
// Arrow function with implicit return
|
|
40
|
+
returnExpr = callback.body;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Look for return statements in the function body
|
|
44
|
+
const body = callback.body;
|
|
45
|
+
if (body.type === 'BlockStatement') {
|
|
46
|
+
for (const stmt of body.body) {
|
|
47
|
+
if (stmt.type === 'ReturnStatement' && stmt.argument) {
|
|
48
|
+
returnExpr = stmt.argument;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// If we found a JSX element or fragment as the return value, mark it
|
|
55
|
+
if (returnExpr) {
|
|
56
|
+
if (returnExpr.type === 'JSXElement') {
|
|
57
|
+
mapCallbackElements.add(returnExpr);
|
|
58
|
+
}
|
|
59
|
+
else if (returnExpr.type === 'JSXFragment') {
|
|
60
|
+
mapCallbackFragments.add(returnExpr);
|
|
61
|
+
// Check if it's a shorthand fragment (<>)
|
|
62
|
+
// Shorthand fragments can't have keys, so suggest using React.Fragment
|
|
63
|
+
context.report({
|
|
64
|
+
node: returnExpr.openingFragment,
|
|
65
|
+
messageId: 'fragmentShouldHaveKey',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
// Find array.map() calls
|
|
73
|
+
'CallExpression[callee.property.name="map"]'(node) {
|
|
74
|
+
processMapCall(node);
|
|
75
|
+
},
|
|
76
|
+
// Check all JSX elements for key props
|
|
77
|
+
JSXElement(node) {
|
|
78
|
+
// Skip if this is the outermost element in a map callback
|
|
79
|
+
if (mapCallbackElements.has(node)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Check if this element has a key prop
|
|
83
|
+
const openingElement = node.openingElement;
|
|
84
|
+
const attributes = openingElement.attributes;
|
|
85
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
86
|
+
const attr = attributes[i];
|
|
87
|
+
if (attr.type === 'JSXAttribute' && attr.name.name === 'key' && !reportedAttributes.has(attr)) {
|
|
88
|
+
// Check if this element is nested inside a map callback element or fragment
|
|
89
|
+
let parent = node.parent;
|
|
90
|
+
let isNestedInMapCallback = false;
|
|
91
|
+
while (parent) {
|
|
92
|
+
if ((parent.type === 'JSXElement' && mapCallbackElements.has(parent)) ||
|
|
93
|
+
(parent.type === 'JSXFragment' && mapCallbackFragments.has(parent))) {
|
|
94
|
+
isNestedInMapCallback = true;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
parent = parent.parent;
|
|
98
|
+
}
|
|
99
|
+
if (isNestedInMapCallback) {
|
|
100
|
+
// Mark this attribute as reported to avoid duplicate reports
|
|
101
|
+
reportedAttributes.add(attr);
|
|
102
|
+
const sourceCode = context.getSourceCode();
|
|
103
|
+
context.report({
|
|
104
|
+
node: attr,
|
|
105
|
+
messageId: 'keyOnlyOutermostElement',
|
|
106
|
+
fix(fixer) {
|
|
107
|
+
// Find the exact range of the attribute in the source code
|
|
108
|
+
const startPos = attr.range[0];
|
|
109
|
+
const endPos = attr.range[1];
|
|
110
|
+
// Get the text before and after the attribute to check for whitespace
|
|
111
|
+
const fullText = sourceCode.getText();
|
|
112
|
+
// Check if there's a space after the attribute
|
|
113
|
+
let rangeEnd = endPos;
|
|
114
|
+
if (rangeEnd < fullText.length && fullText[rangeEnd] === ' ') {
|
|
115
|
+
rangeEnd++;
|
|
116
|
+
}
|
|
117
|
+
return fixer.replaceTextRange([startPos, rangeEnd], '');
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
// Handle conditional expressions that might contain map callbacks
|
|
125
|
+
ConditionalExpression(node) {
|
|
126
|
+
// Check both the consequent and alternate branches
|
|
127
|
+
if (node.consequent.type === 'CallExpression' &&
|
|
128
|
+
node.consequent.callee.type === 'MemberExpression' &&
|
|
129
|
+
node.consequent.callee.property.type === 'Identifier' &&
|
|
130
|
+
node.consequent.callee.property.name === 'map') {
|
|
131
|
+
processMapCall(node.consequent);
|
|
132
|
+
}
|
|
133
|
+
if (node.alternate.type === 'CallExpression' &&
|
|
134
|
+
node.alternate.callee.type === 'MemberExpression' &&
|
|
135
|
+
node.alternate.callee.property.type === 'Identifier' &&
|
|
136
|
+
node.alternate.callee.property.name === 'map') {
|
|
137
|
+
processMapCall(node.alternate);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
// Handle logical expressions (&&, ||) that might contain map callbacks
|
|
141
|
+
LogicalExpression(node) {
|
|
142
|
+
if (node.right.type === 'CallExpression' &&
|
|
143
|
+
node.right.callee.type === 'MemberExpression' &&
|
|
144
|
+
node.right.callee.property.type === 'Identifier' &&
|
|
145
|
+
node.right.callee.property.name === 'map') {
|
|
146
|
+
processMapCall(node.right);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
//# sourceMappingURL=key-only-outermost-element.js.map
|
|
@@ -301,6 +301,25 @@ exports.noAlwaysTrueFalseConditions = (0, createRule_1.createRule)({
|
|
|
301
301
|
}
|
|
302
302
|
// For ||: if either side is always truthy, the whole expression is truthy
|
|
303
303
|
if (node.operator === '||') {
|
|
304
|
+
// Skip evaluation if this is a destructuring assignment with fallback pattern
|
|
305
|
+
if (node.parent) {
|
|
306
|
+
// Check for variable declarator with destructuring pattern
|
|
307
|
+
if (node.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
308
|
+
(node.parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
|
|
309
|
+
node.parent.id.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
|
|
310
|
+
// This is a destructuring with fallback pattern like: const { x } = obj || {}
|
|
311
|
+
// Don't flag this as an always true/false condition
|
|
312
|
+
return {};
|
|
313
|
+
}
|
|
314
|
+
// Check for assignment expression with destructuring pattern
|
|
315
|
+
if (node.parent.type === utils_1.AST_NODE_TYPES.AssignmentExpression &&
|
|
316
|
+
(node.parent.left.type === utils_1.AST_NODE_TYPES.ObjectPattern ||
|
|
317
|
+
node.parent.left.type === utils_1.AST_NODE_TYPES.ArrayPattern)) {
|
|
318
|
+
// This is a destructuring with fallback pattern like: ({ x } = obj || {})
|
|
319
|
+
// Don't flag this as an always true/false condition
|
|
320
|
+
return {};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
304
323
|
if (leftResult.isTruthy) {
|
|
305
324
|
// Short circuit for || - if left is truthy, right is never evaluated
|
|
306
325
|
return { isTruthy: true };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const noCircularReferences: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"circularReference", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|