UNPKG

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