UNPKG

eslint-plugin-unicorn

Version:
207 lines (187 loc) 4.66 kB
'use strict'; const {isParenthesized} = require('eslint-utils'); const getDocumentationUrl = require('./utils/get-documentation-url'); const isLiteralValue = require('./utils/is-literal-value'); const isLogicalExpression = require('./utils/is-logical-expression'); const {isBooleanNode, getBooleanAncestor} = require('./utils/boolean'); const TYPE_NON_ZERO = 'non-zero'; const TYPE_ZERO = 'zero'; const MESSAGE_ID_SUGGESTION = 'suggestion'; const messages = { [TYPE_NON_ZERO]: 'Use `.length {{code}}` when checking length is not zero.', [TYPE_ZERO]: 'Use `.length {{code}}` when checking length is zero.', [MESSAGE_ID_SUGGESTION]: 'Replace `.length` with `.length {{code}}`.' }; const isCompareRight = (node, operator, value) => node.type === 'BinaryExpression' && node.operator === operator && isLiteralValue(node.right, value); const isCompareLeft = (node, operator, value) => node.type === 'BinaryExpression' && node.operator === operator && isLiteralValue(node.left, value); const nonZeroStyles = new Map([ [ 'greater-than', { code: '> 0', test: node => isCompareRight(node, '>', 0) } ], [ 'not-equal', { code: '!== 0', test: node => isCompareRight(node, '!==', 0) } ], [ 'greater-than-or-equal', { code: '>= 1', test: node => isCompareRight(node, '>=', 1) } ] ]); const zeroStyle = { code: '=== 0', test: node => isCompareRight(node, '===', 0) }; const lengthSelector = [ 'MemberExpression', '[computed=false]', '[property.type="Identifier"]', '[property.name="length"]' ].join(''); function getLengthCheckNode(node) { node = node.parent; // Zero length check if ( // `foo.length === 0` isCompareRight(node, '===', 0) || // `foo.length == 0` isCompareRight(node, '==', 0) || // `foo.length < 1` isCompareRight(node, '<', 1) || // `0 === foo.length` isCompareLeft(node, '===', 0) || // `0 == foo.length` isCompareLeft(node, '==', 0) || // `1 > foo.length` isCompareLeft(node, '>', 1) ) { return {isZeroLengthCheck: true, node}; } // Non-Zero length check if ( // `foo.length !== 0` isCompareRight(node, '!==', 0) || // `foo.length != 0` isCompareRight(node, '!=', 0) || // `foo.length > 0` isCompareRight(node, '>', 0) || // `foo.length >= 1` isCompareRight(node, '>=', 1) || // `0 !== foo.length` isCompareLeft(node, '!==', 0) || // `0 !== foo.length` isCompareLeft(node, '!=', 0) || // `0 < foo.length` isCompareLeft(node, '<', 0) || // `1 <= foo.length` isCompareLeft(node, '<=', 1) ) { return {isZeroLengthCheck: false, node}; } return {}; } function create(context) { const options = { 'non-zero': 'greater-than', ...context.options[0] }; const nonZeroStyle = nonZeroStyles.get(options['non-zero']); const sourceCode = context.getSourceCode(); function reportProblem({node, isZeroLengthCheck, lengthNode, autoFix}) { const {code, test} = isZeroLengthCheck ? zeroStyle : nonZeroStyle; if (test(node)) { return; } let fixed = `${sourceCode.getText(lengthNode)} ${code}`; if ( !isParenthesized(node, sourceCode) && node.type === 'UnaryExpression' && node.parent.type === 'UnaryExpression' ) { fixed = `(${fixed})`; } const fix = fixer => fixer.replaceText(node, fixed); const problem = { node, messageId: isZeroLengthCheck ? TYPE_ZERO : TYPE_NON_ZERO, data: {code} }; if (autoFix) { problem.fix = fix; } else { problem.suggest = [ { messageId: MESSAGE_ID_SUGGESTION, data: {code}, fix } ]; } context.report(problem); } return { [lengthSelector](lengthNode) { let node; let autoFix = true; let {isZeroLengthCheck, node: lengthCheckNode} = getLengthCheckNode(lengthNode); if (lengthCheckNode) { const {isNegative, node: ancestor} = getBooleanAncestor(lengthCheckNode); node = ancestor; if (isNegative) { isZeroLengthCheck = !isZeroLengthCheck; } } else { const {isNegative, node: ancestor} = getBooleanAncestor(lengthNode); if (isBooleanNode(ancestor)) { isZeroLengthCheck = isNegative; node = ancestor; } else if (isLogicalExpression(lengthNode.parent)) { isZeroLengthCheck = isNegative; node = lengthNode; autoFix = false; } } if (node) { reportProblem({node, isZeroLengthCheck, lengthNode, autoFix}); } } }; } const schema = [ { type: 'object', properties: { 'non-zero': { enum: [...nonZeroStyles.keys()], default: 'greater-than' } } } ]; module.exports = { create, meta: { type: 'problem', docs: { url: getDocumentationUrl(__filename) }, fixable: 'code', schema, messages } };