1
1
import type { Maybe } from '../jsutils/Maybe.js' ;
2
+ import type { ObjMap } from '../jsutils/ObjMap.js' ;
2
3
3
- import type { ASTNode , FieldNode } from '../language/ast.js' ;
4
+ import type {
5
+ ASTNode ,
6
+ FieldNode ,
7
+ FragmentDefinitionNode ,
8
+ FragmentSpreadNode ,
9
+ } from '../language/ast.js' ;
4
10
import { isNode } from '../language/ast.js' ;
5
11
import { Kind } from '../language/kinds.js' ;
6
12
import type { ASTVisitor } from '../language/visitor.js' ;
@@ -31,6 +37,7 @@ import type { GraphQLDirective } from '../type/directives.js';
31
37
import type { GraphQLSchema } from '../type/schema.js' ;
32
38
33
39
import { typeFromAST } from './typeFromAST.js' ;
40
+ import { valueFromAST } from './valueFromAST.js' ;
34
41
35
42
/**
36
43
* TypeInfo is a utility class which, given a GraphQL schema, can keep track
@@ -39,6 +46,7 @@ import { typeFromAST } from './typeFromAST.js';
39
46
*/
40
47
export class TypeInfo {
41
48
private _schema : GraphQLSchema ;
49
+
42
50
private _typeStack : Array < Maybe < GraphQLOutputType > > ;
43
51
private _parentTypeStack : Array < Maybe < GraphQLCompositeType > > ;
44
52
private _inputTypeStack : Array < Maybe < GraphQLInputType > > ;
@@ -47,6 +55,8 @@ export class TypeInfo {
47
55
private _directive : Maybe < GraphQLDirective > ;
48
56
private _argument : Maybe < GraphQLArgument > ;
49
57
private _enumValue : Maybe < GraphQLEnumValue > ;
58
+ private _fragmentSpread : Maybe < FragmentSpreadNode > ;
59
+ private _fragmentDefinitions : ObjMap < FragmentDefinitionNode > ;
50
60
private _getFieldDef : GetFieldDefFn ;
51
61
52
62
constructor (
@@ -69,6 +79,8 @@ export class TypeInfo {
69
79
this . _directive = null ;
70
80
this . _argument = null ;
71
81
this . _enumValue = null ;
82
+ this . _fragmentSpread = null ;
83
+ this . _fragmentDefinitions = Object . create ( null ) ;
72
84
this . _getFieldDef = getFieldDefFn ?? getFieldDef ;
73
85
if ( initialType ) {
74
86
if ( isInputType ( initialType ) ) {
@@ -142,6 +154,17 @@ export class TypeInfo {
142
154
// checked before continuing since TypeInfo is used as part of validation
143
155
// which occurs before guarantees of schema and document validity.
144
156
switch ( node . kind ) {
157
+ case Kind . DOCUMENT : {
158
+ // A document's fragment definitions are type signatures
159
+ // referenced via fragment spreads. Ensure we can use definitions
160
+ // before visiting their call sites.
161
+ for ( const astNode of node . definitions ) {
162
+ if ( astNode . kind === Kind . FRAGMENT_DEFINITION ) {
163
+ this . _fragmentDefinitions [ astNode . name . value ] = astNode ;
164
+ }
165
+ }
166
+ break ;
167
+ }
145
168
case Kind . SELECTION_SET : {
146
169
const namedType : unknown = getNamedType ( this . getType ( ) ) ;
147
170
this . _parentTypeStack . push (
@@ -180,25 +203,71 @@ export class TypeInfo {
180
203
this . _typeStack . push ( isOutputType ( outputType ) ? outputType : undefined ) ;
181
204
break ;
182
205
}
206
+ case Kind . FRAGMENT_SPREAD : {
207
+ this . _fragmentSpread = node ;
208
+ break ;
209
+ }
183
210
case Kind . VARIABLE_DEFINITION : {
184
211
const inputType : unknown = typeFromAST ( schema , node . type ) ;
185
212
this . _inputTypeStack . push (
186
213
isInputType ( inputType ) ? inputType : undefined ,
187
214
) ;
188
215
break ;
189
216
}
217
+ case Kind . FRAGMENT_ARGUMENT_DEFINITION : {
218
+ const inputType : unknown = typeFromAST ( schema , node . type ) ;
219
+ this . _inputTypeStack . push (
220
+ isInputType ( inputType ) ? inputType : undefined ,
221
+ ) ;
222
+ break ;
223
+ }
190
224
case Kind . ARGUMENT : {
191
225
let argDef ;
192
226
let argType : unknown ;
193
- const fieldOrDirective = this . getDirective ( ) ?? this . getFieldDef ( ) ;
194
- if ( fieldOrDirective ) {
195
- argDef = fieldOrDirective . args . find (
196
- ( arg ) => arg . name === node . name . value ,
227
+ const directive = this . getDirective ( ) ;
228
+ const fragmentSpread = this . _fragmentSpread ;
229
+ const fieldDef = this . getFieldDef ( ) ;
230
+ if ( directive ) {
231
+ argDef = directive . args . find ( ( arg ) => arg . name === node . name . value ) ;
232
+ } else if ( fragmentSpread ) {
233
+ const fragmentDef =
234
+ this . _fragmentDefinitions [ fragmentSpread . name . value ] ;
235
+ const fragArgDef = fragmentDef ?. arguments ?. find (
236
+ ( arg ) => arg . variable . name . value === node . name . value ,
197
237
) ;
198
- if ( argDef ) {
199
- argType = argDef . type ;
238
+ if ( fragArgDef ) {
239
+ const fragArgType = typeFromAST ( schema , fragArgDef . type ) ;
240
+ if ( isInputType ( fragArgType ) ) {
241
+ const fragArgDefault = fragArgDef . defaultValue
242
+ ? valueFromAST ( fragArgDef . defaultValue , fragArgType )
243
+ : undefined ;
244
+
245
+ // Minor hack: transform the FragmentArgDef
246
+ // into a schema Argument definition, to
247
+ // enable visiting identically to field/directive args
248
+ const schemaArgDef : GraphQLArgument = {
249
+ name : fragArgDef . variable . name . value ,
250
+ type : fragArgType ,
251
+ defaultValue : fragArgDefault ,
252
+ description : fragArgDef . description ?. value ,
253
+ deprecationReason : undefined ,
254
+ extensions : { } ,
255
+ astNode : {
256
+ ...fragArgDef ,
257
+ kind : Kind . INPUT_VALUE_DEFINITION ,
258
+ name : fragArgDef . variable . name ,
259
+ } ,
260
+ } ;
261
+ argDef = schemaArgDef ;
262
+ }
200
263
}
264
+ } else if ( fieldDef ) {
265
+ argDef = fieldDef . args . find ( ( arg ) => arg . name === node . name . value ) ;
266
+ }
267
+ if ( argDef ) {
268
+ argType = argDef . type ;
201
269
}
270
+
202
271
this . _argument = argDef ;
203
272
this . _defaultValueStack . push ( argDef ? argDef . defaultValue : undefined ) ;
204
273
this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
@@ -248,6 +317,9 @@ export class TypeInfo {
248
317
249
318
leave ( node : ASTNode ) {
250
319
switch ( node . kind ) {
320
+ case Kind . DOCUMENT :
321
+ this . _fragmentDefinitions = Object . create ( null ) ;
322
+ break ;
251
323
case Kind . SELECTION_SET :
252
324
this . _parentTypeStack . pop ( ) ;
253
325
break ;
@@ -263,6 +335,12 @@ export class TypeInfo {
263
335
case Kind . FRAGMENT_DEFINITION :
264
336
this . _typeStack . pop ( ) ;
265
337
break ;
338
+ case Kind . FRAGMENT_ARGUMENT_DEFINITION :
339
+ this . _inputTypeStack . pop ( ) ;
340
+ break ;
341
+ case Kind . FRAGMENT_SPREAD :
342
+ this . _fragmentSpread = null ;
343
+ break ;
266
344
case Kind . VARIABLE_DEFINITION :
267
345
this . _inputTypeStack . pop ( ) ;
268
346
break ;
0 commit comments