Skip to content

Commit 6667c65

Browse files
committed
Add fragment arguments to TypeInfo
1 parent ec2f92c commit 6667c65

File tree

1 file changed

+73
-7
lines changed

1 file changed

+73
-7
lines changed

src/utilities/TypeInfo.ts

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
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+
} from '../language/ast.js';
49
import { isNode } from '../language/ast.js';
510
import { Kind } from '../language/kinds.js';
611
import type { ASTVisitor } from '../language/visitor.js';
@@ -31,6 +36,7 @@ import type { GraphQLDirective } from '../type/directives.js';
3136
import type { GraphQLSchema } from '../type/schema.js';
3237

3338
import { 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
*/
4046
export 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

Comments
 (0)