forked from daren.hsu/line_push
152 lines
3.1 KiB
JavaScript
152 lines
3.1 KiB
JavaScript
'use strict';
|
|
const {findVariable} = require('eslint-utils');
|
|
const getDocumentationUrl = require('./utils/get-documentation-url');
|
|
const getVariableIdentifiers = require('./utils/get-variable-identifiers');
|
|
const methodSelector = require('./utils/method-selector');
|
|
|
|
// `[]`
|
|
const arrayExpressionSelector = [
|
|
'[init.type="ArrayExpression"]'
|
|
].join('');
|
|
|
|
// `Array()`
|
|
const ArraySelector = [
|
|
'[init.type="CallExpression"]',
|
|
'[init.callee.type="Identifier"]',
|
|
'[init.callee.name="Array"]'
|
|
].join('');
|
|
|
|
// `new Array()`
|
|
const newArraySelector = [
|
|
'[init.type="NewExpression"]',
|
|
'[init.callee.type="Identifier"]',
|
|
'[init.callee.name="Array"]'
|
|
].join('');
|
|
|
|
// `Array.from()`
|
|
// `Array.of()`
|
|
const arrayStaticMethodSelector = methodSelector({
|
|
object: 'Array',
|
|
names: ['from', 'of'],
|
|
property: 'init'
|
|
});
|
|
|
|
// `array.concat()`
|
|
// `array.copyWithin()`
|
|
// `array.fill()`
|
|
// `array.filter()`
|
|
// `array.flat()`
|
|
// `array.flatMap()`
|
|
// `array.map()`
|
|
// `array.reverse()`
|
|
// `array.slice()`
|
|
// `array.sort()`
|
|
// `array.splice()`
|
|
const arrayMethodSelector = methodSelector({
|
|
names: [
|
|
'concat',
|
|
'copyWithin',
|
|
'fill',
|
|
'filter',
|
|
'flat',
|
|
'flatMap',
|
|
'map',
|
|
'reverse',
|
|
'slice',
|
|
'sort',
|
|
'splice'
|
|
],
|
|
property: 'init'
|
|
});
|
|
|
|
const selector = [
|
|
'VariableDeclaration',
|
|
// Exclude `export const foo = [];`
|
|
`:not(${
|
|
[
|
|
'ExportNamedDeclaration',
|
|
'>',
|
|
'VariableDeclaration.declaration'
|
|
].join('')
|
|
})`,
|
|
'>',
|
|
'VariableDeclarator.declarations',
|
|
`:matches(${
|
|
[
|
|
arrayExpressionSelector,
|
|
ArraySelector,
|
|
newArraySelector,
|
|
arrayStaticMethodSelector,
|
|
arrayMethodSelector
|
|
].join(',')
|
|
})`,
|
|
'>',
|
|
'Identifier.id'
|
|
].join('');
|
|
|
|
const MESSAGE_ID = 'preferSetHas';
|
|
|
|
const isIncludesCall = node => {
|
|
/* istanbul ignore next */
|
|
if (!node.parent || !node.parent.parent) {
|
|
return false;
|
|
}
|
|
|
|
const {type, optional, callee, arguments: parameters} = node.parent.parent;
|
|
return (
|
|
type === 'CallExpression' &&
|
|
!optional,
|
|
callee &&
|
|
callee.type === 'MemberExpression' &&
|
|
!callee.computed &&
|
|
callee.object === node &&
|
|
callee.property.type === 'Identifier' &&
|
|
callee.property.name === 'includes' &&
|
|
parameters.length === 1 &&
|
|
parameters[0].type !== 'SpreadElement'
|
|
);
|
|
};
|
|
|
|
const create = context => {
|
|
return {
|
|
[selector]: node => {
|
|
const variable = findVariable(context.getScope(), node);
|
|
const identifiers = getVariableIdentifiers(variable).filter(identifier => identifier !== node);
|
|
|
|
if (
|
|
identifiers.length === 0 ||
|
|
identifiers.some(node => !isIncludesCall(node))
|
|
) {
|
|
return;
|
|
}
|
|
|
|
context.report({
|
|
node,
|
|
messageId: MESSAGE_ID,
|
|
data: {
|
|
name: node.name
|
|
},
|
|
fix: fixer => [
|
|
fixer.insertTextBefore(node.parent.init, 'new Set('),
|
|
fixer.insertTextAfter(node.parent.init, ')'),
|
|
...identifiers.map(identifier => fixer.replaceText(identifier.parent.property, 'has'))
|
|
]
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
module.exports = {
|
|
create,
|
|
meta: {
|
|
type: 'suggestion',
|
|
docs: {
|
|
url: getDocumentationUrl(__filename)
|
|
},
|
|
fixable: 'code',
|
|
messages: {
|
|
[MESSAGE_ID]: '`{{name}}` should be a `Set`, and use `{{name}}.has()` to check existence or non-existence.'
|
|
}
|
|
}
|
|
};
|