@@ -2,9 +2,10 @@ import type { Maybe } from '../jsutils/Maybe.js';
2
2
3
3
import type {
4
4
ASTNode ,
5
+ DocumentNode ,
5
6
FieldNode ,
6
7
FragmentDefinitionNode ,
7
- FragmentSpreadNode ,
8
+ VariableDefinitionNode ,
8
9
} from '../language/ast.js' ;
9
10
import { isNode } from '../language/ast.js' ;
10
11
import { Kind } from '../language/kinds.js' ;
@@ -36,7 +37,11 @@ import type { GraphQLDirective } from '../type/directives.js';
36
37
import type { GraphQLSchema } from '../type/schema.js' ;
37
38
38
39
import { typeFromAST } from './typeFromAST.js' ;
39
- import { valueFromAST } from './valueFromAST.js' ;
40
+
41
+ export interface FragmentSignature {
42
+ readonly definition : FragmentDefinitionNode ;
43
+ readonly variableDefinitions : Map < string , VariableDefinitionNode > ;
44
+ }
40
45
41
46
/**
42
47
* TypeInfo is a utility class which, given a GraphQL schema, can keep track
@@ -53,8 +58,12 @@ export class TypeInfo {
53
58
private _directive : Maybe < GraphQLDirective > ;
54
59
private _argument : Maybe < GraphQLArgument > ;
55
60
private _enumValue : Maybe < GraphQLEnumValue > ;
56
- private _fragmentSpread : Maybe < FragmentSpreadNode > ;
57
- private _fragmentDefinitions : Map < string , FragmentDefinitionNode > ;
61
+ private _fragmentSignaturesByName : (
62
+ fragmentName : string ,
63
+ ) => Maybe < FragmentSignature > ;
64
+
65
+ private _fragmentSignature : Maybe < FragmentSignature > ;
66
+ private _fragmentArgument : Maybe < VariableDefinitionNode > ;
58
67
private _getFieldDef : GetFieldDefFn ;
59
68
60
69
constructor (
@@ -66,7 +75,10 @@ export class TypeInfo {
66
75
initialType ?: Maybe < GraphQLType > ,
67
76
68
77
/** @deprecated will be removed in 17.0.0 */
69
- getFieldDefFn ?: GetFieldDefFn ,
78
+ getFieldDefFn ?: Maybe < GetFieldDefFn > ,
79
+ fragmentSignatures ?: Maybe <
80
+ ( fragmentName : string ) => Maybe < FragmentSignature >
81
+ > ,
70
82
) {
71
83
this . _schema = schema ;
72
84
this . _typeStack = [ ] ;
@@ -77,8 +89,9 @@ export class TypeInfo {
77
89
this . _directive = null ;
78
90
this . _argument = null ;
79
91
this . _enumValue = null ;
80
- this . _fragmentSpread = null ;
81
- this . _fragmentDefinitions = new Map ( ) ;
92
+ this . _fragmentSignaturesByName = fragmentSignatures ?? ( ( ) => null ) ;
93
+ this . _fragmentSignature = null ;
94
+ this . _fragmentArgument = null ;
82
95
this . _getFieldDef = getFieldDefFn ?? getFieldDef ;
83
96
if ( initialType ) {
84
97
if ( isInputType ( initialType ) ) {
@@ -129,6 +142,20 @@ export class TypeInfo {
129
142
return this . _argument ;
130
143
}
131
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
+
132
159
getEnumValue ( ) : Maybe < GraphQLEnumValue > {
133
160
return this . _enumValue ;
134
161
}
@@ -141,14 +168,9 @@ export class TypeInfo {
141
168
// which occurs before guarantees of schema and document validity.
142
169
switch ( node . kind ) {
143
170
case Kind . DOCUMENT : {
144
- // A document's fragment definitions are type signatures
145
- // referenced via fragment spreads. Ensure we can use definitions
146
- // before visiting their call sites.
147
- for ( const astNode of node . definitions ) {
148
- if ( astNode . kind === Kind . FRAGMENT_DEFINITION ) {
149
- this . _fragmentDefinitions . set ( astNode . name . value , astNode ) ;
150
- }
151
- }
171
+ const fragmentSignatures = getFragmentSignatures ( node ) ;
172
+ this . _fragmentSignaturesByName = ( fragmentName : string ) =>
173
+ fragmentSignatures . get ( fragmentName ) ;
152
174
break ;
153
175
}
154
176
case Kind . SELECTION_SET : {
@@ -181,7 +203,9 @@ export class TypeInfo {
181
203
break ;
182
204
}
183
205
case Kind . FRAGMENT_SPREAD : {
184
- this . _fragmentSpread = node ;
206
+ this . _fragmentSignature = this . getFragmentSignatureByName ( ) (
207
+ node . name . value ,
208
+ ) ;
185
209
break ;
186
210
}
187
211
case Kind . INLINE_FRAGMENT :
@@ -200,69 +224,33 @@ export class TypeInfo {
200
224
) ;
201
225
break ;
202
226
}
203
- case Kind . FRAGMENT_ARGUMENT : {
227
+ case Kind . ARGUMENT : {
204
228
let argDef ;
205
229
let argType : unknown ;
206
- const fragmentSpread = this . _fragmentSpread ;
207
-
208
- const fragmentDef = this . _fragmentDefinitions . get (
209
- fragmentSpread ! . name . value ,
210
- ) ;
211
- const fragVarDef = fragmentDef ?. variableDefinitions ?. find (
212
- ( varDef ) => varDef . variable . name . value === node . name . value ,
213
- ) ;
214
- if ( fragVarDef ) {
215
- const fragVarType = typeFromAST ( schema , fragVarDef . type ) ;
216
- if ( isInputType ( fragVarType ) ) {
217
- const fragVarDefault = fragVarDef . defaultValue
218
- ? valueFromAST ( fragVarDef . defaultValue , fragVarType )
219
- : undefined ;
220
-
221
- // Minor hack: transform the FragmentArgDef
222
- // into a schema Argument definition, to
223
- // enable visiting identically to field/directive args
224
- const schemaArgDef : GraphQLArgument = {
225
- name : fragVarDef . variable . name . value ,
226
- type : fragVarType ,
227
- defaultValue : fragVarDefault ,
228
- description : undefined ,
229
- deprecationReason : undefined ,
230
- extensions : { } ,
231
- astNode : {
232
- ...fragVarDef ,
233
- kind : Kind . INPUT_VALUE_DEFINITION ,
234
- name : fragVarDef . variable . name ,
235
- } ,
236
- } ;
237
- argDef = schemaArgDef ;
230
+ const fieldOrDirective = this . getDirective ( ) ?? this . getFieldDef ( ) ;
231
+ if ( fieldOrDirective ) {
232
+ argDef = fieldOrDirective . args . find (
233
+ ( arg ) => arg . name === node . name . value ,
234
+ ) ;
235
+ if ( argDef ) {
236
+ argType = argDef . type ;
238
237
}
239
238
}
240
-
241
- if ( argDef ) {
242
- argType = argDef . type ;
243
- }
244
-
245
239
this . _argument = argDef ;
246
240
this . _defaultValueStack . push ( argDef ? argDef . defaultValue : undefined ) ;
247
241
this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
248
242
break ;
249
243
}
250
- case Kind . ARGUMENT : {
251
- let argDef ;
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 ;
252
250
let argType : unknown ;
253
- const directive = this . getDirective ( ) ;
254
- const fieldDef = this . getFieldDef ( ) ;
255
- if ( directive ) {
256
- argDef = directive . args . find ( ( arg ) => arg . name === node . name . value ) ;
257
- } else if ( fieldDef ) {
258
- argDef = fieldDef . args . find ( ( arg ) => arg . name === node . name . value ) ;
259
- }
260
251
if ( argDef ) {
261
- argType = argDef . type ;
252
+ argType = typeFromAST ( this . _schema , argDef . type ) ;
262
253
}
263
-
264
- this . _argument = argDef ;
265
- this . _defaultValueStack . push ( argDef ? argDef . defaultValue : undefined ) ;
266
254
this . _inputTypeStack . push ( isInputType ( argType ) ? argType : undefined ) ;
267
255
break ;
268
256
}
@@ -311,7 +299,8 @@ export class TypeInfo {
311
299
leave ( node : ASTNode ) {
312
300
switch ( node . kind ) {
313
301
case Kind . DOCUMENT :
314
- this . _fragmentDefinitions = new Map ( ) ;
302
+ this . _fragmentSignaturesByName = /* c8 ignore start */ ( ) =>
303
+ null /* c8 ignore end */ ;
315
304
break ;
316
305
case Kind . SELECTION_SET :
317
306
this . _parentTypeStack . pop ( ) ;
@@ -324,7 +313,7 @@ export class TypeInfo {
324
313
this . _directive = null ;
325
314
break ;
326
315
case Kind . FRAGMENT_SPREAD :
327
- this . _fragmentSpread = null ;
316
+ this . _fragmentSignature = null ;
328
317
break ;
329
318
case Kind . OPERATION_DEFINITION :
330
319
case Kind . INLINE_FRAGMENT :
@@ -334,12 +323,17 @@ export class TypeInfo {
334
323
case Kind . VARIABLE_DEFINITION :
335
324
this . _inputTypeStack . pop ( ) ;
336
325
break ;
337
- case Kind . FRAGMENT_ARGUMENT :
338
326
case Kind . ARGUMENT :
339
327
this . _argument = null ;
340
328
this . _defaultValueStack . pop ( ) ;
341
329
this . _inputTypeStack . pop ( ) ;
342
330
break ;
331
+ case Kind . FRAGMENT_ARGUMENT : {
332
+ this . _fragmentArgument = null ;
333
+ this . _defaultValueStack . pop ( ) ;
334
+ this . _inputTypeStack . pop ( ) ;
335
+ break ;
336
+ }
343
337
case Kind . LIST :
344
338
case Kind . OBJECT_FIELD :
345
339
this . _defaultValueStack . pop ( ) ;
@@ -368,6 +362,25 @@ function getFieldDef(
368
362
return schema . getField ( parentType , fieldNode . name . value ) ;
369
363
}
370
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
+
371
384
/**
372
385
* Creates a new visitor instance which maintains a provided TypeInfo instance
373
386
* along with visiting visitor.
0 commit comments