@@ -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,49 +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 ,
170
- selection ,
171
- context . localVariableValues ,
172
- )
173
- ) {
177
+ if ( ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ) {
174
178
continue ;
175
179
}
176
180
groupedFieldSet . add ( getFieldEntryKey ( selection ) , {
177
181
node : selection ,
178
182
deferUsage,
179
- fragmentVariableValues : localVariableValues ?? undefined ,
183
+ fragmentVariables ,
180
184
} ) ;
181
185
break ;
182
186
}
183
187
case Kind . INLINE_FRAGMENT : {
184
188
if (
185
- ! shouldIncludeNode (
186
- variableValues ,
187
- selection ,
188
- context . localVariableValues ,
189
- ) ||
189
+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) ||
190
190
! doesFragmentConditionMatch ( schema , selection , runtimeType )
191
191
) {
192
192
continue ;
@@ -195,9 +195,9 @@ function collectFieldsImpl(
195
195
const newDeferUsage = getDeferUsage (
196
196
operation ,
197
197
variableValues ,
198
+ fragmentVariables ,
198
199
selection ,
199
200
deferUsage ,
200
- context . localVariableValues ,
201
201
) ;
202
202
203
203
if ( ! newDeferUsage ) {
@@ -207,6 +207,7 @@ function collectFieldsImpl(
207
207
groupedFieldSet ,
208
208
newDeferUsages ,
209
209
deferUsage ,
210
+ fragmentVariables ,
210
211
) ;
211
212
} else {
212
213
newDeferUsages . push ( newDeferUsage ) ;
@@ -216,78 +217,72 @@ function collectFieldsImpl(
216
217
groupedFieldSet ,
217
218
newDeferUsages ,
218
219
newDeferUsage ,
220
+ fragmentVariables ,
219
221
) ;
220
222
}
221
223
222
224
break ;
223
225
}
224
226
case Kind . FRAGMENT_SPREAD : {
225
- const fragmentName = selection . name . value ;
227
+ const fragName = selection . name . value ;
226
228
227
229
const newDeferUsage = getDeferUsage (
228
230
operation ,
229
231
variableValues ,
232
+ fragmentVariables ,
230
233
selection ,
231
234
deferUsage ,
232
- context . localVariableValues ,
233
235
) ;
234
236
235
237
if (
236
238
! newDeferUsage &&
237
- ( visitedFragmentNames . has ( fragmentName ) ||
238
- ! shouldIncludeNode (
239
- variableValues ,
240
- selection ,
241
- context . localVariableValues ,
242
- ) )
239
+ ( visitedFragmentNames . has ( fragName ) ||
240
+ ! shouldIncludeNode ( selection , variableValues , fragmentVariables ) )
243
241
) {
244
242
continue ;
245
243
}
246
244
247
- const fragment = fragments [ fragmentName ] ;
245
+ const fragment = fragments [ fragName ] ;
248
246
if (
249
247
fragment == null ||
250
- ! doesFragmentConditionMatch ( schema , fragment , runtimeType )
248
+ ! doesFragmentConditionMatch ( schema , fragment . definition , runtimeType )
251
249
) {
252
250
continue ;
253
251
}
254
252
255
- // We need to introduce a concept of shadowing:
256
- //
257
- // - when a fragment defines a variable that is in the parent scope but not given
258
- // in the fragment-spread we need to look at this variable as undefined and check
259
- // whether the definition has a defaultValue, if not remove it from the variableValues.
260
- // - when a fragment does not define a variable we need to copy it over from the parent
261
- // scope as that variable can still get used in spreads later on in the selectionSet.
262
- // - when a value is passed in through the fragment-spread we need to copy over the key-value
263
- // into our variable-values.
264
- context . localVariableValues = fragment . variableDefinitions
265
- ? getArgumentValuesFromSpread (
253
+ const fragmentVariableSignatures = fragment . variableSignatures ;
254
+ let newFragmentVariables : FragmentVariables | undefined ;
255
+ if ( fragmentVariableSignatures ) {
256
+ newFragmentVariables = {
257
+ signatures : fragmentVariableSignatures ,
258
+ values : experimentalGetArgumentValues (
266
259
selection ,
267
- schema ,
268
- fragment . variableDefinitions ,
260
+ Object . values ( fragmentVariableSignatures ) ,
269
261
variableValues ,
270
- context . localVariableValues ,
271
- )
272
- : undefined ;
262
+ fragmentVariables ,
263
+ ) ,
264
+ } ;
265
+ }
273
266
274
267
if ( ! newDeferUsage ) {
275
- visitedFragmentNames . add ( fragmentName ) ;
268
+ visitedFragmentNames . add ( fragName ) ;
276
269
collectFieldsImpl (
277
270
context ,
278
- fragment . selectionSet ,
271
+ fragment . definition . selectionSet ,
279
272
groupedFieldSet ,
280
273
newDeferUsages ,
281
274
deferUsage ,
275
+ newFragmentVariables ,
282
276
) ;
283
277
} else {
284
278
newDeferUsages . push ( newDeferUsage ) ;
285
279
collectFieldsImpl (
286
280
context ,
287
- fragment . selectionSet ,
281
+ fragment . definition . selectionSet ,
288
282
groupedFieldSet ,
289
283
newDeferUsages ,
290
284
newDeferUsage ,
285
+ newFragmentVariables ,
291
286
) ;
292
287
}
293
288
break ;
@@ -304,15 +299,15 @@ function collectFieldsImpl(
304
299
function getDeferUsage (
305
300
operation : OperationDefinitionNode ,
306
301
variableValues : { [ variable : string ] : unknown } ,
302
+ fragmentVariables : FragmentVariables | undefined ,
307
303
node : FragmentSpreadNode | InlineFragmentNode ,
308
304
parentDeferUsage : DeferUsage | undefined ,
309
- fragmentVariableValues : { [ variable : string ] : unknown } | undefined ,
310
305
) : DeferUsage | undefined {
311
306
const defer = getDirectiveValues (
312
307
GraphQLDeferDirective ,
313
308
node ,
314
309
variableValues ,
315
- fragmentVariableValues ,
310
+ fragmentVariables ,
316
311
) ;
317
312
318
313
if ( ! defer ) {
@@ -339,15 +334,15 @@ function getDeferUsage(
339
334
* directives, where `@skip` has higher precedence than `@include`.
340
335
*/
341
336
function shouldIncludeNode (
342
- variableValues : { [ variable : string ] : unknown } ,
343
337
node : FragmentSpreadNode | FieldNode | InlineFragmentNode ,
344
- fragmentVariableValues : { [ variable : string ] : unknown } | undefined ,
338
+ variableValues : { [ variable : string ] : unknown } ,
339
+ fragmentVariables : FragmentVariables | undefined ,
345
340
) : boolean {
346
341
const skip = getDirectiveValues (
347
342
GraphQLSkipDirective ,
348
343
node ,
349
344
variableValues ,
350
- fragmentVariableValues ,
345
+ fragmentVariables ,
351
346
) ;
352
347
if ( skip ?. if === true ) {
353
348
return false ;
@@ -357,7 +352,7 @@ function shouldIncludeNode(
357
352
GraphQLIncludeDirective ,
358
353
node ,
359
354
variableValues ,
360
- fragmentVariableValues ,
355
+ fragmentVariables ,
361
356
) ;
362
357
if ( include ?. if === false ) {
363
358
return false ;
0 commit comments