-
-
Notifications
You must be signed in to change notification settings - Fork 70
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
feat!: skip warnings cli flag #104
Conversation
Hi @me4502!, thanks for the Pull Request The first commit message isn't properly formatted. We ask that you update the message to match this format, as we use it to generate changelogs and automate releases.
To Fix: You can fix this problem by running Read more about contributing to ESLint here |
Is there a reason we couldn’t make “quiet” have this behavior? |
This is one of the alternatives considered in the RFC. IMO it would make more sense to the user, but would also introduce a few confusion points around the existing flags that make use of warnings, such as |
Co-authored-by: Bryan Mishkin <698306+bmish@users.noreply.github.com>
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 for putting this together! It seems like a good idea although the consensus seems to favor implementing this behind the --quiet
flag rather than adding a new one. Can you please update your proposal with that in mind?
I'm currently going through processes to get the corporate CLA signed - Will make all requested alterations once that is all sorted :) |
@me4502 sounds good! |
@me4502 there are a couple of other comments left by @mdjermanovic. Can you please take a look at those? |
I'll apply the changes by the end of the week - just been a bit busy :) |
|
||
```typescript | ||
linter.verifyAndFix(text, configs, { | ||
rulePredicate: ({ ruleId: string, rule: Rule, severity: number }) => { |
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.
Maybe filterRules()
would be clearer than rulePredicate
here?
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.
It looks like this would be called once for each rule -- if someone has hundreds of rules enabled, I'm wondering about a perf hit of this approach vs. having a function that just inspects the entire rules
section of a config and just returns the pieces that should be run. What do you think?
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.
or ruleFilter
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.
The name change makes sense; will do
My only concern around the performance change there is, it'd still be constructing an array from the configuredRules and grabbing all the data, and then having to re-construct the configuredRules list to later iterate on. There'd be a fair bit of double-ups on data access, creation of an array, and filtering/mapping/etc. I feel that overhead might outweigh function calls here in most cases
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.
Sorry, I'm not quite following. What change are you referring to here?
I think running this function over every rule is the biggest perf hit we'll take, so I'd like to see if there's a way to avoid that.
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.
Yeah that was my main concern with switching to this style given in any case it'd be checking against every rule and this might be slower due to the extra array allocation (#104 (comment)), although my original concern of duplicated data access didn't end up being an issue in my implementation. While it could be replaced with a for loop and a new array, I feel I trust the inbuilt filter function here to perform the array side of things faster than writing it in userland would be.
I can't think of a way that this overhead could be entirely negated, aside from allowing this function to be undefined and not calling it when it's not in use. That would remove the overhead in cases where it won't likely lead to a performance improvement, but might complicate the API a little more
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.
Sorry, finally getting back to this. What I was thinking is instead of doing this:
{
ruleFilter(ruleId, rule, severity) {
return severity === 1;
}
}
In this case, we are running the function once on every rule when two of those parameters are actually already available inside of the rule config (and it seems like we don't have a use case for the second parameter currently?).
We would instead just pass in the rules config and return a filtered one.
{
ruleFilter(rulesConfig) {
return Object.fromEntries(Object.entries(rulesConfig).filter(([,options]) => options[0] === 1));
}
}
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.
Okay, so after pondering this a bit more, it seems clear to me that there is no performance difference between the two options I listed in preceding comment. Either way, we run a filter on every rule, so the proposal in the RFC is fine as-is.
I think the next question is around the arguments to the predicate. I'd propose we just start with the ruleId and the severity, as that's our only use case right now. If we end up with other use cases in the future, we can add more.
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 - I've removed the rule
option from the RFC document
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.
In this case, would you be happy for me to go back to how it was beforehand where it ran per-rule while iterating through the configuredRules array? The current method where it creates a new array from my testing is actually slower
Edit: actually it might make more sense to keep it as-is, as it means the API can be used to contextually disable a rule if another rule is enabled (eg, disabling a non-type-aware rule if the type-aware one is enabled). The performance difference is negligible anyway
Co-authored-by: Nicholas C. Zakas <nicholas@humanwhocodes.com>
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.
I think this is in good shape now, thanks for your patience throughout this process. We know that the RFC process can take a while and we really appreciate you sticking with us.
I'd like @mdjermanovic to review one more time but overall I think we are good to go.
filterRules: (rules: { ruleId: string; severity: number }[]) => { | ||
return rules.filter((rule) => rule.severity === 2); | ||
}, |
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.
Since this is a new API option that can be used independently of this feature, I'm more in favor of the previous version: ruleFilter({ ruleId: string, severity: number }): boolean
, as it is simpler and there's no ambiguity about what's allowed because it can only filter out rules. A function that gets a list and returns a list may appear to be allowed to change severities or even add new rules, which may or may not work by chance depending on how we implement handling return value.
If we get a request to pass all the rules so that the function can contextually disable a rule if another rule is enabled, as noted in #104 (comment), we could add them as a second argument. That would be similar to Array.prototype methods.
@nzakas what do you think?
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.
I agree. I actually misread the latest update and thought that's what we were doing now, so thanks for pointing it out.
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.
In the interest of time and since we are already so close, I went ahead and added suggestions to get this RFC into the state we'd like before merging. Please take a moment to review and make sure it makes sense.
From an API perspective, this would be implemented by a filter function that filters down to | ||
which rules should be run. The function would take a list of the rule configuration `(ruleId, severity)`, | ||
and return the rule that should be run. For simplicity, this function should not be | ||
given rules marked as `off`, as if this function handled existing behavior then users of the | ||
API would have to mimic that when attempting to extend it. | ||
|
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.
From an API perspective, this would be implemented by a filter function that filters down to | |
which rules should be run. The function would take a list of the rule configuration `(ruleId, severity)`, | |
and return the rule that should be run. For simplicity, this function should not be | |
given rules marked as `off`, as if this function handled existing behavior then users of the | |
API would have to mimic that when attempting to extend it. | |
From an API perspective, this would be implemented by a filter function that filters down to | |
which rules should be run. The function would take `(ruleId, severity)` and return true to include and rule or false to exclude it. For simplicity, this function should not be | |
given rules marked as `off`, as if this function handled existing behavior then users of the | |
API would have to mimic that when attempting to extend it. | |
In `cli.js`'s `translateOptions` function, the `filterRules` option should be assigned to | ||
a function that filters to rules with a `severity` of 2 (error) when the `--quiet` flag is applied, otherwise | ||
acts as an identity function. In `eslint/flat-eslint.js`, the `filterRules` should be taken from | ||
the `eslintOptions` object, and passed down to the `linter.verifyAndFix` call. |
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.
In `cli.js`'s `translateOptions` function, the `filterRules` option should be assigned to | |
a function that filters to rules with a `severity` of 2 (error) when the `--quiet` flag is applied, otherwise | |
acts as an identity function. In `eslint/flat-eslint.js`, the `filterRules` should be taken from | |
the `eslintOptions` object, and passed down to the `linter.verifyAndFix` call. | |
In `cli.js`'s `translateOptions` function, the `ruleFilter` option should be assigned to | |
a function that filters to rules with a `severity` of 2 (error) when the `--quiet` flag is applied, otherwise | |
acts as an identity function. In `eslint/flat-eslint.js`, the `filterRules` should be taken from | |
the `eslintOptions` object, and passed down to the `linter.verifyAndFix` call. |
Within `linter.js`, the API should be added to `VerifyOptions`, and will be passed down into and | ||
utilized within the `runRules` function before the current `configuredRules` loop. Rather than the | ||
current configuredRules loop, this should extract the `severity` and `rule` existence checks and | ||
build a list of `{ ruleId, severity }` objects. This new list should be passed to `filterRules`, | ||
and the resulting list should be iterated on instead of `configuredRules`. |
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.
Within `linter.js`, the API should be added to `VerifyOptions`, and will be passed down into and | |
utilized within the `runRules` function before the current `configuredRules` loop. Rather than the | |
current configuredRules loop, this should extract the `severity` and `rule` existence checks and | |
build a list of `{ ruleId, severity }` objects. This new list should be passed to `filterRules`, | |
and the resulting list should be iterated on instead of `configuredRules`. | |
Within `linter.js`, the API should be added to `VerifyOptions`, and will be passed down into and | |
utilized within the `runRules` function before the current `configuredRules` loop. Rather than the | |
current configuredRules loop, this should use `ruleFilter` as a filter to check the `severity` and `ruleId` to | |
build a list of `ruleId`s. This new list should be iterated on instead of `configuredRules`. |
build a list of `{ ruleId, severity }` objects. This new list should be passed to `filterRules`, | ||
and the resulting list should be iterated on instead of `configuredRules`. | ||
|
||
`normalizeVerifyOptions` should verify that the `filterRules` option is a function, and replace it |
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.
`normalizeVerifyOptions` should verify that the `filterRules` option is a function, and replace it | |
`normalizeVerifyOptions` should verify that the `ruleFilter` option is a function, and replace it |
|
||
`normalizeVerifyOptions` should verify that the `filterRules` option is a function, and replace it | ||
with an identity function if not. `processOptions` in `eslint-helpers.js` should also perform a | ||
validation check that the `filterRules` option is a function. |
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.
validation check that the `filterRules` option is a function. | |
validation check that the `ruleFilter` option is a function. |
with an identity function if not. `processOptions` in `eslint-helpers.js` should also perform a | ||
validation check that the `filterRules` option is a function. | ||
|
||
The new `filterRules` function when implemented would look like this, using the `--quiet` flag |
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.
The new `filterRules` function when implemented would look like this, using the `--quiet` flag | |
The new `ruleFilter` function when implemented would look like this, using the `--quiet` flag |
filterRules: (rules: { ruleId: string; severity: number }[]) => { | ||
return rules.filter((rule) => rule.severity === 2); | ||
}, |
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.
filterRules: (rules: { ruleId: string; severity: number }[]) => { | |
return rules.filter((rule) => rule.severity === 2); | |
}, | |
ruleFilter: ({ ruleId: string; severity: number }) => { | |
return rule.severity === 2; | |
}, |
``` | ||
|
||
The function acts as a filter on the rules to enable, where removal from the list disables a rule. | ||
This function would return all rules which have a severity of 2 (error), effectively filtering out |
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.
This function would return all rules which have a severity of 2 (error), effectively filtering out | |
This function would return true for all rules which have a severity of 2 (error), effectively filtering out |
Thanks, I noticed a few extra places that needed to be changed back to the old predicate function setup so I pushed it as a separate commit rather than including your suggestions via the GitHub UI I believe it should all be back to working that way now |
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! Moving to final commenting. I left one suggestion to add to the Backwards Compatibility Analysis section so that we don't forget why this is a breaking change and what to advise users in the migration guide.
This is a breaking change as it alters the behaviour of the `--quiet` flag. | ||
While the alterations to the quiet flag should not affect the actual outcome of the command, | ||
as cases where it would are covered by this RFC, it is still worth noting as the behaviour | ||
is changing. |
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.
I'd add here the case we discussed in #104 (comment). Rules with side effects, such as the react/jsx-uses-vars
rule, will not be run if set to "warn"
, so users will need to update their configs to "error"
after we make this change.
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.
It’d be nice if there was a way for a rule to know its own severity, so that this rule could warn when it’s on warn :-)
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, I've just added this to the document
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.
Looks good, thanks!
Marking as approved and merging! Thanks so much for your patience @me4502. We are ready to implement. |
Thanks, what would be the next step on my end? As it's a breaking change does that mean the implementation should be held off until a major release is happening, or is implementation able to start now? |
You can start the implementation and just mark the PR as a draft so we know not to merge it until the next major release. |
PR in question (just for visibility) |
Summary
Currently, ESLint will run all rules that are not marked as
off
in the configuration.This RFC proposes adding a way to configure which rules are actually run, to reduce linting
time and better match the reporting outcome. Currently, when a rule is marked as
warn
,ESLint will still run the rule but not report the results when run under
--quiet
. Theintended outcome of this RFC is to allow users to not run these rules when unnecessary.
I have a very rough test implementation patch here, https://gist.github.com/me4502/e75e4677d3ff17ad585fcab0ac5dc6e9. It is missing a few things and is not very clean, but was done to assist in writing the design details of the RFC itself.
Related Issues
eslint/eslint#16450