UNPKG

eslint-plugin-react-snob

Version:
166 lines (165 loc) 7.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.requireDerivedConditionalPrefix = void 0; const utils_1 = require("@typescript-eslint/utils"); const utils_2 = require("../utils"); // Use shared naming utilities from utils // Use shared boolean expression utilities from utils /** * Checks if a logical expression is likely a string fallback pattern (e.g., error || 'default') */ function isStringFallbackPattern(node) { // Check if one side is a string literal if (node.right.type === utils_1.AST_NODE_TYPES.Literal && typeof node.right.value === 'string') { return true; } if (node.left.type === utils_1.AST_NODE_TYPES.Literal && typeof node.left.value === 'string') { return true; } return false; } /** * Checks if an expression is complex enough to warrant underscore prefix * (has logical operators or is a derived boolean expression) */ function isComplexDerivedExpression(node) { // Check if it's a derived boolean expression first if ((0, utils_2.isDerivedBooleanExpression)(node)) { return true; } // Check if it has logical operators (&&, ||) - these are likely used for conditional logic if ((0, utils_2.countLogicalOperators)(node) > 0) { // But exclude string fallback patterns like error || 'default' if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression && node.operator === '||') { if (isStringFallbackPattern(node)) { return false; } } return true; } return false; } // Use shared JSX detection utility from utils exports.requireDerivedConditionalPrefix = (0, utils_2.createRule)({ create(context) { // Keep track of derived conditional variables const derivedConditionals = new Set(); const variableNodes = new Map(); /** * Reports an error for a derived conditional variable that doesn't start with underscore */ function reportDerivedConditional(node, name) { if ((0, utils_2.hasUnderscorePrefix)(name)) return; context.report({ data: { name, suggested: (0, utils_2.suggestUnderscorePrefix)(name), }, messageId: 'derivedConditionalShouldStartWithUnderscore', node, }); } /** * Checks if an identifier is being used in a JSX conditional rendering context */ function checkIdentifierInJSXContext(node) { const variableName = node.name; if (!derivedConditionals.has(variableName)) return; let parent = node.parent; // Walk up the AST to check for JSX conditional contexts while (parent) { // JSX expression container: {variable && <Component />} if (parent.type === utils_1.AST_NODE_TYPES.JSXExpressionContainer) { const expression = parent.expression; // Logical AND for conditional rendering: variable && <JSX> if (expression.type === utils_1.AST_NODE_TYPES.LogicalExpression && expression.operator === '&&') { if (expression.left === node && (0, utils_2.containsJSX)(expression.right)) { const originalNode = variableNodes.get(variableName); if (originalNode) { reportDerivedConditional(originalNode, variableName); return; } } } // Ternary operator: variable ? <JSX> : <JSX> if (expression.type === utils_1.AST_NODE_TYPES.ConditionalExpression && expression.test === node) { if ((0, utils_2.containsJSX)(expression.consequent) || (0, utils_2.containsJSX)(expression.alternate)) { const originalNode = variableNodes.get(variableName); if (originalNode) { reportDerivedConditional(originalNode, variableName); return; } } } } // JSX attribute with condition prop: <Conditional condition={variable}> if (parent.type === utils_1.AST_NODE_TYPES.JSXAttribute) { const attrName = parent.name; if (attrName.type === utils_1.AST_NODE_TYPES.JSXIdentifier && (attrName.name === 'condition' || attrName.name === 'when' || attrName.name === 'if')) { const originalNode = variableNodes.get(variableName); if (originalNode) { reportDerivedConditional(originalNode, variableName); return; } } } // Ternary operator at top level: variable ? <Component /> : null if (parent.type === utils_1.AST_NODE_TYPES.ConditionalExpression && parent.test === node) { if ((0, utils_2.containsJSX)(parent.consequent) || (0, utils_2.containsJSX)(parent.alternate)) { const originalNode = variableNodes.get(variableName); if (originalNode) { reportDerivedConditional(originalNode, variableName); return; } } } // Logical expression at top level: variable && <Component /> if (parent.type === utils_1.AST_NODE_TYPES.LogicalExpression && parent.operator === '&&' && parent.left === node) { if ((0, utils_2.containsJSX)(parent.right)) { const originalNode = variableNodes.get(variableName); if (originalNode) { reportDerivedConditional(originalNode, variableName); return; } } } parent = parent.parent || null; } } return { // Check every identifier to see if it references a tracked variable in JSX context Identifier(node) { checkIdentifierInJSXContext(node); }, // Variable declarations (const, let, var) VariableDeclarator(node) { if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier) return; if (!node.init) return; const name = node.id.name; // Check if this is a derived boolean expression if (isComplexDerivedExpression(node.init)) { derivedConditionals.add(name); variableNodes.set(name, node.id); } }, }; }, defaultOptions: [], meta: { docs: { description: 'Enforce derived conditional variables used in JSX to start with underscore prefix', }, fixable: undefined, messages: { derivedConditionalShouldStartWithUnderscore: 'Derived conditional variable "{{name}}" used in JSX rendering should start with underscore prefix. Consider using "{{suggested}}" instead.', }, schema: [], type: 'suggestion', }, name: 'require-derived-conditional-prefix', });