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

Proposal: Allow a fixer to apply multiple fixes simultaneously #7348

Closed
not-an-aardvark opened this issue Oct 12, 2016 · 18 comments · Fixed by #8101, singapore/lint-condo#308, homezen/hz-test-helpers#42 or renovatebot/renovate#341
Assignees
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

Comments

@not-an-aardvark
Copy link
Member

Problem

As of ESLint 3.7, the autofixer for a rule can only perform one "fix" at a time. In this context, a "fix" is an atomic change (removal/insertion/replacement) to a single contiguous range of code.

From what I understand, this restriction was introduced when the autofixers for core rules almost exclusively fixed whitespace, and didn't touch any tokens. The intention was to ensure that each fix was as small as possible, to increase the likelihood of any given fix getting applied.

There are cases where a rule wants to apply multiple fixes at separate locations. Nowadays, many autofixers modify tokens and the AST, not just whitespace. As a result, these multi-fix operations cannot be split into smaller atomic operations, because the syntax or runtime behavior of the code will be incorrect if only one fix gets applied without the other.

The autofixer for no-extra-parens is a good example of this:

/* eslint no-extra-parens: error */

var foo = (function () {
  // function body
});

The autofixer for no-extra-parens would like to remove the parentheses around the function, to obtain the following code:

var foo = function () {
  // function body
};

Clearly, the left and right parentheses have to be removed in the same pass of the code; if only one paren is removed without the other, the resulting syntax will be invalid. Reporting two separate errors as a workaround does not solve this issue; since the fix from any given reported error is not guaranteed to be applied, reporting an for each paren can still result in only one fix being applied under certain circumstances.

Due to these restrictions, no-extra-parens (and many other core rules, such as prefer-arrow-callback, quote-props, wrap-regex, etc.) uses a a different workaround: it replaces the entire text range between the two parens. Applied to this example, it replaces the entire range containing (function () { ... }) with function () { ... }.

This workaround works because it only applies to a single text range, so it is a single contiguous fix. However, it is not ideal. Since the resulting fix is so large, it can prevent other fixes from being applied during the same pass; this results in a larger number of passes, which hurts the performance of autofixing. The fix is also overcomplicated intuitively; for a large function, it could easily end up replacing tens of thousands of characters, even though it is only trying to remove two characters.

Proposal: Allow multiple fixes

This problem could be solved if a rule could apply multiple fixes per reported problem. For example, the no-extra-parens autofixer would apply two fixes; a single-character fix to remove the left paren, and a single-character fix to remove the right paren.

The fixes for a given reported problem would be applied atomically. In other words, there would still be no guarantee that any problem's fixes would get applied in general, but the core engine would guarantee that all the fixes for a problem would be applied simultaneously, so the code would never end up in a state where only some of a problem's fixes were applied.

API Changes

