-
Notifications
You must be signed in to change notification settings - Fork 7
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
FED-1885: Prop requiredness codemod #290
FED-1885: Prop requiredness codemod #290
Conversation
lib/src/dart3_suggestors/required_props/codemod/required_props_suggestor.dart
Outdated
Show resolved
Hide resolved
lib/src/dart3_suggestors/required_props/codemod/required_props_suggestor.dart
Outdated
Show resolved
Hide resolved
Specifically, there wasn't a test case for --no-trust-required-annotations making props optional based on usage data.
6182a4a
to
407ea41
Compare
@aaronlademann-wf @sydneyjodon-wk Alright, all the items that came up during our meeting yesterday should be addressed 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.
QA +1 I tested it out and it worked great!! This is awesome!
- CI passes
- Verify help output for each subcommand is well-formatted
- Verify all supported package spec formats (from help comment) work for collect command
- Run collect and codemod commands for a package that has at least some public components and verify output looks good
For example, say we're dealing with package A, which is directly consumed by | ||
packages B, E, and F, and so on: | ||
|
||
${r'A---B---C---D'} | ||
${r'|\ /'} | ||
${r'| E----'} | ||
${r'\'} | ||
${r' F---G---H'} | ||
|
||
The least-common consumers would be C (covers both B and E) and F, so we'd run: | ||
|
||
$invocationPrefix pub@…:C pub@…:F | ||
|
||
Note: if F were to re-export members of A, which could potentially get used | ||
in G, we'd do G instead of F. |
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 still don't get this. I understand the last Alternatively,...
would just be doing wdesk
or something, but I don't really understand what on the graph makes it okay to do C and F
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 about that; I was trying to capture that there may be branches of downstream consumers that aren't related. For instance, in this example, there's no package that transitively consumes both C and F.
Was that the part you weren't following, or maybe something else? Happy to hop on a call and talk through it too
And I'm totally down to adjust this example however needed to make things clearer
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 makes sense, I guess I just don't get why it can be F and C and not always D and H
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.
Oh it can be D and H, the idea is just that F and C is preferred (mainly because we get to skip analyzing code for D, H, G, and any other transitive dependencies not pulled in by F or C)
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.
...And also increases the risk of mismatched dependencies talked about in the next step:
- If step 1 yielded more than one package, make sure all of them can resolve to
the latest version of your package.If they can't, then data may be missing for recently-added props, or could be
incorrect if props in your package were moved to different files.
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.
oh wait I get it - I was misunderstanding the transitive dependency thing and that D and H aren't directly consuming A - cool cool that makes sense! Thank you!
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.
Sweet, np! If you have any suggestions on how to make the wording or diagram more clear, I'm all ears!
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.
+1 #nothinbutnits
This is impressive stuff @greglittlefield-wf!!!
When we don't have use for this specific codemod anymore - we should make sure to keep some of the code you wrote here because I could see a lot of it being reusable for other mods in the future!
final commentContents = | ||
"$_todoWithPrefix: No data for prop; either it's never set," | ||
" all places it was set were on dynamic usages," | ||
" or requiredness data was collected on a version before this prop was added."; |
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.
extension on Token { | ||
/// The offset of the next token or comment | ||
/// (since comments can occur before the next token) | ||
/// following this token, or null if nothing follows it. | ||
int? get nextTokenOrCommentOffset { | ||
final next = this.next; | ||
if (next == null) return null; | ||
final nextTokenOrComment = next.precedingComments ?? next; | ||
return nextTokenOrComment.offset; | ||
} | ||
} |
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 feel like we have this - or something like this - in like 5 or 6 codemods lol
final isSamePackage = usage.usagePackage == mixin.mixinPackage; | ||
|
||
final categorizedStats = usageStatsByMixinId.putIfAbsent( | ||
mixin.mixinId, CategorizedPropsMixinUsageStats.new); |
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.
TIL you can use new
for a class constructor tearoff!??!?
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.
Yup, as of language version 2.15!! Soo nice
), | ||
); | ||
|
||
logger.finer('Aggregating final results...'); |
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.
|
||
logProgress(); | ||
|
||
//logger.finest('Processing ${unitResult.uri}'); |
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.
#nit
.where((element) => element.isGetter) | ||
// Note that this is public relative to the library, not necessarily the package. | ||
.where((element) => element.isPublic) |
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.
#supernit
.where((element) => element.isGetter) | |
// Note that this is public relative to the library, not necessarily the package. | |
.where((element) => element.isPublic) | |
.where((element) => element.isGetter && element.isPublic) |
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 a lot of times I like splitting these out separately for organizational purposes
final assignedPropNames = assignedProps.map((p) => p.name.name).toSet(); | ||
final unaccountedForPropNames = {...assignedPropNames}; | ||
|
||
// TODO maybe store mixin metadata separately? |
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.
#nit
@@ -8,30 +8,35 @@ description: > | |||
|
|||
|
|||
environment: | |||
sdk: '>=2.12.0 <3.0.0' | |||
sdk: '>=2.15.0 <3.0.0' |
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.
Just curious, why not 2.19.6
?
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 in this case I probably could have raised it higher, but didn't wanna mess with any potential breakages caused by newer language versions (particularly 2.18).
If I did raise it, I'd probably 2.19.0
to not unnecessarily restrict it, unless the package really needed the patches
@Workiva/release-management-p |
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.
+1 from RM
Motivation
over_react consumers migrating to null safety will need to update each prop to be either required or optional.
Determining the correct requiredness for each prop is tedious to do manually, so we want to help automate this process, similar to how Dart's null safety migrator tool helps determine correct nullability for other code.
Changes
null_safety_required_props
executable with the following commands (more info below):collect
- Collects requiredness data for all OverReact props based on usages in the specified packages and all their transitive dependencies.codemod
- Adds null safety migrator hints to OverReact props using prop requiredness data from 'collect' command.codemod
command usingcollect
command data: required_props_collect_and_codemod_test.dartcollect
command: required_props_collect_test.dartgetAllPropsClassesOrMixins
method fromgetAllProps
*.sg.dart
build.yaml configuration and CI check for outdated generated filescollect
commandHigh-level overview diagram
Help output, containing instructions
codemod
commandHelp output, containing instructions
Release Notes
Review
See CONTRIBUTING.md for more details on review types (+1 / QA +1 / +10) and code review process.
Please review:
QA Instructions
To run the executable locally, either:
dart pub global --source path .
. Then, you can runnull_safety_required_props
ordart pub global run over_react_codemod:null_safety_required_props
QA steps:
collect
commandMerge Checklist
While we perform many automated checks before auto-merging, some manual checks are needed: