99 lines
2.8 KiB
JavaScript
99 lines
2.8 KiB
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, context);
|
|
|
|
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, context)) {
|
|
yield addParenthesizesToReturnOrThrowExpression(fixer, parent, context);
|
|
}
|
|
}
|
|
|
|
yield fixer.remove(bangToken);
|
|
|
|
const previousToken = sourceCode.getTokenBefore(bangToken);
|
|
if (needsSemicolon(previousToken, context, 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: 'unopinionated',
|
|
},
|
|
|
|
hasSuggestions: true,
|
|
messages,
|
|
},
|
|
};
|
|
|
|
export default config;
|