diff --git a/eslint-plugin-expensify/CONST.js b/eslint-plugin-expensify/CONST.js index 7ff5ce1..5f6ca65 100644 --- a/eslint-plugin-expensify/CONST.js +++ b/eslint-plugin-expensify/CONST.js @@ -28,6 +28,7 @@ module.exports = { MUST_USE_VARIABLE_FOR_ASSIGNMENT: '{{key}} must be assigned as a variable instead of direct assignment.', NO_DEFAULT_PROPS: 'defaultProps should not be used in function components. Use default Arguments instead.', USE_PERIODS_ERROR_MESSAGES: 'Use periods at the end of error messages.', + USE_DOUBLE_NEGATION_INSTEAD_OF_BOOLEAN: 'Use !! instead of Boolean().', NO_ACC_SPREAD_IN_REDUCE: 'Avoid a use of spread (`...`) operator on accumulators in reduce callback. Mutate them directly instead.', }, }; diff --git a/eslint-plugin-expensify/tests/use-double-negation-instead-of-boolean.test.js b/eslint-plugin-expensify/tests/use-double-negation-instead-of-boolean.test.js new file mode 100644 index 0000000..e090682 --- /dev/null +++ b/eslint-plugin-expensify/tests/use-double-negation-instead-of-boolean.test.js @@ -0,0 +1,77 @@ +const RuleTester = require('eslint').RuleTester; +const rule = require('../use-double-negation-instead-of-boolean'); +const message = require('../CONST').MESSAGE.USE_DOUBLE_NEGATION_INSTEAD_OF_BOOLEAN; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + }, +}); + +ruleTester.run('use-double-negation-instead-of-Boolean()', rule, { + valid: [ + { + code: '!!test', + }, + { + code: '!!(test1 || test2)', + }, + { + code: '!!(test1 && test2)', + }, + { + code: '!!(test1 && (test2 || test3))', + }, + { + code: '!!(test1 || test2 && test3)', + }, + { + code: '!!test ? "" : "example"', + }, + ], + invalid: [ + { + code: 'Boolean(test)', + output: '!!test', + errors: [{ + message, + }], + }, + { + code: 'Boolean(test1 || test2)', + output: '!!(test1 || test2)', + errors: [{ + message, + }], + }, + { + code: 'Boolean(test1 && test2)', + output: '!!(test1 && test2)', + errors: [{ + message, + }], + }, + { + code: 'Boolean(test1 && (test2 || test3))', + output: '!!(test1 && (test2 || test3))', + errors: [{ + message, + }], + }, + { + code: 'Boolean(test1 || test2 && test3)', + output: '!!(test1 || test2 && test3)', + errors: [{ + message, + }], + }, + { + code: 'Boolean(test) ? "" : "example"', + output: '!!test ? "" : "example"', + errors: [{ + message, + }], + }, + ], +}); diff --git a/eslint-plugin-expensify/use-double-negation-instead-of-boolean.js b/eslint-plugin-expensify/use-double-negation-instead-of-boolean.js new file mode 100644 index 0000000..1f067d2 --- /dev/null +++ b/eslint-plugin-expensify/use-double-negation-instead-of-boolean.js @@ -0,0 +1,31 @@ +const message = require('./CONST').MESSAGE.USE_DOUBLE_NEGATION_INSTEAD_OF_BOOLEAN; + +module.exports = { + meta: { + fixable: 'code', + }, + create(context) { + return { + CallExpression(node) { + if (node.callee.type === 'Identifier' && node.callee.name === 'Boolean' && node.arguments.length === 1) { + const argument = node.arguments[0]; + const sourceCode = context.getSourceCode(); + const argumentText = sourceCode.getText(argument); + let fixedText = `!!${argumentText}`; + + if (argument.type === 'LogicalExpression' || argument.type === 'BinaryExpression') { + fixedText = `!!(${argumentText})`; + } + + context.report({ + node, + message, + fix(fixer) { + return fixer.replaceText(node, fixedText); + }, + }); + } + }, + }; + }, +}; diff --git a/rules/expensify.js b/rules/expensify.js index c484de1..54d4716 100644 --- a/rules/expensify.js +++ b/rules/expensify.js @@ -15,6 +15,7 @@ module.exports = { 'rulesdir/no-call-actions-from-actions': 'error', 'rulesdir/no-api-side-effects-method': 'error', 'rulesdir/prefer-localization': 'error', + 'rulesdir/use-double-negation-instead-of-boolean': 'error', 'rulesdir/no-acc-spread-in-reduce': 'error', 'no-restricted-imports': ['error', { paths: [{