@@ -17,13 +17,24 @@ import {
17
17
} from '../type/directives' ;
18
18
import type { GraphQLSchema } from '../type/schema' ;
19
19
20
+ import type { GraphQLVariableSignature } from '../utilities/getVariableSignature' ;
20
21
import { typeFromAST } from '../utilities/typeFromAST' ;
21
22
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
+ }
23
29
24
30
export interface FieldDetails {
25
31
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 ;
27
38
}
28
39
29
40
/**
@@ -37,7 +48,7 @@ export interface FieldDetails {
37
48
*/
38
49
export function collectFields (
39
50
schema : GraphQLSchema ,
40
- fragments : ObjMap < FragmentDefinitionNode > ,
51
+ fragments : ObjMap < FragmentDetails > ,
41
52
variableValues : { [ variable : string ] : unknown } ,
42
53
runtimeType : GraphQLObjectType ,
43
54
selectionSet : SelectionSetNode ,
@@ -68,7 +79,7 @@ export function collectFields(
68
79
*/
69
80
export function collectSubfields (
70
81
schema : GraphQLSchema ,
71
- fragments : ObjMap < FragmentDefinitionNode > ,
82
+ fragments : ObjMap < FragmentDetails > ,
72
83
variableValues : { [ variable : string ] : unknown } ,
73
84
returnType : GraphQLObjectType ,
74
85
fieldEntries : ReadonlyArray < FieldDetails > ,
@@ -85,6 +96,7 @@ export function collectSubfields(
85
96
entry . node . selectionSet ,
86
97
subFieldEntries ,
87
98
visitedFragmentNames ,
99
+ entry . fragmentVariables ,
88
100
) ;
89
101
}
90
102
}
@@ -93,41 +105,40 @@ export function collectSubfields(
93
105
94
106
function collectFieldsImpl (
95
107
schema : GraphQLSchema ,
96
- fragments : ObjMap < FragmentDefinitionNode > ,
108
+ fragments : ObjMap < FragmentDetails > ,
97
109
variableValues : { [ variable : string ] : unknown } ,
98
110
runtimeType : GraphQLObjectType ,
99
111
selectionSet : SelectionSetNode ,
100
112
fields : Map < string , Array < FieldDetails > > ,
101
113
visitedFragmentNames : Set < string > ,
102
- localVariableValues ?: { [ variable : string ] : unknown } ,
114
+ fragmentVariables ?: FragmentVariables ,
103
115
) : void {
104
116
for ( const selection of selectionSet . selections ) {
105
117
switch ( selection . kind ) {
106
118
case Kind . FIELD : {
107
- const vars = localVariableValues ?? variableValues ;
108
- if ( ! shouldIncludeNode ( vars , selection ) ) {
119
+ if ( ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ) {
109
120
continue ;
110
121
}
111
122
const name = getFieldEntryKey ( selection ) ;
112
123
const fieldList = fields . get ( name ) ;
113
124
if ( fieldList !== undefined ) {
114
125
fieldList . push ( {
115
126
node : selection ,
116
- fragmentVariableValues : localVariableValues ?? undefined ,
127
+ fragmentVariables ,
117
128
} ) ;
118
129
} else {
119
130
fields . set ( name , [
120
131
{
121
132
node : selection ,
122
- fragmentVariableValues : localVariableValues ?? undefined ,
133
+ fragmentVariables ,
123
134
} ,
124
135
] ) ;
125
136
}
126
137
break ;
127
138
}
128
139
case Kind . INLINE_FRAGMENT : {
129
140
if (
130
- ! shouldIncludeNode ( variableValues , selection ) ||
141
+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ||
131
142
! doesFragmentConditionMatch ( schema , selection , runtimeType )
132
143
) {
133
144
continue ;
@@ -140,54 +151,50 @@ function collectFieldsImpl(
140
151
selection . selectionSet ,
141
152
fields ,
142
153
visitedFragmentNames ,
154
+ fragmentVariables ,
143
155
) ;
144
156
break ;
145
157
}
146
158
case Kind . FRAGMENT_SPREAD : {
147
159
const fragName = selection . name . value ;
148
160
if (
149
161
visitedFragmentNames . has ( fragName ) ||
150
- ! shouldIncludeNode ( variableValues , selection )
162
+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables )
151
163
) {
152
164
continue ;
153
165
}
154
166
visitedFragmentNames . add ( fragName ) ;
155
167
const fragment = fragments [ fragName ] ;
156
168
if (
157
169
! fragment ||
158
- ! doesFragmentConditionMatch ( schema , fragment , runtimeType )
170
+ ! doesFragmentConditionMatch ( schema , fragment . definition , runtimeType )
159
171
) {
160
172
continue ;
161
173
}
162
174
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 (
174
181
selection ,
175
- schema ,
176
- fragment . variableDefinitions ,
182
+ Object . values ( fragmentVariableSignatures ) ,
177
183
variableValues ,
178
- localVariableValues ,
179
- )
180
- : undefined ;
184
+ fragmentVariables ,
185
+ ) ,
186
+ } ;
187
+ }
181
188
182
189
collectFieldsImpl (
183
190
schema ,
184
191
fragments ,
185
192
variableValues ,
186
193
runtimeType ,
187
- fragment . selectionSet ,
194
+ fragment . definition . selectionSet ,
188
195
fields ,
189
196
visitedFragmentNames ,
190
- fragmentVariableValues ,
197
+ newFragmentVariables ,
191
198
) ;
192
199
break ;
193
200
}
@@ -200,10 +207,16 @@ function collectFieldsImpl(
200
207
* directives, where `@skip` has higher precedence than `@include`.
201
208
*/
202
209
function shouldIncludeNode (
203
- variableValues : { [ variable : string ] : unknown } ,
204
210
node : FragmentSpreadNode | FieldNode | InlineFragmentNode ,
211
+ variableValues : { [ variable : string ] : unknown } ,
212
+ fragmentVariables : FragmentVariables | undefined ,
205
213
) : boolean {
206
- const skip = getDirectiveValues ( GraphQLSkipDirective , node , variableValues ) ;
214
+ const skip = getDirectiveValues (
215
+ GraphQLSkipDirective ,
216
+ node ,
217
+ variableValues ,
218
+ fragmentVariables ,
219
+ ) ;
207
220
if ( skip ?. if === true ) {
208
221
return false ;
209
222
}
@@ -212,6 +225,7 @@ function shouldIncludeNode(
212
225
GraphQLIncludeDirective ,
213
226
node ,
214
227
variableValues ,
228
+ fragmentVariables ,
215
229
) ;
216
230
if ( include ?. if === false ) {
217
231
return false ;
0 commit comments