-
-
Notifications
You must be signed in to change notification settings - Fork 207
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(eslint-plugin): [sort-lifecycle-methods] add rule #1320
feat(eslint-plugin): [sort-lifecycle-methods] add rule #1320
Conversation
Add feature requested by angular-eslint#995
☁️ Nx Cloud ReportWe didn't find any information for the current pull request with the commit 67abe2f. Check the Nx Cloud Github Integration documentation for more information. Sent with 💌 from NxCloud. |
The issue I had when I worked on the implementation is that, it is relatively easy to determine if methods are not in right order, but it can be hard to determine which methods should be reported... (e.g. Say method B, C are erroneously placed before method A, should B be reported, C be reported, A be reported, or both B, C be reported, ..., or the class instead of individual methods should be reported) Which one way of reporting should be adopted, or is it okay to report in whichever way that is convenient to implement (e.g. the current way)? |
TODO:
Ordering should be:
|
import { ASTUtils, Selectors } from '@angular-eslint/utils'; | ||
import type { TSESTree } from '@typescript-eslint/utils'; | ||
import { createESLintRule } from '../utils/create-eslint-rule'; | ||
import type { AngularLifecycleMethods } from '@angular-eslint/utils/dist/eslint-plugin/ast-utils'; |
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 unexpected to have a deep import here, is the symbol not exported?
Thanks a lot for working on this @Friendseeker! |
Any updates on this? ❤️ |
Sorry. I have been especially busy. I will work on it if I have spare time (If I... will have any spare time). |
@@ -91,6 +91,16 @@ export const angularLifecycleInterfaceKeys = objectKeys( | |||
); | |||
export const angularLifecycleMethodKeys = objectKeys(AngularLifecycleMethods); | |||
|
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.
Would you be able to include a comment containing the URL to the lifecycle event sequence?
https://angular.io/guide/lifecycle-hooks#lifecycle-event-sequence
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.
Done. See below.
node: previous.key, | ||
messageId: 'nonLifecycleMethodBeforeLifecycleMethod', | ||
}); | ||
} |
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.
You could limit to one for
loop by adding an extra condition here, and discarding the second loop.
Pseudo-code:
let previousLifecycleMethod = null; // Declare outside loop.
if (isLifecycleMethod(current) && previousLifecycleMethod != null) {
if (isBefore(current, previousLifecycleMethod)) {
context.report({
node: current.key,
messageId: 'lifecycleMethodsNotSorted',
});
}
previousLifecycleMethod = current;
}
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.
@jerone I considered this. However the first loop looks at all methods, checks the placement of the constructor and non-lifecycle methods. The second loop targets only the lifecycle methods and checks sequence. I added comments for clarity.
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.
Understood. The code change significantly since making this suggestion.
Converted to draft to more accurately reflect its current state |
@Friendseeker @JamesHenry I would like to contribute to this PR if possible. Please advise. |
Hi @sc0tt5! Sorry that it took me so long but I finally regained some spare time during weekends (for which I can use to wrap up this PR). Would definitely appreciate you to work on this PR to wrap it up! First off, would you say my below comment is reflective on what this feature should be? When I first worked on this feature, I did not consider all the cases, and I am still not sure if all cases are considered in the comment below.
Also, if order is incorrect, which methods should be flagged? Should the entire class be flagged inside? I still don't have a good idea on that problem, and would really appreciate your feedback. |
Hello @Friendseeker! No worries. I was delighted to find someone had taken the initiative to work this in. I will double check the ordering and anything we might be missing, then reconfirm with you soon. Give me until later today/tonight. Then once we're on the same page, I'll send over my contribution sometime soon. How should I contribute? I assume PR into your branch? |
Really appreciate, @sc0tt5 ! Feel free to take as much time as you need! Better to consider all cases than rushing it and having to redo things afterwards. PR into my branch sounds great! |
Update from angular-eslint main
@JamesHenry please consider removing the draft status and review the recent updates that I submitted via @Friendseeker's PR. |
`, | ||
messageId: lifecycleMethodBeforeConstructor, | ||
}), | ||
]; |
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's the expected behavior if more than one lifecycle hook is in the incorrect order?
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.
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 assuming that with the following example...
ngOnInit() {}
ngOnChanges() {}
ngAfterContentInit() {}
ngDoCheck() {}
...this rule will report twice: first on ngOnChanges
and then on ngDoCheck
. If my assumption is correct, I think this approach is appropriate.
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 the feedback! Just want to clarify, in the example
ngOnInit() {}
ngOnChanges() {}
ngAfterContentInit() {}
ngDoCheck() {}
There's two inversion pairs (ngOnInit, ngOnChanges)
and (ngAfterContentInit, ngDoCheck)
. The agreed rule is for each inversion pair, we flag the second element in the pair.
And going back to
ngOnInit
ngOnChanges
ngDoCheck
Since the inversion pair is (ngOnInit, ngOnChanges)
, only ngOnChanges
should be flagged.
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.
Thank you for clarifying. Makes sense to me 🙂.
One thing to keep in mind with this approach and if there will be an auto-fixer for this rule in the future... The ESLint fix "process will repeat up to 10 times, or until no more fixable problems are found," as documented here.
Should be okay for this rule because there are only 8 Angular lifecycle events, at least for 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.
If possible, maybe let's play around with test cases that check for multiple errors? See 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.
@sc0tt5 What is your opinion on inversion based flagging?
I'm curious why there's a consideration for where the lifecycle method implementations are, e.g. before or after the constructor and other methods. IMO, I'd expect this rule to only care about the sort order of the lifecycle methods and not where they are implemented, even though I can imagine most developers preferring some order like suggested in the quoted comment. Perhaps I'm missing something from the discussions or established expectations? |
Appreciate your feedback! I did some thinking and I personally think you are right. I overcomplicated the problem. The rule should have one single responsibility only, being the lifecycles methods themselves are in order. Other method's ordering do not matter. @sc0tt5 What do you think? |
@Friendseeker yes, sounds good to me. @abaran30 I'm happy to revert changes referencing the constructor, or wait on updates from you before making any changes. Please let me know. |
@sc0tt5 let's make the rule focus on the sorting of lifecycle methods only. 🙂 |
The proposal with sort and position was just what I was looking for ;-) How could we do it if it's not included with this rule? |
Likely via a combination of multiple rules. |
Sounds more like an overload for Similar request: #1424 |
I like where we landed with this, only focused on sort order of the lifecycle methods. It's concise and serves it's purpose within the Angular scope of things. I did test with @typescript-eslint/member-ordering and as @bevrard mentioned it would be nice to somehow have these positioned, which is out of scope here. It doesn't seem possible with @typescript-eslint/member-ordering. I will probably try a custom rule in my project. |
chore: focus on sorting lifecycle methods only
☁️ Nx Cloud ReportCI is running/has finished running commands for commit 2d9090c. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this branch ✅ Successfully ran 7 targets
Sent with 💌 from NxCloud. |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #1320 +/- ##
==========================================
+ Coverage 89.23% 89.28% +0.05%
==========================================
Files 162 164 +2
Lines 3056 3081 +25
Branches 521 523 +2
==========================================
+ Hits 2727 2751 +24
Misses 201 201
- Partials 128 129 +1
Flags with carried forward coverage won't be shown. Click here to find out 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 everyone for their hard work and consideration on this one! I think we can ship it
Implements feature requested by #995