UNPKG

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