| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 | /*Based on ESLint builtin `no-negated-condition` rulehttps://github.com/eslint/eslint/blob/5c39425fc55ecc0b97bbd07ac22654c0eb4f789c/lib/rules/no-negated-condition.js*/'use strict';const {matches} = require('./selectors/index.js');const {	removeParentheses,	fixSpaceAroundKeyword,	addParenthesizesToReturnOrThrowExpression,} = require('./fix/index.js');const {	getParenthesizedRange,	isParenthesized,} = require('./utils/parentheses.js');const isOnSameLine = require('./utils/is-on-same-line.js');const needsSemicolon = require('./utils/needs-semicolon.js');const MESSAGE_ID = 'no-negated-condition';const messages = {	[MESSAGE_ID]: 'Unexpected negated condition.',};const selector = [	matches([		'IfStatement[alternate][alternate.type!="IfStatement"]',		'ConditionalExpression',	]),	matches([		'[test.type="UnaryExpression"][test.operator="!"]',		'[test.type="BinaryExpression"][test.operator="!="]',		'[test.type="BinaryExpression"][test.operator="!=="]',	]),].join('');function * convertNegatedCondition(fixer, node, sourceCode) {	const {test} = node;	if (test.type === 'UnaryExpression') {		const token = sourceCode.getFirstToken(test);		if (node.type === 'IfStatement') {			yield * removeParentheses(test.argument, fixer, sourceCode);		}		yield fixer.remove(token);		return;	}	const token = sourceCode.getTokenAfter(		test.left,		token => token.type === 'Punctuator' && token.value === test.operator,	);	yield fixer.replaceText(token, '=' + token.value.slice(1));}function * swapConsequentAndAlternate(fixer, node, sourceCode) {	const isIfStatement = node.type === 'IfStatement';	const [consequent, alternate] = [		node.consequent,		node.alternate,	].map(node => {		const range = getParenthesizedRange(node, sourceCode);		let text = sourceCode.text.slice(...range);		// `if (!a) b(); else c()` can't fix to `if (!a) c() else b();`		if (isIfStatement && node.type !== 'BlockStatement') {			text = `{${text}}`;		}		return {			range,			text,		};	});	if (consequent.text === alternate.text) {		return;	}	yield fixer.replaceTextRange(consequent.range, alternate.text);	yield fixer.replaceTextRange(alternate.range, consequent.text);}/** @param {import('eslint').Rule.RuleContext} context */const create = context => ({	[selector](node) {		return {			node: node.test,			messageId: MESSAGE_ID,			/** @param {import('eslint').Rule.RuleFixer} fixer */			* fix(fixer) {				const sourceCode = context.getSourceCode();				yield * convertNegatedCondition(fixer, node, sourceCode);				yield * swapConsequentAndAlternate(fixer, node, sourceCode);				if (					node.type !== 'ConditionalExpression'					|| node.test.type !== 'UnaryExpression'				) {					return;				}				yield * fixSpaceAroundKeyword(fixer, node, sourceCode);				const {test, parent} = node;				const [firstToken, secondToken] = sourceCode.getFirstTokens(test, 2);				if (					(parent.type === 'ReturnStatement' || parent.type === 'ThrowStatement')					&& parent.argument === node					&& !isOnSameLine(firstToken, secondToken)					&& !isParenthesized(node, sourceCode)					&& !isParenthesized(test, sourceCode)				) {					yield * addParenthesizesToReturnOrThrowExpression(fixer, parent, sourceCode);					return;				}				const tokenBefore = sourceCode.getTokenBefore(node);				if (needsSemicolon(tokenBefore, sourceCode, secondToken.value)) {					yield fixer.insertTextBefore(node, ';');				}			},		};	},});/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create,	meta: {		type: 'suggestion',		docs: {			description: 'Disallow negated conditions.',		},		fixable: 'code',		messages,	},};
 |