1 | import { inspect } from '../jsutils/inspect.mjs';
|
2 | import { invariant } from '../jsutils/invariant.mjs';
|
3 | import { isPrintableAsBlockString } from '../language/blockString.mjs';
|
4 | import { Kind } from '../language/kinds.mjs';
|
5 | import { print } from '../language/printer.mjs';
|
6 | import {
|
7 | isEnumType,
|
8 | isInputObjectType,
|
9 | isInterfaceType,
|
10 | isObjectType,
|
11 | isScalarType,
|
12 | isUnionType,
|
13 | } from '../type/definition.mjs';
|
14 | import {
|
15 | DEFAULT_DEPRECATION_REASON,
|
16 | isSpecifiedDirective,
|
17 | } from '../type/directives.mjs';
|
18 | import { isIntrospectionType } from '../type/introspection.mjs';
|
19 | import { isSpecifiedScalarType } from '../type/scalars.mjs';
|
20 | import { astFromValue } from './astFromValue.mjs';
|
21 | export function printSchema(schema) {
|
22 | return printFilteredSchema(
|
23 | schema,
|
24 | (n) => !isSpecifiedDirective(n),
|
25 | isDefinedType,
|
26 | );
|
27 | }
|
28 | export function printIntrospectionSchema(schema) {
|
29 | return printFilteredSchema(schema, isSpecifiedDirective, isIntrospectionType);
|
30 | }
|
31 |
|
32 | function isDefinedType(type) {
|
33 | return !isSpecifiedScalarType(type) && !isIntrospectionType(type);
|
34 | }
|
35 |
|
36 | function 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 |
|
48 | function 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 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | function 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 |
|
112 | export 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 |
|
137 |
|
138 |
|
139 | false || invariant(false, 'Unexpected type: ' + inspect(type));
|
140 | }
|
141 |
|
142 | function printScalar(type) {
|
143 | return (
|
144 | printDescription(type) + `scalar ${type.name}` + printSpecifiedByURL(type)
|
145 | );
|
146 | }
|
147 |
|
148 | function printImplementedInterfaces(type) {
|
149 | const interfaces = type.getInterfaces();
|
150 | return interfaces.length
|
151 | ? ' implements ' + interfaces.map((i) => i.name).join(' & ')
|
152 | : '';
|
153 | }
|
154 |
|
155 | function printObject(type) {
|
156 | return (
|
157 | printDescription(type) +
|
158 | `type ${type.name}` +
|
159 | printImplementedInterfaces(type) +
|
160 | printFields(type)
|
161 | );
|
162 | }
|
163 |
|
164 | function printInterface(type) {
|
165 | return (
|
166 | printDescription(type) +
|
167 | `interface ${type.name}` +
|
168 | printImplementedInterfaces(type) +
|
169 | printFields(type)
|
170 | );
|
171 | }
|
172 |
|
173 | function printUnion(type) {
|
174 | const types = type.getTypes();
|
175 | const possibleTypes = types.length ? ' = ' + types.join(' | ') : '';
|
176 | return printDescription(type) + 'union ' + type.name + possibleTypes;
|
177 | }
|
178 |
|
179 | function 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 |
|
192 | function 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 |
|
199 | function 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 |
|
213 | function printBlock(items) {
|
214 | return items.length !== 0 ? ' {\n' + items.join('\n') + '\n}' : '';
|
215 | }
|
216 |
|
217 | function printArgs(args, indentation = '') {
|
218 | if (args.length === 0) {
|
219 | return '';
|
220 | }
|
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 |
|
243 | function 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 |
|
254 | function 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 |
|
266 | function 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 |
|
282 | function 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 |
|
294 | function 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 | }
|