'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.TypeInfo = undefined; var _kinds = require('../language/kinds'); var _definition = require('../type/definition'); var _introspection = require('../type/introspection'); var _typeFromAST = require('./typeFromAST'); var _find = require('../jsutils/find'); var _find2 = _interopRequireDefault(_find); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * strict */ /** * TypeInfo is a utility class which, given a GraphQL schema, can keep track * of the current field and type definitions at any point in a GraphQL document * AST during a recursive descent by calling `enter(node)` and `leave(node)`. */ var TypeInfo = exports.TypeInfo = function () { function TypeInfo(schema, // NOTE: this experimental optional second parameter is only needed in order // to support non-spec-compliant codebases. You should never need to use it. getFieldDefFn, // Initial type may be provided in rare cases to facilitate traversals initialType) { _classCallCheck(this, TypeInfo); this._schema = schema; this._typeStack = []; this._parentTypeStack = []; this._inputTypeStack = []; this._fieldDefStack = []; this._directive = null; this._argument = null; this._enumValue = null; this._getFieldDef = getFieldDefFn || getFieldDef; if (initialType) { if ((0, _definition.isInputType)(initialType)) { this._inputTypeStack.push(initialType); } if ((0, _definition.isCompositeType)(initialType)) { this._parentTypeStack.push(initialType); } if ((0, _definition.isOutputType)(initialType)) { this._typeStack.push(initialType); } } } TypeInfo.prototype.getType = function getType() { if (this._typeStack.length > 0) { return this._typeStack[this._typeStack.length - 1]; } }; TypeInfo.prototype.getParentType = function getParentType() { if (this._parentTypeStack.length > 0) { return this._parentTypeStack[this._parentTypeStack.length - 1]; } }; TypeInfo.prototype.getInputType = function getInputType() { if (this._inputTypeStack.length > 0) { return this._inputTypeStack[this._inputTypeStack.length - 1]; } }; TypeInfo.prototype.getParentInputType = function getParentInputType() { if (this._inputTypeStack.length > 1) { return this._inputTypeStack[this._inputTypeStack.length - 2]; } }; TypeInfo.prototype.getFieldDef = function getFieldDef() { if (this._fieldDefStack.length > 0) { return this._fieldDefStack[this._fieldDefStack.length - 1]; } }; TypeInfo.prototype.getDirective = function getDirective() { return this._directive; }; TypeInfo.prototype.getArgument = function getArgument() { return this._argument; }; TypeInfo.prototype.getEnumValue = function getEnumValue() { return this._enumValue; }; // Flow does not yet handle this case. TypeInfo.prototype.enter = function enter(node /* ASTNode */) { var schema = this._schema; // Note: many of the types below are explicitly typed as "mixed" to drop // any assumptions of a valid schema to ensure runtime types are properly // checked before continuing since TypeInfo is used as part of validation // which occurs before guarantees of schema and document validity. switch (node.kind) { case _kinds.Kind.SELECTION_SET: var namedType = (0, _definition.getNamedType)(this.getType()); this._parentTypeStack.push((0, _definition.isCompositeType)(namedType) ? namedType : undefined); break; case _kinds.Kind.FIELD: var parentType = this.getParentType(); var fieldDef = void 0; var fieldType = void 0; if (parentType) { fieldDef = this._getFieldDef(schema, parentType, node); if (fieldDef) { fieldType = fieldDef.type; } } this._fieldDefStack.push(fieldDef); this._typeStack.push((0, _definition.isOutputType)(fieldType) ? fieldType : undefined); break; case _kinds.Kind.DIRECTIVE: this._directive = schema.getDirective(node.name.value); break; case _kinds.Kind.OPERATION_DEFINITION: var type = void 0; if (node.operation === 'query') { type = schema.getQueryType(); } else if (node.operation === 'mutation') { type = schema.getMutationType(); } else if (node.operation === 'subscription') { type = schema.getSubscriptionType(); } this._typeStack.push((0, _definition.isObjectType)(type) ? type : undefined); break; case _kinds.Kind.INLINE_FRAGMENT: case _kinds.Kind.FRAGMENT_DEFINITION: var typeConditionAST = node.typeCondition; var outputType = typeConditionAST ? (0, _typeFromAST.typeFromAST)(schema, typeConditionAST) : (0, _definition.getNamedType)(this.getType()); this._typeStack.push((0, _definition.isOutputType)(outputType) ? outputType : undefined); break; case _kinds.Kind.VARIABLE_DEFINITION: var inputType = (0, _typeFromAST.typeFromAST)(schema, node.type); this._inputTypeStack.push((0, _definition.isInputType)(inputType) ? inputType : undefined); break; case _kinds.Kind.ARGUMENT: var argDef = void 0; var argType = void 0; var fieldOrDirective = this.getDirective() || this.getFieldDef(); if (fieldOrDirective) { argDef = (0, _find2.default)(fieldOrDirective.args, function (arg) { return arg.name === node.name.value; }); if (argDef) { argType = argDef.type; } } this._argument = argDef; this._inputTypeStack.push((0, _definition.isInputType)(argType) ? argType : undefined); break; case _kinds.Kind.LIST: var listType = (0, _definition.getNullableType)(this.getInputType()); var itemType = (0, _definition.isListType)(listType) ? listType.ofType : listType; this._inputTypeStack.push((0, _definition.isInputType)(itemType) ? itemType : undefined); break; case _kinds.Kind.OBJECT_FIELD: var objectType = (0, _definition.getNamedType)(this.getInputType()); var inputFieldType = void 0; if ((0, _definition.isInputObjectType)(objectType)) { var inputField = objectType.getFields()[node.name.value]; if (inputField) { inputFieldType = inputField.type; } } this._inputTypeStack.push((0, _definition.isInputType)(inputFieldType) ? inputFieldType : undefined); break; case _kinds.Kind.ENUM: var enumType = (0, _definition.getNamedType)(this.getInputType()); var enumValue = void 0; if ((0, _definition.isEnumType)(enumType)) { enumValue = enumType.getValue(node.value); } this._enumValue = enumValue; break; } }; TypeInfo.prototype.leave = function leave(node) { switch (node.kind) { case _kinds.Kind.SELECTION_SET: this._parentTypeStack.pop(); break; case _kinds.Kind.FIELD: this._fieldDefStack.pop(); this._typeStack.pop(); break; case _kinds.Kind.DIRECTIVE: this._directive = null; break; case _kinds.Kind.OPERATION_DEFINITION: case _kinds.Kind.INLINE_FRAGMENT: case _kinds.Kind.FRAGMENT_DEFINITION: this._typeStack.pop(); break; case _kinds.Kind.VARIABLE_DEFINITION: this._inputTypeStack.pop(); break; case _kinds.Kind.ARGUMENT: this._argument = null; this._inputTypeStack.pop(); break; case _kinds.Kind.LIST: case _kinds.Kind.OBJECT_FIELD: this._inputTypeStack.pop(); break; case _kinds.Kind.ENUM: this._enumValue = null; break; } }; return TypeInfo; }(); /** * Not exactly the same as the executor's definition of getFieldDef, in this * statically evaluated environment we do not always have an Object type, * and need to handle Interface and Union types. */ function getFieldDef(schema, parentType, fieldNode) { var name = fieldNode.name.value; if (name === _introspection.SchemaMetaFieldDef.name && schema.getQueryType() === parentType) { return _introspection.SchemaMetaFieldDef; } if (name === _introspection.TypeMetaFieldDef.name && schema.getQueryType() === parentType) { return _introspection.TypeMetaFieldDef; } if (name === _introspection.TypeNameMetaFieldDef.name && (0, _definition.isCompositeType)(parentType)) { return _introspection.TypeNameMetaFieldDef; } if ((0, _definition.isObjectType)(parentType) || (0, _definition.isInterfaceType)(parentType)) { return parentType.getFields()[name]; } }