UNPKG

3.39 kBJavaScriptView Raw
1'use strict';
2const getDocumentationUrl = require('./utils/get-documentation-url');
3
4const operatorTypes = {
5 gt: ['>'],
6 gte: ['>='],
7 ne: ['!==', '!=']
8};
9
10function reportError(context, node, message, fixDetails) {
11 context.report({
12 node,
13 message,
14 fix: fixDetails && (fixer => {
15 return fixer.replaceText(
16 node,
17 `${context.getSourceCode().getText(fixDetails.node)} ${fixDetails.operator} ${fixDetails.value}`
18 );
19 })
20 });
21}
22
23function checkZeroType(context, node) {
24 if (node.operator === '<' && node.right.value === 1) {
25 reportError(
26 context,
27 node,
28 'Zero `.length` should be compared with `=== 0`.',
29 {
30 node: node.left,
31 operator: '===',
32 value: 0
33 }
34 );
35 }
36}
37
38function checkNonZeroType(context, node, type) {
39 const {value} = node.right;
40 const {operator} = node;
41
42 switch (type) {
43 case 'greater-than':
44 if (
45 (operatorTypes.gte.includes(operator) && value === 1) ||
46 (operatorTypes.ne.includes(operator) && value === 0)
47 ) {
48 reportError(
49 context,
50 node,
51 'Non-zero `.length` should be compared with `> 0`.',
52 {
53 node: node.left,
54 operator: '>',
55 value: 0
56 }
57 );
58 }
59
60 break;
61 case 'greater-than-or-equal':
62 if (
63 (operatorTypes.gt.includes(operator) && value === 0) ||
64 (operatorTypes.ne.includes(operator) && value === 0)
65 ) {
66 reportError(
67 context,
68 node,
69 'Non-zero `.length` should be compared with `>= 1`.',
70 {
71 node: node.left,
72 operator: '>=',
73 value: 1
74 }
75 );
76 }
77
78 break;
79 case 'not-equal':
80 if (
81 (operatorTypes.gt.includes(operator) && value === 0) ||
82 (operatorTypes.gte.includes(operator) && value === 1)
83 ) {
84 reportError(
85 context,
86 node,
87 'Non-zero `.length` should be compared with `!== 0`.',
88 {
89 node: node.left,
90 operator: '!==',
91 value: 0
92 }
93 );
94 }
95
96 break;
97 default:
98 break;
99 }
100}
101
102function checkBinaryExpression(context, node, options) {
103 if (
104 node.right.type === 'Literal' &&
105 node.left.type === 'MemberExpression' &&
106 node.left.property.type === 'Identifier' &&
107 node.left.property.name === 'length'
108 ) {
109 checkZeroType(context, node);
110 checkNonZeroType(context, node, options['non-zero']);
111 }
112}
113
114function checkExpression(context, node) {
115 if (node.type === 'LogicalExpression') {
116 checkExpression(context, node.left);
117 checkExpression(context, node.right);
118 return;
119 }
120
121 if (node.type === 'UnaryExpression' && node.operator === '!') {
122 checkExpression(context, node.argument);
123 return;
124 }
125
126 if (node.type === 'BinaryExpression') {
127 checkBinaryExpression(context, node, context.options[0] || {});
128 return;
129 }
130
131 if (
132 node.type === 'MemberExpression' &&
133 node.property.type === 'Identifier' &&
134 node.property.name === 'length' &&
135 !node.computed
136 ) {
137 reportError(context, node, '`length` property should be compared to a value.');
138 }
139}
140
141const create = context => {
142 return {
143 IfStatement: node => {
144 checkExpression(context, node.test);
145 },
146 ConditionalExpression: node => {
147 checkExpression(context, node.test);
148 }
149 };
150};
151
152const schema = [
153 {
154 type: 'object',
155 properties: {
156 'non-zero': {
157 enum: ['not-equal', 'greater-than', 'greater-than-or-equal']
158 }
159 }
160 }
161];
162
163module.exports = {
164 create,
165 meta: {
166 type: 'problem',
167 docs: {
168 url: getDocumentationUrl(__filename)
169 },
170 fixable: 'code',
171 schema
172 }
173};