| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 | 'use strict';const {getParenthesizedText} = require('./utils/parentheses.js');const MESSAGE_ID = 'prefer-modern-math-apis';const messages = {	[MESSAGE_ID]: 'Prefer `{{replacement}}` over `{{description}}`.',};const isMathProperty = (node, property) =>	node.type === 'MemberExpression'	&& !node.optional	&& !node.computed	&& node.object.type === 'Identifier'	&& node.object.name === 'Math'	&& node.property.type === 'Identifier'	&& node.property.name === property;const isMathMethodCall = (node, method) =>	node.type === 'CallExpression'	&& !node.optional	&& isMathProperty(node.callee, method)	&& node.arguments.length === 1	&& node.arguments[0].type !== 'SpreadElement';// `Math.log(x) * Math.LOG10E` -> `Math.log10(x)`// `Math.LOG10E * Math.log(x)` -> `Math.log10(x)`// `Math.log(x) * Math.LOG2E` -> `Math.log2(x)`// `Math.LOG2E * Math.log(x)` -> `Math.log2(x)`function createLogCallTimesConstantCheck({constantName, replacementMethod}) {	const replacement = `Math.${replacementMethod}(…)`;	return function (node, context) {		if (!(node.type === 'BinaryExpression' && node.operator === '*')) {			return;		}		let mathLogCall;		let description;		if (isMathMethodCall(node.left, 'log') && isMathProperty(node.right, constantName)) {			mathLogCall = node.left;			description = `Math.log(…) * Math.${constantName}`;		} else if (isMathMethodCall(node.right, 'log') && isMathProperty(node.left, constantName)) {			mathLogCall = node.right;			description = `Math.${constantName} * Math.log(…)`;		}		if (!mathLogCall) {			return;		}		const [valueNode] = mathLogCall.arguments;		return {			node,			messageId: MESSAGE_ID,			data: {				replacement,				description,			},			fix: fixer => fixer.replaceText(node, `Math.${replacementMethod}(${getParenthesizedText(valueNode, context.getSourceCode())})`),		};	};}// `Math.log(x) / Math.LN10` -> `Math.log10(x)`// `Math.log(x) / Math.LN2` -> `Math.log2(x)`function createLogCallDivideConstantCheck({constantName, replacementMethod}) {	const message = {		messageId: MESSAGE_ID,		data: {			replacement: `Math.${replacementMethod}(…)`,			description: `Math.log(…) / Math.${constantName}`,		},	};	return function (node, context) {		if (			!(				node.type === 'BinaryExpression'				&& node.operator === '/'				&& isMathMethodCall(node.left, 'log')				&& isMathProperty(node.right, constantName)			)		) {			return;		}		const [valueNode] = node.left.arguments;		return {			...message,			node,			fix: fixer => fixer.replaceText(node, `Math.${replacementMethod}(${getParenthesizedText(valueNode, context.getSourceCode())})`),		};	};}const checkFunctions = [	createLogCallTimesConstantCheck({constantName: 'LOG10E', replacementMethod: 'log10'}),	createLogCallTimesConstantCheck({constantName: 'LOG2E', replacementMethod: 'log2'}),	createLogCallDivideConstantCheck({constantName: 'LN10', replacementMethod: 'log10'}),	createLogCallDivideConstantCheck({constantName: 'LN2', replacementMethod: 'log2'}),];/** @param {import('eslint').Rule.RuleContext} context */const create = context => {	const nodes = [];	return {		BinaryExpression(node) {			nodes.push(node);		},		* 'Program:exit'() {			for (const node of nodes) {				for (const getProblem of checkFunctions) {					const problem = getProblem(node, context);					if (problem) {						yield problem;					}				}			}		},	};};/** @type {import('eslint').Rule.RuleModule} */module.exports = {	create,	meta: {		type: 'suggestion',		docs: {			description: 'Prefer modern `Math` APIs over legacy patterns.',		},		fixable: 'code',		messages,	},};
 |