eslint-plugin-react-snob
Version:
An ESLint plugin for React best practices
116 lines (115 loc) • 4.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isBooleanType = isBooleanType;
exports.isBooleanLiteral = isBooleanLiteral;
exports.isBooleanExpression = isBooleanExpression;
exports.isLikelyBooleanExpression = isLikelyBooleanExpression;
exports.isDerivedBooleanExpression = isDerivedBooleanExpression;
exports.isUseStateWithBoolean = isUseStateWithBoolean;
const utils_1 = require("@typescript-eslint/utils");
/**
* Checks if a type annotation is boolean or includes boolean
*/
function isBooleanType(typeAnnotation) {
if (!typeAnnotation)
return false;
const type = typeAnnotation.typeAnnotation;
if (type.type === 'TSBooleanKeyword') {
return true;
}
if (type.type === 'TSUnionType') {
return type.types.some((unionType) => unionType.type === 'TSBooleanKeyword');
}
return false;
}
/**
* Checks if a literal value is boolean
*/
function isBooleanLiteral(node) {
return node?.type === 'Literal' && typeof node.value === 'boolean';
}
/**
* Checks if an expression is likely to return a boolean value
*/
function isBooleanExpression(node) {
switch (node.type) {
case 'Literal':
return typeof node.value === 'boolean';
case 'UnaryExpression':
return node.operator === '!';
case 'BinaryExpression':
return ['==', '===', '!=', '!==', '<', '>', '<=', '>='].includes(node.operator);
case 'LogicalExpression':
// Only && and || return boolean-like values, ?? returns the right operand's value
if (node.operator === '??') {
return false; // Nullish coalescing doesn't necessarily return boolean
}
return true; // && and || result in boolean-like values
case 'ConditionalExpression':
return isBooleanExpression(node.consequent) || isBooleanExpression(node.alternate);
default:
return false;
}
}
/**
* Checks if an expression is likely to produce a boolean value
*/
function isLikelyBooleanExpression(node) {
switch (node.type) {
case utils_1.AST_NODE_TYPES.UnaryExpression:
// Negation operators like !, especially double negation !!
return node.operator === '!';
case utils_1.AST_NODE_TYPES.BinaryExpression:
// Comparison operators return boolean values
return ['==', '===', '!=', '!==', '<', '>', '<=', '>='].includes(node.operator);
case utils_1.AST_NODE_TYPES.ConditionalExpression:
// Ternary expressions that return boolean values
return isLikelyBooleanExpression(node.consequent) || isLikelyBooleanExpression(node.alternate);
case utils_1.AST_NODE_TYPES.LogicalExpression:
// For logical expressions, check if both operands are likely boolean
return isLikelyBooleanExpression(node.left) && isLikelyBooleanExpression(node.right);
case utils_1.AST_NODE_TYPES.Literal:
return typeof node.value === 'boolean';
case utils_1.AST_NODE_TYPES.Identifier:
// Identifiers that start with common boolean prefixes are likely boolean
return /^(is|has|can|should|will|does|did|was|were|am|are|be)[A-Z]/.test(node.name);
default:
return false;
}
}
/**
* Checks if an expression is a derived boolean expression that should require underscore prefix
*/
function isDerivedBooleanExpression(node) {
switch (node.type) {
case utils_1.AST_NODE_TYPES.LogicalExpression:
// Only consider logical expressions as derived boolean if BOTH sides are likely producing boolean values
// This filters out string fallback patterns like `error || 'default'`
return isLikelyBooleanExpression(node.left) && isLikelyBooleanExpression(node.right);
case utils_1.AST_NODE_TYPES.UnaryExpression:
// Negation operators like !, especially double negation !!
return node.operator === '!';
case utils_1.AST_NODE_TYPES.BinaryExpression:
// Comparison operators return boolean values
return ['==', '===', '!=', '!==', '<', '>', '<=', '>='].includes(node.operator);
case utils_1.AST_NODE_TYPES.ConditionalExpression:
// Ternary expressions that return boolean values
return isLikelyBooleanExpression(node.consequent) && isLikelyBooleanExpression(node.alternate);
default:
return false;
}
}
/**
* Checks if a CallExpression is a useState call with a boolean initial value
*/
function isUseStateWithBoolean(node) {
if (node.callee.type !== 'Identifier' || node.callee.name !== 'useState') {
return false;
}
if (node.arguments.length === 0)
return false;
const firstArg = node.arguments[0];
if (firstArg.type === 'SpreadElement')
return false;
return isBooleanLiteral(firstArg) || isBooleanExpression(firstArg);
}