11import type { Maybe } from '../jsutils/Maybe.js' ;
2+ import type { ObjMap } from '../jsutils/ObjMap.js' ;
23
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' ;
410import { isNode } from '../language/ast.js' ;
511import { Kind } from '../language/kinds.js' ;
612import type { ASTVisitor } from '../language/visitor.js' ;
@@ -31,6 +37,7 @@ import type { GraphQLDirective } from '../type/directives.js';
3137import type { GraphQLSchema } from '../type/schema.js' ;
3238
3339import { typeFromAST } from './typeFromAST.js' ;
40+ import { valueFromAST } from './valueFromAST.js' ;
3441
3542/**
3643 * TypeInfo is a utility class which, given a GraphQL schema, can keep track
@@ -39,6 +46,7 @@ import { typeFromAST } from './typeFromAST.js';
3946 */
4047export class TypeInfo {
4148 private _schema : GraphQLSchema ;
49+
4250 private _typeStack : Array < Maybe < GraphQLOutputType > > ;
4351 private _parentTypeStack : Array < Maybe < GraphQLCompositeType > > ;
4452 private _inputTypeStack : Array < Maybe < GraphQLInputType > > ;
@@ -47,6 +55,8 @@ export class TypeInfo {
4755 private _directive : Maybe < GraphQLDirective > ;
4856 private _argument : Maybe < GraphQLArgument > ;
4957 private _enumValue : Maybe < GraphQLEnumValue > ;
58+ private _fragmentSpread : Maybe < FragmentSpreadNode > ;
59+ private _fragmentDefinitions : ObjMap < FragmentDefinitionNode > ;
5060 private _getFieldDef : GetFieldDefFn ;
5161
5262 constructor (
@@ -69,6 +79,8 @@ export class TypeInfo {
6979 this . _directive = null ;
7080 this . _argument = null ;
7181 this . _enumValue = null ;
82+ this . _fragmentSpread = null ;
83+ this . _fragmentDefinitions = Object . create ( null ) ;
7284 this . _getFieldDef = getFieldDefFn ?? getFieldDef ;
7385 if ( initialType ) {
7486 if ( isInputType ( initialType ) ) {
@@ -142,6 +154,17 @@ export class TypeInfo {
142154 // checked before continuing since TypeInfo is used as part of validation
143155 // which occurs before guarantees of schema and document validity.
144156 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+ }
145168 case Kind . SELECTION_SET : {
146169 const namedType : unknown = getNamedType ( this . getType ( ) ) ;
147170 this . _parentTypeStack . push (
@@ -180,25 +203,71 @@ export class TypeInfo {
180203 this . _typeStack . push ( isOutputType ( outputType ) ? outputType : undefined ) ;
181204 break ;
182205 }
206+ case Kind . FRAGMENT_SPREAD : {
207+ this . _fragmentSpread = node ;
208+ break ;
209+ }
183210 case Kind . VARIABLE_DEFINITION : {
184211 const inputType : unknown = typeFromAST ( schema , node . type ) ;
185212 this . _inputTypeStack . push (
186213 isInputType ( inputType ) ? inputType : undefined ,
187214 ) ;
188215 break ;
189216 }
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+ }
190224 case Kind . ARGUMENT : {
191225 let argDef ;
192226 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 ,
197237 ) ;
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+ }
200263 }
264+ } else if ( fieldDef ) {
265+ argDef = fieldDef . args . find ( ( arg ) => arg . name === node . name . value ) ;
266+ }
267+ if ( argDef ) {
268+ argType = argDef . type ;
201269 }
270+
202271 this . _argument = argDef ;
203272 this . _defaultValueStack . push ( argDef ? argDef . defaultValue : undefined ) ;
204273 this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
@@ -248,6 +317,9 @@ export class TypeInfo {
248317
249318 leave ( node : ASTNode ) {
250319 switch ( node . kind ) {
320+ case Kind . DOCUMENT :
321+ this . _fragmentDefinitions = Object . create ( null ) ;
322+ break ;
251323 case Kind . SELECTION_SET :
252324 this . _parentTypeStack . pop ( ) ;
253325 break ;
@@ -263,6 +335,12 @@ export class TypeInfo {
263335 case Kind . FRAGMENT_DEFINITION :
264336 this . _typeStack . pop ( ) ;
265337 break ;
338+ case Kind . FRAGMENT_ARGUMENT_DEFINITION :
339+ this . _inputTypeStack . pop ( ) ;
340+ break ;
341+ case Kind . FRAGMENT_SPREAD :
342+ this . _fragmentSpread = null ;
343+ break ;
266344 case Kind . VARIABLE_DEFINITION :
267345 this . _inputTypeStack . pop ( ) ;
268346 break ;
0 commit comments