UNPKG

10.2 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true,
5});
6exports.TypeInfo = void 0;
7exports.visitWithTypeInfo = visitWithTypeInfo;
8
9var _ast = require('../language/ast.js');
10
11var _kinds = require('../language/kinds.js');
12
13var _visitor = require('../language/visitor.js');
14
15var _definition = require('../type/definition.js');
16
17var _introspection = require('../type/introspection.js');
18
19var _typeFromAST = require('./typeFromAST.js');
20
21/**
22 * TypeInfo is a utility class which, given a GraphQL schema, can keep track
23 * of the current field and type definitions at any point in a GraphQL document
24 * AST during a recursive descent by calling `enter(node)` and `leave(node)`.
25 */
26class TypeInfo {
27 constructor(
28 schema,
29 /**
30 * Initial type may be provided in rare cases to facilitate traversals
31 * beginning somewhere other than documents.
32 */
33 initialType,
34 /** @deprecated will be removed in 17.0.0 */
35 getFieldDefFn,
36 ) {
37 this._schema = schema;
38 this._typeStack = [];
39 this._parentTypeStack = [];
40 this._inputTypeStack = [];
41 this._fieldDefStack = [];
42 this._defaultValueStack = [];
43 this._directive = null;
44 this._argument = null;
45 this._enumValue = null;
46 this._getFieldDef =
47 getFieldDefFn !== null && getFieldDefFn !== void 0
48 ? getFieldDefFn
49 : getFieldDef;
50
51 if (initialType) {
52 if ((0, _definition.isInputType)(initialType)) {
53 this._inputTypeStack.push(initialType);
54 }
55
56 if ((0, _definition.isCompositeType)(initialType)) {
57 this._parentTypeStack.push(initialType);
58 }
59
60 if ((0, _definition.isOutputType)(initialType)) {
61 this._typeStack.push(initialType);
62 }
63 }
64 }
65
66 get [Symbol.toStringTag]() {
67 return 'TypeInfo';
68 }
69
70 getType() {
71 if (this._typeStack.length > 0) {
72 return this._typeStack[this._typeStack.length - 1];
73 }
74 }
75
76 getParentType() {
77 if (this._parentTypeStack.length > 0) {
78 return this._parentTypeStack[this._parentTypeStack.length - 1];
79 }
80 }
81
82 getInputType() {
83 if (this._inputTypeStack.length > 0) {
84 return this._inputTypeStack[this._inputTypeStack.length - 1];
85 }
86 }
87
88 getParentInputType() {
89 if (this._inputTypeStack.length > 1) {
90 return this._inputTypeStack[this._inputTypeStack.length - 2];
91 }
92 }
93
94 getFieldDef() {
95 if (this._fieldDefStack.length > 0) {
96 return this._fieldDefStack[this._fieldDefStack.length - 1];
97 }
98 }
99
100 getDefaultValue() {
101 if (this._defaultValueStack.length > 0) {
102 return this._defaultValueStack[this._defaultValueStack.length - 1];
103 }
104 }
105
106 getDirective() {
107 return this._directive;
108 }
109
110 getArgument() {
111 return this._argument;
112 }
113
114 getEnumValue() {
115 return this._enumValue;
116 }
117
118 enter(node) {
119 const schema = this._schema; // Note: many of the types below are explicitly typed as "unknown" to drop
120 // any assumptions of a valid schema to ensure runtime types are properly
121 // checked before continuing since TypeInfo is used as part of validation
122 // which occurs before guarantees of schema and document validity.
123
124 switch (node.kind) {
125 case _kinds.Kind.SELECTION_SET: {
126 const namedType = (0, _definition.getNamedType)(this.getType());
127
128 this._parentTypeStack.push(
129 (0, _definition.isCompositeType)(namedType) ? namedType : undefined,
130 );
131
132 break;
133 }
134
135 case _kinds.Kind.FIELD: {
136 const parentType = this.getParentType();
137 let fieldDef;
138 let fieldType;
139
140 if (parentType) {
141 fieldDef = this._getFieldDef(schema, parentType, node);
142
143 if (fieldDef) {
144 fieldType = fieldDef.type;
145 }
146 }
147
148 this._fieldDefStack.push(fieldDef);
149
150 this._typeStack.push(
151 (0, _definition.isOutputType)(fieldType) ? fieldType : undefined,
152 );
153
154 break;
155 }
156
157 case _kinds.Kind.DIRECTIVE:
158 this._directive = schema.getDirective(node.name.value);
159 break;
160
161 case _kinds.Kind.OPERATION_DEFINITION: {
162 const rootType = schema.getRootType(node.operation);
163
164 this._typeStack.push(
165 (0, _definition.isObjectType)(rootType) ? rootType : undefined,
166 );
167
168 break;
169 }
170
171 case _kinds.Kind.INLINE_FRAGMENT:
172 case _kinds.Kind.FRAGMENT_DEFINITION: {
173 const typeConditionAST = node.typeCondition;
174 const outputType = typeConditionAST
175 ? (0, _typeFromAST.typeFromAST)(schema, typeConditionAST)
176 : (0, _definition.getNamedType)(this.getType());
177
178 this._typeStack.push(
179 (0, _definition.isOutputType)(outputType) ? outputType : undefined,
180 );
181
182 break;
183 }
184
185 case _kinds.Kind.VARIABLE_DEFINITION: {
186 const inputType = (0, _typeFromAST.typeFromAST)(schema, node.type);
187
188 this._inputTypeStack.push(
189 (0, _definition.isInputType)(inputType) ? inputType : undefined,
190 );
191
192 break;
193 }
194
195 case _kinds.Kind.ARGUMENT: {
196 var _this$getDirective;
197
198 let argDef;
199 let argType;
200 const fieldOrDirective =
201 (_this$getDirective = this.getDirective()) !== null &&
202 _this$getDirective !== void 0
203 ? _this$getDirective
204 : this.getFieldDef();
205
206 if (fieldOrDirective) {
207 argDef = fieldOrDirective.args.find(
208 (arg) => arg.name === node.name.value,
209 );
210
211 if (argDef) {
212 argType = argDef.type;
213 }
214 }
215
216 this._argument = argDef;
217
218 this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined);
219
220 this._inputTypeStack.push(
221 (0, _definition.isInputType)(argType) ? argType : undefined,
222 );
223
224 break;
225 }
226
227 case _kinds.Kind.LIST: {
228 const listType = (0, _definition.getNullableType)(this.getInputType());
229 const itemType = (0, _definition.isListType)(listType)
230 ? listType.ofType
231 : listType; // List positions never have a default value.
232
233 this._defaultValueStack.push(undefined);
234
235 this._inputTypeStack.push(
236 (0, _definition.isInputType)(itemType) ? itemType : undefined,
237 );
238
239 break;
240 }
241
242 case _kinds.Kind.OBJECT_FIELD: {
243 const objectType = (0, _definition.getNamedType)(this.getInputType());
244 let inputFieldType;
245 let inputField;
246
247 if ((0, _definition.isInputObjectType)(objectType)) {
248 inputField = objectType.getFields()[node.name.value];
249
250 if (inputField) {
251 inputFieldType = inputField.type;
252 }
253 }
254
255 this._defaultValueStack.push(
256 inputField ? inputField.defaultValue : undefined,
257 );
258
259 this._inputTypeStack.push(
260 (0, _definition.isInputType)(inputFieldType)
261 ? inputFieldType
262 : undefined,
263 );
264
265 break;
266 }
267
268 case _kinds.Kind.ENUM: {
269 const enumType = (0, _definition.getNamedType)(this.getInputType());
270 let enumValue;
271
272 if ((0, _definition.isEnumType)(enumType)) {
273 enumValue = enumType.getValue(node.value);
274 }
275
276 this._enumValue = enumValue;
277 break;
278 }
279
280 default: // Ignore other nodes
281 }
282 }
283
284 leave(node) {
285 switch (node.kind) {
286 case _kinds.Kind.SELECTION_SET:
287 this._parentTypeStack.pop();
288
289 break;
290
291 case _kinds.Kind.FIELD:
292 this._fieldDefStack.pop();
293
294 this._typeStack.pop();
295
296 break;
297
298 case _kinds.Kind.DIRECTIVE:
299 this._directive = null;
300 break;
301
302 case _kinds.Kind.OPERATION_DEFINITION:
303 case _kinds.Kind.INLINE_FRAGMENT:
304 case _kinds.Kind.FRAGMENT_DEFINITION:
305 this._typeStack.pop();
306
307 break;
308
309 case _kinds.Kind.VARIABLE_DEFINITION:
310 this._inputTypeStack.pop();
311
312 break;
313
314 case _kinds.Kind.ARGUMENT:
315 this._argument = null;
316
317 this._defaultValueStack.pop();
318
319 this._inputTypeStack.pop();
320
321 break;
322
323 case _kinds.Kind.LIST:
324 case _kinds.Kind.OBJECT_FIELD:
325 this._defaultValueStack.pop();
326
327 this._inputTypeStack.pop();
328
329 break;
330
331 case _kinds.Kind.ENUM:
332 this._enumValue = null;
333 break;
334
335 default: // Ignore other nodes
336 }
337 }
338}
339
340exports.TypeInfo = TypeInfo;
341
342/**
343 * Not exactly the same as the executor's definition of getFieldDef, in this
344 * statically evaluated environment we do not always have an Object type,
345 * and need to handle Interface and Union types.
346 */
347function getFieldDef(schema, parentType, fieldNode) {
348 const name = fieldNode.name.value;
349
350 if (
351 name === _introspection.SchemaMetaFieldDef.name &&
352 schema.getQueryType() === parentType
353 ) {
354 return _introspection.SchemaMetaFieldDef;
355 }
356
357 if (
358 name === _introspection.TypeMetaFieldDef.name &&
359 schema.getQueryType() === parentType
360 ) {
361 return _introspection.TypeMetaFieldDef;
362 }
363
364 if (
365 name === _introspection.TypeNameMetaFieldDef.name &&
366 (0, _definition.isCompositeType)(parentType)
367 ) {
368 return _introspection.TypeNameMetaFieldDef;
369 }
370
371 if (
372 (0, _definition.isObjectType)(parentType) ||
373 (0, _definition.isInterfaceType)(parentType)
374 ) {
375 return parentType.getFields()[name];
376 }
377}
378/**
379 * Creates a new visitor instance which maintains a provided TypeInfo instance
380 * along with visiting visitor.
381 */
382
383function visitWithTypeInfo(typeInfo, visitor) {
384 return {
385 enter(...args) {
386 const node = args[0];
387 typeInfo.enter(node);
388 const fn = (0, _visitor.getEnterLeaveForKind)(visitor, node.kind).enter;
389
390 if (fn) {
391 const result = fn.apply(visitor, args);
392
393 if (result !== undefined) {
394 typeInfo.leave(node);
395
396 if ((0, _ast.isNode)(result)) {
397 typeInfo.enter(result);
398 }
399 }
400
401 return result;
402 }
403 },
404
405 leave(...args) {
406 const node = args[0];
407 const fn = (0, _visitor.getEnterLeaveForKind)(visitor, node.kind).leave;
408 let result;
409
410 if (fn) {
411 result = fn.apply(visitor, args);
412 }
413
414 typeInfo.leave(node);
415 return result;
416 },
417 };
418}