430 lines
13 KiB
JavaScript
430 lines
13 KiB
JavaScript
import objectValues from '../polyfills/objectValues';
|
|
import keyMap from '../jsutils/keyMap';
|
|
import inspect from '../jsutils/inspect';
|
|
import invariant from '../jsutils/invariant';
|
|
import devAssert from '../jsutils/devAssert';
|
|
import keyValMap from '../jsutils/keyValMap';
|
|
import { Kind } from '../language/kinds';
|
|
import { TokenKind } from '../language/tokenKind';
|
|
import { parse } from '../language/parser';
|
|
import { isTypeDefinitionNode } from '../language/predicates';
|
|
import { dedentBlockStringValue } from '../language/blockString';
|
|
import { assertValidSDL } from '../validation/validate';
|
|
import { getDirectiveValues } from '../execution/values';
|
|
import { specifiedScalarTypes } from '../type/scalars';
|
|
import { introspectionTypes } from '../type/introspection';
|
|
import { GraphQLSchema } from '../type/schema';
|
|
import { GraphQLDirective, GraphQLSkipDirective, GraphQLIncludeDirective, GraphQLDeprecatedDirective } from '../type/directives';
|
|
import { GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from '../type/definition';
|
|
import { valueFromAST } from './valueFromAST';
|
|
|
|
/**
|
|
* This takes the ast of a schema document produced by the parse function in
|
|
* src/language/parser.js.
|
|
*
|
|
* If no schema definition is provided, then it will look for types named Query
|
|
* and Mutation.
|
|
*
|
|
* Given that AST it constructs a GraphQLSchema. The resulting schema
|
|
* has no resolve methods, so execution will use default resolvers.
|
|
*
|
|
* Accepts options as a second argument:
|
|
*
|
|
* - commentDescriptions:
|
|
* Provide true to use preceding comments as the description.
|
|
*
|
|
*/
|
|
export function buildASTSchema(documentAST, options) {
|
|
documentAST && documentAST.kind === Kind.DOCUMENT || devAssert(0, 'Must provide valid Document AST');
|
|
|
|
if (!options || !(options.assumeValid || options.assumeValidSDL)) {
|
|
assertValidSDL(documentAST);
|
|
}
|
|
|
|
var schemaDef;
|
|
var typeDefs = [];
|
|
var directiveDefs = [];
|
|
|
|
for (var _i2 = 0, _documentAST$definiti2 = documentAST.definitions; _i2 < _documentAST$definiti2.length; _i2++) {
|
|
var def = _documentAST$definiti2[_i2];
|
|
|
|
if (def.kind === Kind.SCHEMA_DEFINITION) {
|
|
schemaDef = def;
|
|
} else if (isTypeDefinitionNode(def)) {
|
|
typeDefs.push(def);
|
|
} else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
|
|
directiveDefs.push(def);
|
|
}
|
|
}
|
|
|
|
var astBuilder = new ASTDefinitionBuilder(options, function (typeName) {
|
|
var type = typeMap[typeName];
|
|
|
|
if (type === undefined) {
|
|
throw new Error("Type \"".concat(typeName, "\" not found in document."));
|
|
}
|
|
|
|
return type;
|
|
});
|
|
var typeMap = keyByNameNode(typeDefs, function (node) {
|
|
return astBuilder.buildType(node);
|
|
});
|
|
var operationTypes = schemaDef ? getOperationTypes(schemaDef) : {
|
|
query: 'Query',
|
|
mutation: 'Mutation',
|
|
subscription: 'Subscription'
|
|
};
|
|
var directives = directiveDefs.map(function (def) {
|
|
return astBuilder.buildDirective(def);
|
|
}); // If specified directives were not explicitly declared, add them.
|
|
|
|
if (!directives.some(function (directive) {
|
|
return directive.name === 'skip';
|
|
})) {
|
|
directives.push(GraphQLSkipDirective);
|
|
}
|
|
|
|
if (!directives.some(function (directive) {
|
|
return directive.name === 'include';
|
|
})) {
|
|
directives.push(GraphQLIncludeDirective);
|
|
}
|
|
|
|
if (!directives.some(function (directive) {
|
|
return directive.name === 'deprecated';
|
|
})) {
|
|
directives.push(GraphQLDeprecatedDirective);
|
|
}
|
|
|
|
return new GraphQLSchema({
|
|
// Note: While this could make early assertions to get the correctly
|
|
// typed values below, that would throw immediately while type system
|
|
// validation with validateSchema() will produce more actionable results.
|
|
query: operationTypes.query ? typeMap[operationTypes.query] : null,
|
|
mutation: operationTypes.mutation ? typeMap[operationTypes.mutation] : null,
|
|
subscription: operationTypes.subscription ? typeMap[operationTypes.subscription] : null,
|
|
types: objectValues(typeMap),
|
|
directives: directives,
|
|
astNode: schemaDef,
|
|
assumeValid: options && options.assumeValid,
|
|
allowedLegacyNames: options && options.allowedLegacyNames
|
|
});
|
|
|
|
function getOperationTypes(schema) {
|
|
var opTypes = {};
|
|
|
|
for (var _i4 = 0, _schema$operationType2 = schema.operationTypes; _i4 < _schema$operationType2.length; _i4++) {
|
|
var operationType = _schema$operationType2[_i4];
|
|
opTypes[operationType.operation] = operationType.type.name.value;
|
|
}
|
|
|
|
return opTypes;
|
|
}
|
|
}
|
|
var stdTypeMap = keyMap(specifiedScalarTypes.concat(introspectionTypes), function (type) {
|
|
return type.name;
|
|
});
|
|
export var ASTDefinitionBuilder =
|
|
/*#__PURE__*/
|
|
function () {
|
|
function ASTDefinitionBuilder(options, resolveType) {
|
|
this._options = options;
|
|
this._resolveType = resolveType;
|
|
}
|
|
|
|
var _proto = ASTDefinitionBuilder.prototype;
|
|
|
|
_proto.getNamedType = function getNamedType(node) {
|
|
var name = node.name.value;
|
|
return stdTypeMap[name] || this._resolveType(name);
|
|
};
|
|
|
|
_proto.getWrappedType = function getWrappedType(node) {
|
|
if (node.kind === Kind.LIST_TYPE) {
|
|
return new GraphQLList(this.getWrappedType(node.type));
|
|
}
|
|
|
|
if (node.kind === Kind.NON_NULL_TYPE) {
|
|
return new GraphQLNonNull(this.getWrappedType(node.type));
|
|
}
|
|
|
|
return this.getNamedType(node);
|
|
};
|
|
|
|
_proto.buildDirective = function buildDirective(directive) {
|
|
var _this = this;
|
|
|
|
var locations = directive.locations.map(function (_ref) {
|
|
var value = _ref.value;
|
|
return value;
|
|
});
|
|
return new GraphQLDirective({
|
|
name: directive.name.value,
|
|
description: getDescription(directive, this._options),
|
|
locations: locations,
|
|
isRepeatable: directive.repeatable,
|
|
args: keyByNameNode(directive.arguments || [], function (arg) {
|
|
return _this.buildArg(arg);
|
|
}),
|
|
astNode: directive
|
|
});
|
|
};
|
|
|
|
_proto.buildField = function buildField(field) {
|
|
var _this2 = this;
|
|
|
|
return {
|
|
// Note: While this could make assertions to get the correctly typed
|
|
// value, that would throw immediately while type system validation
|
|
// with validateSchema() will produce more actionable results.
|
|
type: this.getWrappedType(field.type),
|
|
description: getDescription(field, this._options),
|
|
args: keyByNameNode(field.arguments || [], function (arg) {
|
|
return _this2.buildArg(arg);
|
|
}),
|
|
deprecationReason: getDeprecationReason(field),
|
|
astNode: field
|
|
};
|
|
};
|
|
|
|
_proto.buildArg = function buildArg(value) {
|
|
// Note: While this could make assertions to get the correctly typed
|
|
// value, that would throw immediately while type system validation
|
|
// with validateSchema() will produce more actionable results.
|
|
var type = this.getWrappedType(value.type);
|
|
return {
|
|
type: type,
|
|
description: getDescription(value, this._options),
|
|
defaultValue: valueFromAST(value.defaultValue, type),
|
|
astNode: value
|
|
};
|
|
};
|
|
|
|
_proto.buildInputField = function buildInputField(value) {
|
|
// Note: While this could make assertions to get the correctly typed
|
|
// value, that would throw immediately while type system validation
|
|
// with validateSchema() will produce more actionable results.
|
|
var type = this.getWrappedType(value.type);
|
|
return {
|
|
type: type,
|
|
description: getDescription(value, this._options),
|
|
defaultValue: valueFromAST(value.defaultValue, type),
|
|
astNode: value
|
|
};
|
|
};
|
|
|
|
_proto.buildEnumValue = function buildEnumValue(value) {
|
|
return {
|
|
description: getDescription(value, this._options),
|
|
deprecationReason: getDeprecationReason(value),
|
|
astNode: value
|
|
};
|
|
};
|
|
|
|
_proto.buildType = function buildType(astNode) {
|
|
var name = astNode.name.value;
|
|
|
|
if (stdTypeMap[name]) {
|
|
return stdTypeMap[name];
|
|
}
|
|
|
|
switch (astNode.kind) {
|
|
case Kind.OBJECT_TYPE_DEFINITION:
|
|
return this._makeTypeDef(astNode);
|
|
|
|
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
return this._makeInterfaceDef(astNode);
|
|
|
|
case Kind.ENUM_TYPE_DEFINITION:
|
|
return this._makeEnumDef(astNode);
|
|
|
|
case Kind.UNION_TYPE_DEFINITION:
|
|
return this._makeUnionDef(astNode);
|
|
|
|
case Kind.SCALAR_TYPE_DEFINITION:
|
|
return this._makeScalarDef(astNode);
|
|
|
|
case Kind.INPUT_OBJECT_TYPE_DEFINITION:
|
|
return this._makeInputObjectDef(astNode);
|
|
} // Not reachable. All possible type definition nodes have been considered.
|
|
|
|
|
|
/* istanbul ignore next */
|
|
invariant(false, 'Unexpected type definition node: ' + inspect(astNode));
|
|
};
|
|
|
|
_proto._makeTypeDef = function _makeTypeDef(astNode) {
|
|
var _this3 = this;
|
|
|
|
var interfaceNodes = astNode.interfaces;
|
|
var fieldNodes = astNode.fields; // Note: While this could make assertions to get the correctly typed
|
|
// values below, that would throw immediately while type system
|
|
// validation with validateSchema() will produce more actionable results.
|
|
|
|
var interfaces = interfaceNodes && interfaceNodes.length > 0 ? function () {
|
|
return interfaceNodes.map(function (ref) {
|
|
return _this3.getNamedType(ref);
|
|
});
|
|
} : [];
|
|
var fields = fieldNodes && fieldNodes.length > 0 ? function () {
|
|
return keyByNameNode(fieldNodes, function (field) {
|
|
return _this3.buildField(field);
|
|
});
|
|
} : Object.create(null);
|
|
return new GraphQLObjectType({
|
|
name: astNode.name.value,
|
|
description: getDescription(astNode, this._options),
|
|
interfaces: interfaces,
|
|
fields: fields,
|
|
astNode: astNode
|
|
});
|
|
};
|
|
|
|
_proto._makeInterfaceDef = function _makeInterfaceDef(astNode) {
|
|
var _this4 = this;
|
|
|
|
var fieldNodes = astNode.fields;
|
|
var fields = fieldNodes && fieldNodes.length > 0 ? function () {
|
|
return keyByNameNode(fieldNodes, function (field) {
|
|
return _this4.buildField(field);
|
|
});
|
|
} : Object.create(null);
|
|
return new GraphQLInterfaceType({
|
|
name: astNode.name.value,
|
|
description: getDescription(astNode, this._options),
|
|
fields: fields,
|
|
astNode: astNode
|
|
});
|
|
};
|
|
|
|
_proto._makeEnumDef = function _makeEnumDef(astNode) {
|
|
var _this5 = this;
|
|
|
|
var valueNodes = astNode.values || [];
|
|
return new GraphQLEnumType({
|
|
name: astNode.name.value,
|
|
description: getDescription(astNode, this._options),
|
|
values: keyByNameNode(valueNodes, function (value) {
|
|
return _this5.buildEnumValue(value);
|
|
}),
|
|
astNode: astNode
|
|
});
|
|
};
|
|
|
|
_proto._makeUnionDef = function _makeUnionDef(astNode) {
|
|
var _this6 = this;
|
|
|
|
var typeNodes = astNode.types; // Note: While this could make assertions to get the correctly typed
|
|
// values below, that would throw immediately while type system
|
|
// validation with validateSchema() will produce more actionable results.
|
|
|
|
var types = typeNodes && typeNodes.length > 0 ? function () {
|
|
return typeNodes.map(function (ref) {
|
|
return _this6.getNamedType(ref);
|
|
});
|
|
} : [];
|
|
return new GraphQLUnionType({
|
|
name: astNode.name.value,
|
|
description: getDescription(astNode, this._options),
|
|
types: types,
|
|
astNode: astNode
|
|
});
|
|
};
|
|
|
|
_proto._makeScalarDef = function _makeScalarDef(astNode) {
|
|
return new GraphQLScalarType({
|
|
name: astNode.name.value,
|
|
description: getDescription(astNode, this._options),
|
|
astNode: astNode
|
|
});
|
|
};
|
|
|
|
_proto._makeInputObjectDef = function _makeInputObjectDef(def) {
|
|
var _this7 = this;
|
|
|
|
var fields = def.fields;
|
|
return new GraphQLInputObjectType({
|
|
name: def.name.value,
|
|
description: getDescription(def, this._options),
|
|
fields: fields ? function () {
|
|
return keyByNameNode(fields, function (field) {
|
|
return _this7.buildInputField(field);
|
|
});
|
|
} : Object.create(null),
|
|
astNode: def
|
|
});
|
|
};
|
|
|
|
return ASTDefinitionBuilder;
|
|
}();
|
|
|
|
function keyByNameNode(list, valFn) {
|
|
return keyValMap(list, function (_ref2) {
|
|
var name = _ref2.name;
|
|
return name.value;
|
|
}, valFn);
|
|
}
|
|
/**
|
|
* Given a field or enum value node, returns the string value for the
|
|
* deprecation reason.
|
|
*/
|
|
|
|
|
|
function getDeprecationReason(node) {
|
|
var deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
|
|
return deprecated && deprecated.reason;
|
|
}
|
|
/**
|
|
* Given an ast node, returns its string description.
|
|
* @deprecated: provided to ease adoption and will be removed in v16.
|
|
*
|
|
* Accepts options as a second argument:
|
|
*
|
|
* - commentDescriptions:
|
|
* Provide true to use preceding comments as the description.
|
|
*
|
|
*/
|
|
|
|
|
|
export function getDescription(node, options) {
|
|
if (node.description) {
|
|
return node.description.value;
|
|
}
|
|
|
|
if (options && options.commentDescriptions) {
|
|
var rawValue = getLeadingCommentBlock(node);
|
|
|
|
if (rawValue !== undefined) {
|
|
return dedentBlockStringValue('\n' + rawValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getLeadingCommentBlock(node) {
|
|
var loc = node.loc;
|
|
|
|
if (!loc) {
|
|
return;
|
|
}
|
|
|
|
var comments = [];
|
|
var token = loc.startToken.prev;
|
|
|
|
while (token && token.kind === TokenKind.COMMENT && token.next && token.prev && token.line + 1 === token.next.line && token.line !== token.prev.line) {
|
|
var value = String(token.value);
|
|
comments.push(value);
|
|
token = token.prev;
|
|
}
|
|
|
|
return comments.reverse().join('\n');
|
|
}
|
|
/**
|
|
* A helper function to build a GraphQLSchema directly from a source
|
|
* document.
|
|
*/
|
|
|
|
|
|
export function buildSchema(source, options) {
|
|
return buildASTSchema(parse(source, options), options);
|
|
}
|