UNPKG

9.05 kBJavaScriptView Raw
1import flatMap from '../polyfills/flatMap';
2import objectValues from '../polyfills/objectValues';
3import inspect from '../jsutils/inspect';
4import invariant from '../jsutils/invariant';
5import { print } from '../language/printer';
6import { printBlockString } from '../language/blockString';
7import { isIntrospectionType } from '../type/introspection';
8import { GraphQLString, isSpecifiedScalarType } from '../type/scalars';
9import { GraphQLDirective, DEFAULT_DEPRECATION_REASON, isSpecifiedDirective } from '../type/directives';
10import { isScalarType, isObjectType, isInterfaceType, isUnionType, isEnumType, isInputObjectType } from '../type/definition';
11import { astFromValue } from '../utilities/astFromValue';
12
13/**
14 * Accepts options as a second argument:
15 *
16 * - commentDescriptions:
17 * Provide true to use preceding comments as the description.
18 *
19 */
20export function printSchema(schema, options) {
21 return printFilteredSchema(schema, function (n) {
22 return !isSpecifiedDirective(n);
23 }, isDefinedType, options);
24}
25export function printIntrospectionSchema(schema, options) {
26 return printFilteredSchema(schema, isSpecifiedDirective, isIntrospectionType, options);
27}
28
29function isDefinedType(type) {
30 return !isSpecifiedScalarType(type) && !isIntrospectionType(type);
31}
32
33function printFilteredSchema(schema, directiveFilter, typeFilter, options) {
34 var directives = schema.getDirectives().filter(directiveFilter);
35 var typeMap = schema.getTypeMap();
36 var types = objectValues(typeMap).sort(function (type1, type2) {
37 return type1.name.localeCompare(type2.name);
38 }).filter(typeFilter);
39 return [printSchemaDefinition(schema)].concat(directives.map(function (directive) {
40 return printDirective(directive, options);
41 }), types.map(function (type) {
42 return printType(type, options);
43 })).filter(Boolean).join('\n\n') + '\n';
44}
45
46function printSchemaDefinition(schema) {
47 if (isSchemaOfCommonNames(schema)) {
48 return;
49 }
50
51 var operationTypes = [];
52 var queryType = schema.getQueryType();
53
54 if (queryType) {
55 operationTypes.push(" query: ".concat(queryType.name));
56 }
57
58 var mutationType = schema.getMutationType();
59
60 if (mutationType) {
61 operationTypes.push(" mutation: ".concat(mutationType.name));
62 }
63
64 var subscriptionType = schema.getSubscriptionType();
65
66 if (subscriptionType) {
67 operationTypes.push(" subscription: ".concat(subscriptionType.name));
68 }
69
70 return "schema {\n".concat(operationTypes.join('\n'), "\n}");
71}
72/**
73 * GraphQL schema define root types for each type of operation. These types are
74 * the same as any other type and can be named in any manner, however there is
75 * a common naming convention:
76 *
77 * schema {
78 * query: Query
79 * mutation: Mutation
80 * }
81 *
82 * When using this naming convention, the schema description can be omitted.
83 */
84
85
86function isSchemaOfCommonNames(schema) {
87 var queryType = schema.getQueryType();
88
89 if (queryType && queryType.name !== 'Query') {
90 return false;
91 }
92
93 var mutationType = schema.getMutationType();
94
95 if (mutationType && mutationType.name !== 'Mutation') {
96 return false;
97 }
98
99 var subscriptionType = schema.getSubscriptionType();
100
101 if (subscriptionType && subscriptionType.name !== 'Subscription') {
102 return false;
103 }
104
105 return true;
106}
107
108export function printType(type, options) {
109 if (isScalarType(type)) {
110 return printScalar(type, options);
111 } else if (isObjectType(type)) {
112 return printObject(type, options);
113 } else if (isInterfaceType(type)) {
114 return printInterface(type, options);
115 } else if (isUnionType(type)) {
116 return printUnion(type, options);
117 } else if (isEnumType(type)) {
118 return printEnum(type, options);
119 } else if (isInputObjectType(type)) {
120 return printInputObject(type, options);
121 } // Not reachable. All possible types have been considered.
122
123
124 /* istanbul ignore next */
125 invariant(false, 'Unexpected type: ' + inspect(type));
126}
127
128function printScalar(type, options) {
129 return printDescription(options, type) + "scalar ".concat(type.name);
130}
131
132function printObject(type, options) {
133 var interfaces = type.getInterfaces();
134 var implementedInterfaces = interfaces.length ? ' implements ' + interfaces.map(function (i) {
135 return i.name;
136 }).join(' & ') : '';
137 return printDescription(options, type) + "type ".concat(type.name).concat(implementedInterfaces) + printFields(options, type);
138}
139
140function printInterface(type, options) {
141 return printDescription(options, type) + "interface ".concat(type.name) + printFields(options, type);
142}
143
144function printUnion(type, options) {
145 var types = type.getTypes();
146 var possibleTypes = types.length ? ' = ' + types.join(' | ') : '';
147 return printDescription(options, type) + 'union ' + type.name + possibleTypes;
148}
149
150function printEnum(type, options) {
151 var values = type.getValues().map(function (value, i) {
152 return printDescription(options, value, ' ', !i) + ' ' + value.name + printDeprecated(value);
153 });
154 return printDescription(options, type) + "enum ".concat(type.name) + printBlock(values);
155}
156
157function printInputObject(type, options) {
158 var fields = objectValues(type.getFields()).map(function (f, i) {
159 return printDescription(options, f, ' ', !i) + ' ' + printInputValue(f);
160 });
161 return printDescription(options, type) + "input ".concat(type.name) + printBlock(fields);
162}
163
164function printFields(options, type) {
165 var fields = objectValues(type.getFields()).map(function (f, i) {
166 return printDescription(options, f, ' ', !i) + ' ' + f.name + printArgs(options, f.args, ' ') + ': ' + String(f.type) + printDeprecated(f);
167 });
168 return printBlock(fields);
169}
170
171function printBlock(items) {
172 return items.length !== 0 ? ' {\n' + items.join('\n') + '\n}' : '';
173}
174
175function printArgs(options, args) {
176 var indentation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
177
178 if (args.length === 0) {
179 return '';
180 } // If every arg does not have a description, print them on one line.
181
182
183 if (args.every(function (arg) {
184 return !arg.description;
185 })) {
186 return '(' + args.map(printInputValue).join(', ') + ')';
187 }
188
189 return '(\n' + args.map(function (arg, i) {
190 return printDescription(options, arg, ' ' + indentation, !i) + ' ' + indentation + printInputValue(arg);
191 }).join('\n') + '\n' + indentation + ')';
192}
193
194function printInputValue(arg) {
195 var defaultAST = astFromValue(arg.defaultValue, arg.type);
196 var argDecl = arg.name + ': ' + String(arg.type);
197
198 if (defaultAST) {
199 argDecl += " = ".concat(print(defaultAST));
200 }
201
202 return argDecl;
203}
204
205function printDirective(directive, options) {
206 return printDescription(options, directive) + 'directive @' + directive.name + printArgs(options, directive.args) + (directive.isRepeatable ? ' repeatable' : '') + ' on ' + directive.locations.join(' | ');
207}
208
209function printDeprecated(fieldOrEnumVal) {
210 if (!fieldOrEnumVal.isDeprecated) {
211 return '';
212 }
213
214 var reason = fieldOrEnumVal.deprecationReason;
215 var reasonAST = astFromValue(reason, GraphQLString);
216
217 if (reasonAST && reason !== '' && reason !== DEFAULT_DEPRECATION_REASON) {
218 return ' @deprecated(reason: ' + print(reasonAST) + ')';
219 }
220
221 return ' @deprecated';
222}
223
224function printDescription(options, def) {
225 var indentation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
226 var firstInBlock = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
227
228 if (!def.description) {
229 return '';
230 }
231
232 var lines = descriptionLines(def.description, 120 - indentation.length);
233
234 if (options && options.commentDescriptions) {
235 return printDescriptionWithComments(lines, indentation, firstInBlock);
236 }
237
238 var text = lines.join('\n');
239 var preferMultipleLines = text.length > 70;
240 var blockString = printBlockString(text, '', preferMultipleLines);
241 var prefix = indentation && !firstInBlock ? '\n' + indentation : indentation;
242 return prefix + blockString.replace(/\n/g, '\n' + indentation) + '\n';
243}
244
245function printDescriptionWithComments(lines, indentation, firstInBlock) {
246 var description = indentation && !firstInBlock ? '\n' : '';
247
248 for (var _i2 = 0; _i2 < lines.length; _i2++) {
249 var line = lines[_i2];
250
251 if (line === '') {
252 description += indentation + '#\n';
253 } else {
254 description += indentation + '# ' + line + '\n';
255 }
256 }
257
258 return description;
259}
260
261function descriptionLines(description, maxLen) {
262 var rawLines = description.split('\n');
263 return flatMap(rawLines, function (line) {
264 if (line.length < maxLen + 5) {
265 return line;
266 } // For > 120 character long lines, cut at space boundaries into sublines
267 // of ~80 chars.
268
269
270 return breakLine(line, maxLen);
271 });
272}
273
274function breakLine(line, maxLen) {
275 var parts = line.split(new RegExp("((?: |^).{15,".concat(maxLen - 40, "}(?= |$))")));
276
277 if (parts.length < 4) {
278 return [line];
279 }
280
281 var sublines = [parts[0] + parts[1] + parts[2]];
282
283 for (var i = 3; i < parts.length; i += 2) {
284 sublines.push(parts[i].slice(1) + parts[i + 1]);
285 }
286
287 return sublines;
288}