Mirror: The spec-compliant minimum of client-side GraphQL.
1import { ASTNode } from './ast';
2
3export function printString(string: string) {
4 return JSON.stringify(string);
5}
6
7export function printBlockString(string: string) {
8 return '"""\n' + string.replace(/"""/g, '\\"""') + '\n"""';
9}
10
11const hasItems = <T>(array: ReadonlyArray<T> | undefined | null): array is ReadonlyArray<T> =>
12 !!(array && array.length);
13
14const MAX_LINE_LENGTH = 80;
15
16export function print(node: ASTNode): string {
17 let out: string;
18 switch (node.kind) {
19 case 'OperationDefinition':
20 if (
21 node.operation === 'query' &&
22 !node.name &&
23 !hasItems(node.variableDefinitions) &&
24 !hasItems(node.directives)
25 ) {
26 return print(node.selectionSet);
27 }
28 out = node.operation;
29 if (node.name) out += ' ' + node.name.value;
30 if (hasItems(node.variableDefinitions)) {
31 if (!node.name) out += ' ';
32 out += '(' + node.variableDefinitions.map(print).join(', ') + ')';
33 }
34 if (hasItems(node.directives)) out += ' ' + node.directives.map(print).join(' ');
35 return out + ' ' + print(node.selectionSet);
36
37 case 'VariableDefinition':
38 out = print(node.variable) + ': ' + print(node.type);
39 if (node.defaultValue) out += ' = ' + print(node.defaultValue);
40 if (hasItems(node.directives)) out += ' ' + node.directives.map(print).join(' ');
41 return out;
42
43 case 'Field':
44 out = (node.alias ? print(node.alias) + ': ' : '') + node.name.value;
45 if (hasItems(node.arguments)) {
46 const args = node.arguments.map(print);
47 const argsLine = out + '(' + args.join(', ') + ')';
48 out =
49 argsLine.length > MAX_LINE_LENGTH
50 ? out + '(\n ' + args.join('\n').replace(/\n/g, '\n ') + '\n)'
51 : argsLine;
52 }
53 if (hasItems(node.directives)) out += ' ' + node.directives.map(print).join(' ');
54 return node.selectionSet ? out + ' ' + print(node.selectionSet) : out;
55
56 case 'StringValue':
57 return node.block ? printBlockString(node.value) : printString(node.value);
58
59 case 'BooleanValue':
60 return '' + node.value;
61
62 case 'NullValue':
63 return 'null';
64
65 case 'IntValue':
66 case 'FloatValue':
67 case 'EnumValue':
68 case 'Name':
69 return node.value;
70
71 case 'ListValue':
72 return '[' + node.values.map(print).join(', ') + ']';
73
74 case 'ObjectValue':
75 return '{' + node.fields.map(print).join(', ') + '}';
76
77 case 'ObjectField':
78 return node.name.value + ': ' + print(node.value);
79
80 case 'Variable':
81 return '$' + node.name.value;
82
83 case 'Document':
84 return hasItems(node.definitions) ? node.definitions.map(print).join('\n\n') : '';
85
86 case 'SelectionSet':
87 return '{\n ' + node.selections.map(print).join('\n').replace(/\n/g, '\n ') + '\n}';
88
89 case 'Argument':
90 return node.name.value + ': ' + print(node.value);
91
92 case 'FragmentSpread':
93 out = '...' + node.name.value;
94 if (hasItems(node.directives)) out += ' ' + node.directives.map(print).join(' ');
95 return out;
96
97 case 'InlineFragment':
98 out = '...';
99 if (node.typeCondition) out += ' on ' + node.typeCondition.name.value;
100 if (hasItems(node.directives)) out += ' ' + node.directives.map(print).join(' ');
101 return out + ' ' + print(node.selectionSet);
102
103 case 'FragmentDefinition':
104 out = 'fragment ' + node.name.value;
105 out += ' on ' + node.typeCondition.name.value;
106 if (hasItems(node.directives)) out += ' ' + node.directives.map(print).join(' ');
107 return out + ' ' + print(node.selectionSet);
108
109 case 'Directive':
110 out = '@' + node.name.value;
111 if (hasItems(node.arguments)) out += '(' + node.arguments.map(print).join(', ') + ')';
112 return out;
113
114 case 'NamedType':
115 return node.name.value;
116
117 case 'ListType':
118 return '[' + print(node.type) + ']';
119
120 case 'NonNullType':
121 return print(node.type) + '!';
122
123 default:
124 return '';
125 }
126}