-
Notifications
You must be signed in to change notification settings - Fork 141
Use getRules and rule URL metadata where available #1067
Conversation
363b9d7
to
cb283ba
Compare
If you would prefer that the refactor work is split out to a separate PR from the |
757e82f
to
e3af87f
Compare
@Arcanemagus I hate to say it, but yeah if you could split out the refractor work that would be helpful. If you want to keep it in this PR, that's fine, but having a separate commit would be good I think. |
69661f7
to
c8cf6a5
Compare
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 splitting up the work, that was very helpful. I like the PR overall, and the specs are great to have. I do have some ideas of ways that I think we could improve some of this, not all of which was added in this PR but this might be a nice time to clean some of it up.
src/helpers.js
Outdated
* Process the updated rules into the local Map and call further update functions | ||
* @param {Array} updatedRules Array of Arrays of the rule and properties | ||
*/ | ||
export function updateRules(updatedRules) { |
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.
Preference
I'm not sure update
is the best name here, because it kind of makes it sound like like the old rules are being kept and just somehow modified. I know this is niggling, but I think I'd prefer a name like replaceRules
in this case.
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 went with update
since as far as anything outside this function is concerned it is an update. The fact that it happens to clear the current contents and then fill in new ones is an implementation detail.
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 see your reasoning. Personally, if I call updateThings(newThing)
, it isn't immediately clear as a consumer of the function whether all the old things will be gone or not. I might think that I'm just adding a new thing to the existing things. I guess all I'm saying is that update
is vague, and I find replace
to be clearer, but I'm only at a 3 of 10 on the SSOGAF.
At the very least, perhaps the docblock can be clarified that the updatedRules
provided will replace the existing rules?
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's certainly not ideal that it clears the old rules in place as a side-effect before updating them. But the general concept of full replacement as an update is totally valid, and the foundation of immutability and great tools like Redux.
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 have no issue with doing it that way, just the naming.
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.
Updating in place gets complex since then you have to determine what you need to remove, and what you need to insert. Since there is nothing actually tied to the contents here a simple replacement works just fine.
I'd say I'm at around a 4 or 5 on the SSOGAF 😛.
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.
Haha, you called it a _replace_ment. 😝
src/helpers.js
Outdated
/** | ||
* Update the list of fixable rules | ||
*/ | ||
function updateFixableRules() { |
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.
What do you think about this function taking fixableRules
as an argument, to minimize the side effects and avoid mutating something outside of its own scope? I realize that if you pass it in and modify it, that's still technically a side effect, but it feels slightly cleaner.
src/helpers.js
Outdated
@@ -8,6 +8,7 @@ import cryptoRandomString from 'crypto-random-string' | |||
// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions | |||
import { Range } from 'atom' | |||
|
|||
const lintRules = new Map() |
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 feels to me like if we go this route, lintRules
should be a property on a class
, and updateFixableRules
, updateRules
, and getRules
should be methods on that class. Normally I'd advocate for a more functional style, but I find maps to be difficult to work with in pure functions.
src/worker-helpers.js
Outdated
const newRuleIds = new Set(newRules.keys()) | ||
|
||
// Check for new rules added since the last time we sent currentRules | ||
const newRulesIds = new Set(newRules.keys()) |
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.
Is it intentional to have newRuleIds
and newRulesIds
? If so, that's pretty confusing, IMHO.
I like the approach to comparing Sets laid out in this SO answer: https://stackoverflow.com/a/44827922/1435658
areSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value));
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's definitely confusing at best, and that's much cleaner. I'll switch it to something like that 😉.
src/worker.js
Outdated
// to check the loaded rules (including plugin rules) and update our list of fixable rules. | ||
updateFixableRules(cliEngine.linter) | ||
const rules = Helpers.getRules(cliEngine) | ||
sendRules = Helpers.didRulesChange(rulesMetadata, rules) |
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.
Preference
I know this wasn't created in this PR, but as a boolean constant, let's name this something like shouldSendRules
.
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 do.
src/worker.js
Outdated
// You can't emit Maps, convert to Array of Arrays to send back. | ||
response.updatedRules = [] | ||
rulesMetadata.forEach((props, rule) => | ||
response.updatedRules.push([rule, props])) |
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.
Doesn't Array.from(rulesMetadata)
do what you want here? And if not, can we use rulesMetadata.entries()
?
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.
Honestly I didn't even think to try it. Array.from(<some Map>)
does exactly this. 👍
efa3a44
to
afc5af5
Compare
src/rules.js
Outdated
constructor(newRules) { | ||
this.rules = new Map() | ||
if (newRules) { | ||
this.updateRules(newRules) |
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'm no Map
expert, but couldn't this be done in one step? Something like:
this.rules = new Map(newRules)
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 the constructor will never be called with a parameter except in specs, sure. If this was a "real" class though we would want validation in there that whatever newRules
is is something that actually makes sense to instantiate with.
afc5af5
to
53f1494
Compare
Refactor the worker to send the full metadata instead of just the fixable rules, allowing additional functionality to be implemented in the main package code.
The rule URL is now pulled directly from the metadata if it is available, falling back to the current method of using `eslint-rule-documentation` if the rule doesn't specify this for itself.
Rule gathering from the ESLint instance has been expanded to take advantage of the new `CLIEngine#getRules` method if it is available.
Rename `sendRules` to `shouldSendRules` to make the purpose of the flag more clear.
Use a much simpler logic to determine whether the rule `Map`s are equal or not. Also fixes up the docblock a bit.
Apparently `Array.from` is quite happy taking a `Map` object directly.
Pull all the logic related to storing the list of known rules and accessing it out into a class of its own.
53f1494
to
03af716
Compare
I'd still prefer |
Check that the given Array contains Arrays that "look" like the exected format: [String, Object]. Also renames `updatedRules` to `replaceRules` since it is now taking advantage of the Map constructor to parse through the Array.
Refactor the worker to send the full metadata instead of just the fixable rules, allowing additional functionality to be implemented in the main package code.
Rule gathering from the ESLint instance has been expanded to take advantage of the new
CLIEngine#getRules
method if it is available.The rule URL is now pulled directly from the metadata (
rule.meta.docs.url
) if it is available, falling back to the current method of usingeslint-rule-documentation
if the rule doesn't specify this for itself.These new features were added in ESLint v4.15.0.
This also refactors how rules are handled a bit so the following behavior is how it works now:
helpers.js
):Rules
object is updated with the new set of rulesRules
instance is queried for the current list of fixable rules.Rules
instance is queried for a URL for the rule's documentation.