Skip to content

Commit

Permalink
Update: Add allowTaggedTemplates to no-unused-expressions (fixes #7632)
Browse files Browse the repository at this point in the history
  • Loading branch information
platinumazure committed Mar 15, 2017
1 parent 7276e6d commit 3929ad2
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
23 changes: 22 additions & 1 deletion docs/rules/no-unused-expressions.md
Expand Up @@ -29,7 +29,8 @@ Sequence expressions (those using a comma, such as `a = 1, b = 2`) are always co
This rule, in its default state, does not require any arguments. If you would like to enable one or more of the following you may pass an object with the options set as follows:

* `allowShortCircuit` set to `true` will allow you to use short circuit evaluations in your expressions (Default: `false`).
* `allowTernary` set to `true` will enable you use ternary operators in your expressions similarly to short circuit evaluations (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`).

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).

Expand All @@ -56,6 +57,8 @@ a() && function namedFunctionInExpressionContext () {f();}

(function anIncompleteIIFE () {});

injectGlobal`body{ color: red; }`

```

Note that one or more string expression statements (with or without semi-colons) will only be considered as unused if they are not in the beginning of a script, module, or function (alone and uninterrupted by other statements). Otherwise, they will be treated as part of a "directive prologue", a section potentially usable by JavaScript engines. This includes "strict mode" directives.
Expand Down Expand Up @@ -140,3 +143,21 @@ Examples of **correct** code for the `{ "allowShortCircuit": true, "allowTernary

a ? b() || (c = d) : e()
```

### allowTaggedTemplates

Examples of **incorrect** code for the `{ "allowTaggedTemplates": true }` option:

```js
/*eslint no-unused-expressions: ["error", { "allowTaggedTemplates": true }]*/

`some untagged template string`;
```

Examples of **correct** code for the `{ "allowTaggedTemplates": true }` option:

```js
/*eslint no-unused-expressions: ["error", { "allowTaggedTemplates": true }]*/

tag`some tagged template string`;
```
11 changes: 10 additions & 1 deletion lib/rules/no-unused-expressions.js
Expand Up @@ -25,6 +25,9 @@ module.exports = {
},
allowTernary: {
type: "boolean"
},
allowTaggedTemplates: {
type: "boolean"
}
},
additionalProperties: false
Expand All @@ -35,7 +38,8 @@ module.exports = {
create(context) {
const config = context.options[0] || {},
allowShortCircuit = config.allowShortCircuit || false,
allowTernary = config.allowTernary || false;
allowTernary = config.allowTernary || false,
allowTaggedTemplates = config.allowTaggedTemplates || false;

/**
* @param {ASTNode} node - any node
Expand Down Expand Up @@ -95,12 +99,17 @@ module.exports = {
return isValidExpression(node.consequent) && isValidExpression(node.alternate);
}
}

if (allowShortCircuit) {
if (node.type === "LogicalExpression") {
return isValidExpression(node.right);
}
}

if (allowTaggedTemplates && node.type === "TaggedTemplateExpression") {
return true;
}

return /^(?:Assignment|Call|New|Update|Yield|Await)Expression$/.test(node.type) ||
(node.type === "UnaryExpression" && ["delete", "void"].indexOf(node.operator) >= 0);
}
Expand Down
40 changes: 39 additions & 1 deletion tests/lib/rules/no-unused-expressions.js
Expand Up @@ -60,6 +60,16 @@ ruleTester.run("no-unused-expressions", rule, {
code: "async function foo() { foo ? await bar : await baz; }",
options: [{ allowTernary: true }],
parserOptions: { ecmaVersion: 8 }
},
{
code: "tag`tagged template literal`",
options: [{ allowTaggedTemplates: true }],
parserOptions: { ecmaVersion: 6 }
},
{
code: "shouldNotBeAffectedByAllowTemplateTagsOption()",
options: [{ allowTaggedTemplates: true }],
parserOptions: { ecmaVersion: 6 }
}
],
invalid: [
Expand All @@ -72,6 +82,16 @@ ruleTester.run("no-unused-expressions", rule, {
{ 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: "`untagged template literal`",
errors: ["Expected an assignment or function call and instead saw an expression."],
parserOptions: { ecmaVersion: 6 }
},
{
code: "tag`tagged template literal`",
errors: ["Expected an assignment or function call and instead saw an expression."],
parserOptions: { ecmaVersion: 6 }
},
{ 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 || b", options: [{ allowShortCircuit: true }], errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] },
Expand All @@ -85,6 +105,24 @@ ruleTester.run("no-unused-expressions", rule, {
{ code: "function foo() {\"directive one\"; f(); \"directive two\"; }", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] },
{ code: "if (0) { \"not a directive\"; f(); }", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] },
{ code: "function foo() { var foo = true; \"use strict\"; }", errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] },
{ code: "var foo = () => { var foo = true; \"use strict\"; }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] }
{ code: "var foo = () => { var foo = true; \"use strict\"; }", parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Expected an assignment or function call and instead saw an expression.", type: "ExpressionStatement" }] },
{
code: "`untagged template literal`",
errors: ["Expected an assignment or function call and instead saw an expression."],
options: [{ allowTaggedTemplates: true }],
parserOptions: { ecmaVersion: 6 }
},
{
code: "`untagged template literal`",
errors: ["Expected an assignment or function call and instead saw an expression."],
options: [{ allowTaggedTemplates: false }],
parserOptions: { ecmaVersion: 6 }
},
{
code: "tag`tagged template literal`",
errors: ["Expected an assignment or function call and instead saw an expression."],
options: [{ allowTaggedTemplates: false }],
parserOptions: { ecmaVersion: 6 }
}
]
});

0 comments on commit 3929ad2

Please sign in to comment.