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
16const nodes: {
17 [NodeT in ASTNode as NodeT['kind']]?: (node: NodeT) => string;
18} = {
19 OperationDefinition(node) {
20 if (
21 node.operation === 'query' &&
22 !node.name &&
23 !hasItems(node.variableDefinitions) &&
24 !hasItems(node.directives)
25 ) {
26 return nodes.SelectionSet!(node.selectionSet);
27 }
28 let out: string = 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(nodes.VariableDefinition!).join(', ') + ')';
33 }
34 if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
35 return out + ' ' + nodes.SelectionSet!(node.selectionSet);
36 },
37 VariableDefinition(node) {
38 let out = nodes.Variable!(node.variable) + ': ' + print(node.type);
39 if (node.defaultValue) out += ' = ' + print(node.defaultValue);
40 if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
41 return out;
42 },
43 Field(node) {
44 let out = (node.alias ? node.alias.value + ': ' : '') + node.name.value;
45 if (hasItems(node.arguments)) {
46 const args = node.arguments.map(nodes.Argument!);
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(nodes.Directive!).join(' ');
54 return node.selectionSet ? out + ' ' + nodes.SelectionSet!(node.selectionSet) : out;
55 },
56 StringValue(node) {
57 return node.block ? printBlockString(node.value) : printString(node.value);
58 },
59 BooleanValue(node) {
60 return '' + node.value;
61 },
62 NullValue(_node) {
63 return 'null';
64 },
65 IntValue(node) {
66 return node.value;
67 },
68 FloatValue(node) {
69 return node.value;
70 },
71 EnumValue(node) {
72 return node.value;
73 },
74 Name(node) {
75 return node.value;
76 },
77 Variable(node) {
78 return '$' + node.name.value;
79 },
80 ListValue(node) {
81 return '[' + node.values.map(print).join(', ') + ']';
82 },
83 ObjectValue(node) {
84 return '{' + node.fields.map(nodes.ObjectField!).join(', ') + '}';
85 },
86 ObjectField(node) {
87 return node.name.value + ': ' + print(node.value);
88 },
89 Document(node) {
90 return hasItems(node.definitions) ? node.definitions.map(print).join('\n\n') : '';
91 },
92 SelectionSet(node) {
93 return '{\n ' + node.selections.map(print).join('\n').replace(/\n/g, '\n ') + '\n}';
94 },
95 Argument(node) {
96 return node.name.value + ': ' + print(node.value);
97 },
98 FragmentSpread(node) {
99 let out = '...' + node.name.value;
100 if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
101 return out;
102 },
103 InlineFragment(node) {
104 let out = '...';
105 if (node.typeCondition) out += ' on ' + node.typeCondition.name.value;
106 if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
107 return out + ' ' + print(node.selectionSet);
108 },
109 FragmentDefinition(node) {
110 let out = 'fragment ' + node.name.value;
111 out += ' on ' + node.typeCondition.name.value;
112 if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' ');
113 return out + ' ' + print(node.selectionSet);
114 },
115 Directive(node) {
116 let out = '@' + node.name.value;
117 if (hasItems(node.arguments)) out += '(' + node.arguments.map(nodes.Argument!).join(', ') + ')';
118 return out;
119 },
120 NamedType(node) {
121 return node.name.value;
122 },
123 ListType(node) {
124 return '[' + print(node.type) + ']';
125 },
126 NonNullType(node) {
127 return print(node.type) + '!';
128 },
129};
130
131export function print(node: ASTNode): string {
132 return nodes[node.kind] ? (nodes as any)[node.kind]!(node) : '';
133}