Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update: support multiple fixes in a report (fixes #7348) #8101

Merged
merged 9 commits into from Jun 18, 2017

Conversation

mysticatea
Copy link
Member

@mysticatea mysticatea commented Feb 19, 2017

What is the purpose of this pull request? (put an "X" next to item)

[X] Add something to the core

Fixes #7348.

What changes did you make? (Give an overview)

This PR adds the ability to handle multiple fixes for each report. The fix methods can return an iterable object, includes arrays.

context.report({
    node,
    message,
    fix(fixer) {
        return [
            fixer.insertTextBefore(node, "("),
            fixer.insertTextAfter(node, ")")
        ];
    }
})

Those fixes are merged to one internally, so this does not change about the result of ESLint; each message has one fix property as same as the current behavior. This is not a breaking change.

Pros:

  • We can make more readable fix functions.

Cons:

  • Nothing in particular.

This PR includes 2 examples.

One is about arrow-body-style. The rule had a super complex fix function.

Before:
fix(fixer) {
    if (blockBody.length !== 1 || blockBody[0].type !== "ReturnStatement" || !blockBody[0].argument) {
        return null;
    }

    const sourceText = sourceCode.getText();
    const returnKeyword = sourceCode.getFirstToken(blockBody[0]);
    const firstValueToken = sourceCode.getTokenAfter(returnKeyword);
    let lastValueToken = sourceCode.getLastToken(blockBody[0]);

    if (lastValueToken.type === "Punctuator" && lastValueToken.value === ";") {

        /* The last token of the returned value is the last token of the ReturnExpression (if
         * the ReturnExpression has no semicolon), or the second-to-last token (if the ReturnExpression
         * has a semicolon).
         */
        lastValueToken = sourceCode.getTokenBefore(lastValueToken);
    }

    const tokenAfterArrowBody = sourceCode.getTokenAfter(arrowBody);

    if (tokenAfterArrowBody && tokenAfterArrowBody.type === "Punctuator" && /^[([/`+-]/.test(tokenAfterArrowBody.value)) {

        // Don't do a fix if the next token would cause ASI issues when preceded by the returned value.
        return null;
    }

    const textBeforeReturn = sourceText.slice(arrowBody.range[0] + 1, returnKeyword.range[0]);
    const textBetweenReturnAndValue = sourceText.slice(returnKeyword.range[1], firstValueToken.range[0]);
    const rawReturnValueText = sourceText.slice(firstValueToken.range[0], lastValueToken.range[1]);
    const returnValueText = firstValueToken.value === "{" ? `(${rawReturnValueText})` : rawReturnValueText;
    const textAfterValue = sourceText.slice(lastValueToken.range[1], blockBody[0].range[1] - 1);
    const textAfterReturnStatement = sourceText.slice(blockBody[0].range[1], arrowBody.range[1] - 1);

    /*
     * For fixes that only contain spaces around the return value, remove the extra spaces.
     * This avoids ugly fixes that end up with extra spaces after the arrow, e.g. `() =>   0 ;`
     */
    return fixer.replaceText(
        arrowBody,
        (textBeforeReturn + textBetweenReturnAndValue).replace(/^\s*$/, "") + returnValueText + (textAfterValue + textAfterReturnStatement).replace(/^\s*$/, "")
    );
}
After:
*fix(fixer) {
    if (blockBody.length !== 1 ||
        blockBody[0].type !== "ReturnStatement" ||
        !blockBody[0].argument ||
        hasASIProblem(sourceCode.getTokenAfter(arrowBody))
    ) {
        return;
    }

    const openingBrace = sourceCode.getFirstToken(arrowBody);
    const closingBrace = sourceCode.getLastToken(arrowBody);
    const firstValueToken = sourceCode.getFirstToken(blockBody[0], 1);
    const lastValueToken = sourceCode.getLastToken(blockBody[0]);
    const commentsExist =
        sourceCode.commentsExistBetween(openingBrace, firstValueToken) ||
        sourceCode.commentsExistBetween(lastValueToken, closingBrace);

    // Remove tokens around the return value.
    // If comments don't exist, remove extra spaces as well.
    if (commentsExist) {
        yield fixer.remove(openingBrace);
        yield fixer.remove(closingBrace);
        yield fixer.remove(sourceCode.getTokenAfter(openingBrace)); // return keyword
    } else {
        yield fixer.removeRange([openingBrace.range[0], firstValueToken.range[0]]);
        yield fixer.removeRange([lastValueToken.range[1], closingBrace.range[1]]);
    }

    // If the first token of the reutrn value is `{`,
    // enclose the return value by parentheses to avoid syntax error.
    if (astUtils.isOpeningBraceToken(firstValueToken)) {
        yield fixer.insertTextBefore(firstValueToken, "(");
        yield fixer.insertTextAfter(lastValueToken, ")");
    }

    // If the last token of the return statement is semicolon, remove it.
    // Non-block arrow body is an expression, not a statement.
    if (astUtils.isSemicolonToken(lastValueToken)) {
        yield fixer.remove(lastValueToken);
    }
}

.

I think the "After" is more readable. Because "Before" had to focus on the operation of the text which we want to not change mainly. On the other hand, "After" focuses on the operation of the text (token) that we want to change mainly. I think this is a huge difference.

Another one is semi. This is an example about to control conflictions of autofix.

After:
fix = function *(fixer) {
    yield fixer.remove(lastToken);

    // To avoid no-extra-semi removes the next semicolon.
    // https://github.com/eslint/eslint/issues/7928
    if (nextToken && astUtils.isSemicolonToken(nextToken)) {
        yield fixer.keep(nextToken);
    }
};

In addition, This PR adds some APIs.

  • sourceCode.commentsExistBetween(left, right) returns true if comments exist between the left and the right. Otherwise it returns false. Since we frequently use this check to avoid removing comments by autofix, it would be useful.
  • fixer.keep(nodeOrToken) and fixer.keepRange(range) returns a fix object which does not change code. Those would allow us to control conflictions of autofix, utilizing the mechanism that overlapped fixes are not applied at the same time.

Is there anything you'd like reviewers to focus on?

Direction. The original issue is not accepted yet, but I want to advance this feature. Currently, we have to focus on the text which we want to not change to keep comments when we implement autofix. This feature would allow us to focus on the text which we want to change.

I'm a champion for this.

@mysticatea mysticatea added core Relates to ESLint's core APIs and features enhancement This change enhances an existing feature of ESLint evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Feb 19, 2017
@mention-bot
Copy link

@mysticatea, thanks for your PR! By analyzing the history of the files in this pull request, we identified @alberto, @nzakas and @vitorbal to be potential reviewers.

@mysticatea mysticatea self-assigned this Feb 19, 2017
@eslintbot
Copy link

LGTM

2 similar comments
@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@mysticatea
Copy link
Member Author

mysticatea commented Feb 23, 2017

I will try to explain more details about this PR.

The purpose of this PR is that it supports multiple fix objects per report, as keeping no breaking change.

context.report({
    node,
    message,
    fix(fixer) {
        return [
            fixer.insertTextBefore(node, "("),
            fixer.insertTextAfter(node, ")")
        ];
    }
})

There are changes in RuleContext#report function.
If the fix function returns multiple fixes objects, RuleContext#report merges those to one.

For example:

/* eslint arrow-body-style: error */

const foo = () => {
    return {a: 1}
}

In this case, arrow-body-style of this PR generates 4 fix objects:

[ { "range": [ 56, 69 ], "text": "" },
  { "range": [ 69, 69 ], "text": "(" },
  { "range": [ 75, 75 ], "text": ")" },
  { "range": [ 75, 77 ], "text": "" } ]

Then RuleContext#report merges those to one:

{ "range": [ 56, 77 ], "text": "({a: 1})" }

After that, it does existing autofix process if --fix option is given.


  • This does not effect performance if existing fix functions since this merging process is run only when a fix function returns multiple fix objects.
  • This keeps the result of the report as currently.
[
    {
        "filePath":"C:\\Users\\t-nagashima.AD\\Documents\\GitHub\\eslint\\test.js",
        "messages":[
            {
                "ruleId":"arrow-body-style",
                "severity":2,
                "message":"Unexpected block statement surrounding arrow body.",
                "line":3,
                "column":19,
                "nodeType":"ArrowFunctionExpression",
                "source":"const foo = () => {",
                "fix":{
                    "range":[
                        56,
                        77
                    ],
                    "text":"({a: 1})"
                }
            }
        ],
        "errorCount":1,
        "warningCount":0,
        "source":"/* eslint arrow-body-style: error */\n\nconst foo = () => {\n    return {a: 1}\n}\n"
    }
]

I want to investigate the way of no-merging and overlap detection improvement after this PR. (maybe it makes a breaking change)
Related to #7928 (comment)

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@gyandeeps
Copy link
Member

LGTM 👍 as always nicely done @mysticatea ...

Copy link
Contributor

@alangpierce alangpierce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good as far as I can tell, although I would love to be able to call keep on a larger range than my fix in order to fix #8026.

@@ -61,6 +62,71 @@ const PASSTHROUGHS = [
//------------------------------------------------------------------------------

/**
* Compares items in a fixes array by range.
* @param {Fix} a The first message.
* @param {Fix} b The second message.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "The first fix" and "The second fix"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, it's my copy/paste mistake. Thank you!

let lastPos = start;

for (const fix of fixes) {
assert(fix.range[0] >= lastPos, "Fix objects must not be overlapped in a report.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring the ranges to be disjoint could make fixer.keep a lot less useful, I think. In #8067 for the no-else-return/no-useless-return fix (#8026), I intentionally want to keep all of an outer node (the function containing the fix) while modifying something inside of it.

With this API, I guess what I'd do is compute fixStart and fixEnd and yield that fix, then yield keepRange(functionStart, fixStart) and keepRange(fixEnd, functionEnd). That isn't so bad, I suppose, but seems a lot more clunky than saying "preserve the function and replace this text within the function".

One possible solution, if we're ok with it being a little magical, is to allow fixes to be completely contained within other fixes and always use the most specific one. Or maybe the fixes could be applied in order in a reasonable way, with later ones taking precedence.

(Although I recognize that there's maybe some disagreement about whether keep should be used that way in my use case anyway.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think it's rare case if we need locking the outer node.
In no-else-return case, it needs locking only the previous if block.
In no-useless-return case, it needs locking only after the return statement.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I can see how the fixes for no-else-return and no-useless-return would work in that case. (I'm still a little unsure about how to properly reason about the implementation of no-useless-return, but it certainly seems easy and correct to call keepRange from the end of the fix to the end of the function.)

I think locking the outer node can be nice because it's safer and makes the rule simpler, but I can see the argument that it's usually not necessary. And if the long-term plan is to allow a distinction between "fixing" and "locking" operations, then I think it shouldn't be hard to allow the two types to overlap, which would address my concerns there.

* @param {SourceCode} sourceCode The source code object to get the text between fixes.
* @returns {void}
*/
function mergeFixes(fixes, sourceCode) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth noting that mergeFixes could be made an internal utility instead of a public API if there's any controversy around this API addition.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As public API, this is simple enhancement; the fix functions support for an iterable object as their return value.

There are 2 topics here.

  • To not modify comments, this feature would be helpful.
  • To avoid autofix conflict, this feature would be helpful.

As I mentioned in this post, currently we have a pain to remain comments. The purpose of the fix functions is "to modify code", but we have to focus on "to not modify code" mainly in order to not remove comments. This enhancement would resolve the problem. I think this is valuable for plugin ecosystem as well.

In addition, we can address locked ranges in the same mechanism.


In fact, this is the 1st step of my plan.

Because the report of linting has fix properties which have a fix object, this PR is merging the multiple fixes before it makes the report for now. I have to avoid a breaking change about the fix property (the shape of the report also are public API).

However, I have a plan (though it's not accepted yet) to change the fix property to fixes property that has an array of fix objects, so this mergeFixes function would be removed in future. It implies that fixing process can distinguish between fixing ranges and locking ranges. I expect it to allow us smarter conflict check.

@eslintbot
Copy link

LGTM

@mysticatea
Copy link
Member Author

I updated this PR; now we can use fixer.keepRange(containingNode).

@ilyavolodin
Copy link
Member

@mysticatea I will support this enhancement with one change request. I really would like to avoid generators in our code base. While I understand that they can be very helpful, but I feel like majority of JS community doesn't really understand them and they significantly increasing bar of entry. I would like to avoid both generators and yield keyword as much as humanly possible.

@mysticatea
Copy link
Member Author

Thank you, @ilyavolodin !

Hmm, though I don't think generators make hurdles.
Anyway, I would remove yield for now.

@gyandeeps
Copy link
Member

Marking this accepted as we have the support and @mysticatea will champion.
I think we are almost their accept that last part of the discussion. Can you please update on whats the plan is @mysticatea

@not-an-aardvark
Copy link
Member

Technically, I think we would need TSC consensus for this because it's a core change.

I like the general idea of allowing multiple fixes, but I'm not sure we should add fixer.keep and fixer.keepRange to core yet. I'm wondering if we should try out the solution from #7928 for longer before we make a core change.

@mysticatea
Copy link
Member Author

I'm sorry for late. I will get the time to work on this within this week end.

  • I will remove yield in this PR. But I will not remove yield support.
  • I will remove keep/keepRange. I will separate PR in order to continue discussion.

@eslintbot
Copy link

LGTM

@not-an-aardvark not-an-aardvark removed the evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion label May 19, 2017
@not-an-aardvark
Copy link
Member

I think the autofixing and SourceCode documentation need to be updated to describe the new API.

(I'm also planning to review this PR later, assuming it's not going into the release today.)

Copy link
Member

@not-an-aardvark not-an-aardvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This mostly looks good but I think there is a bug if a fix range contains -1 as an index.

for (const fix of fixes) {
assert(fix.range[0] >= lastPos, "Fix objects must not be overlapped in a report.");

text += originalText.slice(lastPos, fix.range[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some fix ranges, like unicode-bom, use -1 as the start to indicate that the fix should replace the BOM. I think that will cause a bug here because .slice(-1) will take the last character in the string.

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@mysticatea
Copy link
Member Author

Thank you for the review.

  • I updated code to address negative indices.
  • I added documents.


for (const fix of fixes) {
assert(fix.range[0] >= lastPos, "Fix objects must not be overlapped in a report.");
assert(fix.range[0] >= lastPos || fix.range[0] < 0, "Fix objects must not be overlapped in a report.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this fail to detect a conflict if two fixes have ranges [-1, 0] and [-1, 5]? It seems like the assertion will always pass for fixes that start at -1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

@eslintbot
Copy link

LGTM

Copy link
Member

@not-an-aardvark not-an-aardvark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@@ -92,18 +92,18 @@ function mergeFixes(fixes, sourceCode) {
const start = fixes[0].range[0];
const end = fixes[fixes.length - 1].range[1];
let text = "";
let lastPos = Math.max(0, start);
let lastPos = Number.MIN_SAFE_INTEGER;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to use -Infinity in case someone uses extremely small numbers in fix ranges for whatever reason, but I think it's probably fine -- files usually don't have 253 BOMs. 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah 😄

I expect that integer variables are faster by compiler optimization.

@mysticatea mysticatea merged commit 673a58b into master Jun 18, 2017
@mysticatea mysticatea deleted the multiple-fixes-per-report branch June 18, 2017 22:14
bors bot added a commit to IMA-WorldHealth/bhima that referenced this pull request Jul 9, 2017
1817: Update eslint to the latest version 🚀 r=jniles


## Version **4.1.0** of [eslint](https://github.com/eslint/eslint) just got published.

<table>
  <tr>
    <th align=left>
      Dependency
    </td>
    <td>
      eslint
    </td>
  </tr>
  <tr>
    <th align=left>
      Current Version
    </td>
    <td>
      3.19.0
    </td>
  </tr>
  <tr>
    <th align=left>
      Type
    </td>
    <td>
      devDependency
    </td>
  </tr>
</table>

The version **4.1.0** is **not covered** by your **current version range**.

Without accepting this pull request your project will work just like it did before. There might be a bunch of new features, fixes and perf improvements that the maintainers worked on for you though.

I recommend you look into these changes and try to get onto the latest version of eslint.
Given that you have a decent test suite, a passing build is a strong indicator that you can take advantage of these changes by merging the proposed change into your project. Otherwise this branch is a great starting point for you to work on the update.


---


<details>
<summary>Release Notes</summary>
<strong>v4.1.0</strong>

<ul>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/e8f1362ab640c883a5d296e951308fab22073e7f" class="commit-link"><tt>e8f1362</tt></a> Docs: Remove wrong descriptions in <code>padded-block</code> rule (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8783" class="issue-link js-issue-link" data-url="eslint/eslint#8783" data-id="237771100" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8783</a>) (Plusb Preco)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/291a78302c1d5d402c6582b3f4cc836e61fde787" class="commit-link"><tt>291a783</tt></a> Update: <code>enforceForArrowConditionals</code> to <code>no-extra-parens</code> (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/6196" class="issue-link js-issue-link" data-url="eslint/eslint#6196" data-id="155067290" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#6196</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8439" class="issue-link js-issue-link" data-url="eslint/eslint#8439" data-id="220697521" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8439</a>) (Evilebot Tnawi)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/a21dd32c46f95bc232a67929c224824692f94b70" class="commit-link"><tt>a21dd32</tt></a> New: Add <code>overrides</code>/<code>files</code> options for glob-based config (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/3611" class="issue-link js-issue-link" data-url="eslint/eslint#3611" data-id="104053558" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#3611</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8081" class="issue-link js-issue-link" data-url="eslint/eslint#8081" data-id="207675247" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8081</a>) (Sylvan Mably)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/879688ce96f80aa0692f732759c6f67a0c36c4c3" class="commit-link"><tt>879688c</tt></a> Update: Add ignoreComments option to no-trailing-spaces (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8061" class="issue-link js-issue-link" data-url="eslint/eslint#8061" data-id="206865549" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8061</a>) (Jake Roussel)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/b58ae2e6d6bd4662b549ca5c0472943055a74df8" class="commit-link"><tt>b58ae2e</tt></a> Chore: Only instantiate fileEntryCache when cache flage set (perf) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8763" class="issue-link js-issue-link" data-url="eslint/eslint#8763" data-id="236756225" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8763</a>) (Gyandeep Singh)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/98512881f1fc2417011247931fa089d987ee8cc6" class="commit-link"><tt>9851288</tt></a> Update: fix indent errors on multiline destructure (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8729" class="issue-link js-issue-link" data-url="eslint/eslint#8729" data-id="235733166" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8729</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8756" class="issue-link js-issue-link" data-url="eslint/eslint#8756" data-id="236673913" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8756</a>) (Victor Hom)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/3608f06c2a412587c2d05dec0297803b25f3e630" class="commit-link"><tt>3608f06</tt></a> Docs: Increase visibility of code of conduct (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8758" class="issue-link js-issue-link" data-url="eslint/eslint#8758" data-id="236687424" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8758</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8764" class="issue-link js-issue-link" data-url="eslint/eslint#8764" data-id="236758243" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8764</a>) (Kai Cataldo)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/673a58bc8420075ba698cee6762e17322a5263c3" class="commit-link"><tt>673a58b</tt></a> Update: support multiple fixes in a report (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/7348" class="issue-link js-issue-link" data-url="eslint/eslint#7348" data-id="182620143" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#7348</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8101" class="issue-link js-issue-link" data-url="eslint/eslint#8101" data-id="208681921" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8101</a>) (Toru Nagashima)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/7a1bc3893ab55d0ab16ccf4b7a62c85329ab4007" class="commit-link"><tt>7a1bc38</tt></a> Fix: don't pass default parserOptions to custom parsers (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8744" class="issue-link js-issue-link" data-url="eslint/eslint#8744" data-id="236336414" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8744</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8745" class="issue-link js-issue-link" data-url="eslint/eslint#8745" data-id="236373829" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8745</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/c5b405280409698d14b62cbf3c87b7cf6cf71391" class="commit-link"><tt>c5b4052</tt></a> Chore: enable computed-property-spacing on ESLint codebase (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8760" class="issue-link js-issue-link" data-url="eslint/eslint#8760" data-id="236699991" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8760</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/3419f6446e205d79d9db77f6c176b9167d1fd8a7" class="commit-link"><tt>3419f64</tt></a> Docs: describe how to use formatters on the formatter demo page (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8754" class="issue-link js-issue-link" data-url="eslint/eslint#8754" data-id="236645523" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8754</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/a3ff8f21106cf8eca55978d4b3e053973f5e1bf2" class="commit-link"><tt>a3ff8f2</tt></a> Chore: combine tests in tests/lib/eslint.js and tests/lib/linter.js (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8746" class="issue-link js-issue-link" data-url="eslint/eslint#8746" data-id="236375849" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8746</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/b7cc1e6fe995d52e581fcb2b1a44e37a18680e90" class="commit-link"><tt>b7cc1e6</tt></a> Fix: Space-infix-ops should ignore type annotations in TypeScript (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8341" class="issue-link js-issue-link" data-url="eslint/eslint#8341" data-id="217102387" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8341</a>) (Reyad Attiyat)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/46e73eea69abc2ba80bb1397c6779b8789dbd385" class="commit-link"><tt>46e73ee</tt></a> Fix: eslint --init installs wrong dependencies of popular styles (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/7338" class="issue-link js-issue-link" data-url="eslint/eslint#7338" data-id="182134634" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#7338</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8713" class="issue-link js-issue-link" data-url="eslint/eslint#8713" data-id="235217725" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8713</a>) (Toru Nagashima)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/a82361b65699653761436a2e9acc7f485c827ca0" class="commit-link"><tt>a82361b</tt></a> Chore: Prevent package-lock.json files from being created (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8742" class="issue-link js-issue-link" data-url="eslint/eslint#8742" data-id="236292937" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8742</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8747" class="issue-link js-issue-link" data-url="eslint/eslint#8747" data-id="236397701" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8747</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/5f81a68a3904a559764872e3f0c7453865a6c6dc" class="commit-link"><tt>5f81a68</tt></a> New: Add eslintIgnore support to package.json (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8458" class="issue-link js-issue-link" data-url="eslint/eslint#8458" data-id="221689525" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8458</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8690" class="issue-link js-issue-link" data-url="eslint/eslint#8690" data-id="233757916" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8690</a>) (Victor Hom)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/b5a70b4e8c20dc1ea3e31137706fc20da339f379" class="commit-link"><tt>b5a70b4</tt></a> Update: fix multiline binary operator/parentheses indentation (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8719" class="issue-link js-issue-link" data-url="eslint/eslint#8719" data-id="235421314" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8719</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/ab8b0167bdaf3b8851eab3fbc2769f2bdd71677b" class="commit-link"><tt>ab8b016</tt></a> Update: fix MemberExpression indentation with "off" option (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8721" class="issue-link js-issue-link" data-url="eslint/eslint#8721" data-id="235434741" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8721</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8724" class="issue-link js-issue-link" data-url="eslint/eslint#8724" data-id="235459105" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8724</a>) (Teddy Katz)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/eb5d12c15a32084907f1c58bcbec721b5008495d" class="commit-link"><tt>eb5d12c</tt></a> Update: Add Fixer method to Linter API (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8631" class="issue-link js-issue-link" data-url="eslint/eslint#8631" data-id="230242473" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8631</a>) (Gyandeep Singh)</li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/26a2daab311c8c59942c52f436d380a195db2bd4" class="commit-link"><tt>26a2daa</tt></a> Chore: Cache fs reads in ignored-paths (fixes <a href="https://urls.greenkeeper.io/eslint/eslint/issues/8363" class="issue-link js-issue-link" data-url="eslint/eslint#8363" data-id="218136776" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8363</a>) (<a href="https://urls.greenkeeper.io/eslint/eslint/pull/8706" class="issue-link js-issue-link" data-url="eslint/eslint#8706" data-id="235004396" data-error-text="Failed to load issue title" data-permission-text="Issue title is private">#8706</a>) (Victor Hom)</li>
</ul>
</details>

<details>
<summary>Commits</summary>
<p>The new version differs by 141 commits.</p>
<ul>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/7d9e3beeb58c1ee71d53dfcfd3e3b0721dd79b46"><code>7d9e3be</code></a> <code>4.1.0</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/e727b7bdfcfb0564aabd713b32364e6f4afcfeec"><code>e727b7b</code></a> <code>Build: changelog update for 4.1.0</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/e8f1362ab640c883a5d296e951308fab22073e7f"><code>e8f1362</code></a> <code>Docs: Remove wrong descriptions in <code>padded-block</code> rule (#8783)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/291a78302c1d5d402c6582b3f4cc836e61fde787"><code>291a783</code></a> <code>Update: <code>enforceForArrowConditionals</code> to <code>no-extra-parens</code> (fixes #6196) (#8439)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/a21dd32c46f95bc232a67929c224824692f94b70"><code>a21dd32</code></a> <code>New: Add <code>overrides</code>/<code>files</code> options for glob-based config (fixes #3611) (#8081)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/879688ce96f80aa0692f732759c6f67a0c36c4c3"><code>879688c</code></a> <code>Update: Add ignoreComments option to no-trailing-spaces (#8061)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/b58ae2e6d6bd4662b549ca5c0472943055a74df8"><code>b58ae2e</code></a> <code>Chore: Only instantiate fileEntryCache when cache flage set (perf) (#8763)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/98512881f1fc2417011247931fa089d987ee8cc6"><code>9851288</code></a> <code>Update: fix indent errors on multiline destructure (fixes #8729) (#8756)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/3608f06c2a412587c2d05dec0297803b25f3e630"><code>3608f06</code></a> <code>Docs: Increase visibility of code of conduct (fixes #8758) (#8764)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/673a58bc8420075ba698cee6762e17322a5263c3"><code>673a58b</code></a> <code>Update: support multiple fixes in a report (fixes #7348) (#8101)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/7a1bc3893ab55d0ab16ccf4b7a62c85329ab4007"><code>7a1bc38</code></a> <code>Fix: don't pass default parserOptions to custom parsers (fixes #8744) (#8745)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/c5b405280409698d14b62cbf3c87b7cf6cf71391"><code>c5b4052</code></a> <code>Chore: enable computed-property-spacing on ESLint codebase (#8760)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/3419f6446e205d79d9db77f6c176b9167d1fd8a7"><code>3419f64</code></a> <code>Docs: describe how to use formatters on the formatter demo page (#8754)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/a3ff8f21106cf8eca55978d4b3e053973f5e1bf2"><code>a3ff8f2</code></a> <code>Chore: combine tests in tests/lib/eslint.js and tests/lib/linter.js (#8746)</code></li>
<li><a href="https://urls.greenkeeper.io/eslint/eslint/commit/b7cc1e6fe995d52e581fcb2b1a44e37a18680e90"><code>b7cc1e6</code></a> <code>Fix: Space-infix-ops should ignore type annotations in TypeScript (#8341)</code></li>
</ul>
<p>There are 141 commits in total.</p>
<p>See the <a href="https://urls.greenkeeper.io/eslint/eslint/compare/421aab44a9c167c82210bed52f68cf990b7edbea...7d9e3beeb58c1ee71d53dfcfd3e3b0721dd79b46">full diff</a></p>
</details>

<details>
  <summary>Not sure how things should work exactly?</summary>

  There is a collection of [frequently asked questions](https://greenkeeper.io/faq.html) and of course you may always [ask my humans](https://github.com/greenkeeperio/greenkeeper/issues/new).
</details>


---


Your [Greenkeeper](https://greenkeeper.io) Bot 🌴
@eslint-deprecated eslint-deprecated bot locked and limited conversation to collaborators Feb 6, 2018
@eslint-deprecated eslint-deprecated bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Feb 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion core Relates to ESLint's core APIs and features enhancement This change enhances an existing feature of ESLint
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal: Allow a fixer to apply multiple fixes simultaneously
8 participants