UNPKG

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