| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 | 'use strict';const {matches, methodCallSelector} = require('./selectors/index.js');const {getParenthesizedRange} = require('./utils/parentheses.js');const MESSAGE_ID_RESOLVE = 'resolve';const MESSAGE_ID_REJECT = 'reject';const messages = {	[MESSAGE_ID_RESOLVE]: 'Prefer `{{type}} value` over `{{type}} Promise.resolve(value)`.',	[MESSAGE_ID_REJECT]: 'Prefer `throw error` over `{{type}} Promise.reject(error)`.',};const selector = [	methodCallSelector({		object: 'Promise',		methods: ['resolve', 'reject'],	}),	matches([		'ArrowFunctionExpression > .body',		'ReturnStatement > .argument',		'YieldExpression[delegate!=true] > .argument',	]),].join('');const functionTypes = new Set([	'ArrowFunctionExpression',	'FunctionDeclaration',	'FunctionExpression',]);function getFunctionNode(node) {	let isInTryStatement = false;	let functionNode;	for (; node; node = node.parent) {		if (functionTypes.has(node.type)) {			functionNode = node;			break;		}		if (node.type === 'TryStatement') {			isInTryStatement = true;		}	}	return {		functionNode,		isInTryStatement,	};}function isPromiseCallback(node) {	if (		node.parent.type === 'CallExpression'		&& node.parent.callee.type === 'MemberExpression'		&& !node.parent.callee.computed		&& node.parent.callee.property.type === 'Identifier'	) {		const {callee: {property}, arguments: arguments_} = node.parent;		if (			arguments_.length === 1			&& (				property.name === 'then'				|| property.name === 'catch'				|| property.name === 'finally'			)			&& arguments_[0] === node		) {			return true;		}		if (			arguments_.length === 2			&& property.name === 'then'			&& (				arguments_[0] === node				|| (arguments_[0].type !== 'SpreadElement' && arguments_[1] === node)			)		) {			return true;		}	}	return false;}function createProblem(callExpression, fix) {	const {callee, parent} = callExpression;	const method = callee.property.name;	const type = parent.type === 'YieldExpression' ? 'yield' : 'return';	return {		node: callee,		messageId: method,		data: {type},		fix,	};}function fix(callExpression, isInTryStatement, sourceCode) {	if (callExpression.arguments.length > 1) {		return;	}	const {callee, parent, arguments: [errorOrValue]} = callExpression;	if (errorOrValue?.type === 'SpreadElement') {		return;	}	const isReject = callee.property.name === 'reject';	const isYieldExpression = parent.type === 'YieldExpression';	if (		isReject		&& (			isInTryStatement			|| (isYieldExpression && parent.parent.type !== 'ExpressionStatement')		)	) {		return;	}	return function (fixer) {		const isArrowFunctionBody = parent.type === 'ArrowFunctionExpression';		let text = errorOrValue ? sourceCode.getText(errorOrValue) : '';		if (errorOrValue?.type === 'SequenceExpression') {			text = `(${text})`;		}		if (isReject) {			// `return Promise.reject()` -> `throw undefined`			text = text || 'undefined';			text = `throw ${text}`;			if (isYieldExpression) {				return fixer.replaceTextRange(					getParenthesizedRange(parent, sourceCode),					text,				);			}			text += ';';			// `=> Promise.reject(error)` -> `=> { throw error; }`			if (isArrowFunctionBody) {				text = `{ ${text} }`;				return fixer.replaceTextRange(					getParenthesizedRange(callExpression, sourceCode),					text,				);			}		} else {			// eslint-disable-next-line no-lonely-if			if (isYieldExpression) {				text = `yield${text ? ' ' : ''}${text}`;			} else if (parent.type === 'ReturnStatement') {				text = `return${text ? ' ' : ''}${text};`;			} else {				if (errorOrValue?.type === 'ObjectExpression') {					text = `(${text})`;				}				// `=> Promise.resolve()` -> `=> {}`				text = text || '{}';			}		}		return fixer.replaceText(			isArrowFunctionBody ? callExpression : parent,			text,		);	};}/** @param {import('eslint').Rule.RuleContext} context */const create = context => {	const sourceCode = context.getSourceCode();	return {		[selector](callExpression) {			const {functionNode, isInTryStatement} = getFunctionNode(callExpression);			if (!functionNode || !(functionNode.async || isPromiseCallback(functionNode))) {				return;			}			return createProblem(				callExpression,				fix(callExpression, isInTryStatement, sourceCode),			);		},	};};/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create,	meta: {		type: 'suggestion',		docs: {			description: 'Disallow returning/yielding `Promise.resolve/reject()` in async functions or promise callbacks',		},		fixable: 'code',		messages,	},};
 |