Mirror: The spec-compliant minimum of client-side GraphQL.
at main 6.2 kB view raw
1import 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'; 28 29function 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 38function printString(string: string): string { 39 return JSON.stringify(string); 40} 41 42function printBlockString(string: string): string { 43 return '"""\n' + string.replace(/"""/g, '\\"""') + '\n"""'; 44} 45 46const MAX_LINE_LENGTH = 80; 47 48let LF = '\n'; 49 50const nodes = { 51 OperationDefinition(node: OperationDefinitionNode): string { 52 let out: string = ''; 53 if (node.description) { 54 out += nodes.StringValue(node.description) + '\n'; 55 } 56 out += node.operation; 57 if (node.name) out += ' ' + node.name.value; 58 if (node.variableDefinitions && node.variableDefinitions.length) { 59 if (!node.name) out += ' '; 60 out += '(' + mapJoin(node.variableDefinitions, ', ', nodes.VariableDefinition) + ')'; 61 } 62 if (node.directives && node.directives.length) 63 out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); 64 const selectionSet = nodes.SelectionSet(node.selectionSet); 65 return out !== 'query' ? out + ' ' + selectionSet : selectionSet; 66 }, 67 VariableDefinition(node: VariableDefinitionNode): string { 68 let out = ''; 69 if (node.description) { 70 out += nodes.StringValue(node.description) + ' '; 71 } 72 out += nodes.Variable!(node.variable) + ': ' + _print(node.type); 73 if (node.defaultValue) out += ' = ' + _print(node.defaultValue); 74 if (node.directives && node.directives.length) 75 out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); 76 return out; 77 }, 78 Field(node: FieldNode): string { 79 let out = node.alias ? node.alias.value + ': ' + node.name.value : node.name.value; 80 if (node.arguments && node.arguments.length) { 81 const args = mapJoin(node.arguments, ', ', nodes.Argument); 82 if (out.length + args.length + 2 > MAX_LINE_LENGTH) { 83 out += 84 '(' + 85 (LF += ' ') + 86 mapJoin(node.arguments, LF, nodes.Argument) + 87 (LF = LF.slice(0, -2)) + 88 ')'; 89 } else { 90 out += '(' + args + ')'; 91 } 92 } 93 if (node.directives && node.directives.length) 94 out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); 95 if (node.selectionSet && node.selectionSet.selections.length) { 96 out += ' ' + nodes.SelectionSet(node.selectionSet); 97 } 98 return out; 99 }, 100 StringValue(node: StringValueNode): string { 101 if (node.block) { 102 return printBlockString(node.value).replace(/\n/g, LF); 103 } else { 104 return printString(node.value); 105 } 106 }, 107 BooleanValue(node: BooleanValueNode): string { 108 return '' + node.value; 109 }, 110 NullValue(_node: NullValueNode): string { 111 return 'null'; 112 }, 113 IntValue(node: IntValueNode): string { 114 return node.value; 115 }, 116 FloatValue(node: FloatValueNode): string { 117 return node.value; 118 }, 119 EnumValue(node: EnumValueNode): string { 120 return node.value; 121 }, 122 Name(node: NameNode): string { 123 return node.value; 124 }, 125 Variable(node: VariableNode): string { 126 return '$' + node.name.value; 127 }, 128 ListValue(node: ListValueNode): string { 129 return '[' + mapJoin(node.values, ', ', _print) + ']'; 130 }, 131 ObjectValue(node: ObjectValueNode): string { 132 return '{' + mapJoin(node.fields, ', ', nodes.ObjectField) + '}'; 133 }, 134 ObjectField(node: ObjectFieldNode): string { 135 return node.name.value + ': ' + _print(node.value); 136 }, 137 Document(node: DocumentNode): string { 138 if (!node.definitions || !node.definitions.length) return ''; 139 return mapJoin(node.definitions, '\n\n', _print); 140 }, 141 SelectionSet(node: SelectionSetNode): string { 142 return '{' + (LF += ' ') + mapJoin(node.selections, LF, _print) + (LF = LF.slice(0, -2)) + '}'; 143 }, 144 Argument(node: ArgumentNode): string { 145 return node.name.value + ': ' + _print(node.value); 146 }, 147 FragmentSpread(node: FragmentSpreadNode): string { 148 let out = '...' + node.name.value; 149 if (node.directives && node.directives.length) 150 out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); 151 return out; 152 }, 153 InlineFragment(node: InlineFragmentNode): string { 154 let out = '...'; 155 if (node.typeCondition) out += ' on ' + node.typeCondition.name.value; 156 if (node.directives && node.directives.length) 157 out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); 158 out += ' ' + nodes.SelectionSet(node.selectionSet); 159 return out; 160 }, 161 FragmentDefinition(node: FragmentDefinitionNode): string { 162 let out = ''; 163 if (node.description) { 164 out += nodes.StringValue(node.description) + '\n'; 165 } 166 out += 'fragment ' + node.name.value; 167 out += ' on ' + node.typeCondition.name.value; 168 if (node.directives && node.directives.length) 169 out += ' ' + mapJoin(node.directives, ' ', nodes.Directive); 170 return out + ' ' + nodes.SelectionSet(node.selectionSet); 171 }, 172 Directive(node: DirectiveNode): string { 173 let out = '@' + node.name.value; 174 if (node.arguments && node.arguments.length) 175 out += '(' + mapJoin(node.arguments, ', ', nodes.Argument) + ')'; 176 return out; 177 }, 178 NamedType(node: NamedTypeNode): string { 179 return node.name.value; 180 }, 181 ListType(node: ListTypeNode): string { 182 return '[' + _print(node.type) + ']'; 183 }, 184 NonNullType(node: NonNullTypeNode): string { 185 return _print(node.type) + '!'; 186 }, 187} as const; 188 189const _print = (node: ASTNode): string => nodes[node.kind](node); 190 191function print(node: ASTNode): string { 192 LF = '\n'; 193 return nodes[node.kind] ? nodes[node.kind](node) : ''; 194} 195 196export { print, printString, printBlockString };