blob: f083b7ea9ceecc79b6bc9da602c5d15a6f84d5b2 [file] [log] [blame]
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.visitWithTypeInfo = visitWithTypeInfo;
exports.TypeInfo = void 0;
var _find = _interopRequireDefault(require("../polyfills/find"));
var _kinds = require("../language/kinds");
var _visitor = require("../language/visitor");
var _ast = require("../language/ast");
var _definition = require("../type/definition");
var _introspection = require("../type/introspection");
var _typeFromAST = require("./typeFromAST");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 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 =
/*#__PURE__*/
function () {
function TypeInfo(schema, // NOTE: this experimental optional second parameter is only needed in order
// to support non-spec-compliant code bases. You should never need to use it.
// It may disappear in the future.
getFieldDefFn, // Initial type may be provided in rare cases to facilitate traversals
// beginning somewhere other than documents.
initialType) {
this._schema = schema;
this._typeStack = [];
this._parentTypeStack = [];
this._inputTypeStack = [];
this._fieldDefStack = [];
this._defaultValueStack = [];
this._directive = null;
this._argument = null;
this._enumValue = null;
this._getFieldDef = getFieldDefFn !== null && getFieldDefFn !== void 0 ? 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);
}
}
}
var _proto = TypeInfo.prototype;
_proto.getType = function getType() {
if (this._typeStack.length > 0) {
return this._typeStack[this._typeStack.length - 1];
}
};
_proto.getParentType = function getParentType() {
if (this._parentTypeStack.length > 0) {
return this._parentTypeStack[this._parentTypeStack.length - 1];
}
};
_proto.getInputType = function getInputType() {
if (this._inputTypeStack.length > 0) {
return this._inputTypeStack[this._inputTypeStack.length - 1];
}
};
_proto.getParentInputType = function getParentInputType() {
if (this._inputTypeStack.length > 1) {
return this._inputTypeStack[this._inputTypeStack.length - 2];
}
};
_proto.getFieldDef = function getFieldDef() {
if (this._fieldDefStack.length > 0) {
return this._fieldDefStack[this._fieldDefStack.length - 1];
}
};
_proto.getDefaultValue = function getDefaultValue() {
if (this._defaultValueStack.length > 0) {
return this._defaultValueStack[this._defaultValueStack.length - 1];
}
};
_proto.getDirective = function getDirective() {
return this._directive;
};
_proto.getArgument = function getArgument() {
return this._argument;
};
_proto.getEnumValue = function getEnumValue() {
return this._enumValue;
};
_proto.enter = function enter(node) {
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;
var fieldType;
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;
switch (node.operation) {
case 'query':
type = schema.getQueryType();
break;
case 'mutation':
type = schema.getMutationType();
break;
case 'subscription':
type = schema.getSubscriptionType();
break;
}
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 _this$getDirective;
var argDef;
var argType;
var fieldOrDirective = (_this$getDirective = this.getDirective()) !== null && _this$getDirective !== void 0 ? _this$getDirective : this.getFieldDef();
if (fieldOrDirective) {
argDef = (0, _find.default)(fieldOrDirective.args, function (arg) {
return arg.name === node.name.value;
});
if (argDef) {
argType = argDef.type;
}
}
this._argument = argDef;
this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined);
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; // List positions never have a default value.
this._defaultValueStack.push(undefined);
this._inputTypeStack.push((0, _definition.isInputType)(itemType) ? itemType : undefined);
break;
}
case _kinds.Kind.OBJECT_FIELD:
{
var objectType = (0, _definition.getNamedType)(this.getInputType());
var inputFieldType;
var inputField;
if ((0, _definition.isInputObjectType)(objectType)) {
inputField = objectType.getFields()[node.name.value];
if (inputField) {
inputFieldType = inputField.type;
}
}
this._defaultValueStack.push(inputField ? inputField.defaultValue : undefined);
this._inputTypeStack.push((0, _definition.isInputType)(inputFieldType) ? inputFieldType : undefined);
break;
}
case _kinds.Kind.ENUM:
{
var enumType = (0, _definition.getNamedType)(this.getInputType());
var enumValue;
if ((0, _definition.isEnumType)(enumType)) {
enumValue = enumType.getValue(node.value);
}
this._enumValue = enumValue;
break;
}
}
};
_proto.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._defaultValueStack.pop();
this._inputTypeStack.pop();
break;
case _kinds.Kind.LIST:
case _kinds.Kind.OBJECT_FIELD:
this._defaultValueStack.pop();
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.
*/
exports.TypeInfo = TypeInfo;
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];
}
}
/**
* Creates a new visitor instance which maintains a provided TypeInfo instance
* along with visiting visitor.
*/
function visitWithTypeInfo(typeInfo, visitor) {
return {
enter: function enter(node) {
typeInfo.enter(node);
var fn = (0, _visitor.getVisitFn)(visitor, node.kind,
/* isLeaving */
false);
if (fn) {
var result = fn.apply(visitor, arguments);
if (result !== undefined) {
typeInfo.leave(node);
if ((0, _ast.isNode)(result)) {
typeInfo.enter(result);
}
}
return result;
}
},
leave: function leave(node) {
var fn = (0, _visitor.getVisitFn)(visitor, node.kind,
/* isLeaving */
true);
var result;
if (fn) {
result = fn.apply(visitor, arguments);
}
typeInfo.leave(node);
return result;
}
};
}