Mirror: The spec-compliant minimum of client-side GraphQL.
1import { Kind } from './kind';
2import { ASTNode } from './ast';
3
4export function printString(string: string) {
5 return JSON.stringify(string);
6}
7
8export function printBlockString(string: string) {
9 return '"""\n' + string.replace(/"""/g, '\\"""') + '\n"""';
10}
11
12const hasItems = <T>(
13 array: ReadonlyArray<T> | undefined | null
14): array is ReadonlyArray<T> => !!(array && array.length);
15
16const MAX_LINE_LENGTH = 80;
17
18export function print(node: ASTNode): string {
19 let out: string;
20 switch (node.kind) {
21 case Kind.OPERATION_DEFINITION:
22 if (node.operation === 'query' && !node.name && !hasItems(node.variableDefinitions) && !hasItems(node.directives)) {
23 return print(node.selectionSet);
24 }
25 out = node.operation;
26 if (node.name)
27 out += ' ' + node.name.value;
28 if (hasItems(node.variableDefinitions)) {
29 if (!node.name) out += ' ';
30 out += '(' + node.variableDefinitions.map(print).join(', ') + ')';
31 }
32 if (hasItems(node.directives))
33 out += ' ' + node.directives.map(print).join(' ');
34 return out + ' ' + print(node.selectionSet);
35
36 case Kind.VARIABLE_DEFINITION:
37 out = print(node.variable) +
38 ': ' +
39 print(node.type);
40 if (node.defaultValue)
41 out += ' = ' + print(node.defaultValue);
42 if (hasItems(node.directives))
43 out += ' ' + node.directives.map(print).join(' ');
44 return out;
45
46 case Kind.FIELD:
47 out = (node.alias ? print(node.alias) + ': ' : '') + node.name.value
48 if (hasItems(node.arguments)) {
49 const args = node.arguments.map(print);
50 const argsLine = out + '(' + args.join(', ') + ')';
51 out = argsLine.length > MAX_LINE_LENGTH
52 ? out + '(\n ' + args.join('\n').replace(/\n/g, '\n ') + '\n)'
53 : argsLine;
54 }
55 if (hasItems(node.directives))
56 out += ' ' + node.directives.map(print).join(' ');
57 return node.selectionSet
58 ? out + ' ' + print(node.selectionSet)
59 : out;
60
61 case Kind.STRING:
62 return node.block
63 ? printBlockString(node.value)
64 : printString(node.value);
65
66 case Kind.BOOLEAN:
67 return '' + node.value;
68
69 case Kind.NULL:
70 return 'null';
71
72 case Kind.INT:
73 case Kind.FLOAT:
74 case Kind.ENUM:
75 case Kind.NAME:
76 return node.value;
77
78 case Kind.LIST:
79 return '[' + node.values.map(print).join(', ') + ']';
80
81 case Kind.OBJECT:
82 return '{' + node.fields.map(print).join(', ') + '}';
83
84 case Kind.OBJECT_FIELD:
85 return node.name.value + ': ' + print(node.value);
86
87 case Kind.VARIABLE:
88 return '$' + node.name.value;
89
90 case Kind.DOCUMENT:
91 return hasItems(node.definitions)
92 ? node.definitions.map(print).join('\n\n')
93 : '';
94
95 case Kind.SELECTION_SET:
96 return '{\n ' +
97 node.selections.map(print).join('\n').replace(/\n/g, '\n ') +
98 '\n}';
99
100 case Kind.ARGUMENT:
101 return node.name.value + ': ' + print(node.value);
102
103 case Kind.FRAGMENT_SPREAD:
104 out = '...' + node.name.value;
105 if (hasItems(node.directives))
106 out += ' ' + node.directives.map(print).join(' ');
107 return out;
108
109 case Kind.INLINE_FRAGMENT:
110 out = '...';
111 if (node.typeCondition)
112 out += ' on ' + node.typeCondition.name.value;
113 if (hasItems(node.directives))
114 out += ' ' + node.directives.map(print).join(' ');
115 return out + ' ' + print(node.selectionSet);
116
117 case Kind.FRAGMENT_DEFINITION:
118 out = 'fragment ' + node.name.value;
119 out += ' on ' + node.typeCondition.name.value;
120 if (hasItems(node.directives))
121 out += ' ' + node.directives.map(print).join(' ');
122 return out + ' ' + print(node.selectionSet);
123
124 case Kind.DIRECTIVE:
125 out = '@' + node.name.value;
126 if (hasItems(node.arguments))
127 out += '(' + node.arguments.map(print).join(', ') + ')';
128 return out;
129
130 case Kind.NAMED_TYPE:
131 return node.name.value;
132
133 case Kind.LIST_TYPE:
134 return '[' + print(node.type) + ']';
135
136 case Kind.NON_NULL_TYPE:
137 return print(node.type) + '!';
138 }
139}