|
1 |
| -import type { ASTNode } from './ast'; |
| 1 | +import type { |
| 2 | + ASTNode, |
| 3 | + NameNode, |
| 4 | + DocumentNode, |
| 5 | + VariableNode, |
| 6 | + SelectionSetNode, |
| 7 | + FieldNode, |
| 8 | + ArgumentNode, |
| 9 | + FragmentSpreadNode, |
| 10 | + InlineFragmentNode, |
| 11 | + VariableDefinitionNode, |
| 12 | + OperationDefinitionNode, |
| 13 | + FragmentDefinitionNode, |
| 14 | + IntValueNode, |
| 15 | + FloatValueNode, |
| 16 | + StringValueNode, |
| 17 | + BooleanValueNode, |
| 18 | + NullValueNode, |
| 19 | + EnumValueNode, |
| 20 | + ListValueNode, |
| 21 | + ObjectValueNode, |
| 22 | + ObjectFieldNode, |
| 23 | + DirectiveNode, |
| 24 | + NamedTypeNode, |
| 25 | + ListTypeNode, |
| 26 | + NonNullTypeNode, |
| 27 | +} from './ast'; |
2 | 28 |
|
3 |
| -export function printString(string: string) { |
| 29 | +function mapJoin<T>(value: readonly T[], joiner: string, mapper: (value: T) => string): string { |
| 30 | + let out = ''; |
| 31 | + for (let index = 0; index < value.length; index++) { |
| 32 | + if (index) out += joiner; |
| 33 | + out += mapper(value[index]); |
| 34 | + } |
| 35 | + return out; |
| 36 | +} |
| 37 | + |
| 38 | +function printString(string: string) { |
4 | 39 | return JSON.stringify(string);
|
5 | 40 | }
|
6 | 41 |
|
7 |
| -export function printBlockString(string: string) { |
| 42 | +function printBlockString(string: string) { |
8 | 43 | return '"""\n' + string.replace(/"""/g, '\\"""') + '\n"""';
|
9 | 44 | }
|
10 | 45 |
|
11 |
| -const hasItems = <T>(array: ReadonlyArray<T> | undefined | null): array is ReadonlyArray<T> => |
12 |
| - !!(array && array.length); |
13 |
| - |
14 | 46 | const MAX_LINE_LENGTH = 80;
|
15 | 47 |
|
16 |
| -const 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 |
| - } |
| 48 | +let LF = '\n'; |
| 49 | + |
| 50 | +const nodes = { |
| 51 | + OperationDefinition(node: OperationDefinitionNode): string { |
28 | 52 | let out: string = node.operation;
|
29 | 53 | if (node.name) out += ' ' + node.name.value;
|
30 |
| - if (hasItems(node.variableDefinitions)) { |
| 54 | + if (node.variableDefinitions && node.variableDefinitions.length) { |
31 | 55 | if (!node.name) out += ' ';
|
32 |
| - out += '(' + node.variableDefinitions.map(nodes.VariableDefinition!).join(', ') + ')'; |
| 56 | + out += '(' + mapJoin(node.variableDefinitions, ', ', nodes.VariableDefinition) + ')'; |
33 | 57 | }
|
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(' '); |
| 58 | + if (node.directives && node.directives.length) |
| 59 | + out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); |
| 60 | + return out !== 'query' |
| 61 | + ? out + ' ' + nodes.SelectionSet(node.selectionSet) |
| 62 | + : nodes.SelectionSet(node.selectionSet); |
| 63 | + }, |
| 64 | + VariableDefinition(node: VariableDefinitionNode): string { |
| 65 | + let out = nodes.Variable!(node.variable) + ': ' + _print(node.type); |
| 66 | + if (node.defaultValue) out += ' = ' + _print(node.defaultValue); |
| 67 | + if (node.directives && node.directives.length) |
| 68 | + out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); |
41 | 69 | return out;
|
42 | 70 | },
|
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; |
| 71 | + Field(node: FieldNode): string { |
| 72 | + let out = node.alias ? node.alias.value + ': ' + node.name.value : node.name.value; |
| 73 | + if (node.arguments && node.arguments.length) { |
| 74 | + const args = mapJoin(node.arguments, ', ', nodes.Argument); |
| 75 | + if (out.length + args.length + 2 > MAX_LINE_LENGTH) { |
| 76 | + out += |
| 77 | + '(' + |
| 78 | + (LF += ' ') + |
| 79 | + mapJoin(node.arguments, LF, nodes.Argument) + |
| 80 | + (LF = LF.slice(0, -2)) + |
| 81 | + ')'; |
| 82 | + } else { |
| 83 | + out += '(' + args + ')'; |
| 84 | + } |
52 | 85 | }
|
53 |
| - if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' '); |
54 |
| - return node.selectionSet ? out + ' ' + nodes.SelectionSet!(node.selectionSet) : out; |
| 86 | + if (node.directives && node.directives.length) |
| 87 | + out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); |
| 88 | + if (node.selectionSet) out += ' ' + nodes.SelectionSet(node.selectionSet); |
| 89 | + return out; |
55 | 90 | },
|
56 |
| - StringValue(node) { |
57 |
| - return node.block ? printBlockString(node.value) : printString(node.value); |
| 91 | + StringValue(node: StringValueNode): string { |
| 92 | + if (node.block) { |
| 93 | + return printBlockString(node.value).replace(/\n/g, LF); |
| 94 | + } else { |
| 95 | + return printString(node.value); |
| 96 | + } |
58 | 97 | },
|
59 |
| - BooleanValue(node) { |
| 98 | + BooleanValue(node: BooleanValueNode): string { |
60 | 99 | return '' + node.value;
|
61 | 100 | },
|
62 |
| - NullValue(_node) { |
| 101 | + NullValue(_node: NullValueNode): string { |
63 | 102 | return 'null';
|
64 | 103 | },
|
65 |
| - IntValue(node) { |
| 104 | + IntValue(node: IntValueNode): string { |
66 | 105 | return node.value;
|
67 | 106 | },
|
68 |
| - FloatValue(node) { |
| 107 | + FloatValue(node: FloatValueNode): string { |
69 | 108 | return node.value;
|
70 | 109 | },
|
71 |
| - EnumValue(node) { |
| 110 | + EnumValue(node: EnumValueNode): string { |
72 | 111 | return node.value;
|
73 | 112 | },
|
74 |
| - Name(node) { |
| 113 | + Name(node: NameNode): string { |
75 | 114 | return node.value;
|
76 | 115 | },
|
77 |
| - Variable(node) { |
| 116 | + Variable(node: VariableNode): string { |
78 | 117 | return '$' + node.name.value;
|
79 | 118 | },
|
80 |
| - ListValue(node) { |
81 |
| - return '[' + node.values.map(print).join(', ') + ']'; |
| 119 | + ListValue(node: ListValueNode): string { |
| 120 | + return '[' + mapJoin(node.values, ', ', _print) + ']'; |
82 | 121 | },
|
83 |
| - ObjectValue(node) { |
84 |
| - return '{' + node.fields.map(nodes.ObjectField!).join(', ') + '}'; |
| 122 | + ObjectValue(node: ObjectValueNode): string { |
| 123 | + return '{' + mapJoin(node.fields, ', ', nodes.ObjectField) + '}'; |
85 | 124 | },
|
86 |
| - ObjectField(node) { |
87 |
| - return node.name.value + ': ' + print(node.value); |
| 125 | + ObjectField(node: ObjectFieldNode): string { |
| 126 | + return node.name.value + ': ' + _print(node.value); |
88 | 127 | },
|
89 |
| - Document(node) { |
90 |
| - return hasItems(node.definitions) ? node.definitions.map(print).join('\n\n') : ''; |
| 128 | + Document(node: DocumentNode): string { |
| 129 | + if (!node.definitions || !node.definitions.length) return ''; |
| 130 | + return mapJoin(node.definitions, '\n\n', _print); |
91 | 131 | },
|
92 |
| - SelectionSet(node) { |
93 |
| - return '{\n ' + node.selections.map(print).join('\n').replace(/\n/g, '\n ') + '\n}'; |
| 132 | + SelectionSet(node: SelectionSetNode): string { |
| 133 | + return '{' + (LF += ' ') + mapJoin(node.selections, LF, _print) + (LF = LF.slice(0, -2)) + '}'; |
94 | 134 | },
|
95 |
| - Argument(node) { |
96 |
| - return node.name.value + ': ' + print(node.value); |
| 135 | + Argument(node: ArgumentNode): string { |
| 136 | + return node.name.value + ': ' + _print(node.value); |
97 | 137 | },
|
98 |
| - FragmentSpread(node) { |
| 138 | + FragmentSpread(node: FragmentSpreadNode): string { |
99 | 139 | let out = '...' + node.name.value;
|
100 |
| - if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' '); |
| 140 | + if (node.directives && node.directives.length) |
| 141 | + out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); |
101 | 142 | return out;
|
102 | 143 | },
|
103 |
| - InlineFragment(node) { |
| 144 | + InlineFragment(node: InlineFragmentNode): string { |
104 | 145 | let out = '...';
|
105 | 146 | 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); |
| 147 | + if (node.directives && node.directives.length) |
| 148 | + out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); |
| 149 | + out += ' ' + nodes.SelectionSet(node.selectionSet); |
| 150 | + return out; |
108 | 151 | },
|
109 |
| - FragmentDefinition(node) { |
| 152 | + FragmentDefinition(node: FragmentDefinitionNode): string { |
110 | 153 | let out = 'fragment ' + node.name.value;
|
111 | 154 | out += ' on ' + node.typeCondition.name.value;
|
112 |
| - if (hasItems(node.directives)) out += ' ' + node.directives.map(nodes.Directive!).join(' '); |
113 |
| - return out + ' ' + print(node.selectionSet); |
| 155 | + if (node.directives && node.directives.length) |
| 156 | + out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); |
| 157 | + return out + ' ' + nodes.SelectionSet(node.selectionSet); |
114 | 158 | },
|
115 |
| - Directive(node) { |
| 159 | + Directive(node: DirectiveNode): string { |
116 | 160 | let out = '@' + node.name.value;
|
117 |
| - if (hasItems(node.arguments)) out += '(' + node.arguments.map(nodes.Argument!).join(', ') + ')'; |
| 161 | + if (node.arguments && node.arguments.length) |
| 162 | + out += '(' + mapJoin(node.arguments, ', ', nodes.Argument) + ')'; |
118 | 163 | return out;
|
119 | 164 | },
|
120 |
| - NamedType(node) { |
| 165 | + NamedType(node: NamedTypeNode): string { |
121 | 166 | return node.name.value;
|
122 | 167 | },
|
123 |
| - ListType(node) { |
124 |
| - return '[' + print(node.type) + ']'; |
| 168 | + ListType(node: ListTypeNode): string { |
| 169 | + return '[' + _print(node.type) + ']'; |
125 | 170 | },
|
126 |
| - NonNullType(node) { |
127 |
| - return print(node.type) + '!'; |
| 171 | + NonNullType(node: NonNullTypeNode): string { |
| 172 | + return _print(node.type) + '!'; |
128 | 173 | },
|
129 |
| -}; |
| 174 | +} as const; |
130 | 175 |
|
131 |
| -export function print(node: ASTNode): string { |
132 |
| - return nodes[node.kind] ? (nodes as any)[node.kind]!(node) : ''; |
| 176 | +const _print = (node: ASTNode): string => nodes[node.kind](node); |
| 177 | + |
| 178 | +function print(node: ASTNode): string { |
| 179 | + LF = '\n'; |
| 180 | + return nodes[node.kind] ? nodes[node.kind](node) : ''; |
133 | 181 | }
|
| 182 | + |
| 183 | +export { print, printString, printBlockString }; |
0 commit comments