UNPKG

eslint-plugin-react-snob

Version:
123 lines (122 loc) 4.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.noComplexJsxConditions = void 0; const utils_1 = require("@typescript-eslint/utils"); const utils_2 = require("../utils"); function hasComplexTernaryCondition(node) { // Check if test condition has logical operators const testLogicalCount = (0, utils_2.countLogicalOperators)(node.test); // Also check if consequent or alternate are complex ternaries let consequentComplex = false; let alternateComplex = false; if (node.consequent.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { consequentComplex = hasComplexTernaryCondition(node.consequent); } if (node.alternate.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { alternateComplex = hasComplexTernaryCondition(node.alternate); } return testLogicalCount > 0 || consequentComplex || alternateComplex; } function isComplexCondition(node) { // Count logical operators (&&, ||) const logicalCount = (0, utils_2.countLogicalOperators)(node); // Consider complex if more than 2 logical operators (allows simple a && b && c) if (logicalCount > 2) { return true; } // Check for complex ternary conditions if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) { return hasComplexTernaryCondition(node); } // For 2 or 3 logical operators, check if operands are complex if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression && logicalCount >= 2) { return hasComplexOperandsInChain(node); } // Check for template literals combined with logical operators (even single logical operator makes it complex) if (logicalCount >= 1 && (0, utils_2.hasTemplateLiteral)(node)) { return true; } // Check for logical assignment operators if ((0, utils_2.hasLogicalAssignment)(node)) { return true; } return false; } function hasComplexOperandsInChain(node) { // Check if the logical chain contains complex operands function checkOperand(operand) { return (0, utils_2.isComplexOperand)(operand); } function traverseLogical(expr) { if (expr.type === utils_1.AST_NODE_TYPES.LogicalExpression) { return traverseLogical(expr.left) || traverseLogical(expr.right); } return checkOperand(expr); } return traverseLogical(node); } function isInsideClassNameUtility(node) { // Check if this JSX expression is inside a className utility function call const parent = node.parent; if (parent && parent.type === utils_1.AST_NODE_TYPES.JSXAttribute) { const jsxAttribute = parent; if (jsxAttribute.name.type === utils_1.AST_NODE_TYPES.JSXIdentifier && jsxAttribute.name.name === 'className' && node.expression.type === utils_1.AST_NODE_TYPES.CallExpression) { const callExpression = node.expression; if (callExpression.callee.type === utils_1.AST_NODE_TYPES.Identifier) { const functionName = callExpression.callee.name; // Allow className utility functions return ['cn', 'clsx', 'cva', 'cx'].includes(functionName); } } } return false; } exports.noComplexJsxConditions = (0, utils_2.createRule)({ create(context) { const checkedNodes = new Set(); function checkExpression(node) { if (checkedNodes.has(node)) { return; } checkedNodes.add(node); if (node.expression.type === utils_1.AST_NODE_TYPES.JSXEmptyExpression) { return; } // Skip complex condition checking if inside className utility functions if (isInsideClassNameUtility(node)) { return; } if (isComplexCondition(node.expression)) { context.report({ messageId: 'complexCondition', node: node, }); } } return { JSXAttribute(node) { if (node.value && node.value.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer) { checkExpression(node.value); } }, JSXExpressionContainer(node) { checkExpression(node); }, }; }, defaultOptions: [], meta: { docs: { description: 'Disallow complex boolean conditions in JSX expressions and component props', }, fixable: undefined, messages: { complexCondition: 'Complex boolean condition found in JSX. Extract to a descriptive variable (e.g., _isReady, _canEdit) to improve readability.', }, schema: [], type: 'suggestion', }, name: 'no-complex-jsx-conditions', });