UNPKG

8.96 kBJavaScriptView Raw
1import find from '../polyfills/find';
2import { Kind } from '../language/kinds';
3import { isObjectType, isInterfaceType, isEnumType, isInputObjectType, isListType, isCompositeType, isInputType, isOutputType, getNullableType, getNamedType } from '../type/definition';
4import { SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef } from '../type/introspection';
5import { typeFromAST } from './typeFromAST';
6/**
7 * TypeInfo is a utility class which, given a GraphQL schema, can keep track
8 * of the current field and type definitions at any point in a GraphQL document
9 * AST during a recursive descent by calling `enter(node)` and `leave(node)`.
10 */
11
12export var TypeInfo =
13/*#__PURE__*/
14function () {
15 function TypeInfo(schema, // NOTE: this experimental optional second parameter is only needed in order
16 // to support non-spec-compliant codebases. You should never need to use it.
17 // It may disappear in the future.
18 getFieldDefFn, // Initial type may be provided in rare cases to facilitate traversals
19 // beginning somewhere other than documents.
20 initialType) {
21 this._schema = schema;
22 this._typeStack = [];
23 this._parentTypeStack = [];
24 this._inputTypeStack = [];
25 this._fieldDefStack = [];
26 this._defaultValueStack = [];
27 this._directive = null;
28 this._argument = null;
29 this._enumValue = null;
30 this._getFieldDef = getFieldDefFn || getFieldDef;
31
32 if (initialType) {
33 if (isInputType(initialType)) {
34 this._inputTypeStack.push(initialType);
35 }
36
37 if (isCompositeType(initialType)) {
38 this._parentTypeStack.push(initialType);
39 }
40
41 if (isOutputType(initialType)) {
42 this._typeStack.push(initialType);
43 }
44 }
45 }
46
47 var _proto = TypeInfo.prototype;
48
49 _proto.getType = function getType() {
50 if (this._typeStack.length > 0) {
51 return this._typeStack[this._typeStack.length - 1];
52 }
53 };
54
55 _proto.getParentType = function getParentType() {
56 if (this._parentTypeStack.length > 0) {
57 return this._parentTypeStack[this._parentTypeStack.length - 1];
58 }
59 };
60
61 _proto.getInputType = function getInputType() {
62 if (this._inputTypeStack.length > 0) {
63 return this._inputTypeStack[this._inputTypeStack.length - 1];
64 }
65 };
66
67 _proto.getParentInputType = function getParentInputType() {
68 if (this._inputTypeStack.length > 1) {
69 return this._inputTypeStack[this._inputTypeStack.length - 2];
70 }
71 };
72
73 _proto.getFieldDef = function getFieldDef() {
74 if (this._fieldDefStack.length > 0) {
75 return this._fieldDefStack[this._fieldDefStack.length - 1];
76 }
77 };
78
79 _proto.getDefaultValue = function getDefaultValue() {
80 if (this._defaultValueStack.length > 0) {
81 return this._defaultValueStack[this._defaultValueStack.length - 1];
82 }
83 };
84
85 _proto.getDirective = function getDirective() {
86 return this._directive;
87 };
88
89 _proto.getArgument = function getArgument() {
90 return this._argument;
91 };
92
93 _proto.getEnumValue = function getEnumValue() {
94 return this._enumValue;
95 };
96
97 _proto.enter = function enter(node) {
98 var schema = this._schema; // Note: many of the types below are explicitly typed as "mixed" to drop
99 // any assumptions of a valid schema to ensure runtime types are properly
100 // checked before continuing since TypeInfo is used as part of validation
101 // which occurs before guarantees of schema and document validity.
102
103 switch (node.kind) {
104 case Kind.SELECTION_SET:
105 {
106 var namedType = getNamedType(this.getType());
107
108 this._parentTypeStack.push(isCompositeType(namedType) ? namedType : undefined);
109
110 break;
111 }
112
113 case Kind.FIELD:
114 {
115 var parentType = this.getParentType();
116 var fieldDef;
117 var fieldType;
118
119 if (parentType) {
120 fieldDef = this._getFieldDef(schema, parentType, node);
121
122 if (fieldDef) {
123 fieldType = fieldDef.type;
124 }
125 }
126
127 this._fieldDefStack.push(fieldDef);
128
129 this._typeStack.push(isOutputType(fieldType) ? fieldType : undefined);
130
131 break;
132 }
133
134 case Kind.DIRECTIVE:
135 this._directive = schema.getDirective(node.name.value);
136 break;
137
138 case Kind.OPERATION_DEFINITION:
139 {
140 var type;
141
142 if (node.operation === 'query') {
143 type = schema.getQueryType();
144 } else if (node.operation === 'mutation') {
145 type = schema.getMutationType();
146 } else if (node.operation === 'subscription') {
147 type = schema.getSubscriptionType();
148 }
149
150 this._typeStack.push(isObjectType(type) ? type : undefined);
151
152 break;
153 }
154
155 case Kind.INLINE_FRAGMENT:
156 case Kind.FRAGMENT_DEFINITION:
157 {
158 var typeConditionAST = node.typeCondition;
159 var outputType = typeConditionAST ? typeFromAST(schema, typeConditionAST) : getNamedType(this.getType());
160
161 this._typeStack.push(isOutputType(outputType) ? outputType : undefined);
162
163 break;
164 }
165
166 case Kind.VARIABLE_DEFINITION:
167 {
168 var inputType = typeFromAST(schema, node.type);
169
170 this._inputTypeStack.push(isInputType(inputType) ? inputType : undefined);
171
172 break;
173 }
174
175 case Kind.ARGUMENT:
176 {
177 var argDef;
178 var argType;
179 var fieldOrDirective = this.getDirective() || this.getFieldDef();
180
181 if (fieldOrDirective) {
182 argDef = find(fieldOrDirective.args, function (arg) {
183 return arg.name === node.name.value;
184 });
185
186 if (argDef) {
187 argType = argDef.type;
188 }
189 }
190
191 this._argument = argDef;
192
193 this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined);
194
195 this._inputTypeStack.push(isInputType(argType) ? argType : undefined);
196
197 break;
198 }
199
200 case Kind.LIST:
201 {
202 var listType = getNullableType(this.getInputType());
203 var itemType = isListType(listType) ? listType.ofType : listType; // List positions never have a default value.
204
205 this._defaultValueStack.push(undefined);
206
207 this._inputTypeStack.push(isInputType(itemType) ? itemType : undefined);
208
209 break;
210 }
211
212 case Kind.OBJECT_FIELD:
213 {
214 var objectType = getNamedType(this.getInputType());
215 var inputFieldType;
216 var inputField;
217
218 if (isInputObjectType(objectType)) {
219 inputField = objectType.getFields()[node.name.value];
220
221 if (inputField) {
222 inputFieldType = inputField.type;
223 }
224 }
225
226 this._defaultValueStack.push(inputField ? inputField.defaultValue : undefined);
227
228 this._inputTypeStack.push(isInputType(inputFieldType) ? inputFieldType : undefined);
229
230 break;
231 }
232
233 case Kind.ENUM:
234 {
235 var enumType = getNamedType(this.getInputType());
236 var enumValue;
237
238 if (isEnumType(enumType)) {
239 enumValue = enumType.getValue(node.value);
240 }
241
242 this._enumValue = enumValue;
243 break;
244 }
245 }
246 };
247
248 _proto.leave = function leave(node) {
249 switch (node.kind) {
250 case Kind.SELECTION_SET:
251 this._parentTypeStack.pop();
252
253 break;
254
255 case Kind.FIELD:
256 this._fieldDefStack.pop();
257
258 this._typeStack.pop();
259
260 break;
261
262 case Kind.DIRECTIVE:
263 this._directive = null;
264 break;
265
266 case Kind.OPERATION_DEFINITION:
267 case Kind.INLINE_FRAGMENT:
268 case Kind.FRAGMENT_DEFINITION:
269 this._typeStack.pop();
270
271 break;
272
273 case Kind.VARIABLE_DEFINITION:
274 this._inputTypeStack.pop();
275
276 break;
277
278 case Kind.ARGUMENT:
279 this._argument = null;
280
281 this._defaultValueStack.pop();
282
283 this._inputTypeStack.pop();
284
285 break;
286
287 case Kind.LIST:
288 case Kind.OBJECT_FIELD:
289 this._defaultValueStack.pop();
290
291 this._inputTypeStack.pop();
292
293 break;
294
295 case Kind.ENUM:
296 this._enumValue = null;
297 break;
298 }
299 };
300
301 return TypeInfo;
302}();
303/**
304 * Not exactly the same as the executor's definition of getFieldDef, in this
305 * statically evaluated environment we do not always have an Object type,
306 * and need to handle Interface and Union types.
307 */
308
309function getFieldDef(schema, parentType, fieldNode) {
310 var name = fieldNode.name.value;
311
312 if (name === SchemaMetaFieldDef.name && schema.getQueryType() === parentType) {
313 return SchemaMetaFieldDef;
314 }
315
316 if (name === TypeMetaFieldDef.name && schema.getQueryType() === parentType) {
317 return TypeMetaFieldDef;
318 }
319
320 if (name === TypeNameMetaFieldDef.name && isCompositeType(parentType)) {
321 return TypeNameMetaFieldDef;
322 }
323
324 if (isObjectType(parentType) || isInterfaceType(parentType)) {
325 return parentType.getFields()[name];
326 }
327}