Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
if (initialExpression.left !== initialIdentifierOrNotEqualsExpr) {
// the identifier is not the deepest left node
return;
}
if (!isValidChainTarget(initialIdentifierOrNotEqualsExpr, true)) {
return;
}
// walk up the tree to figure out how many logical expressions we can include
let previous: TSESTree.LogicalExpression = initialExpression;
let current: TSESTree.Node = initialExpression;
let previousLeftText = getText(initialIdentifierOrNotEqualsExpr);
let optionallyChainedCode = previousLeftText;
let expressionCount = 1;
while (current.type === AST_NODE_TYPES.LogicalExpression) {
if (
!isValidChainTarget(
current.right,
// only allow identifiers for the first chain - foo && foo()
expressionCount === 1,
)
) {
break;
}
const leftText = previousLeftText;
const rightText = getText(current.right);
// can't just use startsWith because of cases like foo && fooBar.baz;
const matchRegex = new RegExp(
`^${
// escape regex characters
function isMixedLogicalExpression(node: TSESTree.LogicalExpression): boolean {
const seen = new Set();
const queue = [node.parent, node.left, node.right];
for (const current of queue) {
if (seen.has(current)) {
continue;
}
seen.add(current);
if (current && current.type === AST_NODE_TYPES.LogicalExpression) {
if (current.operator === '&&') {
return true;
} else if (current.operator === '||') {
// check the pieces of the node to catch cases like `a || b || c && d`
queue.push(current.parent, current.left, current.right);
}
}
}
return false;
}
return false;
}
/*
* Navigate legal ancestors to determine whether this IIFE is outer.
* A "legal ancestor" is an expression or statement that causes the function to get executed immediately.
* For example, `!(function(){})()` is an outer IIFE even though it is preceded by a ! operator.
*/
let statement = node.parent && node.parent.parent;
while (
statement &&
((statement.type === AST_NODE_TYPES.UnaryExpression &&
['!', '~', '+', '-'].includes(statement.operator)) ||
statement.type === AST_NODE_TYPES.AssignmentExpression ||
statement.type === AST_NODE_TYPES.LogicalExpression ||
statement.type === AST_NODE_TYPES.SequenceExpression ||
statement.type === AST_NODE_TYPES.VariableDeclarator)
) {
statement = statement.parent;
}
return (
!!statement &&
(statement.type === AST_NODE_TYPES.ExpressionStatement ||
statement.type === AST_NODE_TYPES.VariableDeclaration) &&
!!statement.parent &&
statement.parent.type === AST_NODE_TYPES.Program
);
}
AST_NODE_TYPES.ConditionalExpression,
AST_NODE_TYPES.ContinueStatement,
AST_NODE_TYPES.DoWhileStatement,
AST_NODE_TYPES.DebuggerStatement,
AST_NODE_TYPES.EmptyStatement,
AST_NODE_TYPES.ExpressionStatement,
AST_NODE_TYPES.ForStatement,
AST_NODE_TYPES.ForInStatement,
AST_NODE_TYPES.ForOfStatement,
AST_NODE_TYPES.FunctionDeclaration,
AST_NODE_TYPES.FunctionExpression,
AST_NODE_TYPES.Identifier,
AST_NODE_TYPES.IfStatement,
AST_NODE_TYPES.Literal,
AST_NODE_TYPES.LabeledStatement,
AST_NODE_TYPES.LogicalExpression,
AST_NODE_TYPES.MemberExpression,
AST_NODE_TYPES.MetaProperty,
AST_NODE_TYPES.MethodDefinition,
AST_NODE_TYPES.NewExpression,
AST_NODE_TYPES.ObjectExpression,
AST_NODE_TYPES.ObjectPattern,
AST_NODE_TYPES.Program,
AST_NODE_TYPES.Property,
AST_NODE_TYPES.RestElement,
AST_NODE_TYPES.ReturnStatement,
AST_NODE_TYPES.SequenceExpression,
AST_NODE_TYPES.SpreadElement,
AST_NODE_TYPES.Super,
AST_NODE_TYPES.SwitchCase,
AST_NODE_TYPES.SwitchStatement,
AST_NODE_TYPES.TaggedTemplateExpression,
function checkIfLoopIsNecessaryConditional(
node:
| TSESTree.DoWhileStatement
| TSESTree.ForStatement
| TSESTree.WhileStatement,
): void {
if (
node.test === null ||
node.test.type === AST_NODE_TYPES.LogicalExpression
) {
return;
}
/**
* Allow:
* while (true) {}
* for (;true;) {}
* do {} while (true)
*/
if (
allowConstantLoopConditions &&
isBooleanLiteralType(getNodeType(node.test), true)
) {
return;
}
function checkIfTestExpressionIsNecessaryConditional(
node: TSESTree.ConditionalExpression | TSESTree.IfStatement,
): void {
if (node.test.type === AST_NODE_TYPES.LogicalExpression) {
return;
}
checkNode(node.test);
}
case AST_NODE_TYPES.ForStatement:
case AST_NODE_TYPES.MemberExpression:
case AST_NODE_TYPES.OptionalMemberExpression:
case AST_NODE_TYPES.SwitchStatement:
case AST_NODE_TYPES.UpdateExpression:
case AST_NODE_TYPES.WhileStatement:
return true;
case AST_NODE_TYPES.CallExpression:
case AST_NODE_TYPES.OptionalCallExpression:
return parent.callee === node;
case AST_NODE_TYPES.ConditionalExpression:
return parent.test === node;
case AST_NODE_TYPES.LogicalExpression:
return parent.operator !== '||';
case AST_NODE_TYPES.TaggedTemplateExpression:
return parent.tag === node;
case AST_NODE_TYPES.UnaryExpression:
return parent.operator === 'typeof';
case AST_NODE_TYPES.BinaryExpression:
return ['instanceof', '==', '!=', '===', '!=='].includes(parent.operator);
case AST_NODE_TYPES.TSNonNullExpression:
case AST_NODE_TYPES.TSAsExpression:
case AST_NODE_TYPES.TSTypeAssertion:
return isSafeUse(parent);
}
function assertTestExpressionContainsBoolean(
node: ExpressionWithTest,
): void {
if (
node.test !== null &&
node.test.type !== AST_NODE_TYPES.LogicalExpression &&
!isBooleanType(node.test)
) {
reportNode(node.test);
}
}
function isLogicalOrOperator(node: TSESTree.Node): boolean {
return (
node.type === AST_NODE_TYPES.LogicalExpression && node.operator === '||'
);
}