diff --git a/README.md b/README.md index e695899..f4e0d71 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ This rule, in its default state, does not require any arguments. If you would li - `allowShortCircuit` set to `true` will allow you to use short circuit evaluations in your expressions (Default: `false`). - `allowTernary` set to `true` will enable you to use ternary operators in your expressions similarly to short circuit evaluations (Default: `false`). - `allowTaggedTemplates` set to `true` will enable you to use tagged template literals in your expressions (Default: `false`). +- `enforceForJSX` set to `true` will flag unused JSX element expressions (Default: `false`). These options allow unused expressions only if all of the code paths either directly change the state (for example, assignment statement) or could have side effects (for example, function call). diff --git a/lib/rules/no-unused-expressions.js b/lib/rules/no-unused-expressions.js index 3e83099..67353f5 100644 --- a/lib/rules/no-unused-expressions.js +++ b/lib/rules/no-unused-expressions.js @@ -48,6 +48,10 @@ module.exports = { }, allowTaggedTemplates: { type: "boolean" + }, + enforceForJSX: { + type: "boolean", + default: false } }, additionalProperties: false @@ -62,8 +66,8 @@ module.exports = { var config = context.options[0] || {}, allowShortCircuit = config.allowShortCircuit || false, allowTernary = config.allowTernary || false, - allowTaggedTemplates = config.allowTaggedTemplates || false; - + allowTaggedTemplates = config.allowTaggedTemplates || false, + enforceForJSX = config.enforceForJSX || false; /** * @param {ASTNode} node - any node @@ -135,6 +139,12 @@ module.exports = { FunctionExpression: alwaysTrue, Identifier: alwaysTrue, Literal: alwaysTrue, + JSXElement() { + return enforceForJSX; + }, + JSXFragment() { + return enforceForJSX; + }, LogicalExpression(node) { if (allowShortCircuit) { return Checker.isDisallowed(node.right); diff --git a/tests/lib/rules/no-unused-expressions.js b/tests/lib/rules/no-unused-expressions.js index 1059523..a8acfff 100644 --- a/tests/lib/rules/no-unused-expressions.js +++ b/tests/lib/rules/no-unused-expressions.js @@ -83,6 +83,29 @@ ruleTester.run("no-unused-expressions", rule, { code: "obj?.foo(\"bar\")", parserOptions: { ecmaVersion: 11 } }, + // JSX + { + code: "
", + parserOptions: { ecmaFeatures: { jsx: true } } + }, + { + code: "<>", + parserOptions: { ecmaFeatures: { jsx: true } } + }, + { + code: "var partial =
", + parserOptions: { ecmaFeatures: { jsx: true } } + }, + { + code: "var partial =
", + options: [{ enforceForJSX: true }], + parserOptions: { ecmaFeatures: { jsx: true } } + }, + { + code: "var partial = <>", + options: [{ enforceForJSX: true }], + parserOptions: { ecmaFeatures: { jsx: true } } + }, // Chai statements "expect(foo).to.be.true;", @@ -98,11 +121,11 @@ ruleTester.run("no-unused-expressions", rule, { { code: "{0}", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement"}]}, { code: "[]", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement"}]}, { code: "a && b();", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement"}]}, - { code: "a() || false", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }]}, - { code: "a || (b = c)", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }]}, - { code: "a ? b() || (c = d) : e", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }]}, - { code: "a && b()", options: [{ allowTernary: true }], errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }]}, - { code: "a ? b() : c()", options: [{ allowShortCircuit: true }], errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }]}, + { code: "a() || false", errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]}, + { code: "a || (b = c)", errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]}, + { code: "a ? b() || (c = d) : e", errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]}, + { code: "a && b()", options: [{ allowTernary: true }], errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]}, + { code: "a ? b() : c()", options: [{ allowShortCircuit: true }], errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]}, { code: "a || b", options: [{ allowShortCircuit: true }], errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement"}]}, { code: "a() && b", options: [{ allowShortCircuit: true }], errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement"}]}, { code: "a ? b : 0", options: [{ allowTernary: true }], errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement"}]}, @@ -160,9 +183,22 @@ ruleTester.run("no-unused-expressions", rule, { parserOptions: { ecmaVersion: 2020 }, errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }] }, + // JSX + { + code: "
", + options: [{ enforceForJSX: true }], + parserOptions: { ecmaFeatures: { jsx: true } }, + errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }] + }, + { + code: "<>", + options: [{ enforceForJSX: true }], + parserOptions: { ecmaFeatures: { jsx: true } }, + errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }] + }, // Chai statements - { code: "foo.expect('bar').not.to.pass;", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }]}, - { code: "should.not.pass;", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] }, + { code: "foo.expect('bar').not.to.pass;", errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }]}, + { code: "should.not.pass;", errors: [{ messageId: "unusedExpression", type: "ExpressionStatement" }] }, ] });