buildASTSchema.mjs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. import objectValues from '../polyfills/objectValues';
  2. import keyMap from '../jsutils/keyMap';
  3. import inspect from '../jsutils/inspect';
  4. import invariant from '../jsutils/invariant';
  5. import devAssert from '../jsutils/devAssert';
  6. import keyValMap from '../jsutils/keyValMap';
  7. import { Kind } from '../language/kinds';
  8. import { TokenKind } from '../language/tokenKind';
  9. import { parse } from '../language/parser';
  10. import { isTypeDefinitionNode } from '../language/predicates';
  11. import { dedentBlockStringValue } from '../language/blockString';
  12. import { assertValidSDL } from '../validation/validate';
  13. import { getDirectiveValues } from '../execution/values';
  14. import { specifiedScalarTypes } from '../type/scalars';
  15. import { introspectionTypes } from '../type/introspection';
  16. import { GraphQLSchema } from '../type/schema';
  17. import { GraphQLDirective, GraphQLSkipDirective, GraphQLIncludeDirective, GraphQLDeprecatedDirective } from '../type/directives';
  18. import { GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from '../type/definition';
  19. import { valueFromAST } from './valueFromAST';
  20. /**
  21. * This takes the ast of a schema document produced by the parse function in
  22. * src/language/parser.js.
  23. *
  24. * If no schema definition is provided, then it will look for types named Query
  25. * and Mutation.
  26. *
  27. * Given that AST it constructs a GraphQLSchema. The resulting schema
  28. * has no resolve methods, so execution will use default resolvers.
  29. *
  30. * Accepts options as a second argument:
  31. *
  32. * - commentDescriptions:
  33. * Provide true to use preceding comments as the description.
  34. *
  35. */
  36. export function buildASTSchema(documentAST, options) {
  37. documentAST && documentAST.kind === Kind.DOCUMENT || devAssert(0, 'Must provide valid Document AST');
  38. if (!options || !(options.assumeValid || options.assumeValidSDL)) {
  39. assertValidSDL(documentAST);
  40. }
  41. var schemaDef;
  42. var typeDefs = [];
  43. var directiveDefs = [];
  44. for (var _i2 = 0, _documentAST$definiti2 = documentAST.definitions; _i2 < _documentAST$definiti2.length; _i2++) {
  45. var def = _documentAST$definiti2[_i2];
  46. if (def.kind === Kind.SCHEMA_DEFINITION) {
  47. schemaDef = def;
  48. } else if (isTypeDefinitionNode(def)) {
  49. typeDefs.push(def);
  50. } else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
  51. directiveDefs.push(def);
  52. }
  53. }
  54. var astBuilder = new ASTDefinitionBuilder(options, function (typeName) {
  55. var type = typeMap[typeName];
  56. if (type === undefined) {
  57. throw new Error("Type \"".concat(typeName, "\" not found in document."));
  58. }
  59. return type;
  60. });
  61. var typeMap = keyByNameNode(typeDefs, function (node) {
  62. return astBuilder.buildType(node);
  63. });
  64. var operationTypes = schemaDef ? getOperationTypes(schemaDef) : {
  65. query: 'Query',
  66. mutation: 'Mutation',
  67. subscription: 'Subscription'
  68. };
  69. var directives = directiveDefs.map(function (def) {
  70. return astBuilder.buildDirective(def);
  71. }); // If specified directives were not explicitly declared, add them.
  72. if (!directives.some(function (directive) {
  73. return directive.name === 'skip';
  74. })) {
  75. directives.push(GraphQLSkipDirective);
  76. }
  77. if (!directives.some(function (directive) {
  78. return directive.name === 'include';
  79. })) {
  80. directives.push(GraphQLIncludeDirective);
  81. }
  82. if (!directives.some(function (directive) {
  83. return directive.name === 'deprecated';
  84. })) {
  85. directives.push(GraphQLDeprecatedDirective);
  86. }
  87. return new GraphQLSchema({
  88. // Note: While this could make early assertions to get the correctly
  89. // typed values below, that would throw immediately while type system
  90. // validation with validateSchema() will produce more actionable results.
  91. query: operationTypes.query ? typeMap[operationTypes.query] : null,
  92. mutation: operationTypes.mutation ? typeMap[operationTypes.mutation] : null,
  93. subscription: operationTypes.subscription ? typeMap[operationTypes.subscription] : null,
  94. types: objectValues(typeMap),
  95. directives: directives,
  96. astNode: schemaDef,
  97. assumeValid: options && options.assumeValid,
  98. allowedLegacyNames: options && options.allowedLegacyNames
  99. });
  100. function getOperationTypes(schema) {
  101. var opTypes = {};
  102. for (var _i4 = 0, _schema$operationType2 = schema.operationTypes; _i4 < _schema$operationType2.length; _i4++) {
  103. var operationType = _schema$operationType2[_i4];
  104. opTypes[operationType.operation] = operationType.type.name.value;
  105. }
  106. return opTypes;
  107. }
  108. }
  109. var stdTypeMap = keyMap(specifiedScalarTypes.concat(introspectionTypes), function (type) {
  110. return type.name;
  111. });
  112. export var ASTDefinitionBuilder =
  113. /*#__PURE__*/
  114. function () {
  115. function ASTDefinitionBuilder(options, resolveType) {
  116. this._options = options;
  117. this._resolveType = resolveType;
  118. }
  119. var _proto = ASTDefinitionBuilder.prototype;
  120. _proto.getNamedType = function getNamedType(node) {
  121. var name = node.name.value;
  122. return stdTypeMap[name] || this._resolveType(name);
  123. };
  124. _proto.getWrappedType = function getWrappedType(node) {
  125. if (node.kind === Kind.LIST_TYPE) {
  126. return new GraphQLList(this.getWrappedType(node.type));
  127. }
  128. if (node.kind === Kind.NON_NULL_TYPE) {
  129. return new GraphQLNonNull(this.getWrappedType(node.type));
  130. }
  131. return this.getNamedType(node);
  132. };
  133. _proto.buildDirective = function buildDirective(directive) {
  134. var _this = this;
  135. var locations = directive.locations.map(function (_ref) {
  136. var value = _ref.value;
  137. return value;
  138. });
  139. return new GraphQLDirective({
  140. name: directive.name.value,
  141. description: getDescription(directive, this._options),
  142. locations: locations,
  143. isRepeatable: directive.repeatable,
  144. args: keyByNameNode(directive.arguments || [], function (arg) {
  145. return _this.buildArg(arg);
  146. }),
  147. astNode: directive
  148. });
  149. };
  150. _proto.buildField = function buildField(field) {
  151. var _this2 = this;
  152. return {
  153. // Note: While this could make assertions to get the correctly typed
  154. // value, that would throw immediately while type system validation
  155. // with validateSchema() will produce more actionable results.
  156. type: this.getWrappedType(field.type),
  157. description: getDescription(field, this._options),
  158. args: keyByNameNode(field.arguments || [], function (arg) {
  159. return _this2.buildArg(arg);
  160. }),
  161. deprecationReason: getDeprecationReason(field),
  162. astNode: field
  163. };
  164. };
  165. _proto.buildArg = function buildArg(value) {
  166. // Note: While this could make assertions to get the correctly typed
  167. // value, that would throw immediately while type system validation
  168. // with validateSchema() will produce more actionable results.
  169. var type = this.getWrappedType(value.type);
  170. return {
  171. type: type,
  172. description: getDescription(value, this._options),
  173. defaultValue: valueFromAST(value.defaultValue, type),
  174. astNode: value
  175. };
  176. };
  177. _proto.buildInputField = function buildInputField(value) {
  178. // Note: While this could make assertions to get the correctly typed
  179. // value, that would throw immediately while type system validation
  180. // with validateSchema() will produce more actionable results.
  181. var type = this.getWrappedType(value.type);
  182. return {
  183. type: type,
  184. description: getDescription(value, this._options),
  185. defaultValue: valueFromAST(value.defaultValue, type),
  186. astNode: value
  187. };
  188. };
  189. _proto.buildEnumValue = function buildEnumValue(value) {
  190. return {
  191. description: getDescription(value, this._options),
  192. deprecationReason: getDeprecationReason(value),
  193. astNode: value
  194. };
  195. };
  196. _proto.buildType = function buildType(astNode) {
  197. var name = astNode.name.value;
  198. if (stdTypeMap[name]) {
  199. return stdTypeMap[name];
  200. }
  201. switch (astNode.kind) {
  202. case Kind.OBJECT_TYPE_DEFINITION:
  203. return this._makeTypeDef(astNode);
  204. case Kind.INTERFACE_TYPE_DEFINITION:
  205. return this._makeInterfaceDef(astNode);
  206. case Kind.ENUM_TYPE_DEFINITION:
  207. return this._makeEnumDef(astNode);
  208. case Kind.UNION_TYPE_DEFINITION:
  209. return this._makeUnionDef(astNode);
  210. case Kind.SCALAR_TYPE_DEFINITION:
  211. return this._makeScalarDef(astNode);
  212. case Kind.INPUT_OBJECT_TYPE_DEFINITION:
  213. return this._makeInputObjectDef(astNode);
  214. } // Not reachable. All possible type definition nodes have been considered.
  215. /* istanbul ignore next */
  216. invariant(false, 'Unexpected type definition node: ' + inspect(astNode));
  217. };
  218. _proto._makeTypeDef = function _makeTypeDef(astNode) {
  219. var _this3 = this;
  220. var interfaceNodes = astNode.interfaces;
  221. var fieldNodes = astNode.fields; // Note: While this could make assertions to get the correctly typed
  222. // values below, that would throw immediately while type system
  223. // validation with validateSchema() will produce more actionable results.
  224. var interfaces = interfaceNodes && interfaceNodes.length > 0 ? function () {
  225. return interfaceNodes.map(function (ref) {
  226. return _this3.getNamedType(ref);
  227. });
  228. } : [];
  229. var fields = fieldNodes && fieldNodes.length > 0 ? function () {
  230. return keyByNameNode(fieldNodes, function (field) {
  231. return _this3.buildField(field);
  232. });
  233. } : Object.create(null);
  234. return new GraphQLObjectType({
  235. name: astNode.name.value,
  236. description: getDescription(astNode, this._options),
  237. interfaces: interfaces,
  238. fields: fields,
  239. astNode: astNode
  240. });
  241. };
  242. _proto._makeInterfaceDef = function _makeInterfaceDef(astNode) {
  243. var _this4 = this;
  244. var fieldNodes = astNode.fields;
  245. var fields = fieldNodes && fieldNodes.length > 0 ? function () {
  246. return keyByNameNode(fieldNodes, function (field) {
  247. return _this4.buildField(field);
  248. });
  249. } : Object.create(null);
  250. return new GraphQLInterfaceType({
  251. name: astNode.name.value,
  252. description: getDescription(astNode, this._options),
  253. fields: fields,
  254. astNode: astNode
  255. });
  256. };
  257. _proto._makeEnumDef = function _makeEnumDef(astNode) {
  258. var _this5 = this;
  259. var valueNodes = astNode.values || [];
  260. return new GraphQLEnumType({
  261. name: astNode.name.value,
  262. description: getDescription(astNode, this._options),
  263. values: keyByNameNode(valueNodes, function (value) {
  264. return _this5.buildEnumValue(value);
  265. }),
  266. astNode: astNode
  267. });
  268. };
  269. _proto._makeUnionDef = function _makeUnionDef(astNode) {
  270. var _this6 = this;
  271. var typeNodes = astNode.types; // Note: While this could make assertions to get the correctly typed
  272. // values below, that would throw immediately while type system
  273. // validation with validateSchema() will produce more actionable results.
  274. var types = typeNodes && typeNodes.length > 0 ? function () {
  275. return typeNodes.map(function (ref) {
  276. return _this6.getNamedType(ref);
  277. });
  278. } : [];
  279. return new GraphQLUnionType({
  280. name: astNode.name.value,
  281. description: getDescription(astNode, this._options),
  282. types: types,
  283. astNode: astNode
  284. });
  285. };
  286. _proto._makeScalarDef = function _makeScalarDef(astNode) {
  287. return new GraphQLScalarType({
  288. name: astNode.name.value,
  289. description: getDescription(astNode, this._options),
  290. astNode: astNode
  291. });
  292. };
  293. _proto._makeInputObjectDef = function _makeInputObjectDef(def) {
  294. var _this7 = this;
  295. var fields = def.fields;
  296. return new GraphQLInputObjectType({
  297. name: def.name.value,
  298. description: getDescription(def, this._options),
  299. fields: fields ? function () {
  300. return keyByNameNode(fields, function (field) {
  301. return _this7.buildInputField(field);
  302. });
  303. } : Object.create(null),
  304. astNode: def
  305. });
  306. };
  307. return ASTDefinitionBuilder;
  308. }();
  309. function keyByNameNode(list, valFn) {
  310. return keyValMap(list, function (_ref2) {
  311. var name = _ref2.name;
  312. return name.value;
  313. }, valFn);
  314. }
  315. /**
  316. * Given a field or enum value node, returns the string value for the
  317. * deprecation reason.
  318. */
  319. function getDeprecationReason(node) {
  320. var deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
  321. return deprecated && deprecated.reason;
  322. }
  323. /**
  324. * Given an ast node, returns its string description.
  325. * @deprecated: provided to ease adoption and will be removed in v16.
  326. *
  327. * Accepts options as a second argument:
  328. *
  329. * - commentDescriptions:
  330. * Provide true to use preceding comments as the description.
  331. *
  332. */
  333. export function getDescription(node, options) {
  334. if (node.description) {
  335. return node.description.value;
  336. }
  337. if (options && options.commentDescriptions) {
  338. var rawValue = getLeadingCommentBlock(node);
  339. if (rawValue !== undefined) {
  340. return dedentBlockStringValue('\n' + rawValue);
  341. }
  342. }
  343. }
  344. function getLeadingCommentBlock(node) {
  345. var loc = node.loc;
  346. if (!loc) {
  347. return;
  348. }
  349. var comments = [];
  350. var token = loc.startToken.prev;
  351. while (token && token.kind === TokenKind.COMMENT && token.next && token.prev && token.line + 1 === token.next.line && token.line !== token.prev.line) {
  352. var value = String(token.value);
  353. comments.push(value);
  354. token = token.prev;
  355. }
  356. return comments.reverse().join('\n');
  357. }
  358. /**
  359. * A helper function to build a GraphQLSchema directly from a source
  360. * document.
  361. */
  362. export function buildSchema(source, options) {
  363. return buildASTSchema(parse(source, options), options);
  364. }