UNPKG

14.3 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2015-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 *
8 */
9import objectValues from '../polyfills/objectValues';
10import invariant from '../jsutils/invariant';
11import keyMap from '../jsutils/keyMap';
12import keyValMap from '../jsutils/keyValMap';
13import { valueFromAST } from './valueFromAST';
14import { assertValidSDL } from '../validation/validate';
15import blockStringValue from '../language/blockStringValue';
16import { TokenKind } from '../language/lexer';
17import { parse } from '../language/parser';
18import { getDirectiveValues } from '../execution/values';
19import { Kind } from '../language/kinds';
20import { isTypeDefinitionNode } from '../language/predicates';
21import { GraphQLScalarType, GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, GraphQLNonNull } from '../type/definition';
22import { GraphQLDirective, GraphQLSkipDirective, GraphQLIncludeDirective, GraphQLDeprecatedDirective } from '../type/directives';
23import { introspectionTypes } from '../type/introspection';
24import { specifiedScalarTypes } from '../type/scalars';
25import { GraphQLSchema } from '../type/schema';
26
27/**
28 * This takes the ast of a schema document produced by the parse function in
29 * src/language/parser.js.
30 *
31 * If no schema definition is provided, then it will look for types named Query
32 * and Mutation.
33 *
34 * Given that AST it constructs a GraphQLSchema. The resulting schema
35 * has no resolve methods, so execution will use default resolvers.
36 *
37 * Accepts options as a second argument:
38 *
39 * - commentDescriptions:
40 * Provide true to use preceding comments as the description.
41 *
42 */
43export function buildASTSchema(documentAST, options) {
44 !(documentAST && documentAST.kind === Kind.DOCUMENT) ? invariant(0, 'Must provide valid Document AST') : void 0;
45
46 if (!options || !(options.assumeValid || options.assumeValidSDL)) {
47 assertValidSDL(documentAST);
48 }
49
50 var schemaDef;
51 var nodeMap = Object.create(null);
52 var directiveDefs = [];
53 var _iteratorNormalCompletion = true;
54 var _didIteratorError = false;
55 var _iteratorError = undefined;
56
57 try {
58 for (var _iterator = documentAST.definitions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
59 var def = _step.value;
60
61 if (def.kind === Kind.SCHEMA_DEFINITION) {
62 schemaDef = def;
63 } else if (isTypeDefinitionNode(def)) {
64 nodeMap[def.name.value] = def;
65 } else if (def.kind === Kind.DIRECTIVE_DEFINITION) {
66 directiveDefs.push(def);
67 }
68 }
69 } catch (err) {
70 _didIteratorError = true;
71 _iteratorError = err;
72 } finally {
73 try {
74 if (!_iteratorNormalCompletion && _iterator.return != null) {
75 _iterator.return();
76 }
77 } finally {
78 if (_didIteratorError) {
79 throw _iteratorError;
80 }
81 }
82 }
83
84 var operationTypes = schemaDef ? getOperationTypes(schemaDef) : {
85 query: nodeMap.Query,
86 mutation: nodeMap.Mutation,
87 subscription: nodeMap.Subscription
88 };
89 var definitionBuilder = new ASTDefinitionBuilder(nodeMap, options, function (typeName) {
90 throw new Error("Type \"".concat(typeName, "\" not found in document."));
91 });
92 var directives = directiveDefs.map(function (def) {
93 return definitionBuilder.buildDirective(def);
94 }); // If specified directives were not explicitly declared, add them.
95
96 if (!directives.some(function (directive) {
97 return directive.name === 'skip';
98 })) {
99 directives.push(GraphQLSkipDirective);
100 }
101
102 if (!directives.some(function (directive) {
103 return directive.name === 'include';
104 })) {
105 directives.push(GraphQLIncludeDirective);
106 }
107
108 if (!directives.some(function (directive) {
109 return directive.name === 'deprecated';
110 })) {
111 directives.push(GraphQLDeprecatedDirective);
112 } // Note: While this could make early assertions to get the correctly
113 // typed values below, that would throw immediately while type system
114 // validation with validateSchema() will produce more actionable results.
115
116
117 return new GraphQLSchema({
118 query: operationTypes.query ? definitionBuilder.buildType(operationTypes.query) : null,
119 mutation: operationTypes.mutation ? definitionBuilder.buildType(operationTypes.mutation) : null,
120 subscription: operationTypes.subscription ? definitionBuilder.buildType(operationTypes.subscription) : null,
121 types: objectValues(nodeMap).map(function (node) {
122 return definitionBuilder.buildType(node);
123 }),
124 directives: directives,
125 astNode: schemaDef,
126 assumeValid: options && options.assumeValid,
127 allowedLegacyNames: options && options.allowedLegacyNames
128 });
129
130 function getOperationTypes(schema) {
131 var opTypes = {};
132 var _iteratorNormalCompletion2 = true;
133 var _didIteratorError2 = false;
134 var _iteratorError2 = undefined;
135
136 try {
137 for (var _iterator2 = schema.operationTypes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
138 var operationType = _step2.value;
139 opTypes[operationType.operation] = operationType.type;
140 }
141 } catch (err) {
142 _didIteratorError2 = true;
143 _iteratorError2 = err;
144 } finally {
145 try {
146 if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
147 _iterator2.return();
148 }
149 } finally {
150 if (_didIteratorError2) {
151 throw _iteratorError2;
152 }
153 }
154 }
155
156 return opTypes;
157 }
158}
159export var ASTDefinitionBuilder =
160/*#__PURE__*/
161function () {
162 function ASTDefinitionBuilder(typeDefinitionsMap, options, resolveType) {
163 this._typeDefinitionsMap = typeDefinitionsMap;
164 this._options = options;
165 this._resolveType = resolveType; // Initialize to the GraphQL built in scalars and introspection types.
166
167 this._cache = keyMap(specifiedScalarTypes.concat(introspectionTypes), function (type) {
168 return type.name;
169 });
170 }
171
172 var _proto = ASTDefinitionBuilder.prototype;
173
174 _proto.buildType = function buildType(node) {
175 var typeName = node.name.value;
176
177 if (!this._cache[typeName]) {
178 if (node.kind === Kind.NAMED_TYPE) {
179 var defNode = this._typeDefinitionsMap[typeName];
180 this._cache[typeName] = defNode ? this._makeSchemaDef(defNode) : this._resolveType(node.name.value);
181 } else {
182 this._cache[typeName] = this._makeSchemaDef(node);
183 }
184 }
185
186 return this._cache[typeName];
187 };
188
189 _proto._buildWrappedType = function _buildWrappedType(typeNode) {
190 if (typeNode.kind === Kind.LIST_TYPE) {
191 return GraphQLList(this._buildWrappedType(typeNode.type));
192 }
193
194 if (typeNode.kind === Kind.NON_NULL_TYPE) {
195 return GraphQLNonNull( // Note: GraphQLNonNull constructor validates this type
196 this._buildWrappedType(typeNode.type));
197 }
198
199 return this.buildType(typeNode);
200 };
201
202 _proto.buildDirective = function buildDirective(directiveNode) {
203 return new GraphQLDirective({
204 name: directiveNode.name.value,
205 description: getDescription(directiveNode, this._options),
206 locations: directiveNode.locations.map(function (node) {
207 return node.value;
208 }),
209 args: directiveNode.arguments && this._makeInputValues(directiveNode.arguments),
210 astNode: directiveNode
211 });
212 };
213
214 _proto.buildField = function buildField(field) {
215 return {
216 // Note: While this could make assertions to get the correctly typed
217 // value, that would throw immediately while type system validation
218 // with validateSchema() will produce more actionable results.
219 type: this._buildWrappedType(field.type),
220 description: getDescription(field, this._options),
221 args: field.arguments && this._makeInputValues(field.arguments),
222 deprecationReason: getDeprecationReason(field),
223 astNode: field
224 };
225 };
226
227 _proto.buildInputField = function buildInputField(value) {
228 // Note: While this could make assertions to get the correctly typed
229 // value, that would throw immediately while type system validation
230 var type = this._buildWrappedType(value.type);
231
232 return {
233 name: value.name.value,
234 type: type,
235 description: getDescription(value, this._options),
236 defaultValue: valueFromAST(value.defaultValue, type),
237 astNode: value
238 };
239 };
240
241 _proto.buildEnumValue = function buildEnumValue(value) {
242 return {
243 description: getDescription(value, this._options),
244 deprecationReason: getDeprecationReason(value),
245 astNode: value
246 };
247 };
248
249 _proto._makeSchemaDef = function _makeSchemaDef(def) {
250 switch (def.kind) {
251 case Kind.OBJECT_TYPE_DEFINITION:
252 return this._makeTypeDef(def);
253
254 case Kind.INTERFACE_TYPE_DEFINITION:
255 return this._makeInterfaceDef(def);
256
257 case Kind.ENUM_TYPE_DEFINITION:
258 return this._makeEnumDef(def);
259
260 case Kind.UNION_TYPE_DEFINITION:
261 return this._makeUnionDef(def);
262
263 case Kind.SCALAR_TYPE_DEFINITION:
264 return this._makeScalarDef(def);
265
266 case Kind.INPUT_OBJECT_TYPE_DEFINITION:
267 return this._makeInputObjectDef(def);
268
269 default:
270 throw new Error("Type kind \"".concat(def.kind, "\" not supported."));
271 }
272 };
273
274 _proto._makeTypeDef = function _makeTypeDef(def) {
275 var _this = this;
276
277 var interfaces = def.interfaces;
278 return new GraphQLObjectType({
279 name: def.name.value,
280 description: getDescription(def, this._options),
281 fields: function fields() {
282 return _this._makeFieldDefMap(def);
283 },
284 // Note: While this could make early assertions to get the correctly
285 // typed values, that would throw immediately while type system
286 // validation with validateSchema() will produce more actionable results.
287 interfaces: interfaces ? function () {
288 return interfaces.map(function (ref) {
289 return _this.buildType(ref);
290 });
291 } : [],
292 astNode: def
293 });
294 };
295
296 _proto._makeFieldDefMap = function _makeFieldDefMap(def) {
297 var _this2 = this;
298
299 return def.fields ? keyValMap(def.fields, function (field) {
300 return field.name.value;
301 }, function (field) {
302 return _this2.buildField(field);
303 }) : {};
304 };
305
306 _proto._makeInputValues = function _makeInputValues(values) {
307 var _this3 = this;
308
309 return keyValMap(values, function (value) {
310 return value.name.value;
311 }, function (value) {
312 return _this3.buildInputField(value);
313 });
314 };
315
316 _proto._makeInterfaceDef = function _makeInterfaceDef(def) {
317 var _this4 = this;
318
319 return new GraphQLInterfaceType({
320 name: def.name.value,
321 description: getDescription(def, this._options),
322 fields: function fields() {
323 return _this4._makeFieldDefMap(def);
324 },
325 astNode: def
326 });
327 };
328
329 _proto._makeEnumDef = function _makeEnumDef(def) {
330 return new GraphQLEnumType({
331 name: def.name.value,
332 description: getDescription(def, this._options),
333 values: this._makeValueDefMap(def),
334 astNode: def
335 });
336 };
337
338 _proto._makeValueDefMap = function _makeValueDefMap(def) {
339 var _this5 = this;
340
341 return def.values ? keyValMap(def.values, function (enumValue) {
342 return enumValue.name.value;
343 }, function (enumValue) {
344 return _this5.buildEnumValue(enumValue);
345 }) : {};
346 };
347
348 _proto._makeUnionDef = function _makeUnionDef(def) {
349 var _this6 = this;
350
351 var types = def.types;
352 return new GraphQLUnionType({
353 name: def.name.value,
354 description: getDescription(def, this._options),
355 // Note: While this could make assertions to get the correctly typed
356 // values below, that would throw immediately while type system
357 // validation with validateSchema() will produce more actionable results.
358 types: types ? function () {
359 return types.map(function (ref) {
360 return _this6.buildType(ref);
361 });
362 } : [],
363 astNode: def
364 });
365 };
366
367 _proto._makeScalarDef = function _makeScalarDef(def) {
368 return new GraphQLScalarType({
369 name: def.name.value,
370 description: getDescription(def, this._options),
371 astNode: def,
372 serialize: function serialize(value) {
373 return value;
374 }
375 });
376 };
377
378 _proto._makeInputObjectDef = function _makeInputObjectDef(def) {
379 var _this7 = this;
380
381 return new GraphQLInputObjectType({
382 name: def.name.value,
383 description: getDescription(def, this._options),
384 fields: function fields() {
385 return def.fields ? _this7._makeInputValues(def.fields) : {};
386 },
387 astNode: def
388 });
389 };
390
391 return ASTDefinitionBuilder;
392}();
393/**
394 * Given a field or enum value node, returns the string value for the
395 * deprecation reason.
396 */
397
398function getDeprecationReason(node) {
399 var deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node);
400 return deprecated && deprecated.reason;
401}
402/**
403 * Given an ast node, returns its string description.
404 * @deprecated: provided to ease adoption and will be removed in v16.
405 *
406 * Accepts options as a second argument:
407 *
408 * - commentDescriptions:
409 * Provide true to use preceding comments as the description.
410 *
411 */
412
413
414export function getDescription(node, options) {
415 if (node.description) {
416 return node.description.value;
417 }
418
419 if (options && options.commentDescriptions) {
420 var rawValue = getLeadingCommentBlock(node);
421
422 if (rawValue !== undefined) {
423 return blockStringValue('\n' + rawValue);
424 }
425 }
426}
427
428function getLeadingCommentBlock(node) {
429 var loc = node.loc;
430
431 if (!loc) {
432 return;
433 }
434
435 var comments = [];
436 var token = loc.startToken.prev;
437
438 while (token && token.kind === TokenKind.COMMENT && token.next && token.prev && token.line + 1 === token.next.line && token.line !== token.prev.line) {
439 var value = String(token.value);
440 comments.push(value);
441 token = token.prev;
442 }
443
444 return comments.reverse().join('\n');
445}
446/**
447 * A helper function to build a GraphQLSchema directly from a source
448 * document.
449 */
450
451
452export function buildSchema(source, options) {
453 return buildASTSchema(parse(source, options), options);
454}
\No newline at end of file