UNPKG

10.2 kBJavaScriptView Raw
1import find from "../polyfills/find.mjs";
2import { Kind } from "../language/kinds.mjs";
3import { isNode } from "../language/ast.mjs";
4import { getVisitFn } from "../language/visitor.mjs";
5import { isObjectType, isInterfaceType, isEnumType, isInputObjectType, isListType, isCompositeType, isInputType, isOutputType, getNullableType, getNamedType } from "../type/definition.mjs";
6import { SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef } from "../type/introspection.mjs";
7import { typeFromAST } from "./typeFromAST.mjs";
8/**
9 * TypeInfo is a utility class which, given a GraphQL schema, can keep track
10 * of the current field and type definitions at any point in a GraphQL document
11 * AST during a recursive descent by calling `enter(node)` and `leave(node)`.
12 */
13
14export var TypeInfo = /*#__PURE__*/function () {
15 function TypeInfo(schema, // NOTE: this experimental optional second parameter is only needed in order
16 // to support non-spec-compliant code bases. 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 !== null && getFieldDefFn !== void 0 ? 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 switch (node.operation) {
143 case 'query':
144 type = schema.getQueryType();
145 break;
146
147 case 'mutation':
148 type = schema.getMutationType();
149 break;
150
151 case 'subscription':
152 type = schema.getSubscriptionType();
153 break;
154 }
155
156 this._typeStack.push(isObjectType(type) ? type : undefined);
157
158 break;
159 }
160
161 case Kind.INLINE_FRAGMENT:
162 case Kind.FRAGMENT_DEFINITION:
163 {
164 var typeConditionAST = node.typeCondition;
165 var outputType = typeConditionAST ? typeFromAST(schema, typeConditionAST) : getNamedType(this.getType());
166
167 this._typeStack.push(isOutputType(outputType) ? outputType : undefined);
168
169 break;
170 }
171
172 case Kind.VARIABLE_DEFINITION:
173 {
174 var inputType = typeFromAST(schema, node.type);
175
176 this._inputTypeStack.push(isInputType(inputType) ? inputType : undefined);
177
178 break;
179 }
180
181 case Kind.ARGUMENT:
182 {
183 var _this$getDirective;
184
185 var argDef;
186 var argType;
187 var fieldOrDirective = (_this$getDirective = this.getDirective()) !== null && _this$getDirective !== void 0 ? _this$getDirective : this.getFieldDef();
188
189 if (fieldOrDirective) {
190 argDef = find(fieldOrDirective.args, function (arg) {
191 return arg.name === node.name.value;
192 });
193
194 if (argDef) {
195 argType = argDef.type;
196 }
197 }
198
199 this._argument = argDef;
200
201 this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined);
202
203 this._inputTypeStack.push(isInputType(argType) ? argType : undefined);
204
205 break;
206 }
207
208 case Kind.LIST:
209 {
210 var listType = getNullableType(this.getInputType());
211 var itemType = isListType(listType) ? listType.ofType : listType; // List positions never have a default value.
212
213 this._defaultValueStack.push(undefined);
214
215 this._inputTypeStack.push(isInputType(itemType) ? itemType : undefined);
216
217 break;
218 }
219
220 case Kind.OBJECT_FIELD:
221 {
222 var objectType = getNamedType(this.getInputType());
223 var inputFieldType;
224 var inputField;
225
226 if (isInputObjectType(objectType)) {
227 inputField = objectType.getFields()[node.name.value];
228
229 if (inputField) {
230 inputFieldType = inputField.type;
231 }
232 }
233
234 this._defaultValueStack.push(inputField ? inputField.defaultValue : undefined);
235
236 this._inputTypeStack.push(isInputType(inputFieldType) ? inputFieldType : undefined);
237
238 break;
239 }
240
241 case Kind.ENUM:
242 {
243 var enumType = getNamedType(this.getInputType());
244 var enumValue;
245
246 if (isEnumType(enumType)) {
247 enumValue = enumType.getValue(node.value);
248 }
249
250 this._enumValue = enumValue;
251 break;
252 }
253 }
254 };
255
256 _proto.leave = function leave(node) {
257 switch (node.kind) {
258 case Kind.SELECTION_SET:
259 this._parentTypeStack.pop();
260
261 break;
262
263 case Kind.FIELD:
264 this._fieldDefStack.pop();
265
266 this._typeStack.pop();
267
268 break;
269
270 case Kind.DIRECTIVE:
271 this._directive = null;
272 break;
273
274 case Kind.OPERATION_DEFINITION:
275 case Kind.INLINE_FRAGMENT:
276 case Kind.FRAGMENT_DEFINITION:
277 this._typeStack.pop();
278
279 break;
280
281 case Kind.VARIABLE_DEFINITION:
282 this._inputTypeStack.pop();
283
284 break;
285
286 case Kind.ARGUMENT:
287 this._argument = null;
288
289 this._defaultValueStack.pop();
290
291 this._inputTypeStack.pop();
292
293 break;
294
295 case Kind.LIST:
296 case Kind.OBJECT_FIELD:
297 this._defaultValueStack.pop();
298
299 this._inputTypeStack.pop();
300
301 break;
302
303 case Kind.ENUM:
304 this._enumValue = null;
305 break;
306 }
307 };
308
309 return TypeInfo;
310}();
311/**
312 * Not exactly the same as the executor's definition of getFieldDef, in this
313 * statically evaluated environment we do not always have an Object type,
314 * and need to handle Interface and Union types.
315 */
316
317function getFieldDef(schema, parentType, fieldNode) {
318 var name = fieldNode.name.value;
319
320 if (name === SchemaMetaFieldDef.name && schema.getQueryType() === parentType) {
321 return SchemaMetaFieldDef;
322 }
323
324 if (name === TypeMetaFieldDef.name && schema.getQueryType() === parentType) {
325 return TypeMetaFieldDef;
326 }
327
328 if (name === TypeNameMetaFieldDef.name && isCompositeType(parentType)) {
329 return TypeNameMetaFieldDef;
330 }
331
332 if (isObjectType(parentType) || isInterfaceType(parentType)) {
333 return parentType.getFields()[name];
334 }
335}
336/**
337 * Creates a new visitor instance which maintains a provided TypeInfo instance
338 * along with visiting visitor.
339 */
340
341
342export function visitWithTypeInfo(typeInfo, visitor) {
343 return {
344 enter: function enter(node) {
345 typeInfo.enter(node);
346 var fn = getVisitFn(visitor, node.kind,
347 /* isLeaving */
348 false);
349
350 if (fn) {
351 var result = fn.apply(visitor, arguments);
352
353 if (result !== undefined) {
354 typeInfo.leave(node);
355
356 if (isNode(result)) {
357 typeInfo.enter(result);
358 }
359 }
360
361 return result;
362 }
363 },
364 leave: function leave(node) {
365 var fn = getVisitFn(visitor, node.kind,
366 /* isLeaving */
367 true);
368 var result;
369
370 if (fn) {
371 result = fn.apply(visitor, arguments);
372 }
373
374 typeInfo.leave(node);
375 return result;
376 }
377 };
378}