forked from daren.hsu/line_push
174 lines
3.3 KiB
JavaScript
174 lines
3.3 KiB
JavaScript
'use strict';
|
|
const getDocumentationUrl = require('./utils/get-documentation-url');
|
|
|
|
const operatorTypes = {
|
|
gt: ['>'],
|
|
gte: ['>='],
|
|
ne: ['!==', '!=']
|
|
};
|
|
|
|
function reportError(context, node, message, fixDetails) {
|
|
context.report({
|
|
node,
|
|
message,
|
|
fix: fixDetails && (fixer => {
|
|
return fixer.replaceText(
|
|
node,
|
|
`${context.getSourceCode().getText(fixDetails.node)} ${fixDetails.operator} ${fixDetails.value}`
|
|
);
|
|
})
|
|
});
|
|
}
|
|
|
|
function checkZeroType(context, node) {
|
|
if (node.operator === '<' && node.right.value === 1) {
|
|
reportError(
|
|
context,
|
|
node,
|
|
'Zero `.length` should be compared with `=== 0`.',
|
|
{
|
|
node: node.left,
|
|
operator: '===',
|
|
value: 0
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
function checkNonZeroType(context, node, type) {
|
|
const {value} = node.right;
|
|
const {operator} = node;
|
|
|
|
switch (type) {
|
|
case 'greater-than':
|
|
if (
|
|
(operatorTypes.gte.includes(operator) && value === 1) ||
|
|
(operatorTypes.ne.includes(operator) && value === 0)
|
|
) {
|
|
reportError(
|
|
context,
|
|
node,
|
|
'Non-zero `.length` should be compared with `> 0`.',
|
|
{
|
|
node: node.left,
|
|
operator: '>',
|
|
value: 0
|
|
}
|
|
);
|
|
}
|
|
|
|
break;
|
|
case 'greater-than-or-equal':
|
|
if (
|
|
(operatorTypes.gt.includes(operator) && value === 0) ||
|
|
(operatorTypes.ne.includes(operator) && value === 0)
|
|
) {
|
|
reportError(
|
|
context,
|
|
node,
|
|
'Non-zero `.length` should be compared with `>= 1`.',
|
|
{
|
|
node: node.left,
|
|
operator: '>=',
|
|
value: 1
|
|
}
|
|
);
|
|
}
|
|
|
|
break;
|
|
case 'not-equal':
|
|
if (
|
|
(operatorTypes.gt.includes(operator) && value === 0) ||
|
|
(operatorTypes.gte.includes(operator) && value === 1)
|
|
) {
|
|
reportError(
|
|
context,
|
|
node,
|
|
'Non-zero `.length` should be compared with `!== 0`.',
|
|
{
|
|
node: node.left,
|
|
operator: '!==',
|
|
value: 0
|
|
}
|
|
);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
function checkBinaryExpression(context, node, options) {
|
|
if (
|
|
node.right.type === 'Literal' &&
|
|
node.left.type === 'MemberExpression' &&
|
|
node.left.property.type === 'Identifier' &&
|
|
node.left.property.name === 'length'
|
|
) {
|
|
checkZeroType(context, node);
|
|
checkNonZeroType(context, node, options['non-zero']);
|
|
}
|
|
}
|
|
|
|
function checkExpression(context, node) {
|
|
if (node.type === 'LogicalExpression') {
|
|
checkExpression(context, node.left);
|
|
checkExpression(context, node.right);
|
|
return;
|
|
}
|
|
|
|
if (node.type === 'UnaryExpression' && node.operator === '!') {
|
|
checkExpression(context, node.argument);
|
|
return;
|
|
}
|
|
|
|
if (node.type === 'BinaryExpression') {
|
|
checkBinaryExpression(context, node, context.options[0] || {});
|
|
return;
|
|
}
|
|
|
|
if (
|
|
node.type === 'MemberExpression' &&
|
|
node.property.type === 'Identifier' &&
|
|
node.property.name === 'length' &&
|
|
!node.computed
|
|
) {
|
|
reportError(context, node, '`length` property should be compared to a value.');
|
|
}
|
|
}
|
|
|
|
const create = context => {
|
|
return {
|
|
IfStatement: node => {
|
|
checkExpression(context, node.test);
|
|
},
|
|
ConditionalExpression: node => {
|
|
checkExpression(context, node.test);
|
|
}
|
|
};
|
|
};
|
|
|
|
const schema = [
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
'non-zero': {
|
|
enum: ['not-equal', 'greater-than', 'greater-than-or-equal']
|
|
}
|
|
}
|
|
}
|
|
];
|
|
|
|
module.exports = {
|
|
create,
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
url: getDocumentationUrl(__filename)
|
|
},
|
|
fixable: 'code',
|
|
schema
|
|
}
|
|
};
|