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
New: Allow passing a function as fix
option (fixes #8039)
#8730
Conversation
Thanks for the pull request, @IanVS! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
@IanVS, thanks for your PR! By analyzing the history of the files in this pull request, we identified @nzakas, @gyandeeps and @roadhump to be potential reviewers. |
fix
option (fixes #8039)fix
option (fixes #8039)
96f7558
to
c2f0916
Compare
LGTM |
I've rebased this to account for the recent addition of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This looks good, I just have a few minor suggestions.
lib/linter.js
Outdated
* @returns {Object} The result of the fix operation as returned from the | ||
* SourceCodeFixer. | ||
*/ | ||
verifyAndFix(text, config, options) { | ||
verifyAndFix(text, config, options, shouldFix) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Could fix
be one of the options (third parameter) rather than adding another parameter to this method (e.g. as a fixFilter
property)? It would be nice to avoid the boolean trap.
const result = SourceCodeFixer.applyFixes(sourceCode, [INSERT_AT_START], shouldFixSpy); | ||
|
||
assert.equal(result.output, TEST_CODE); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test to assert that the fix filter is called as a pure function (without a this
value)? I don't want to accidentally expose internal objects because that could make it difficult to change the implementation later if people start to rely on the this
value.
const result = SourceCodeFixer.applyFixes(sourceCode, [INSERT_AT_START], shouldFixSpy); | ||
|
||
assert.equal(result.output, TEST_CODE); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test that asserts that only the fixes for which the fix filter returns true
are applied? All of the filters in the existing tests appear to either always return true or always return false, but as a sanity check it would be useful to make sure the function works when it only sometimes returns true.
lib/util/source-code-fixer.js
Outdated
output = ""; | ||
if (typeof shouldFix === "function") { | ||
if (shouldFix(problem)) { | ||
fixesWereApplied = attemptFix(problem) || fixesWereApplied; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm understanding correctly, attemptFix
will only return false
if the current fix conflicts with another fix that has already been applied. In that case, will fixesWereApplied
ever be false
after this line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's a great point. I can't remember my thinking here, but I think I was trying to be cautious and not build in assumptions about the conditions under which attemptFix
would return true or false. But, the diff shows that currently we always return true
from this block as it is. I'll be happy to simplify this down, and add a comment about why it is always true.
lib/util/source-code-fixer.js
Outdated
if ((start < 0 && end >= 0) || (start === 0 && fix.text.startsWith(BOM))) { | ||
output = ""; | ||
if (typeof shouldFix === "function") { | ||
if (shouldFix(problem)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Maybe this could be simplified to
if (typeof shouldFix !== "function" || shouldFix(problem)) {
// ...
} else {
// ...
}
to avoid nesting/duplication.
lib/util/source-code-fixer.js
Outdated
return { | ||
fixed: false, | ||
messages, | ||
output: "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the output here be sourceCode.text
rather than the empty string, since the resulting text is the same as the original text? (I'm not sure whether the other parts of the code check the fixed
value before using the output, but setting the output to the original text seems like it would be least surprising in case fixed
isn't checked.)
Thanks for the review and great suggestions, @not-an-aardvark. I'll make your requested changes within the next few days. |
LGTM |
const fix = problem.fix; | ||
const start = fix.range[0]; | ||
const end = fix.range[1]; | ||
if (typeof shouldFix !== "function" || shouldFix(problem)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, we are only checking "truthiness" here. I think that's okay, but the other option would be to check for an explicit true
return value.
Thanks again, @not-an-aardvark. I believe I've addressed your comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
73f556d
to
d06f84a
Compare
LGTM |
This way, the code that uses this doesn’t need to necessarily check the value of `fixed`.
If we’ve gotten to this point, at least one fix will have been applied.
Meaning, that they cannot access the `this` value of SourceCodeFixer.
This is to verify that the problem can be used to return true or false conditionally, and that eslint will only apply fixes when true is returned.
LGTM |
This is to account for #8809
What is the purpose of this pull request? (put an "X" next to item)
[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofixing to a rule
[ ] Add a CLI option
[X] Add something to the core
[ ] Other, please explain:
Closes #8039
What changes did you make? (Give an overview)
The main change here is to allow the
fix
option of the CLIEngine to be either a boolean or now also a function which is given each linting message and is expected to return a boolean. This will give users greater control over what kind of fixes are applied by ESLint.Is there anything you'd like reviewers to focus on?
This may be somewhat incomplete. I worked on it a few months ago, and then put it aside. I've rebased on latest master and believe the happy path works correctly, but there may need to be more safety added around executing the function that is provided as the
fix
option, as right now all it does is check for truthiness of the return value (which is maybe fine, but is worth some consideration).I'm happy to continue working on this to get it across the finish line, based on feedback from reviewers. Or alternatively if anyone wants to jump in and take it over, I'm happy to turn it over as well.