| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 | 'use strict';const {findVariable, getFunctionHeadLocation} = require('@eslint-community/eslint-utils');const {matches, not, memberExpressionSelector} = require('./selectors/index.js');const ERROR_PROMISE = 'promise';const ERROR_IIFE = 'iife';const ERROR_IDENTIFIER = 'identifier';const SUGGESTION_ADD_AWAIT = 'add-await';const messages = {	[ERROR_PROMISE]: 'Prefer top-level await over using a promise chain.',	[ERROR_IIFE]: 'Prefer top-level await over an async IIFE.',	[ERROR_IDENTIFIER]: 'Prefer top-level await over an async function `{{name}}` call.',	[SUGGESTION_ADD_AWAIT]: 'Insert `await`.',};const promiseMethods = ['then', 'catch', 'finally'];const topLevelCallExpression = [	'CallExpression',	not([':function *', 'ClassDeclaration *', 'ClassExpression *']),].join('');const iife = [	topLevelCallExpression,	matches([		'[callee.type="FunctionExpression"]',		'[callee.type="ArrowFunctionExpression"]',	]),	'[callee.async!=false]',	'[callee.generator!=true]',].join('');const promise = [	topLevelCallExpression,	memberExpressionSelector({		path: 'callee',		properties: promiseMethods,		includeOptional: true,	}),].join('');const identifier = [	topLevelCallExpression,	'[callee.type="Identifier"]',].join('');const isPromiseMethodCalleeObject = node =>	node.parent.type === 'MemberExpression'	&& node.parent.object === node	&& !node.parent.computed	&& node.parent.property.type === 'Identifier'	&& promiseMethods.includes(node.parent.property.name)	&& node.parent.parent.type === 'CallExpression'	&& node.parent.parent.callee === node.parent;const isAwaitArgument = node => {	if (node.parent.type === 'ChainExpression') {		node = node.parent;	}	return node.parent.type === 'AwaitExpression' && node.parent.argument === node;};/** @param {import('eslint').Rule.RuleContext} context */function create(context) {	return {		[promise](node) {			if (isPromiseMethodCalleeObject(node) || isAwaitArgument(node)) {				return;			}			return {				node: node.callee.property,				messageId: ERROR_PROMISE,			};		},		[iife](node) {			if (isPromiseMethodCalleeObject(node) || isAwaitArgument(node)) {				return;			}			return {				node,				loc: getFunctionHeadLocation(node.callee, context.getSourceCode()),				messageId: ERROR_IIFE,			};		},		[identifier](node) {			if (isPromiseMethodCalleeObject(node) || isAwaitArgument(node)) {				return;			}			const variable = findVariable(context.getScope(), node.callee);			if (!variable || variable.defs.length !== 1) {				return;			}			const [definition] = variable.defs;			const value = definition.type === 'Variable' && definition.kind === 'const'				? definition.node.init				: definition.node;			if (				!value				|| !(					(						value.type === 'ArrowFunctionExpression'						|| value.type === 'FunctionExpression'						|| value.type === 'FunctionDeclaration'					) && !value.generator && value.async				)			) {				return;			}			return {				node,				messageId: ERROR_IDENTIFIER,				data: {name: node.callee.name},				suggest: [					{						messageId: SUGGESTION_ADD_AWAIT,						fix: fixer => fixer.insertTextBefore(node, 'await '),					},				],			};		},	};}/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create,	meta: {		type: 'suggestion',		docs: {			description: 'Prefer top-level await over top-level promises and async function calls.',		},		hasSuggestions: true,		messages,	},};
 |