Skip to content

Commit

Permalink
Update: Add a 'requireForBlockBody' modifier to the 'arrow-parens' ru…
Browse files Browse the repository at this point in the history
…le (fixes #6557)

The `'as-needed'` option can now be modified with the `{requireForBlockBody: true}` option allowing to
 require parentheses for arrow functions whose body is wrapped into curly braces like the following:
 ```js
 (a) => {}
 ```
  • Loading branch information
nfroidure committed Jul 22, 2016
1 parent e2b2030 commit 8bd0d94
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 11 deletions.
54 changes: 49 additions & 5 deletions docs/rules/arrow-parens.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,16 @@ a => {}

## Options

The rule takes one option, a string, which could be either `"always"` or `"as-needed"`. The default is `"always"`.
This rule has a string option:

* `"always"` (default) requires parens in all cases.
* `"as-needed"` allows ommitting parens in some cases.

This rule has an object option for exceptions to the `"as-needed"` option:

You can set the option in configuration like this:

```json
"arrow-parens": ["error", "always"]
```
* `"requireForBlockBody": true` modifies the as-needed rule in order to require parens if the function body is in an intructions block (surrounded by braces).

### always

Expand Down Expand Up @@ -176,4 +179,45 @@ a.then(foo => { if (true) {}; });
(a = 10) => a;
([a, b]) => a;
({a, b}) => a;
```
```


### requireForBlockBody

When the rule is set to `"as-needed", { "requireForBlockBody": true }` the following patterns are considered problems:

```js
/*eslint arrow-parens: [2, "as-needed", { "requireForBlockBody": true }]*/
/*eslint-env es6*/

(a) => a;
a => {};
a => {'\n'};
a.map((x) => x * x);
a.map(x => {
return x * x;
});
a.then(foo => {});
```

The following patterns are not considered problems:

```js
/*eslint arrow-parens: [2, "as-needed", { "requireForBlockBody": true }]*/
/*eslint-env es6*/

(a) => {};
(a) => {'\n'};
() => {};
a => a;
a.then((foo) => {});
a.then(foo => { if (true) {}; });
a((foo) => { if (true) {}; });
(a, b, c) => a;
(a = 10) => a;
([a, b]) => a;
({a, b}) => a;
```

The `"as-needed", { "requireForBlockBody": true }` rule is directly inspired by the Airbnb
[JS Style Guide](https://github.com/airbnb/javascript#arrows--one-arg-parens).
60 changes: 57 additions & 3 deletions lib/rules/arrow-parens.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,16 @@ module.exports = {

schema: [
{
enum: ["always", "as-needed"]
enum: ["always", "as-needed", "when-brace"]
},
{
type: "object",
properties: {
requireForBlockBody: {
type: "boolean"
}
},
additionalProperties: false
}
]
},
Expand All @@ -29,18 +38,63 @@ module.exports = {
var message = "Expected parentheses around arrow function argument.";
var asNeededMessage = "Unexpected parentheses around single function argument";
var asNeeded = context.options[0] === "as-needed";

var requireForBlockBodyMessage = "Unexpected parentheses around single function argument having a body with no curly braces";
var requireForBlockBodyNoParensMessage = "Expected parentheses around arrow function argument having a body with curly braces.";
var requireForBlockBody = asNeeded && context.options[1] && context.options[1].requireForBlockBody === true;
var sourceCode = context.getSourceCode();



/**
* Determines whether a arrow function argument end with `)`
* @param {ASTNode} node The arrow function node.
* @returns {void}
*/
function parens(node) {
var token = sourceCode.getFirstToken(node);
var bodyToken = sourceCode.getFirstToken(node.body);

// "as-needed", { "requireForBlockBody": true }: x => x
if (
requireForBlockBody &&
node.params.length === 1 && node.params[0].type === "Identifier" &&
bodyToken.type !== "Punctuator" && bodyToken.value !== "{"
) {
if (token.type === "Punctuator" && token.value === "(") {
context.report({
node: node,
message: requireForBlockBodyMessage,
fix: function(fixer) {
var paramToken = context.getTokenAfter(token);
var closingParenToken = context.getTokenAfter(paramToken);

return fixer.replaceTextRange([
token.range[0],
closingParenToken.range[1]
], paramToken.value);
}
});
}
return;
}

if (
requireForBlockBody &&
bodyToken.type === "Punctuator" && bodyToken.value === "{"
) {
if (token.type !== "Punctuator" || token.value !== "(") {
context.report({
node: node,
message: requireForBlockBodyNoParensMessage,
fix: function(fixer) {
return fixer.replaceText(token, "(" + token.value + ")");
}
});
}
return;
}

// as-needed: x => x
// "as-needed": x => x
if (asNeeded && node.params.length === 1 && node.params[0].type === "Identifier") {
if (token.type === "Punctuator" && token.value === "(") {
context.report({
Expand Down
67 changes: 64 additions & 3 deletions tests/lib/rules/arrow-parens.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,53 @@ var rule = require("../../../lib/rules/arrow-parens"),
var ruleTester = new RuleTester();

var valid = [

// "always" (by default)
{ code: "() => {}", parserOptions: { ecmaVersion: 6 } },
{ code: "(a) => {}", parserOptions: { ecmaVersion: 6 } },
{ code: "(a) => a", parserOptions: { ecmaVersion: 6 } },
{ code: "(a) => {\n}", parserOptions: { ecmaVersion: 6 } },
{ code: "a.then((foo) => {});", parserOptions: { ecmaVersion: 6 } },
{ code: "a.then((foo) => { if (true) {}; });", parserOptions: { ecmaVersion: 6 } },

// as-needed
// "always" (explicit)
{ code: "() => {}", options: ["always"], parserOptions: { ecmaVersion: 6 } },
{ code: "(a) => {}", options: ["always"], parserOptions: { ecmaVersion: 6 } },
{ code: "(a) => a", options: ["always"], parserOptions: { ecmaVersion: 6 } },
{ code: "(a) => {\n}", options: ["always"], parserOptions: { ecmaVersion: 6 } },
{ code: "a.then((foo) => {});", options: ["always"], parserOptions: { ecmaVersion: 6 } },
{ code: "a.then((foo) => { if (true) {}; });", options: ["always"], parserOptions: { ecmaVersion: 6 } },

// "as-needed"
{ code: "() => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "a => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "a => a", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "([a, b]) => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "({ a, b }) => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "(a = 10) => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "(...a) => a[0]", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },
{ code: "(a, b) => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } }
{ code: "(a, b) => {}", options: ["as-needed"], parserOptions: { ecmaVersion: 6 } },

// "as-needed", { "requireForBlockBody": true }
{ code: "() => {}", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } },
{ code: "a => a", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } },
{ code: "([a, b]) => {}", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } },
{ code: "({ a, b }) => {}", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } },
{ code: "(a = 10) => {}", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } },
{ code: "(...a) => a[0]", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } },
{ code: "(a, b) => {}", options: ["as-needed", {requireForBlockBody: true}], parserOptions: { ecmaVersion: 6 } }

];

var message = "Expected parentheses around arrow function argument.";
var asNeededMessage = "Unexpected parentheses around single function argument";
var requireForBlockBodyMessage = "Unexpected parentheses around single function argument having a body with no curly braces";
var requireForBlockBodyNoParensMessage = "Expected parentheses around arrow function argument having a body with curly braces.";
var type = "ArrowFunctionExpression";

var invalid = [

// "always" (by default)
{
code: "a => {}",
output: "(a) => {}",
Expand Down Expand Up @@ -109,7 +132,7 @@ var invalid = [
}]
},

// as-needed
// "as-needed"
{
code: "(a) => a",
output: "a => a",
Expand All @@ -133,6 +156,44 @@ var invalid = [
message: asNeededMessage,
type: type
}]
},

// "as-needed", { "requireForBlockBody": true }
{
code: "a => {}",
output: "(a) => {}",
options: ["as-needed", {requireForBlockBody: true}],
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 1,
column: 1,
message: requireForBlockBodyNoParensMessage,
type: type
}]
},
{
code: "(a) => a",
output: "a => a",
options: ["as-needed", {requireForBlockBody: true}],
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 1,
column: 1,
message: requireForBlockBodyMessage,
type: type
}]
},
{
code: "(b) => b",
output: "b => b",
options: ["as-needed", {requireForBlockBody: true}],
parserOptions: { ecmaVersion: 6 },
errors: [{
line: 1,
column: 1,
message: requireForBlockBodyMessage,
type: type
}]
}

];
Expand Down

0 comments on commit 8bd0d94

Please sign in to comment.