Currently, fixes are applied when a fixer function returns an object with range and text keys. (Usually, these fix objects are generated by helper functions such as RuleFixer#replaceText.)

To apply multiple fixes, a fixer function could instead return an array of these fix objects. Returning a single fix object would still be supported for backwards-compatibility.

For example, the fixer function for no-extra-parens would look something like this:

context.report({
  node,
  message,
  fix(fixer) {
    return [
      fixer.remove(leftParenToken),
      fixer.remove(rightParenToken)
    ];
  }
});
@not-an-aardvark not-an-aardvark added enhancement This change enhances an existing feature of ESLint core Relates to ESLint's core APIs and features evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Oct 12, 2016
@not-an-aardvark not-an-aardvark self-assigned this Oct 12, 2016
@platinumazure
Copy link
Member

platinumazure commented Oct 12, 2016

I'm just going to leave one example showing why this would be somewhat difficult:

Before:

const sum = (5+8);

Rules being applied:

  • space-infix-ops (should add spaces to each side of + token)
  • no-extra-parens (remove the parentheses)

Desired after:

const sum = 5 + 8;

Fixes to be applied:

[
    // no-extra-parens: remove parentheses
    [{ range: [12, 13], text: "" }, { range: [18, 19], text: "" }],

    // space-infix-ops: replace operator with padded operator
    { range: [14, 15], text: " + " }
]

The current implementation is efficient (in terms of time performance) because fixes are applied in reverse order of starting range. This means we iterate through fixes in linear time and we do not have to alter the ranges of any fixes (because a fix later in the text does not affect the location of a fix earlier in the source).

A naive implementation that focuses on atomicity would have to iterate through the fix queue and potentially update locations for remaining fixes, so this would become O(n^2) (on the number of fixes) without very clever thinking. In the example above, this applies no matter which rule's fixer you apply first:

  • space-infix-ops first: We need to ensure the range for the second fix of no-extra-parens has its location adjusted.
  • no-extra-parens first: We need to ensure the range for the space-infix-ops fix has its location adjusted.

I'm sure nzakas will have many more examples. I just wanted to make sure the size of the problem (which I may still be under-representing) is clear.

@not-an-aardvark
Copy link
Member Author

Interesting example. I'll think about whether it's possible to maintain linear time and atomicity, but I'm also wondering: How bad for overall performance would a quadratic-time implementation actually be?

For example, if a file has 100 fixes (probably much larger than the average), this requires updating ranges O(10000) times. I'm inclined to think that updating a range is computationally inexpensive, as it just requires a couple arithmetic operations, so even 10000 range updates would not cause a performance difference noticeable to the user. In comparison, doing another autofixing pass is very expensive, as it requires reparsing the file and running every rule again on the new AST.

In other words, even if this change would require the autofixing implementation to be O(n2) in the number of fixes, it might still improve performance overall by reducing the number of passes required (since fewer fixes would conflict with each other)

@not-an-aardvark
Copy link
Member Author

not-an-aardvark commented Oct 12, 2016

Here's a linear-time implementation:

  1. Create a linked list of character blobs in the file. A character blob is a group of consecutive characters that is not split by any fixes. For n fixes, there will be at most 2n+1 blobs.
  2. Match each fix to a starting blob and ending blob, based on the ranges of the fix.
  3. Determine whether each fix can be applied, based on whether its ranges overlap with a different fix. If a fix can't be applied, none of the fixes from the same report can be applied.
  4. Given the list of fixes that should be applied, apply each of them by replacing the appropriate blobs for a given fix with a new blob for the replacement text.
  5. Reassemble the linked list of blobs into a string

edit: Added an explicit step to determine whether a fix should be applied

@nzakas
Copy link
Member

nzakas commented Oct 13, 2016

Related: #5329

If I'm understanding you correctly, it sounds like in the case of an autofix that needs to apply two changes, you'll apply the first one, then apply any intervening fixes while keeping an offset of the fixes already made so that the second one can be made in the correct location?

@not-an-aardvark
Copy link
Member Author

Not quite: I was saying I would store the text of the file as a linked list and convert all the ranges appropriately at the start. That way, the fixer wouldn't have to deal with updating ranges and offsets, and it could still be O(n).

Although now that you mention it, keeping an offset might also allow for O(n) time while keeping the solution simpler overall.

Not to deter from the implementation discussion, but what do you think of the proposed API change itself? If the proposal ends up getting accepted, then I can draft an implementation which might make discussion about it easier.

@platinumazure
Copy link
Member

I'm okay with the API. I just want to make sure we agree on what the requirements of a new implementation should be (which may include things like linear time performance, atomicity of sets of fixes, etc.)

@not-an-aardvark
Copy link
Member Author

Atomicity should definitely be required for all the fixes from a given reported problem. (A fix for removing parens, such as the one in the original comment, is only safe if the fixes are atomic).

I think linear-time performance should also be a requirement for the implementation, provided that linear-time performance is possible to achieve. I'm pretty sure it is possible, but if we discover that I'm wrong and it's impossible, then I think it would still be worth implementing to determine the actual performance impact of a quadratic-time implementation.

@not-an-aardvark
Copy link
Member Author

Slight correction: The existing implementation appears to be O(n log n) time, not linear time (since the list of fixes is sorted beforehand). So the new implementation should be at most O(n log n) time.

@not-an-aardvark
Copy link
Member Author

Sample O(n log n) implementation: 48357ea

It ended up being simpler than I was describing it; the fixer now does the check for conflicts before any fixing takes place. This allows allows it to "undo" a fix by simply removing it from the list of fixes to apply if it later discovers a conflict on the same reported problem. After checking for conflicts and creating a list of fixes that can be applied without conflicts, the fixer goes ahead and applies the fixes.

@nzakas
Copy link
Member

nzakas commented Oct 17, 2016

The API itself is fine by me, I'm much more worried about the implementation, and most specifically, that we won't end up doing fixes incorrectly. The primary reason I required just one change per message was to ensure the correctness of the output. For instance, if one rule removes a comma while another tries to insert a space before a comma, if the comma removal happens before the space insertion, we've now altered the text modification and might have inserted a space in the middle of an identifier.

How do you ensure that multiple fixes for a message are always applied and that fixes that might apply in between don't interfere?

(note: I'm not nearly as worried about performance as I am about correctness)

@not-an-aardvark
Copy link
Member Author

not-an-aardvark commented Oct 19, 2016

Interesting question. I was going to make an argument involving context-free grammars, but then I realized that it's moot because JS isn't completely context-free anyway. As a result, there are a few cases where non-overlapping fixes can conflict with each other, even with the current behavior.


Example:

Rule A is a custom rule that replaces all non-default function parameters with a default parameter of undefined. For example, it performs this fix:

function foo(param) {}

// Rule A fixes this to:

function foo(param = undefined) {}

Rule B is like the opposite of ESLint's strict rule; it adds redundant 'use strict'; directives to functions:

'use strict';
function foo(param) {}

// Rule B fixes this to:

'use strict';
function foo(param) {'use strict';}

(Granted, I'm not sure why anyone would actually want to use either of these rules, but bear with me.)

Suppose rules A and B are applied to the following code, in autofix mode:

'use strict';

function foo(param} {}

Rule A will replace param with param = undefined, and Rule B will insert 'use strict'; in the function body. Individually, each of the fixes can be applied without changing the semantics of the code. These fixes do not overlap, so both of them will be applied by the current version of the fixer:

'use strict';

function foo(param = undefined) {'use strict'}

But this is a SyntaxError, since default parameters aren't allowed in functions with 'use strict'; directives. (edit: coincidentally, I saw this blog post right after writing this comment)


Admittedly, this doesn't really answer your question, but I thought it was worth pointing out that the existing behavior isn't correct 100% of the time either.

@alberto
Copy link
Member

alberto commented Oct 21, 2016

I was intending to try to implement something like this for a while, I think it's doable with the current design, so thanks @not-an-aardvark for bringing it up.

We apply non-overlapping fixes from the bottom up. With this approach a fix would consist of a set of multiple ranges. We would sort them by the highest range end, and the whole set would be applied on a single pass or not applied at all. Other fixes would only be applied if their ranges didn't overlap with the sets of fixes already applied (or queued for applying in that pass). Everything else would work as usual. Makes sense?

@nzakas
Copy link
Member

nzakas commented Oct 21, 2016

@not-an-aardvark oh yeah, I know the current approach can't guarantee every fix won't introduce a syntax error. However, it can guarantee that syntax errors won't be created by two different rules modifying the same text at the same time.

@alberto I'm unsure if you're addressing me or @not-an-aardvark, so I'm just going to assume you're addressing me and if not, no worries.

We would sort them by the highest range end, and the whole set would be applied on a single pass or not applied at all. Other fixes would only be applied if their ranges didn't overlap with the sets of fixes already applied (or queued for applying in that pass).

I guess I'm having a hard time seeing how this is much different than what we have right now. If the changes must be applied atomically, then that mimics today's behavior. The only difference is that instead of you extract text, modifying it, and reinserting it, now you're specifying the same fix as a number of text operations for the fixer to do (essentially, the same stuff you're currently doing in the rule). Am I missing something?

@gibson042
Copy link

I think you want Operational Transformation, and in particular https://www.npmjs.com/package/ot .

@not-an-aardvark
Copy link
Member Author

@gibson042 Sorry, I don't understand; could you clarify how OT is useful in making sure no syntax errors are produced?

@gibson042
Copy link

It can't guarantee that (as you pointed out above, even current application can generate them). But it will allow atomic application of multiple ranges and conflict detection/resolution without reinventing the wheel, addressing @nzakas concerns at #7348 (comment) and #7348 (comment) .

@alberto
Copy link
Member

alberto commented Oct 22, 2016

@nzakas I was replying to everyone in the sense that I think my implementation proposal is not the same as @not-an-aardvark if I understood him correctly.

My proposal is not very different from what we have now, only adapted to multiple changes. Let me explain the process in more detail:

  • a fix can consist of one or more changes (right now, one fix === one change). The same api @not-an-aardvark proposed in the first comment.
  • We sort fixes in descending change order (by their highest change range). We take the first fix in the list (the one with the highest change range) and apply that change, since there are no possible conflicts yet. We don't apply the rest of the changes in this fix immediately, but store them to apply them in the future, but in the same fix iteration (before we lint the file again, hence the "atomicity"). Let's say these changes have been accepted, for lack of a better term.
  • We take the next fix from the list. If its changes don't overlap with any of the changes already accepted, the last change of this fix is applied and the rest of the changes are added to the accepted list. If any of the changes in this fix overlaps with the already accepted changes, the fix is not applied.
  • Repeat for every remaining fix.

Since changes are applied in reverse order, as they are currently done, there is no need for an offset or updating ranges.

In the example @platinumazure mentioned, the order in which the changes would be applied would be:
[18,19], [14,15], [12, 13]

@not-an-aardvark not-an-aardvark added accepted There is consensus among the team that this change meets the criteria for inclusion and removed evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels May 15, 2017
@not-an-aardvark
Copy link
Member Author

Marking as accepted because #8101 was accepted in the TSC meeting a few days ago.

bors bot added a commit to IMA-WorldHealth/bhima that referenced this issue 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
5 participants