Skip to content

Commit

Permalink
Merge pull request #4486 from bryanrsmith/issue-4115
Browse files Browse the repository at this point in the history
Breaking: Implement yield-star-spacing rule (fixes #4115)
  • Loading branch information
nzakas committed Dec 1, 2015
2 parents fc7af8e + 290e0f7 commit d6722e1
Show file tree
Hide file tree
Showing 7 changed files with 524 additions and 29 deletions.
1 change: 1 addition & 0 deletions conf/eslint.json
Expand Up @@ -191,6 +191,7 @@
"vars-on-top": 0,
"wrap-iife": 0,
"wrap-regex": 0,
"yield-star-spacing": 0,
"yoda": [0, "never"]
}
}
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -227,6 +227,7 @@ These rules are only relevant to ES6 environments.
* [prefer-spread](prefer-spread.md) - suggest using the spread operator instead of `.apply()`.
* [prefer-template](prefer-template.md) - suggest using template literals instead of strings concatenation
* [require-yield](require-yield.md) - disallow generator functions that do not have `yield`
* [yield-star-spacing](yield-star-spacing.md) - enforce spacing around the `*` in `yield*` expressions (fixable)


## Removed
Expand Down
86 changes: 86 additions & 0 deletions docs/rules/yield-star-spacing.md
@@ -0,0 +1,86 @@
# Enforce spacing around the `*` in `yield*` expressions (yield-star-spacing)

**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line.

## Rule Details

This rule enforces spacing around the `*` in `yield*` expressions.

The rule takes one option, an object, which has two keys `before` and `after` having boolean values `true` or `false`.

* `before` enforces spacing between the `yield` and the `*`.
If `true`, a space is required, otherwise spaces are disallowed.

* `after` enforces spacing between the `*` and the argument.
If it is `true`, a space is required, otherwise spaces are disallowed.

The default is `{"before": false, "after": true}`.

```json
"yield-star-spacing": [2, {"before": true, "after": false}]
```

The option also has a string shorthand:

* `{"before": false, "after": true}``"after"`
* `{"before": true, "after": false}``"before"`
* `{"before": true, "after": true}``"both"`
* `{"before": false, "after": false}``"neither"`

```json
"yield-star-spacing": [2, "after"]
```

When using `"after"` this spacing will be enforced:

```js
/*eslint yield-star-spacing: [2, "after"]*/
/*eslint-env es6*/

function *generator() {
yield* other();
}
```

When using `"before"` this spacing will be enforced:

```js
/*eslint yield-star-spacing: [2, "before"]*/
/*eslint-env es6*/

function *generator() {
yield *other();
}
```

When using `"both"` this spacing will be enforced:

```js
/*eslint yield-star-spacing: [2, "both"]*/
/*eslint-env es6*/

function *generator() {
yield * other();
}
```

When using `"neither"` this spacing will be enforced:

```js
/*eslint yield-star-spacing: [2, "neither"]*/
/*eslint-env es6*/

function *generator() {
yield*other();
}
```

To use this rule you must set the `generators` flag to `true` in the `ecmaFeatures` configuration object.

## When Not To Use It

If your project will not be using generators or you are not concerned with spacing consistency, you do not need this rule.

## Further Reading

* [Understanding ES6: Generators](https://leanpub.com/understandinges6/read/#leanpub-auto-generators)
9 changes: 2 additions & 7 deletions lib/rules/space-unary-ops.js
Expand Up @@ -148,16 +148,11 @@ module.exports = function(context) {
var tokens = context.getFirstTokens(node, 3),
word = "yield";

if (!node.argument) {
if (!node.argument || node.delegate) {
return;
}

if (node.delegate) {
word += "*";
checkUnaryWordOperatorForSpaces(node, tokens[1], tokens[2], word);
} else {
checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], word);
}
checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], word);
}
};

Expand Down
101 changes: 101 additions & 0 deletions lib/rules/yield-star-spacing.js
@@ -0,0 +1,101 @@
/**
* @fileoverview Rule to check the spacing around the * in yield* expressions.
* @author Bryan Smith
* @copyright 2015 Bryan Smith. All rights reserved.
*/

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
var sourceCode = context.getSourceCode();

var mode = (function(option) {
if (!option || typeof option === "string") {
return {
before: { before: true, after: false },
after: { before: false, after: true },
both: { before: true, after: true },
neither: { before: false, after: false }
}[option || "after"];
}
return option;
}(context.options[0]));

/**
* Checks the spacing between two tokens before or after the star token.
* @param {string} side Either "before" or "after".
* @param {Token} leftToken `function` keyword token if side is "before", or
* star token if side is "after".
* @param {Token} rightToken Star token if side is "before", or identifier
* token if side is "after".
* @returns {void}
*/
function checkSpacing(side, leftToken, rightToken) {
if (sourceCode.isSpaceBetweenTokens(leftToken, rightToken) !== mode[side]) {
var after = leftToken.value === "*";
var spaceRequired = mode[side];
var node = after ? leftToken : rightToken;
var type = spaceRequired ? "Missing" : "Unexpected";
var message = type + " space " + side + " *.";
context.report({
node: node,
message: message,
fix: function(fixer) {
if (spaceRequired) {
if (after) {
return fixer.insertTextAfter(node, " ");
}
return fixer.insertTextBefore(node, " ");
}
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
}
});
}
}

/**
* Enforces the spacing around the star if node is a yield* expression.
* @param {ASTNode} node A yield expression node.
* @returns {void}
*/
function checkExpression(node) {
if (!node.delegate) {
return;
}

var tokens = sourceCode.getFirstTokens(node, 3);
var yieldToken = tokens[0];
var starToken = tokens[1];
var nextToken = tokens[2];

checkSpacing("before", yieldToken, starToken);
checkSpacing("after", starToken, nextToken);
}

return {
"YieldExpression": checkExpression
};

};

module.exports.schema = [
{
"oneOf": [
{
"enum": ["before", "after", "both", "neither"]
},
{
"type": "object",
"properties": {
"before": {"type": "boolean"},
"after": {"type": "boolean"}
},
"additionalProperties": false
}
]
}
];
30 changes: 8 additions & 22 deletions tests/lib/rules/space-unary-ops.js
Expand Up @@ -147,6 +147,14 @@ ruleTester.run("space-unary-ops", rule, {
{
code: "function *foo() { (yield) * 0 }",
ecmaFeatures: { generators: true }
},
{
code: "function *foo() { yield*0 }",
ecmaFeatures: { generators: true }
},
{
code: "function *foo() { yield *0 }",
ecmaFeatures: { generators: true }
}
],

Expand Down Expand Up @@ -365,28 +373,6 @@ ruleTester.run("space-unary-ops", rule, {
line: 1,
column: 19
}]
},
{
code: "function *foo() { yield*0 }",
output: "function *foo() { yield* 0 }",
ecmaFeatures: { generators: true },
errors: [{
message: "Unary word operator \"yield*\" must be followed by whitespace.",
type: "YieldExpression",
line: 1,
column: 19
}]
},
{
code: "function *foo() { yield *0 }",
output: "function *foo() { yield * 0 }",
ecmaFeatures: { generators: true },
errors: [{
message: "Unary word operator \"yield*\" must be followed by whitespace.",
type: "YieldExpression",
line: 1,
column: 19
}]
}
]
});

0 comments on commit d6722e1

Please sign in to comment.