@@ -17,13 +17,24 @@ import {
1717} from '../type/directives' ;
1818import type { GraphQLSchema } from '../type/schema' ;
1919
20+ import type { GraphQLVariableSignature } from '../utilities/getVariableSignature' ;
2021import { typeFromAST } from '../utilities/typeFromAST' ;
2122
22- import { getArgumentValuesFromSpread , getDirectiveValues } from './values' ;
23+ import { experimentalGetArgumentValues , getDirectiveValues } from './values' ;
24+
25+ export interface FragmentVariables {
26+ signatures : ObjMap < GraphQLVariableSignature > ;
27+ values : ObjMap < unknown > ;
28+ }
2329
2430export interface FieldDetails {
2531 node : FieldNode ;
26- fragmentVariableValues ?: ObjMap < unknown > | undefined ;
32+ fragmentVariables ?: FragmentVariables | undefined ;
33+ }
34+
35+ export interface FragmentDetails {
36+ definition : FragmentDefinitionNode ;
37+ variableSignatures ?: ObjMap < GraphQLVariableSignature > | undefined ;
2738}
2839
2940/**
@@ -37,7 +48,7 @@ export interface FieldDetails {
3748 */
3849export function collectFields (
3950 schema : GraphQLSchema ,
40- fragments : ObjMap < FragmentDefinitionNode > ,
51+ fragments : ObjMap < FragmentDetails > ,
4152 variableValues : { [ variable : string ] : unknown } ,
4253 runtimeType : GraphQLObjectType ,
4354 selectionSet : SelectionSetNode ,
@@ -68,7 +79,7 @@ export function collectFields(
6879 */
6980export function collectSubfields (
7081 schema : GraphQLSchema ,
71- fragments : ObjMap < FragmentDefinitionNode > ,
82+ fragments : ObjMap < FragmentDetails > ,
7283 variableValues : { [ variable : string ] : unknown } ,
7384 returnType : GraphQLObjectType ,
7485 fieldEntries : ReadonlyArray < FieldDetails > ,
@@ -85,6 +96,7 @@ export function collectSubfields(
8596 entry . node . selectionSet ,
8697 subFieldEntries ,
8798 visitedFragmentNames ,
99+ entry . fragmentVariables ,
88100 ) ;
89101 }
90102 }
@@ -93,41 +105,40 @@ export function collectSubfields(
93105
94106function collectFieldsImpl (
95107 schema : GraphQLSchema ,
96- fragments : ObjMap < FragmentDefinitionNode > ,
108+ fragments : ObjMap < FragmentDetails > ,
97109 variableValues : { [ variable : string ] : unknown } ,
98110 runtimeType : GraphQLObjectType ,
99111 selectionSet : SelectionSetNode ,
100112 fields : Map < string , Array < FieldDetails > > ,
101113 visitedFragmentNames : Set < string > ,
102- localVariableValues ?: { [ variable : string ] : unknown } ,
114+ fragmentVariables ?: FragmentVariables ,
103115) : void {
104116 for ( const selection of selectionSet . selections ) {
105117 switch ( selection . kind ) {
106118 case Kind . FIELD : {
107- const vars = localVariableValues ?? variableValues ;
108- if ( ! shouldIncludeNode ( vars , selection ) ) {
119+ if ( ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ) {
109120 continue ;
110121 }
111122 const name = getFieldEntryKey ( selection ) ;
112123 const fieldList = fields . get ( name ) ;
113124 if ( fieldList !== undefined ) {
114125 fieldList . push ( {
115126 node : selection ,
116- fragmentVariableValues : localVariableValues ?? undefined ,
127+ fragmentVariables ,
117128 } ) ;
118129 } else {
119130 fields . set ( name , [
120131 {
121132 node : selection ,
122- fragmentVariableValues : localVariableValues ?? undefined ,
133+ fragmentVariables ,
123134 } ,
124135 ] ) ;
125136 }
126137 break ;
127138 }
128139 case Kind . INLINE_FRAGMENT : {
129140 if (
130- ! shouldIncludeNode ( variableValues , selection ) ||
141+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ||
131142 ! doesFragmentConditionMatch ( schema , selection , runtimeType )
132143 ) {
133144 continue ;
@@ -140,54 +151,50 @@ function collectFieldsImpl(
140151 selection . selectionSet ,
141152 fields ,
142153 visitedFragmentNames ,
154+ fragmentVariables ,
143155 ) ;
144156 break ;
145157 }
146158 case Kind . FRAGMENT_SPREAD : {
147159 const fragName = selection . name . value ;
148160 if (
149161 visitedFragmentNames . has ( fragName ) ||
150- ! shouldIncludeNode ( variableValues , selection )
162+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables )
151163 ) {
152164 continue ;
153165 }
154166 visitedFragmentNames . add ( fragName ) ;
155167 const fragment = fragments [ fragName ] ;
156168 if (
157169 ! fragment ||
158- ! doesFragmentConditionMatch ( schema , fragment , runtimeType )
170+ ! doesFragmentConditionMatch ( schema , fragment . definition , runtimeType )
159171 ) {
160172 continue ;
161173 }
162174
163- // We need to introduce a concept of shadowing:
164- //
165- // - when a fragment defines a variable that is in the parent scope but not given
166- // in the fragment-spread we need to look at this variable as undefined and check
167- // whether the definition has a defaultValue, if not remove it from the variableValues.
168- // - when a fragment does not define a variable we need to copy it over from the parent
169- // scope as that variable can still get used in spreads later on in the selectionSet.
170- // - when a value is passed in through the fragment-spread we need to copy over the key-value
171- // into our variable-values.
172- const fragmentVariableValues = fragment . variableDefinitions
173- ? getArgumentValuesFromSpread (
175+ const fragmentVariableSignatures = fragment . variableSignatures ;
176+ let newFragmentVariables : FragmentVariables | undefined ;
177+ if ( fragmentVariableSignatures ) {
178+ newFragmentVariables = {
179+ signatures : fragmentVariableSignatures ,
180+ values : experimentalGetArgumentValues (
174181 selection ,
175- schema ,
176- fragment . variableDefinitions ,
182+ Object . values ( fragmentVariableSignatures ) ,
177183 variableValues ,
178- localVariableValues ,
179- )
180- : undefined ;
184+ fragmentVariables ,
185+ ) ,
186+ } ;
187+ }
181188
182189 collectFieldsImpl (
183190 schema ,
184191 fragments ,
185192 variableValues ,
186193 runtimeType ,
187- fragment . selectionSet ,
194+ fragment . definition . selectionSet ,
188195 fields ,
189196 visitedFragmentNames ,
190- fragmentVariableValues ,
197+ newFragmentVariables ,
191198 ) ;
192199 break ;
193200 }
@@ -200,10 +207,16 @@ function collectFieldsImpl(
200207 * directives, where `@skip` has higher precedence than `@include`.
201208 */
202209function shouldIncludeNode (
203- variableValues : { [ variable : string ] : unknown } ,
204210 node : FragmentSpreadNode | FieldNode | InlineFragmentNode ,
211+ variableValues : { [ variable : string ] : unknown } ,
212+ fragmentVariables : FragmentVariables | undefined ,
205213) : boolean {
206- const skip = getDirectiveValues ( GraphQLSkipDirective , node , variableValues ) ;
214+ const skip = getDirectiveValues (
215+ GraphQLSkipDirective ,
216+ node ,
217+ variableValues ,
218+ fragmentVariables ,
219+ ) ;
207220 if ( skip ?. if === true ) {
208221 return false ;
209222 }
@@ -212,6 +225,7 @@ function shouldIncludeNode(
212225 GraphQLIncludeDirective ,
213226 node ,
214227 variableValues ,
228+ fragmentVariables ,
215229 ) ;
216230 if ( include ?. if === false ) {
217231 return false ;
0 commit comments