@@ -22,33 +22,44 @@ import {
22
22
} from '../type/directives.js' ;
23
23
import type { GraphQLSchema } from '../type/schema.js' ;
24
24
25
+ import type { GraphQLVariableSignature } from '../utilities/getVariableSignature.js' ;
25
26
import { typeFromAST } from '../utilities/typeFromAST.js' ;
26
27
27
- import { getArgumentValuesFromSpread , getDirectiveValues } from './values.js' ;
28
+ import { experimentalGetArgumentValues , getDirectiveValues } from './values.js' ;
28
29
29
30
export interface DeferUsage {
30
31
label : string | undefined ;
31
32
parentDeferUsage : DeferUsage | undefined ;
32
33
}
33
34
35
+ export interface FragmentVariables {
36
+ signatures : ObjMap < GraphQLVariableSignature > ;
37
+ values : ObjMap < unknown > ;
38
+ }
39
+
34
40
export interface FieldDetails {
35
41
node : FieldNode ;
36
- deferUsage : DeferUsage | undefined ;
37
- fragmentVariableValues ?: ObjMap < unknown > | undefined ;
42
+ deferUsage ? : DeferUsage | undefined ;
43
+ fragmentVariables ?: FragmentVariables | undefined ;
38
44
}
39
45
40
46
export type FieldGroup = ReadonlyArray < FieldDetails > ;
41
47
42
48
export type GroupedFieldSet = ReadonlyMap < string , FieldGroup > ;
43
49
50
+ export interface FragmentDetails {
51
+ definition : FragmentDefinitionNode ;
52
+ variableSignatures ?: ObjMap < GraphQLVariableSignature > | undefined ;
53
+ }
54
+
44
55
interface CollectFieldsContext {
45
56
schema : GraphQLSchema ;
46
- fragments : ObjMap < FragmentDefinitionNode > ;
57
+ fragments : ObjMap < FragmentDetails > ;
58
+ variableValues : { [ variable : string ] : unknown } ;
59
+ fragmentVariableValues ?: FragmentVariables ;
47
60
operation : OperationDefinitionNode ;
48
61
runtimeType : GraphQLObjectType ;
49
62
visitedFragmentNames : Set < string > ;
50
- localVariableValues : { [ variable : string ] : unknown } | undefined ;
51
- variableValues : { [ variable : string ] : unknown } ;
52
63
}
53
64
54
65
/**
@@ -62,7 +73,7 @@ interface CollectFieldsContext {
62
73
*/
63
74
export function collectFields (
64
75
schema : GraphQLSchema ,
65
- fragments : ObjMap < FragmentDefinitionNode > ,
76
+ fragments : ObjMap < FragmentDetails > ,
66
77
variableValues : { [ variable : string ] : unknown } ,
67
78
runtimeType : GraphQLObjectType ,
68
79
operation : OperationDefinitionNode ,
@@ -75,10 +86,9 @@ export function collectFields(
75
86
const context : CollectFieldsContext = {
76
87
schema,
77
88
fragments,
78
- runtimeType,
79
89
variableValues,
90
+ runtimeType,
80
91
operation,
81
- localVariableValues : undefined ,
82
92
visitedFragmentNames : new Set ( ) ,
83
93
} ;
84
94
@@ -104,7 +114,7 @@ export function collectFields(
104
114
// eslint-disable-next-line max-params
105
115
export function collectSubfields (
106
116
schema : GraphQLSchema ,
107
- fragments : ObjMap < FragmentDefinitionNode > ,
117
+ fragments : ObjMap < FragmentDetails > ,
108
118
variableValues : { [ variable : string ] : unknown } ,
109
119
operation : OperationDefinitionNode ,
110
120
returnType : GraphQLObjectType ,
@@ -116,9 +126,8 @@ export function collectSubfields(
116
126
const context : CollectFieldsContext = {
117
127
schema,
118
128
fragments,
119
- runtimeType : returnType ,
120
- localVariableValues : undefined ,
121
129
variableValues,
130
+ runtimeType : returnType ,
122
131
operation,
123
132
visitedFragmentNames : new Set ( ) ,
124
133
} ;
@@ -144,47 +153,40 @@ export function collectSubfields(
144
153
} ;
145
154
}
146
155
156
+ // eslint-disable-next-line max-params
147
157
function collectFieldsImpl (
148
158
context : CollectFieldsContext ,
149
159
selectionSet : SelectionSetNode ,
150
160
groupedFieldSet : AccumulatorMap < string , FieldDetails > ,
151
161
newDeferUsages : Array < DeferUsage > ,
152
162
deferUsage ?: DeferUsage ,
163
+ fragmentVariables ?: FragmentVariables ,
153
164
) : void {
154
165
const {
155
166
schema,
156
167
fragments,
157
- runtimeType,
158
168
variableValues,
159
- localVariableValues ,
169
+ runtimeType ,
160
170
operation,
161
171
visitedFragmentNames,
162
172
} = context ;
163
173
164
174
for ( const selection of selectionSet . selections ) {
165
175
switch ( selection . kind ) {
166
176
case Kind . FIELD : {
167
- if (
168
- ! shouldIncludeNode (
169
- { ...variableValues , ...localVariableValues } ,
170
- selection ,
171
- )
172
- ) {
177
+ if ( ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ) {
173
178
continue ;
174
179
}
175
180
groupedFieldSet . add ( getFieldEntryKey ( selection ) , {
176
181
node : selection ,
177
182
deferUsage,
178
- fragmentVariableValues : localVariableValues ?? undefined ,
183
+ fragmentVariables ,
179
184
} ) ;
180
185
break ;
181
186
}
182
187
case Kind . INLINE_FRAGMENT : {
183
188
if (
184
- ! shouldIncludeNode (
185
- { ...variableValues , ...localVariableValues } ,
186
- selection ,
187
- ) ||
189
+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ||
188
190
! doesFragmentConditionMatch ( schema , selection , runtimeType )
189
191
) {
190
192
continue ;
@@ -193,6 +195,7 @@ function collectFieldsImpl(
193
195
const newDeferUsage = getDeferUsage (
194
196
operation ,
195
197
variableValues ,
198
+ fragmentVariables ,
196
199
selection ,
197
200
deferUsage ,
198
201
) ;
@@ -204,6 +207,7 @@ function collectFieldsImpl(
204
207
groupedFieldSet ,
205
208
newDeferUsages ,
206
209
deferUsage ,
210
+ fragmentVariables ,
207
211
) ;
208
212
} else {
209
213
newDeferUsages . push ( newDeferUsage ) ;
@@ -213,76 +217,72 @@ function collectFieldsImpl(
213
217
groupedFieldSet ,
214
218
newDeferUsages ,
215
219
newDeferUsage ,
220
+ fragmentVariables ,
216
221
) ;
217
222
}
218
223
219
224
break ;
220
225
}
221
226
case Kind . FRAGMENT_SPREAD : {
222
- const fragmentName = selection . name . value ;
227
+ const fragName = selection . name . value ;
223
228
224
229
const newDeferUsage = getDeferUsage (
225
230
operation ,
226
- { ...variableValues , ...localVariableValues } ,
231
+ variableValues ,
232
+ fragmentVariables ,
227
233
selection ,
228
234
deferUsage ,
229
235
) ;
230
236
231
237
if (
232
238
! newDeferUsage &&
233
- ( visitedFragmentNames . has ( fragmentName ) ||
234
- ! shouldIncludeNode (
235
- { ...variableValues , ...localVariableValues } ,
236
- selection ,
237
- ) )
239
+ ( visitedFragmentNames . has ( fragName ) ||
240
+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) )
238
241
) {
239
242
continue ;
240
243
}
241
244
242
- const fragment = fragments [ fragmentName ] ;
245
+ const fragment = fragments [ fragName ] ;
243
246
if (
244
247
fragment == null ||
245
- ! doesFragmentConditionMatch ( schema , fragment , runtimeType )
248
+ ! doesFragmentConditionMatch ( schema , fragment . definition , runtimeType )
246
249
) {
247
250
continue ;
248
251
}
249
252
250
- // We need to introduce a concept of shadowing:
251
- //
252
- // - when a fragment defines a variable that is in the parent scope but not given
253
- // in the fragment-spread we need to look at this variable as undefined and check
254
- // whether the definition has a defaultValue, if not remove it from the variableValues.
255
- // - when a fragment does not define a variable we need to copy it over from the parent
256
- // scope as that variable can still get used in spreads later on in the selectionSet.
257
- // - when a value is passed in through the fragment-spread we need to copy over the key-value
258
- // into our variable-values.
259
- context . localVariableValues = fragment . variableDefinitions
260
- ? getArgumentValuesFromSpread (
253
+ const fragmentVariableSignatures = fragment . variableSignatures ;
254
+ let newFragmentVariables : FragmentVariables | undefined ;
255
+ if ( fragmentVariableSignatures ) {
256
+ newFragmentVariables = {
257
+ signatures : fragmentVariableSignatures ,
258
+ values : experimentalGetArgumentValues (
261
259
selection ,
262
- schema ,
263
- fragment . variableDefinitions ,
260
+ Object . values ( fragmentVariableSignatures ) ,
264
261
variableValues ,
265
- context . localVariableValues ,
266
- )
267
- : undefined ;
262
+ fragmentVariables ,
263
+ ) ,
264
+ } ;
265
+ }
268
266
269
267
if ( ! newDeferUsage ) {
270
- visitedFragmentNames . add ( fragmentName ) ;
268
+ visitedFragmentNames . add ( fragName ) ;
271
269
collectFieldsImpl (
272
270
context ,
273
- fragment . selectionSet ,
271
+ fragment . definition . selectionSet ,
274
272
groupedFieldSet ,
275
273
newDeferUsages ,
276
274
deferUsage ,
275
+ newFragmentVariables ,
277
276
) ;
278
277
} else {
279
278
newDeferUsages . push ( newDeferUsage ) ;
280
279
collectFieldsImpl (
281
280
context ,
282
- fragment . selectionSet ,
281
+ fragment . definition . selectionSet ,
283
282
groupedFieldSet ,
284
283
newDeferUsages ,
285
284
newDeferUsage ,
285
+ newFragmentVariables ,
286
286
) ;
287
287
}
288
288
break ;
@@ -299,10 +299,16 @@ function collectFieldsImpl(
299
299
function getDeferUsage (
300
300
operation : OperationDefinitionNode ,
301
301
variableValues : { [ variable : string ] : unknown } ,
302
+ fragmentVariables : FragmentVariables | undefined ,
302
303
node : FragmentSpreadNode | InlineFragmentNode ,
303
304
parentDeferUsage : DeferUsage | undefined ,
304
305
) : DeferUsage | undefined {
305
- const defer = getDirectiveValues ( GraphQLDeferDirective , node , variableValues ) ;
306
+ const defer = getDirectiveValues (
307
+ GraphQLDeferDirective ,
308
+ node ,
309
+ variableValues ,
310
+ fragmentVariables ,
311
+ ) ;
306
312
307
313
if ( ! defer ) {
308
314
return ;
@@ -328,10 +334,16 @@ function getDeferUsage(
328
334
* directives, where `@skip` has higher precedence than `@include`.
329
335
*/
330
336
function shouldIncludeNode (
331
- variableValues : { [ variable : string ] : unknown } ,
332
337
node : FragmentSpreadNode | FieldNode | InlineFragmentNode ,
338
+ variableValues : { [ variable : string ] : unknown } ,
339
+ fragmentVariables : FragmentVariables | undefined ,
333
340
) : boolean {
334
- const skip = getDirectiveValues ( GraphQLSkipDirective , node , variableValues ) ;
341
+ const skip = getDirectiveValues (
342
+ GraphQLSkipDirective ,
343
+ node ,
344
+ variableValues ,
345
+ fragmentVariables ,
346
+ ) ;
335
347
if ( skip ?. if === true ) {
336
348
return false ;
337
349
}
@@ -340,6 +352,7 @@ function shouldIncludeNode(
340
352
GraphQLIncludeDirective ,
341
353
node ,
342
354
variableValues ,
355
+ fragmentVariables ,
343
356
) ;
344
357
if ( include ?. if === false ) {
345
358
return false ;
0 commit comments