AP-682 Strip FragmentDefinitions in client:check when only included in @client fields#1454
Conversation
These will make sure we don't break the original functionality
…nitions If we remove a client field that includes fragment definitions, we should remove the fragment definition as well if that fragment isn't reused
…tatedFields This ensures that the type passed in extends from ASTNode and then returns the same type that is passed in, which may be a more specific version of ASTNode (like DocumentNode).
…d in @client field If we spread a fragment inside of a `@client` field, we remove that fragment spread. If we don't use that fragment anywhere else then we should remove the fragment definition. It _is_ possible that a fragment is used on a @client field and non-client field if they are fragments on an interface and not and object.
83bb034 to
f04abf4
Compare
jbaxleyiii
left a comment
There was a problem hiding this comment.
@justinanastos this is a good start, but fragments can be really tricky especially as the recurse. Happy to pair on this if you have questions about my comments!
|
@jbaxleyiii Great suggestions for new test cases and awesome explanations of what my first pass was missing. I've added the suggested test cases and another one to make sure we don't remove fragments that we didn't remove because of |
fc4b4c9 to
32601c1
Compare
jbaxleyiii
left a comment
There was a problem hiding this comment.
This is getting closer! I left a few more questions where a couple more complex test cases with nested behaviors may be helpful to make sure all of the work is being done the way you want it to
| } | ||
|
|
||
| // All nested fragment spreads inside of this definition are now eligible to be removed | ||
| visit(ast, { |
There was a problem hiding this comment.
I don't think we should nest visitors here. This implementation doesn't do what I think you want it to anyway. By revisiting the root ast, instead of the specific node that you are in currently, you aren't "filtering" fragment spreads inside of this definition.
Since the visitor works depth first, you should be able to visit on FragmentDefinition to create your list, then visit the FragmentSpread next as long as you sort the document prior to doing the work so that fragment definitions always come first
There was a problem hiding this comment.
Absolutely right.
There was a problem hiding this comment.
This appears to still be nested? Why was this resolved?
There was a problem hiding this comment.
I'm sorry about that; I have no idea. I'll figure this last piece out on Monday! Thanks again @jbaxleyiii !
There was a problem hiding this comment.
My original logic was to visit all the descendents of node to find all fragment spreads being removed because this field was being removed as well. I wrote this to visit ast by accident; great catch @jbaxleyiii .
I think that I do, however, want to use a nested visitor here to flag all the fragment spreads under this node as eligible for deletion. I've added a comment to hopefully explain this. Please let me know if this is the correct way to handle this problem.
We're getting a list of all the fragment spreads and then removing all fragment spreads that aren't in the list we just created. This will never happen.
jbaxleyiii
left a comment
There was a problem hiding this comment.
@justinanastos this looks great except for the nesting of the visitors still?
| /** | ||
| * Remove nodes that have zero-length selection sets | ||
| * | ||
| * The `ast` param must extend ASTNode. We use a genetic to indicate that this function returns the same type |
| } | ||
|
|
||
| // All nested fragment spreads inside of this definition are now eligible to be removed | ||
| visit(ast, { |
There was a problem hiding this comment.
This appears to still be nested? Why was this resolved?
jbaxleyiii
left a comment
There was a problem hiding this comment.
@justinanastos thanks for sticking with me for the review cycles. I really appreciate the time and effort you put into this PR! I think it is good to go as is!
For future readers looking back, another approach that we could have taken here would be to look at the overall DocumentNode and collect 1) all fragments (inline and defined) 2) determine the relationships between them and the conditionality of that related to trimmable directives (i.e. @client) then re-walk over the AST using the visitor to selectively remove the listed fragments and trim their selection sets. This would be more kin to an "ahead of time" style versus a "just in time" style which this PR implements. If performance ever becomes a big concern, we may need to try the AOT but I'm happy with where this solution has landed
We're getting
client:checkfailures when using aFragmentin a@clientfield because while the fields with@clientare stripped,Fragments that are nested below them are not. We now check thatFragments can be removed if they are only used in@clientfields.I added tests to the existing functionality on one commit, added a failing test for how I expected the method to behave, and then fixed the test. Please look at the last new test to see a reproduction.
TODO:
*Make sure changelog entries note which project(s) has been affected. See older entries for examples on what this looks like.