| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 | 
							- 'use strict';
 
- const {isParenthesized} = require('eslint-utils');
 
- const avoidCapture = require('./utils/avoid-capture.js');
 
- const needsSemicolon = require('./utils/needs-semicolon.js');
 
- const isSameReference = require('./utils/is-same-reference.js');
 
- const getIndentString = require('./utils/get-indent-string.js');
 
- const {getParenthesizedText} = require('./utils/parentheses.js');
 
- const shouldAddParenthesesToConditionalExpressionChild = require('./utils/should-add-parentheses-to-conditional-expression-child.js');
 
- const {extendFixRange} = require('./fix/index.js');
 
- const getScopes = require('./utils/get-scopes.js');
 
- const messageId = 'prefer-ternary';
 
- const selector = [
 
- 	'IfStatement',
 
- 	':not(IfStatement > .alternate)',
 
- 	'[test.type!="ConditionalExpression"]',
 
- 	'[consequent]',
 
- 	'[alternate]',
 
- ].join('');
 
- const isTernary = node => node && node.type === 'ConditionalExpression';
 
- function getNodeBody(node) {
 
- 	/* c8 ignore next 3 */
 
- 	if (!node) {
 
- 		return;
 
- 	}
 
- 	if (node.type === 'ExpressionStatement') {
 
- 		return getNodeBody(node.expression);
 
- 	}
 
- 	if (node.type === 'BlockStatement') {
 
- 		const body = node.body.filter(({type}) => type !== 'EmptyStatement');
 
- 		if (body.length === 1) {
 
- 			return getNodeBody(body[0]);
 
- 		}
 
- 	}
 
- 	return node;
 
- }
 
- const isSingleLineNode = node => node.loc.start.line === node.loc.end.line;
 
- /** @param {import('eslint').Rule.RuleContext} context */
 
- const create = context => {
 
- 	const onlySingleLine = context.options[0] === 'only-single-line';
 
- 	const sourceCode = context.getSourceCode();
 
- 	const scopeToNamesGeneratedByFixer = new WeakMap();
 
- 	const isSafeName = (name, scopes) => scopes.every(scope => {
 
- 		const generatedNames = scopeToNamesGeneratedByFixer.get(scope);
 
- 		return !generatedNames || !generatedNames.has(name);
 
- 	});
 
- 	const getText = node => {
 
- 		let text = getParenthesizedText(node, sourceCode);
 
- 		if (
 
- 			!isParenthesized(node, sourceCode)
 
- 			&& shouldAddParenthesesToConditionalExpressionChild(node)
 
- 		) {
 
- 			text = `(${text})`;
 
- 		}
 
- 		return text;
 
- 	};
 
- 	function merge(options, mergeOptions) {
 
- 		const {
 
- 			before = '',
 
- 			after = ';',
 
- 			consequent,
 
- 			alternate,
 
- 			node,
 
- 		} = options;
 
- 		const {
 
- 			checkThrowStatement,
 
- 			returnFalseIfNotMergeable,
 
- 		} = {
 
- 			checkThrowStatement: false,
 
- 			returnFalseIfNotMergeable: false,
 
- 			...mergeOptions,
 
- 		};
 
- 		if (!consequent || !alternate || consequent.type !== alternate.type) {
 
- 			return returnFalseIfNotMergeable ? false : options;
 
- 		}
 
- 		const {type, argument, delegate, left, right, operator} = consequent;
 
- 		if (
 
- 			type === 'ReturnStatement'
 
- 			&& !isTernary(argument)
 
- 			&& !isTernary(alternate.argument)
 
- 		) {
 
- 			return merge({
 
- 				before: `${before}return `,
 
- 				after,
 
- 				consequent: argument === null ? 'undefined' : argument,
 
- 				alternate: alternate.argument === null ? 'undefined' : alternate.argument,
 
- 				node,
 
- 			});
 
- 		}
 
- 		if (
 
- 			type === 'YieldExpression'
 
- 			&& delegate === alternate.delegate
 
- 			&& !isTernary(argument)
 
- 			&& !isTernary(alternate.argument)
 
- 		) {
 
- 			return merge({
 
- 				before: `${before}yield${delegate ? '*' : ''} (`,
 
- 				after: `)${after}`,
 
- 				consequent: argument === null ? 'undefined' : argument,
 
- 				alternate: alternate.argument === null ? 'undefined' : alternate.argument,
 
- 				node,
 
- 			});
 
- 		}
 
- 		if (
 
- 			type === 'AwaitExpression'
 
- 			&& !isTernary(argument)
 
- 			&& !isTernary(alternate.argument)
 
- 		) {
 
- 			return merge({
 
- 				before: `${before}await (`,
 
- 				after: `)${after}`,
 
- 				consequent: argument,
 
- 				alternate: alternate.argument,
 
- 				node,
 
- 			});
 
- 		}
 
- 		if (
 
- 			checkThrowStatement
 
- 			&& type === 'ThrowStatement'
 
- 			&& !isTernary(argument)
 
- 			&& !isTernary(alternate.argument)
 
- 		) {
 
- 			// `ThrowStatement` don't check nested
 
- 			// If `IfStatement` is not a `BlockStatement`, need add `{}`
 
- 			const {parent} = node;
 
- 			const needBraces = parent && parent.type !== 'BlockStatement';
 
- 			return {
 
- 				type,
 
- 				before: `${before}${needBraces ? '{\n{{INDENT_STRING}}' : ''}const {{ERROR_NAME}} = `,
 
- 				after: `;\n{{INDENT_STRING}}throw {{ERROR_NAME}};${needBraces ? '\n}' : ''}`,
 
- 				consequent: argument,
 
- 				alternate: alternate.argument,
 
- 			};
 
- 		}
 
- 		if (
 
- 			type === 'AssignmentExpression'
 
- 			&& operator === alternate.operator
 
- 			&& !isTernary(left)
 
- 			&& !isTernary(alternate.left)
 
- 			&& !isTernary(right)
 
- 			&& !isTernary(alternate.right)
 
- 			&& isSameReference(left, alternate.left)
 
- 		) {
 
- 			return merge({
 
- 				before: `${before}${sourceCode.getText(left)} ${operator} `,
 
- 				after,
 
- 				consequent: right,
 
- 				alternate: alternate.right,
 
- 				node,
 
- 			});
 
- 		}
 
- 		return returnFalseIfNotMergeable ? false : options;
 
- 	}
 
- 	return {
 
- 		[selector](node) {
 
- 			const consequent = getNodeBody(node.consequent);
 
- 			const alternate = getNodeBody(node.alternate);
 
- 			if (
 
- 				onlySingleLine
 
- 				&& [consequent, alternate, node.test].some(node => !isSingleLineNode(node))
 
- 			) {
 
- 				return;
 
- 			}
 
- 			const result = merge({node, consequent, alternate}, {
 
- 				checkThrowStatement: true,
 
- 				returnFalseIfNotMergeable: true,
 
- 			});
 
- 			if (!result) {
 
- 				return;
 
- 			}
 
- 			const problem = {node, messageId};
 
- 			// Don't fix if there are comments
 
- 			if (sourceCode.getCommentsInside(node).length > 0) {
 
- 				return problem;
 
- 			}
 
- 			const scope = context.getScope();
 
- 			problem.fix = function * (fixer) {
 
- 				const testText = getText(node.test);
 
- 				const consequentText = typeof result.consequent === 'string'
 
- 					? result.consequent
 
- 					: getText(result.consequent);
 
- 				const alternateText = typeof result.alternate === 'string'
 
- 					? result.alternate
 
- 					: getText(result.alternate);
 
- 				let {type, before, after} = result;
 
- 				let generateNewVariables = false;
 
- 				if (type === 'ThrowStatement') {
 
- 					const scopes = getScopes(scope);
 
- 					const errorName = avoidCapture('error', scopes, isSafeName);
 
- 					for (const scope of scopes) {
 
- 						if (!scopeToNamesGeneratedByFixer.has(scope)) {
 
- 							scopeToNamesGeneratedByFixer.set(scope, new Set());
 
- 						}
 
- 						const generatedNames = scopeToNamesGeneratedByFixer.get(scope);
 
- 						generatedNames.add(errorName);
 
- 					}
 
- 					const indentString = getIndentString(node, sourceCode);
 
- 					after = after
 
- 						.replace('{{INDENT_STRING}}', indentString)
 
- 						.replace('{{ERROR_NAME}}', errorName);
 
- 					before = before
 
- 						.replace('{{INDENT_STRING}}', indentString)
 
- 						.replace('{{ERROR_NAME}}', errorName);
 
- 					generateNewVariables = true;
 
- 				}
 
- 				let fixed = `${before}${testText} ? ${consequentText} : ${alternateText}${after}`;
 
- 				const tokenBefore = sourceCode.getTokenBefore(node);
 
- 				const shouldAddSemicolonBefore = needsSemicolon(tokenBefore, sourceCode, fixed);
 
- 				if (shouldAddSemicolonBefore) {
 
- 					fixed = `;${fixed}`;
 
- 				}
 
- 				yield fixer.replaceText(node, fixed);
 
- 				if (generateNewVariables) {
 
- 					yield * extendFixRange(fixer, sourceCode.ast.range);
 
- 				}
 
- 			};
 
- 			return problem;
 
- 		},
 
- 	};
 
- };
 
- const schema = [
 
- 	{
 
- 		enum: ['always', 'only-single-line'],
 
- 		default: 'always',
 
- 	},
 
- ];
 
- /** @type {import('eslint').Rule.RuleModule} */
 
- module.exports = {
 
- 	create,
 
- 	meta: {
 
- 		type: 'suggestion',
 
- 		docs: {
 
- 			description: 'Prefer ternary expressions over simple `if-else` statements.',
 
- 		},
 
- 		fixable: 'code',
 
- 		schema,
 
- 		messages: {
 
- 			[messageId]: 'This `if` statement can be replaced by a ternary expression.',
 
- 		},
 
- 	},
 
- };
 
 
  |