123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- 'use strict';
- const {hasSideEffect, isParenthesized, findVariable} = require('@eslint-community/eslint-utils');
- const {matches, methodCallSelector} = require('../selectors/index.js');
- const isFunctionSelfUsedInside = require('../utils/is-function-self-used-inside.js');
- const getBinaryExpressionSelector = path => [
- `[${path}.type="BinaryExpression"]`,
- `[${path}.operator="==="]`,
- `:matches([${path}.left.type="Identifier"], [${path}.right.type="Identifier"])`,
- ].join('');
- const getFunctionSelector = path => [
- `[${path}.generator!=true]`,
- `[${path}.async!=true]`,
- `[${path}.params.length=1]`,
- `[${path}.params.0.type="Identifier"]`,
- ].join('');
- const callbackFunctionSelector = path => matches([
- // Matches `foo.findIndex(bar => bar === baz)`
- [
- `[${path}.type="ArrowFunctionExpression"]`,
- getFunctionSelector(path),
- getBinaryExpressionSelector(`${path}.body`),
- ].join(''),
- // Matches `foo.findIndex(bar => {return bar === baz})`
- // Matches `foo.findIndex(function (bar) {return bar === baz})`
- [
- `:matches([${path}.type="ArrowFunctionExpression"], [${path}.type="FunctionExpression"])`,
- getFunctionSelector(path),
- `[${path}.body.type="BlockStatement"]`,
- `[${path}.body.body.length=1]`,
- `[${path}.body.body.0.type="ReturnStatement"]`,
- getBinaryExpressionSelector(`${path}.body.body.0.argument`),
- ].join(''),
- ]);
- const isIdentifierNamed = ({type, name}, expectName) => type === 'Identifier' && name === expectName;
- function simpleArraySearchRule({method, replacement}) {
- // Add prefix to avoid conflicts in `prefer-includes` rule
- const MESSAGE_ID_PREFIX = `prefer-${replacement}-over-${method}/`;
- const ERROR = `${MESSAGE_ID_PREFIX}error`;
- const SUGGESTION = `${MESSAGE_ID_PREFIX}suggestion`;
- const ERROR_MESSAGES = {
- findIndex: 'Use `.indexOf()` instead of `.findIndex()` when looking for the index of an item.',
- findLastIndex: 'Use `.lastIndexOf()` instead of `findLastIndex() when looking for the index of an item.`',
- some: `Use \`.${replacement}()\` instead of \`.${method}()\` when checking value existence.`,
- };
- const messages = {
- [ERROR]: ERROR_MESSAGES[method],
- [SUGGESTION]: `Replace \`.${method}()\` with \`.${replacement}()\`.`,
- };
- const selector = [
- methodCallSelector({
- method,
- argumentsLength: 1,
- }),
- callbackFunctionSelector('arguments.0'),
- ].join('');
- function createListeners(context) {
- const sourceCode = context.getSourceCode();
- const {scopeManager} = sourceCode;
- return {
- [selector](node) {
- const [callback] = node.arguments;
- const binaryExpression = callback.body.type === 'BinaryExpression'
- ? callback.body
- : callback.body.body[0].argument;
- const [parameter] = callback.params;
- const {left, right} = binaryExpression;
- const {name} = parameter;
- let searchValueNode;
- let parameterInBinaryExpression;
- if (isIdentifierNamed(left, name)) {
- searchValueNode = right;
- parameterInBinaryExpression = left;
- } else if (isIdentifierNamed(right, name)) {
- searchValueNode = left;
- parameterInBinaryExpression = right;
- } else {
- return;
- }
- const callbackScope = scopeManager.acquire(callback);
- if (
- // `parameter` is used somewhere else
- findVariable(callbackScope, parameter).references.some(({identifier}) => identifier !== parameterInBinaryExpression)
- || isFunctionSelfUsedInside(callback, callbackScope)
- ) {
- return;
- }
- const method = node.callee.property;
- const problem = {
- node: method,
- messageId: ERROR,
- suggest: [],
- };
- const fix = function * (fixer) {
- let text = sourceCode.getText(searchValueNode);
- if (isParenthesized(searchValueNode, sourceCode) && !isParenthesized(callback, sourceCode)) {
- text = `(${text})`;
- }
- yield fixer.replaceText(method, replacement);
- yield fixer.replaceText(callback, text);
- };
- if (hasSideEffect(searchValueNode, sourceCode)) {
- problem.suggest.push({messageId: SUGGESTION, fix});
- } else {
- problem.fix = fix;
- }
- return problem;
- },
- };
- }
- return {messages, createListeners};
- }
- module.exports = simpleArraySearchRule;
|