securityos/node_modules/eslint-plugin-unicorn/rules/no-array-method-this-argume...

187 lines
4.2 KiB
JavaScript

'use strict';
const {hasSideEffect} = require('@eslint-community/eslint-utils');
const {methodCallSelector, notFunctionSelector} = require('./selectors/index.js');
const {removeArgument} = require('./fix/index.js');
const {getParentheses, getParenthesizedText} = require('./utils/parentheses.js');
const shouldAddParenthesesToMemberExpressionObject = require('./utils/should-add-parentheses-to-member-expression-object.js');
const {isNodeMatches} = require('./utils/is-node-matches.js');
const ERROR = 'error';
const SUGGESTION_BIND = 'suggestion-bind';
const SUGGESTION_REMOVE = 'suggestion-remove';
const messages = {
[ERROR]: 'Do not use the `this` argument in `Array#{{method}}()`.',
[SUGGESTION_REMOVE]: 'Remove the second argument.',
[SUGGESTION_BIND]: 'Use a bound function.',
};
const ignored = [
'lodash.every',
'_.every',
'underscore.every',
'lodash.filter',
'_.filter',
'underscore.filter',
'Vue.filter',
'R.filter',
'lodash.find',
'_.find',
'underscore.find',
'R.find',
'lodash.findLast',
'_.findLast',
'underscore.findLast',
'R.findLast',
'lodash.findIndex',
'_.findIndex',
'underscore.findIndex',
'R.findIndex',
'lodash.findLastIndex',
'_.findLastIndex',
'underscore.findLastIndex',
'R.findLastIndex',
'lodash.flatMap',
'_.flatMap',
'lodash.forEach',
'_.forEach',
'React.Children.forEach',
'Children.forEach',
'R.forEach',
'lodash.map',
'_.map',
'underscore.map',
'React.Children.map',
'Children.map',
'jQuery.map',
'$.map',
'R.map',
'lodash.some',
'_.some',
'underscore.some',
];
const selector = [
methodCallSelector({
methods: [
'every',
'filter',
'find',
'findLast',
'findIndex',
'findLastIndex',
'flatMap',
'forEach',
'map',
'some',
],
argumentsLength: 2,
}),
notFunctionSelector('arguments.0'),
].join('');
function removeThisArgument(callExpression, sourceCode) {
return fixer => removeArgument(fixer, callExpression.arguments[1], sourceCode);
}
function useBoundFunction(callExpression, sourceCode) {
return function * (fixer) {
yield removeThisArgument(callExpression, sourceCode)(fixer);
const [callback, thisArgument] = callExpression.arguments;
const callbackParentheses = getParentheses(callback, sourceCode);
const isParenthesized = callbackParentheses.length > 0;
const callbackLastToken = isParenthesized
? callbackParentheses[callbackParentheses.length - 1]
: callback;
if (
!isParenthesized
&& shouldAddParenthesesToMemberExpressionObject(callback, sourceCode)
) {
yield fixer.insertTextBefore(callbackLastToken, '(');
yield fixer.insertTextAfter(callbackLastToken, ')');
}
const thisArgumentText = getParenthesizedText(thisArgument, sourceCode);
// `thisArgument` was a argument, no need add extra parentheses
yield fixer.insertTextAfter(callbackLastToken, `.bind(${thisArgumentText})`);
};
}
/** @param {import('eslint').Rule.RuleContext} context */
const create = context => {
const sourceCode = context.getSourceCode();
return {
[selector](callExpression) {
const {callee} = callExpression;
if (isNodeMatches(callee, ignored)) {
return;
}
const method = callee.property.name;
const [callback, thisArgument] = callExpression.arguments;
const problem = {
node: thisArgument,
messageId: ERROR,
data: {method},
};
const thisArgumentHasSideEffect = hasSideEffect(thisArgument, sourceCode);
const isArrowCallback = callback.type === 'ArrowFunctionExpression';
if (isArrowCallback) {
if (thisArgumentHasSideEffect) {
problem.suggest = [
{
messageId: SUGGESTION_REMOVE,
fix: removeThisArgument(callExpression, sourceCode),
},
];
} else {
problem.fix = removeThisArgument(callExpression, sourceCode);
}
return problem;
}
problem.suggest = [
{
messageId: SUGGESTION_REMOVE,
fix: removeThisArgument(callExpression, sourceCode),
},
{
messageId: SUGGESTION_BIND,
fix: useBoundFunction(callExpression, sourceCode),
},
];
return problem;
},
};
};
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
description: 'Disallow using the `this` argument in array methods.',
},
fixable: 'code',
hasSuggestions: true,
messages,
},
};