import { visit } from './visitor'; import { printBlockString } from './blockString'; /** * Converts an AST into a string, using one set of reasonable * formatting rules. */ export function print(ast) { return visit(ast, { leave: printDocASTReducer }); } // TODO: provide better type coverage in future var printDocASTReducer = { Name: function Name(node) { return node.value; }, Variable: function Variable(node) { return '$' + node.name; }, // Document Document: function Document(node) { return join(node.definitions, '\n\n') + '\n'; }, OperationDefinition: function OperationDefinition(node) { var op = node.operation; var name = node.name; var varDefs = wrap('(', join(node.variableDefinitions, ', '), ')'); var directives = join(node.directives, ' '); var selectionSet = node.selectionSet; // Anonymous queries with no directives or variable definitions can use // the query short form. return !name && !directives && !varDefs && op === 'query' ? selectionSet : join([op, join([name, varDefs]), directives, selectionSet], ' '); }, VariableDefinition: function VariableDefinition(_ref) { var variable = _ref.variable, type = _ref.type, defaultValue = _ref.defaultValue, directives = _ref.directives; return variable + ': ' + type + wrap(' = ', defaultValue) + wrap(' ', join(directives, ' ')); }, SelectionSet: function SelectionSet(_ref2) { var selections = _ref2.selections; return block(selections); }, Field: function Field(_ref3) { var alias = _ref3.alias, name = _ref3.name, args = _ref3.arguments, directives = _ref3.directives, selectionSet = _ref3.selectionSet; return join([wrap('', alias, ': ') + name + wrap('(', join(args, ', '), ')'), join(directives, ' '), selectionSet], ' '); }, Argument: function Argument(_ref4) { var name = _ref4.name, value = _ref4.value; return name + ': ' + value; }, // Fragments FragmentSpread: function FragmentSpread(_ref5) { var name = _ref5.name, directives = _ref5.directives; return '...' + name + wrap(' ', join(directives, ' ')); }, InlineFragment: function InlineFragment(_ref6) { var typeCondition = _ref6.typeCondition, directives = _ref6.directives, selectionSet = _ref6.selectionSet; return join(['...', wrap('on ', typeCondition), join(directives, ' '), selectionSet], ' '); }, FragmentDefinition: function FragmentDefinition(_ref7) { var name = _ref7.name, typeCondition = _ref7.typeCondition, variableDefinitions = _ref7.variableDefinitions, directives = _ref7.directives, selectionSet = _ref7.selectionSet; return (// Note: fragment variable definitions are experimental and may be changed // or removed in the future. "fragment ".concat(name).concat(wrap('(', join(variableDefinitions, ', '), ')'), " ") + "on ".concat(typeCondition, " ").concat(wrap('', join(directives, ' '), ' ')) + selectionSet ); }, // Value IntValue: function IntValue(_ref8) { var value = _ref8.value; return value; }, FloatValue: function FloatValue(_ref9) { var value = _ref9.value; return value; }, StringValue: function StringValue(_ref10, key) { var value = _ref10.value, isBlockString = _ref10.block; return isBlockString ? printBlockString(value, key === 'description' ? '' : ' ') : JSON.stringify(value); }, BooleanValue: function BooleanValue(_ref11) { var value = _ref11.value; return value ? 'true' : 'false'; }, NullValue: function NullValue() { return 'null'; }, EnumValue: function EnumValue(_ref12) { var value = _ref12.value; return value; }, ListValue: function ListValue(_ref13) { var values = _ref13.values; return '[' + join(values, ', ') + ']'; }, ObjectValue: function ObjectValue(_ref14) { var fields = _ref14.fields; return '{' + join(fields, ', ') + '}'; }, ObjectField: function ObjectField(_ref15) { var name = _ref15.name, value = _ref15.value; return name + ': ' + value; }, // Directive Directive: function Directive(_ref16) { var name = _ref16.name, args = _ref16.arguments; return '@' + name + wrap('(', join(args, ', '), ')'); }, // Type NamedType: function NamedType(_ref17) { var name = _ref17.name; return name; }, ListType: function ListType(_ref18) { var type = _ref18.type; return '[' + type + ']'; }, NonNullType: function NonNullType(_ref19) { var type = _ref19.type; return type + '!'; }, // Type System Definitions SchemaDefinition: function SchemaDefinition(_ref20) { var directives = _ref20.directives, operationTypes = _ref20.operationTypes; return join(['schema', join(directives, ' '), block(operationTypes)], ' '); }, OperationTypeDefinition: function OperationTypeDefinition(_ref21) { var operation = _ref21.operation, type = _ref21.type; return operation + ': ' + type; }, ScalarTypeDefinition: addDescription(function (_ref22) { var name = _ref22.name, directives = _ref22.directives; return join(['scalar', name, join(directives, ' ')], ' '); }), ObjectTypeDefinition: addDescription(function (_ref23) { var name = _ref23.name, interfaces = _ref23.interfaces, directives = _ref23.directives, fields = _ref23.fields; return join(['type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '); }), FieldDefinition: addDescription(function (_ref24) { var name = _ref24.name, args = _ref24.arguments, type = _ref24.type, directives = _ref24.directives; return name + (hasMultilineItems(args) ? wrap('(\n', indent(join(args, '\n')), '\n)') : wrap('(', join(args, ', '), ')')) + ': ' + type + wrap(' ', join(directives, ' ')); }), InputValueDefinition: addDescription(function (_ref25) { var name = _ref25.name, type = _ref25.type, defaultValue = _ref25.defaultValue, directives = _ref25.directives; return join([name + ': ' + type, wrap('= ', defaultValue), join(directives, ' ')], ' '); }), InterfaceTypeDefinition: addDescription(function (_ref26) { var name = _ref26.name, directives = _ref26.directives, fields = _ref26.fields; return join(['interface', name, join(directives, ' '), block(fields)], ' '); }), UnionTypeDefinition: addDescription(function (_ref27) { var name = _ref27.name, directives = _ref27.directives, types = _ref27.types; return join(['union', name, join(directives, ' '), types && types.length !== 0 ? '= ' + join(types, ' | ') : ''], ' '); }), EnumTypeDefinition: addDescription(function (_ref28) { var name = _ref28.name, directives = _ref28.directives, values = _ref28.values; return join(['enum', name, join(directives, ' '), block(values)], ' '); }), EnumValueDefinition: addDescription(function (_ref29) { var name = _ref29.name, directives = _ref29.directives; return join([name, join(directives, ' ')], ' '); }), InputObjectTypeDefinition: addDescription(function (_ref30) { var name = _ref30.name, directives = _ref30.directives, fields = _ref30.fields; return join(['input', name, join(directives, ' '), block(fields)], ' '); }), DirectiveDefinition: addDescription(function (_ref31) { var name = _ref31.name, args = _ref31.arguments, repeatable = _ref31.repeatable, locations = _ref31.locations; return 'directive @' + name + (hasMultilineItems(args) ? wrap('(\n', indent(join(args, '\n')), '\n)') : wrap('(', join(args, ', '), ')')) + (repeatable ? ' repeatable' : '') + ' on ' + join(locations, ' | '); }), SchemaExtension: function SchemaExtension(_ref32) { var directives = _ref32.directives, operationTypes = _ref32.operationTypes; return join(['extend schema', join(directives, ' '), block(operationTypes)], ' '); }, ScalarTypeExtension: function ScalarTypeExtension(_ref33) { var name = _ref33.name, directives = _ref33.directives; return join(['extend scalar', name, join(directives, ' ')], ' '); }, ObjectTypeExtension: function ObjectTypeExtension(_ref34) { var name = _ref34.name, interfaces = _ref34.interfaces, directives = _ref34.directives, fields = _ref34.fields; return join(['extend type', name, wrap('implements ', join(interfaces, ' & ')), join(directives, ' '), block(fields)], ' '); }, InterfaceTypeExtension: function InterfaceTypeExtension(_ref35) { var name = _ref35.name, directives = _ref35.directives, fields = _ref35.fields; return join(['extend interface', name, join(directives, ' '), block(fields)], ' '); }, UnionTypeExtension: function UnionTypeExtension(_ref36) { var name = _ref36.name, directives = _ref36.directives, types = _ref36.types; return join(['extend union', name, join(directives, ' '), types && types.length !== 0 ? '= ' + join(types, ' | ') : ''], ' '); }, EnumTypeExtension: function EnumTypeExtension(_ref37) { var name = _ref37.name, directives = _ref37.directives, values = _ref37.values; return join(['extend enum', name, join(directives, ' '), block(values)], ' '); }, InputObjectTypeExtension: function InputObjectTypeExtension(_ref38) { var name = _ref38.name, directives = _ref38.directives, fields = _ref38.fields; return join(['extend input', name, join(directives, ' '), block(fields)], ' '); } }; function addDescription(cb) { return function (node) { return join([node.description, cb(node)], '\n'); }; } /** * Given maybeArray, print an empty string if it is null or empty, otherwise * print all items together separated by separator if provided */ function join(maybeArray, separator) { return maybeArray ? maybeArray.filter(function (x) { return x; }).join(separator || '') : ''; } /** * Given array, print each item on its own line, wrapped in an * indented "{ }" block. */ function block(array) { return array && array.length !== 0 ? '{\n' + indent(join(array, '\n')) + '\n}' : ''; } /** * If maybeString is not null or empty, then wrap with start and end, otherwise * print an empty string. */ function wrap(start, maybeString, end) { return maybeString ? start + maybeString + (end || '') : ''; } function indent(maybeString) { return maybeString && ' ' + maybeString.replace(/\n/g, '\n '); } function isMultiline(string) { return string.indexOf('\n') !== -1; } function hasMultilineItems(maybeArray) { return maybeArray && maybeArray.some(isMultiline); }