1
1
import type { Maybe } from '../jsutils/Maybe.js' ;
2
2
3
- import type { ASTNode , FieldNode } from '../language/ast.js' ;
3
+ import type {
4
+ ASTNode ,
5
+ DocumentNode ,
6
+ FieldNode ,
7
+ FragmentDefinitionNode ,
8
+ VariableDefinitionNode ,
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' ;
@@ -32,6 +38,11 @@ import type { GraphQLSchema } from '../type/schema.js';
32
38
33
39
import { typeFromAST } from './typeFromAST.js' ;
34
40
41
+ export interface FragmentSignature {
42
+ readonly definition : FragmentDefinitionNode ;
43
+ readonly variableDefinitions : Map < string , VariableDefinitionNode > ;
44
+ }
45
+
35
46
/**
36
47
* TypeInfo is a utility class which, given a GraphQL schema, can keep track
37
48
* of the current field and type definitions at any point in a GraphQL document
@@ -47,6 +58,12 @@ export class TypeInfo {
47
58
private _directive : Maybe < GraphQLDirective > ;
48
59
private _argument : Maybe < GraphQLArgument > ;
49
60
private _enumValue : Maybe < GraphQLEnumValue > ;
61
+ private _fragmentSignaturesByName : (
62
+ fragmentName : string ,
63
+ ) => Maybe < FragmentSignature > ;
64
+
65
+ private _fragmentSignature : Maybe < FragmentSignature > ;
66
+ private _fragmentArgument : Maybe < VariableDefinitionNode > ;
50
67
private _getFieldDef : GetFieldDefFn ;
51
68
52
69
constructor (
@@ -58,7 +75,10 @@ export class TypeInfo {
58
75
initialType ?: Maybe < GraphQLType > ,
59
76
60
77
/** @deprecated will be removed in 17.0.0 */
61
- getFieldDefFn ?: GetFieldDefFn ,
78
+ getFieldDefFn ?: Maybe < GetFieldDefFn > ,
79
+ fragmentSignatures ?: Maybe <
80
+ ( fragmentName : string ) => Maybe < FragmentSignature >
81
+ > ,
62
82
) {
63
83
this . _schema = schema ;
64
84
this . _typeStack = [ ] ;
@@ -69,6 +89,9 @@ export class TypeInfo {
69
89
this . _directive = null ;
70
90
this . _argument = null ;
71
91
this . _enumValue = null ;
92
+ this . _fragmentSignaturesByName = fragmentSignatures ?? ( ( ) => null ) ;
93
+ this . _fragmentSignature = null ;
94
+ this . _fragmentArgument = null ;
72
95
this . _getFieldDef = getFieldDefFn ?? getFieldDef ;
73
96
if ( initialType ) {
74
97
if ( isInputType ( initialType ) ) {
@@ -119,6 +142,20 @@ export class TypeInfo {
119
142
return this . _argument ;
120
143
}
121
144
145
+ getFragmentSignature ( ) : Maybe < FragmentSignature > {
146
+ return this . _fragmentSignature ;
147
+ }
148
+
149
+ getFragmentSignatureByName ( ) : (
150
+ fragmentName : string ,
151
+ ) => Maybe < FragmentSignature > {
152
+ return this . _fragmentSignaturesByName ;
153
+ }
154
+
155
+ getFragmentArgument ( ) : Maybe < VariableDefinitionNode > {
156
+ return this . _fragmentArgument ;
157
+ }
158
+
122
159
getEnumValue ( ) : Maybe < GraphQLEnumValue > {
123
160
return this . _enumValue ;
124
161
}
@@ -130,6 +167,12 @@ export class TypeInfo {
130
167
// checked before continuing since TypeInfo is used as part of validation
131
168
// which occurs before guarantees of schema and document validity.
132
169
switch ( node . kind ) {
170
+ case Kind . DOCUMENT : {
171
+ const fragmentSignatures = getFragmentSignatures ( node ) ;
172
+ this . _fragmentSignaturesByName = ( fragmentName : string ) =>
173
+ fragmentSignatures . get ( fragmentName ) ;
174
+ break ;
175
+ }
133
176
case Kind . SELECTION_SET : {
134
177
const namedType : unknown = getNamedType ( this . getType ( ) ) ;
135
178
this . _parentTypeStack . push (
@@ -159,6 +202,12 @@ export class TypeInfo {
159
202
this . _typeStack . push ( isObjectType ( rootType ) ? rootType : undefined ) ;
160
203
break ;
161
204
}
205
+ case Kind . FRAGMENT_SPREAD : {
206
+ this . _fragmentSignature = this . getFragmentSignatureByName ( ) (
207
+ node . name . value ,
208
+ ) ;
209
+ break ;
210
+ }
162
211
case Kind . INLINE_FRAGMENT :
163
212
case Kind . FRAGMENT_DEFINITION : {
164
213
const typeConditionAST = node . typeCondition ;
@@ -192,6 +241,19 @@ export class TypeInfo {
192
241
this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
193
242
break ;
194
243
}
244
+ case Kind . FRAGMENT_ARGUMENT : {
245
+ const fragmentSignature = this . getFragmentSignature ( ) ;
246
+ const argDef = fragmentSignature ?. variableDefinitions . get (
247
+ node . name . value ,
248
+ ) ;
249
+ this . _fragmentArgument = argDef ;
250
+ let argType : unknown ;
251
+ if ( argDef ) {
252
+ argType = typeFromAST ( this . _schema , argDef . type ) ;
253
+ }
254
+ this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
255
+ break ;
256
+ }
195
257
case Kind . LIST : {
196
258
const listType : unknown = getNullableType ( this . getInputType ( ) ) ;
197
259
const itemType : unknown = isListType ( listType )
@@ -236,6 +298,10 @@ export class TypeInfo {
236
298
237
299
leave ( node : ASTNode ) {
238
300
switch ( node . kind ) {
301
+ case Kind . DOCUMENT :
302
+ this . _fragmentSignaturesByName = /* c8 ignore start */ ( ) =>
303
+ null /* c8 ignore end */ ;
304
+ break ;
239
305
case Kind . SELECTION_SET :
240
306
this . _parentTypeStack . pop ( ) ;
241
307
break ;
@@ -246,6 +312,9 @@ export class TypeInfo {
246
312
case Kind . DIRECTIVE :
247
313
this . _directive = null ;
248
314
break ;
315
+ case Kind . FRAGMENT_SPREAD :
316
+ this . _fragmentSignature = null ;
317
+ break ;
249
318
case Kind . OPERATION_DEFINITION :
250
319
case Kind . INLINE_FRAGMENT :
251
320
case Kind . FRAGMENT_DEFINITION :
@@ -259,6 +328,12 @@ export class TypeInfo {
259
328
this . _defaultValueStack . pop ( ) ;
260
329
this . _inputTypeStack . pop ( ) ;
261
330
break ;
331
+ case Kind . FRAGMENT_ARGUMENT : {
332
+ this . _fragmentArgument = null ;
333
+ this . _defaultValueStack . pop ( ) ;
334
+ this . _inputTypeStack . pop ( ) ;
335
+ break ;
336
+ }
262
337
case Kind . LIST :
263
338
case Kind . OBJECT_FIELD :
264
339
this . _defaultValueStack . pop ( ) ;
@@ -287,6 +362,25 @@ function getFieldDef(
287
362
return schema . getField ( parentType , fieldNode . name . value ) ;
288
363
}
289
364
365
+ function getFragmentSignatures (
366
+ document : DocumentNode ,
367
+ ) : Map < string , FragmentSignature > {
368
+ const fragmentSignatures = new Map < string , FragmentSignature > ( ) ;
369
+ for ( const definition of document . definitions ) {
370
+ if ( definition . kind === Kind . FRAGMENT_DEFINITION ) {
371
+ const variableDefinitions = new Map < string , VariableDefinitionNode > ( ) ;
372
+ if ( definition . variableDefinitions ) {
373
+ for ( const varDef of definition . variableDefinitions ) {
374
+ variableDefinitions . set ( varDef . variable . name . value , varDef ) ;
375
+ }
376
+ }
377
+ const signature = { definition, variableDefinitions } ;
378
+ fragmentSignatures . set ( definition . name . value , signature ) ;
379
+ }
380
+ }
381
+ return fragmentSignatures ;
382
+ }
383
+
290
384
/**
291
385
* Creates a new visitor instance which maintains a provided TypeInfo instance
292
386
* along with visiting visitor.
0 commit comments