Skip to content

Commit

Permalink
Update: space-before-function-paren supports async/await (refs #7101)…
Browse files Browse the repository at this point in the history
… (#7180)
  • Loading branch information
mysticatea committed Oct 14, 2016
1 parent d0d3b28 commit c4abaf0
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 24 deletions.
66 changes: 51 additions & 15 deletions docs/rules/space-before-function-paren.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,37 @@ This rule aims to enforce consistent spacing before function parentheses and as

## Options

This rule takes one argument. If it is `"always"` then all named functions and anonymous functions must have space before function parentheses. If `"never"` then all named functions and anonymous functions must not have space before function parentheses. If you want different spacing for named and anonymous functions you can pass a configuration object as the rule argument to configure those separately (e. g. `{"anonymous": "always", "named": "never"}`). In this case, you can use "ignore" to only apply the rule to one type of function (e. g. `{"anonymous": "ignore", "named": "never"}` will warn on spaces for named functions, but will not warn on anonymous functions one way or the other).
This rule has a string option or an object option:

The default configuration is `"always"`.
```js
{
"space-before-function-paren": ["error", "always"],
// or
"space-before-function-paren": ["error", {
"anonymous": "always",
"named": "always",
"asyncArrow": "ignore"
}],
}
```

* `always` (default) requires a space followed by the `(` of arguments.
* `never` disallows any space followed by the `(` of arguments.

The string option does not check async arrow function expressions for backward compatibility.

You can also use a separate option for each type of function.
Each of the following options can be set to `"always"`, `"never"`, or `"ignore"`.
Default is `"always"` basically.

* `anonymous` is for anonymous function expressions (e.g. `function () {}`).
* `named` is for named function expressions (e.g. `function foo () {}`).
* `asyncArrow` is for async arrow function expressions (e.g. `async () => {}`).
`asyncArrow` is set to `"ignore"` by default for backwards compatibility.

### "always"

The following patterns are considered problems:
Examples of **incorrect** code for this rule with the default `"always"` option:

```js
/*eslint space-before-function-paren: "error"*/
Expand Down Expand Up @@ -63,7 +87,7 @@ var foo = {
};
```

The following patterns are not considered problems:
Examples of **correct** code for this rule with the default `"always"` option:

```js
/*eslint space-before-function-paren: "error"*/
Expand Down Expand Up @@ -92,11 +116,15 @@ var foo = {
// ...
}
};

// async arrow function expressions are ignored by default.
var foo = async () => 1
var foo = async() => 1
```

### "never"

The following patterns are considered problems:
Examples of **incorrect** code for this rule with the `"never"` option:

```js
/*eslint space-before-function-paren: ["error", "never"]*/
Expand Down Expand Up @@ -127,7 +155,7 @@ var foo = {
};
```

The following patterns are not considered problems:
Examples of **correct** code for this rule with the `"never"` option:

```js
/*eslint space-before-function-paren: ["error", "never"]*/
Expand Down Expand Up @@ -156,14 +184,18 @@ var foo = {
// ...
}
};

// async arrow function expressions are ignored by default.
var foo = async () => 1
var foo = async() => 1
```

### `{"anonymous": "always", "named": "never"}`
### `{"anonymous": "always", "named": "never", "asyncArrow": "always"}`

The following patterns are considered problems:
Examples of **incorrect** code for this rule with the `{"anonymous": "always", "named": "never", "asyncArrow": "always"}` option:

```js
/*eslint space-before-function-paren: ["error", { "anonymous": "always", "named": "never" }]*/
/*eslint space-before-function-paren: ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}]*/
/*eslint-env es6*/

function foo () {
Expand All @@ -185,12 +217,14 @@ var foo = {
// ...
}
};

var foo = async(a) => await a
```

The following patterns are not considered problems:
Examples of **correct** code for this rule with the `{"anonymous": "always", "named": "never", "asyncArrow": "always"}` option:

```js
/*eslint space-before-function-paren: ["error", { "anonymous": "always", "named": "never" }]*/
/*eslint space-before-function-paren: ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}]*/
/*eslint-env es6*/

function foo() {
Expand All @@ -212,11 +246,13 @@ var foo = {
// ...
}
};

var foo = async (a) => await a
```

### `{"anonymous": "never", "named": "always"}`

The following patterns are considered problems:
Examples of **incorrect** code for this rule with the `{"anonymous": "never", "named": "always"}` option:

```js
/*eslint space-before-function-paren: ["error", { "anonymous": "never", "named": "always" }]*/
Expand All @@ -243,7 +279,7 @@ var foo = {
};
```

The following patterns are not considered problems:
Examples of **correct** code for this rule with the `{"anonymous": "never", "named": "always"}` option:

```js
/*eslint space-before-function-paren: ["error", { "anonymous": "never", "named": "always" }]*/
Expand Down Expand Up @@ -272,7 +308,7 @@ var foo = {

### `{"anonymous": "ignore", "named": "always"}`

The following patterns are considered problems:
Examples of **incorrect** code for this rule with the `{"anonymous": "ignore", "named": "always"}` option:

```js
/*eslint space-before-function-paren: ["error", { "anonymous": "ignore", "named": "always" }]*/
Expand All @@ -295,7 +331,7 @@ var foo = {
};
```

The following patterns are not considered problems:
Examples of **correct** code for this rule with the `{"anonymous": "ignore", "named": "always"}` option:

```js
/*eslint space-before-function-paren: ["error", { "anonymous": "ignore", "named": "always" }]*/
Expand Down
40 changes: 33 additions & 7 deletions lib/rules/space-before-function-paren.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ module.exports = {
},
named: {
enum: ["always", "never", "ignore"]
},
asyncArrow: {
enum: ["always", "never", "ignore"]
}
},
additionalProperties: false
Expand All @@ -48,7 +51,9 @@ module.exports = {
let requireAnonymousFunctionSpacing = true,
forbidAnonymousFunctionSpacing = false,
requireNamedFunctionSpacing = true,
forbidNamedFunctionSpacing = false;
forbidNamedFunctionSpacing = false,
requireArrowFunctionSpacing = false,
forbidArrowFunctionSpacing = false;

if (typeof configuration === "object") {
requireAnonymousFunctionSpacing = (
Expand All @@ -57,6 +62,8 @@ module.exports = {
requireNamedFunctionSpacing = (
!configuration.named || configuration.named === "always");
forbidNamedFunctionSpacing = configuration.named === "never";
requireArrowFunctionSpacing = configuration.asyncArrow === "always";
forbidArrowFunctionSpacing = configuration.asyncArrow === "never";
} else if (configuration === "never") {
requireAnonymousFunctionSpacing = false;
forbidAnonymousFunctionSpacing = true;
Expand Down Expand Up @@ -92,13 +99,31 @@ module.exports = {
* @returns {void}
*/
function validateSpacingBeforeParentheses(node) {
const isNamed = isNamedFunction(node);
let rightToken;
const isArrow = node.type === "ArrowFunctionExpression";
const isNamed = !isArrow && isNamedFunction(node);
const isAnonymousGenerator = node.generator && !isNamed;
const isNormalArrow = isArrow && !node.async;
const isArrowWithoutParens = isArrow && sourceCode.getFirstToken(node, 1).value !== "(";
let forbidSpacing, requireSpacing, rightToken;

if (node.generator && !isNamed) {
// isAnonymousGenerator → `generator-star-spacing` should warn it. E.g. `function* () {}`
// isNormalArrow → ignore always.
// isArrowWithoutParens → ignore always. E.g. `async a => a`
if (isAnonymousGenerator || isNormalArrow || isArrowWithoutParens) {
return;
}

if (isArrow) {
forbidSpacing = forbidArrowFunctionSpacing;
requireSpacing = requireArrowFunctionSpacing;
} else if (isNamed) {
forbidSpacing = forbidNamedFunctionSpacing;
requireSpacing = requireNamedFunctionSpacing;
} else {
forbidSpacing = forbidAnonymousFunctionSpacing;
requireSpacing = requireAnonymousFunctionSpacing;
}

rightToken = sourceCode.getFirstToken(node);
while (rightToken.value !== "(") {
rightToken = sourceCode.getTokenAfter(rightToken);
Expand All @@ -107,7 +132,7 @@ module.exports = {
const location = leftToken.loc.end;

if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken)) {
if ((isNamed && forbidNamedFunctionSpacing) || (!isNamed && forbidAnonymousFunctionSpacing)) {
if (forbidSpacing) {
context.report({
node,
loc: location,
Expand All @@ -118,7 +143,7 @@ module.exports = {
});
}
} else {
if ((isNamed && requireNamedFunctionSpacing) || (!isNamed && requireAnonymousFunctionSpacing)) {
if (requireSpacing) {
context.report({
node,
loc: location,
Expand All @@ -133,7 +158,8 @@ module.exports = {

return {
FunctionDeclaration: validateSpacingBeforeParentheses,
FunctionExpression: validateSpacingBeforeParentheses
FunctionExpression: validateSpacingBeforeParentheses,
ArrowFunctionExpression: validateSpacingBeforeParentheses,
};
}
};
42 changes: 40 additions & 2 deletions tests/lib/rules/space-before-function-paren.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,25 @@ ruleTester.run("space-before-function-paren", rule, {
},
{ code: "var bar = function foo () {}",
options: [ { named: "ignore", anonymous: "always" } ]
}
},

// Async arrow functions
{ code: "() => 1", parserOptions: {ecmaVersion: 6} },
{ code: "async a => a", parserOptions: {ecmaVersion: 8} },
{ code: "async a => a", options: [{asyncArrow: "always"}], parserOptions: {ecmaVersion: 8} },
{ code: "async a => a", options: [{asyncArrow: "never"}], parserOptions: {ecmaVersion: 8} },
{ code: "async () => 1", options: [{asyncArrow: "always"}], parserOptions: {ecmaVersion: 8} },
{ code: "async() => 1", options: [{asyncArrow: "never"}], parserOptions: {ecmaVersion: 8} },
{ code: "async () => 1", options: [{asyncArrow: "ignore"}], parserOptions: {ecmaVersion: 8} },
{ code: "async() => 1", options: [{asyncArrow: "ignore"}], parserOptions: {ecmaVersion: 8} },

// ignore by default for now.
{ code: "async () => 1", parserOptions: {ecmaVersion: 8} },
{ code: "async() => 1", parserOptions: {ecmaVersion: 8} },
{ code: "async () => 1", options: ["always"], parserOptions: {ecmaVersion: 8} },
{ code: "async() => 1", options: ["always"], parserOptions: {ecmaVersion: 8} },
{ code: "async () => 1", options: ["never"], parserOptions: {ecmaVersion: 8} },
{ code: "async() => 1", options: ["never"], parserOptions: {ecmaVersion: 8} },
],

invalid: [
Expand Down Expand Up @@ -406,6 +424,7 @@ ruleTester.run("space-before-function-paren", rule, {
},
{
code: "var foo = function() {}",
output: "var foo = function () {}",
options: [ { named: "ignore", anonymous: "always" } ],
errors: [
{
Expand All @@ -418,6 +437,7 @@ ruleTester.run("space-before-function-paren", rule, {
},
{
code: "var foo = function () {}",
output: "var foo = function() {}",
options: [ { named: "ignore", anonymous: "never" } ],
errors: [
{
Expand All @@ -430,6 +450,7 @@ ruleTester.run("space-before-function-paren", rule, {
},
{
code: "var bar = function foo() {}",
output: "var bar = function foo () {}",
options: [ { named: "always", anonymous: "ignore" } ],
errors: [
{
Expand All @@ -442,6 +463,7 @@ ruleTester.run("space-before-function-paren", rule, {
},
{
code: "var bar = function foo () {}",
output: "var bar = function foo() {}",
options: [ { named: "never", anonymous: "ignore" } ],
errors: [
{
Expand All @@ -451,6 +473,22 @@ ruleTester.run("space-before-function-paren", rule, {
column: 23
}
]
}
},

// Async arrow functions
{
code: "async() => 1",
output: "async () => 1",
options: [{asyncArrow: "always"}],
parserOptions: {ecmaVersion: 8},
errors: ["Missing space before function parentheses."]
},
{
code: "async () => 1",
output: "async() => 1",
options: [{asyncArrow: "never"}],
parserOptions: {ecmaVersion: 8},
errors: ["Unexpected space before function parentheses."]
},
]
});

0 comments on commit c4abaf0

Please sign in to comment.