eslint-plugin-unicorn
Version:
More than 100 powerful ESLint rules
100 lines (84 loc) • 2.89 kB
JavaScript
import {fixSpaceAroundKeyword, addParenthesizesToReturnOrThrowExpression} from './fix/index.js';
import {needsSemicolon, isParenthesized, isOnSameLine} from './utils/index.js';
const MESSAGE_ID_ERROR = 'no-negation-in-equality-check/error';
const MESSAGE_ID_SUGGESTION = 'no-negation-in-equality-check/suggestion';
const messages = {
[MESSAGE_ID_ERROR]: 'Negated expression is not allowed in equality check.',
[MESSAGE_ID_SUGGESTION]: 'Switch to \'{{operator}}\' check.',
};
const EQUALITY_OPERATORS = new Set([
'===',
'!==',
'==',
'!=',
]);
const isEqualityCheck = node => node.type === 'BinaryExpression' && EQUALITY_OPERATORS.has(node.operator);
const isNegatedExpression = node => node.type === 'UnaryExpression' && node.prefix && node.operator === '!';
/** @param {import('eslint').Rule.RuleContext} context */
const create = context => ({
BinaryExpression(binaryExpression) {
const {operator, left} = binaryExpression;
if (!(
isEqualityCheck(binaryExpression)
&& isNegatedExpression(left)
&& !isNegatedExpression(left.argument)
)) {
return;
}
const {sourceCode} = context;
const bangToken = sourceCode.getFirstToken(left);
const negatedOperator = `${operator.startsWith('!') ? '=' : '!'}${operator.slice(1)}`;
return {
node: bangToken,
messageId: MESSAGE_ID_ERROR,
/** @param {import('eslint').Rule.RuleFixer} fixer */
suggest: [
{
messageId: MESSAGE_ID_SUGGESTION,
data: {
operator: negatedOperator,
},
/** @param {import('eslint').Rule.RuleFixer} fixer */
* fix(fixer) {
yield * fixSpaceAroundKeyword(fixer, binaryExpression, sourceCode);
const tokenAfterBang = sourceCode.getTokenAfter(bangToken);
const {parent} = binaryExpression;
if (
(parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')
&& !isParenthesized(binaryExpression, sourceCode)
) {
const returnToken = sourceCode.getFirstToken(parent);
if (!isOnSameLine(returnToken, tokenAfterBang)) {
yield * addParenthesizesToReturnOrThrowExpression(fixer, parent, sourceCode);
}
}
yield fixer.remove(bangToken);
const previousToken = sourceCode.getTokenBefore(bangToken);
if (needsSemicolon(previousToken, sourceCode, tokenAfterBang.value)) {
yield fixer.insertTextAfter(bangToken, ';');
}
const operatorToken = sourceCode.getTokenAfter(
left,
token => token.type === 'Punctuator' && token.value === operator,
);
yield fixer.replaceText(operatorToken, negatedOperator);
},
},
],
};
},
});
/** @type {import('eslint').Rule.RuleModule} */
const config = {
create,
meta: {
type: 'problem',
docs: {
description: 'Disallow negated expression in equality check.',
recommended: true,
},
hasSuggestions: true,
messages,
},
};
export default config;