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
+ } from '../language/ast.js' ;
4
9
import { isNode } from '../language/ast.js' ;
5
10
import { Kind } from '../language/kinds.js' ;
6
11
import type { ASTVisitor } from '../language/visitor.js' ;
@@ -31,6 +36,7 @@ import type { GraphQLDirective } from '../type/directives.js';
31
36
import type { GraphQLSchema } from '../type/schema.js' ;
32
37
33
38
import { typeFromAST } from './typeFromAST.js' ;
39
+ import { valueFromAST } from './valueFromAST.js' ;
34
40
35
41
/**
36
42
* TypeInfo is a utility class which, given a GraphQL schema, can keep track
@@ -39,6 +45,7 @@ import { typeFromAST } from './typeFromAST.js';
39
45
*/
40
46
export class TypeInfo {
41
47
private _schema : GraphQLSchema ;
48
+
42
49
private _typeStack : Array < Maybe < GraphQLOutputType > > ;
43
50
private _parentTypeStack : Array < Maybe < GraphQLCompositeType > > ;
44
51
private _inputTypeStack : Array < Maybe < GraphQLInputType > > ;
@@ -47,6 +54,8 @@ export class TypeInfo {
47
54
private _directive : Maybe < GraphQLDirective > ;
48
55
private _argument : Maybe < GraphQLArgument > ;
49
56
private _enumValue : Maybe < GraphQLEnumValue > ;
57
+ private _fragmentDefinition : Maybe < FragmentDefinitionNode > ;
58
+ private _fragmentDefinitions : ObjMap < FragmentDefinitionNode > ;
50
59
private _getFieldDef : GetFieldDefFn ;
51
60
52
61
constructor (
@@ -60,6 +69,8 @@ export class TypeInfo {
60
69
/** @deprecated will be removed in 17.0.0 */
61
70
getFieldDefFn ?: GetFieldDefFn ,
62
71
) {
72
+ this . _fragmentDefinitions = Object . create ( null ) ;
73
+
63
74
this . _schema = schema ;
64
75
this . _typeStack = [ ] ;
65
76
this . _parentTypeStack = [ ] ;
@@ -69,6 +80,7 @@ export class TypeInfo {
69
80
this . _directive = null ;
70
81
this . _argument = null ;
71
82
this . _enumValue = null ;
83
+ this . _fragmentDefinition = null ;
72
84
this . _getFieldDef = getFieldDefFn ?? getFieldDef ;
73
85
if ( initialType ) {
74
86
if ( isInputType ( initialType ) ) {
@@ -135,13 +147,28 @@ export class TypeInfo {
135
147
return this . _enumValue ;
136
148
}
137
149
150
+ getFragmentDefinition ( ) : Maybe < FragmentDefinitionNode > {
151
+ return this . _fragmentDefinition ;
152
+ }
153
+
138
154
enter ( node : ASTNode ) {
139
155
const schema = this . _schema ;
140
156
// Note: many of the types below are explicitly typed as "unknown" to drop
141
157
// any assumptions of a valid schema to ensure runtime types are properly
142
158
// checked before continuing since TypeInfo is used as part of validation
143
159
// which occurs before guarantees of schema and document validity.
144
160
switch ( node . kind ) {
161
+ case Kind . DOCUMENT : {
162
+ // A document's fragment definitions are type signatures
163
+ // referenced via fragment spreads. Ensure we can use definitions
164
+ // before visiting their call sites.
165
+ for ( const astNode of node . definitions ) {
166
+ if ( astNode . kind === Kind . FRAGMENT_DEFINITION ) {
167
+ this . _fragmentDefinitions [ astNode . name . value ] = astNode ;
168
+ }
169
+ }
170
+ break ;
171
+ }
145
172
case Kind . SELECTION_SET : {
146
173
const namedType : unknown = getNamedType ( this . getType ( ) ) ;
147
174
this . _parentTypeStack . push (
@@ -180,6 +207,10 @@ export class TypeInfo {
180
207
this . _typeStack . push ( isOutputType ( outputType ) ? outputType : undefined ) ;
181
208
break ;
182
209
}
210
+ case Kind . FRAGMENT_SPREAD : {
211
+ this . _fragmentDefinition = this . _fragmentDefinitions [ node . name . value ] ;
212
+ break ;
213
+ }
183
214
case Kind . VARIABLE_DEFINITION : {
184
215
const inputType : unknown = typeFromAST ( schema , node . type ) ;
185
216
this . _inputTypeStack . push (
@@ -190,15 +221,44 @@ export class TypeInfo {
190
221
case Kind . ARGUMENT : {
191
222
let argDef ;
192
223
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 ,
224
+ const directive = this . getDirective ( ) ;
225
+ const fragmentDef = this . getFragmentDefinition ( ) ;
226
+ const fieldDef = this . getFieldDef ( ) ;
227
+ if ( directive ) {
228
+ argDef = directive . args . find ( ( arg ) => arg . name === node . name . value ) ;
229
+ } else if ( fragmentDef ) {
230
+ const fragArgDef = fragmentDef . argumentDefinitions ?. find (
231
+ ( arg ) => arg . variable . name . value === node . name . value ,
197
232
) ;
198
- if ( argDef ) {
199
- argType = argDef . type ;
233
+ if ( fragArgDef ) {
234
+ const fragArgType = typeFromAST ( schema , fragArgDef . type ) ;
235
+ if ( isInputType ( fragArgType ) ) {
236
+ const fragArgDefault = fragArgDef . defaultValue
237
+ ? valueFromAST ( fragArgDef . defaultValue , fragArgType )
238
+ : undefined ;
239
+ const schemaArgDef : GraphQLArgument = {
240
+ name : fragArgDef . variable . name . value ,
241
+ type : fragArgType ,
242
+ defaultValue : fragArgDefault ,
243
+ description : undefined ,
244
+ deprecationReason : undefined ,
245
+ extensions : { } ,
246
+ astNode : {
247
+ ...fragArgDef ,
248
+ kind : Kind . INPUT_VALUE_DEFINITION ,
249
+ name : fragArgDef . variable . name ,
250
+ } ,
251
+ } ;
252
+ argDef = schemaArgDef ;
253
+ }
200
254
}
255
+ } else if ( fieldDef ) {
256
+ argDef = fieldDef . args . find ( ( arg ) => arg . name === node . name . value ) ;
257
+ }
258
+ if ( argDef ) {
259
+ argType = argDef . type ;
201
260
}
261
+
202
262
this . _argument = argDef ;
203
263
this . _defaultValueStack . push ( argDef ? argDef . defaultValue : undefined ) ;
204
264
this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
@@ -248,6 +308,9 @@ export class TypeInfo {
248
308
249
309
leave ( node : ASTNode ) {
250
310
switch ( node . kind ) {
311
+ case Kind . DOCUMENT :
312
+ this . _fragmentDefinitions = Object . create ( null ) ;
313
+ break ;
251
314
case Kind . SELECTION_SET :
252
315
this . _parentTypeStack . pop ( ) ;
253
316
break ;
@@ -263,6 +326,9 @@ export class TypeInfo {
263
326
case Kind . FRAGMENT_DEFINITION :
264
327
this . _typeStack . pop ( ) ;
265
328
break ;
329
+ case Kind . FRAGMENT_SPREAD :
330
+ this . _fragmentDefinition = null ;
331
+ break ;
266
332
case Kind . VARIABLE_DEFINITION :
267
333
this . _inputTypeStack . pop ( ) ;
268
334
break ;
0 commit comments