Skip to content

Commit 938a983

Browse files
committed
Add Fragment Arg info to TypeInfo
1 parent 914fd83 commit 938a983

File tree

1 file changed

+85
-7
lines changed

1 file changed

+85
-7
lines changed

src/utilities/TypeInfo.ts

+85-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import 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';
410
import { isNode } from '../language/ast.js';
511
import { Kind } from '../language/kinds.js';
612
import type { ASTVisitor } from '../language/visitor.js';
@@ -31,6 +37,7 @@ import type { GraphQLDirective } from '../type/directives.js';
3137
import type { GraphQLSchema } from '../type/schema.js';
3238

3339
import { 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
*/
4047
export 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

Comments
 (0)