'use strict';
|
|
const kebabCase = require('kebab-case');
|
|
const rules = require('./rules');
|
|
|
|
const forbiddenLibs = ['lodash', 'lodash-es'];
|
|
|
|
function getAssignmentLeftHandSide(node) {
|
|
// For VariableDeclarator nodes, the left hand side is called `id`
|
|
// The `x` on `var x = 3;
|
|
if (node.type === 'VariableDeclarator') {
|
|
return node.id;
|
|
}
|
|
// For AssignmentExpression nodes, the left hand side is called `left`
|
|
// The `x` on `x = 3;
|
|
if (node.type === 'AssignmentExpression') {
|
|
return node.left;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
for (const rule in rules) {
|
|
const alternative = rules[rule].alternative;
|
|
const ruleName = rules[rule].ruleName || kebabCase(rule);
|
|
const forbiddenImports = { [`lodash/${rule}`]: 1, [`lodash-es/${rule}`]: 1, [`lodash.${rule.toLowerCase()}`]: 1 };
|
|
module.exports[ruleName] = {
|
|
create(context) {
|
|
return {
|
|
CallExpression(node) {
|
|
const callee = node.callee;
|
|
const objectName = callee.name || (callee.object && callee.object.name) || (callee.object && callee.object.callee && callee.object.callee.name);
|
|
|
|
if (objectName === 'require' && node.arguments.length === 1) {
|
|
const requiredModuleName = node.arguments[0].value;
|
|
const { parent } = node;
|
|
if (forbiddenLibs.includes(requiredModuleName)) {
|
|
const leftHandSide = getAssignmentLeftHandSide(parent);
|
|
// ex: const { indexOf } = require('lodash');
|
|
// ex: ({ indexOf } = require('lodash'));
|
|
if (leftHandSide && leftHandSide.type === 'ObjectPattern') {
|
|
leftHandSide.properties.forEach(property => {
|
|
if (property.key.name === rule) {
|
|
context.report({
|
|
node,
|
|
message: `{ ${rule} } = require('${requiredModuleName}') detected. Consider using the native ${alternative}`
|
|
});
|
|
}
|
|
});
|
|
}
|
|
} else if (forbiddenImports.hasOwnProperty(requiredModuleName)) {
|
|
// ex: const indexOf = require('lodash.indexof');
|
|
// ex: const indexOf = require('lodash/indexOf');
|
|
context.report({
|
|
node,
|
|
message: `require('${requiredModuleName}') detected. Consider using the native ${alternative}`
|
|
});
|
|
}
|
|
} else if ((objectName === '_' || objectName === 'lodash' || objectName === 'underscore') && callee.property && callee.property.name === rule) {
|
|
context.report({
|
|
node,
|
|
message: `Consider using the native ${alternative}`
|
|
});
|
|
}
|
|
},
|
|
ImportDeclaration(node) {
|
|
if (forbiddenLibs.includes(node.source.value)) {
|
|
// ex: import { indexOf } from 'lodash';
|
|
// ex: import { indexOf as x } from 'lodash';
|
|
node.specifiers.forEach(specifier => {
|
|
if (specifier.type === 'ImportSpecifier' && specifier.imported.name === rule) {
|
|
context.report({
|
|
node,
|
|
message: `Import { ${rule} } from '${node.source.value}' detected. Consider using the native ${alternative}`
|
|
});
|
|
}
|
|
});
|
|
} else if (forbiddenImports.hasOwnProperty(node.source.value)) {
|
|
// ex: import indexOf from 'lodash/indexOf';
|
|
// ex: import indexOf from 'lodash.indexof';
|
|
context.report({
|
|
node,
|
|
message: `Import from '${node.source.value}' detected. Consider using the native ${alternative}`
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|
|
}
|