eslint-plugin-lodash
Version:
Lodash specific linting rules for ESLint
129 lines (109 loc) • 5 kB
JavaScript
/**
* @fileoverview Rule to check if there's a JS native method in the lodash chain
*/
;
/**
* @fileoverview Rule to check if there's a JS native method in the lodash chain
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
schema: [{
enum: ['as-needed', 'array', 'string']
}]
},
create: function create(context) {
var _require = require('../util/lodashUtil');
var getLodashMethodVisitors = _require.getLodashMethodVisitors;
var _require2 = require('../util/methodDataUtil');
var isAliasOfMethod = _require2.isAliasOfMethod;
var objectPathMethods = {
regular: { methods: ['get', 'has', 'hasIn', 'set', 'unset', 'invoke'], index: 1 },
higherOrder: { methods: ['property', 'matchesProperty'], index: 0 }
};
var find = require('lodash/find');
var findIndex = require('lodash/findIndex');
var some = require('lodash/some');
var every = require('lodash/every');
var get = require('lodash/get');
var matches = require('lodash/matches');
var isPropAccess = function isPropAccess(x) {
return x === '.' || x === '[';
};
function endsWithPropAccess(str) {
return isPropAccess(str[str.length - 1]);
}
function startsWithPropAccess(str) {
return isPropAccess(str[0]);
}
function getIndexByMethodName(method, version) {
var isAliasOfSuspect = function isAliasOfSuspect(m) {
return isAliasOfMethod(version, m, method);
};
var pathMethodGroup = find(objectPathMethods, function (type) {
return some(type.methods, isAliasOfSuspect);
});
return pathMethodGroup ? pathMethodGroup.index : -1;
}
function getPropertyPathNode(node, method, version, callType) {
var index = getIndexByMethodName(method, version);
return node.arguments[callType === 'chained' ? index - 1 : index];
}
var isArrayExpression = matches({ type: 'ArrayExpression' });
var isLiteral = matches({ type: 'Literal' });
var isAddition = matches({ type: 'BinaryExpression', operator: '+' });
var isTemplateLiteral = matches({ type: 'TemplateLiteral' });
function isArrayOfLiterals(node) {
return isArrayExpression(node) && every(node.elements, isLiteral);
}
function isAdjacentToPropAccessInTemplate(exp, literal) {
var quasiAfterIndex = findIndex(literal.quasis, function (quasi) {
return quasi.start > exp.end;
});
var quasiBefore = literal.quasis[quasiAfterIndex - 1];
var quasiAfter = literal.quasis[quasiAfterIndex];
return quasiBefore && endsWithPropAccess(quasiBefore.value.raw) || quasiAfter && startsWithPropAccess(quasiAfter.value.raw);
}
function isTemplateStringWithVariableProps(node) {
return isTemplateLiteral(node) && some(node.expressions, function (exp) {
return isAdjacentToPropAccessInTemplate(exp, node);
});
}
function isStringConcatWithVariableProps(node) {
return isAddition(node) && (isLiteral(node.left) && endsWithPropAccess(node.left.value) || isLiteral(node.right) && startsWithPropAccess(node.right.value));
}
function isPathStringWithVariableProps(node) {
return isTemplateStringWithVariableProps(node) || isStringConcatWithVariableProps(node);
}
var reportIfViolates = {
'as-needed': function asNeeded(node) {
if (isArrayOfLiterals(node)) {
context.report(node, 'Use a string for simple paths');
} else if (isPathStringWithVariableProps(node)) {
context.report(node, 'Use an array for paths with variables');
}
},
array: function array(node) {
if (isLiteral(node)) {
context.report(node, 'Use an array for paths');
}
},
string: function string(node) {
if (isArrayExpression(node)) {
context.report(node, 'Use a string for paths');
}
}
};
return getLodashMethodVisitors(context, function (node, iteratee, _ref) {
var method = _ref.method;
var version = _ref.version;
var callType = _ref.callType;
var propertyPathNode = getPropertyPathNode(node, method, version, callType);
if (propertyPathNode) {
reportIfViolates[context.options[0] || 'as-needed'](propertyPathNode);
}
});
}
};