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+ } from '../language/ast.js' ;
49import { isNode } from '../language/ast.js' ;
510import { Kind } from '../language/kinds.js' ;
611import type { ASTVisitor } from '../language/visitor.js' ;
@@ -31,6 +36,7 @@ import type { GraphQLDirective } from '../type/directives.js';
3136import type { GraphQLSchema } from '../type/schema.js' ;
3237
3338import { typeFromAST } from './typeFromAST.js' ;
39+ import { valueFromAST } from './valueFromAST.js' ;
3440
3541/**
3642 * TypeInfo is a utility class which, given a GraphQL schema, can keep track
@@ -39,6 +45,7 @@ import { typeFromAST } from './typeFromAST.js';
3945 */
4046export class TypeInfo {
4147 private _schema : GraphQLSchema ;
48+
4249 private _typeStack : Array < Maybe < GraphQLOutputType > > ;
4350 private _parentTypeStack : Array < Maybe < GraphQLCompositeType > > ;
4451 private _inputTypeStack : Array < Maybe < GraphQLInputType > > ;
@@ -47,6 +54,8 @@ export class TypeInfo {
4754 private _directive : Maybe < GraphQLDirective > ;
4855 private _argument : Maybe < GraphQLArgument > ;
4956 private _enumValue : Maybe < GraphQLEnumValue > ;
57+ private _fragmentDefinition : Maybe < FragmentDefinitionNode > ;
58+ private _fragmentDefinitions : ObjMap < FragmentDefinitionNode > ;
5059 private _getFieldDef : GetFieldDefFn ;
5160
5261 constructor (
@@ -60,6 +69,8 @@ export class TypeInfo {
6069 /** @deprecated will be removed in 17.0.0 */
6170 getFieldDefFn ?: GetFieldDefFn ,
6271 ) {
72+ this . _fragmentDefinitions = Object . create ( null ) ;
73+
6374 this . _schema = schema ;
6475 this . _typeStack = [ ] ;
6576 this . _parentTypeStack = [ ] ;
@@ -69,6 +80,7 @@ export class TypeInfo {
6980 this . _directive = null ;
7081 this . _argument = null ;
7182 this . _enumValue = null ;
83+ this . _fragmentDefinition = null ;
7284 this . _getFieldDef = getFieldDefFn ?? getFieldDef ;
7385 if ( initialType ) {
7486 if ( isInputType ( initialType ) ) {
@@ -135,13 +147,28 @@ export class TypeInfo {
135147 return this . _enumValue ;
136148 }
137149
150+ getFragmentDefinition ( ) : Maybe < FragmentDefinitionNode > {
151+ return this . _fragmentDefinition ;
152+ }
153+
138154 enter ( node : ASTNode ) {
139155 const schema = this . _schema ;
140156 // Note: many of the types below are explicitly typed as "unknown" to drop
141157 // any assumptions of a valid schema to ensure runtime types are properly
142158 // checked before continuing since TypeInfo is used as part of validation
143159 // which occurs before guarantees of schema and document validity.
144160 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+ }
145172 case Kind . SELECTION_SET : {
146173 const namedType : unknown = getNamedType ( this . getType ( ) ) ;
147174 this . _parentTypeStack . push (
@@ -180,6 +207,10 @@ export class TypeInfo {
180207 this . _typeStack . push ( isOutputType ( outputType ) ? outputType : undefined ) ;
181208 break ;
182209 }
210+ case Kind . FRAGMENT_SPREAD : {
211+ this . _fragmentDefinition = this . _fragmentDefinitions [ node . name . value ] ;
212+ break ;
213+ }
183214 case Kind . VARIABLE_DEFINITION : {
184215 const inputType : unknown = typeFromAST ( schema , node . type ) ;
185216 this . _inputTypeStack . push (
@@ -190,15 +221,44 @@ export class TypeInfo {
190221 case Kind . ARGUMENT : {
191222 let argDef ;
192223 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 ,
197232 ) ;
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+ }
200254 }
255+ } else if ( fieldDef ) {
256+ argDef = fieldDef . args . find ( ( arg ) => arg . name === node . name . value ) ;
257+ }
258+ if ( argDef ) {
259+ argType = argDef . type ;
201260 }
261+
202262 this . _argument = argDef ;
203263 this . _defaultValueStack . push ( argDef ? argDef . defaultValue : undefined ) ;
204264 this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
@@ -248,6 +308,9 @@ export class TypeInfo {
248308
249309 leave ( node : ASTNode ) {
250310 switch ( node . kind ) {
311+ case Kind . DOCUMENT :
312+ this . _fragmentDefinitions = Object . create ( null ) ;
313+ break ;
251314 case Kind . SELECTION_SET :
252315 this . _parentTypeStack . pop ( ) ;
253316 break ;
@@ -263,6 +326,9 @@ export class TypeInfo {
263326 case Kind . FRAGMENT_DEFINITION :
264327 this . _typeStack . pop ( ) ;
265328 break ;
329+ case Kind . FRAGMENT_SPREAD :
330+ this . _fragmentDefinition = null ;
331+ break ;
266332 case Kind . VARIABLE_DEFINITION :
267333 this . _inputTypeStack . pop ( ) ;
268334 break ;
0 commit comments