import type { Maybe } from '../jsutils/Maybe' ; import type { ASTNode } from './ast' ; import { printBlockString } from './blockString' ; import { printString } from './printString' ; import type { ASTReducer } from './visitor' ; import { visit } from './visitor' ; /**
 * Converts an AST into a string, using one set of reasonable
 * formatting rules.
 */ export function print ( ast ) { return visit ( ast , printDocASTReducer ) ; } const MAX_LINE_LENGTH = 80 ; const printDocASTReducer = { Name : { leave : ( node ) => node . value } , Variable : { leave : ( node ) => '$' + node . name } , Document : { leave : ( node ) => join ( node . definitions , '\n\n' ) , } , OperationDefinition : { leave ( node ) { const varDefs = wrap ( '(' , join ( node . variableDefinitions , ', ' ) , ')' ) ; const prefix = join ( [ node . operation , join ( [ node . name , varDefs ] ) , join ( node . directives , ' ' ) , ] , ' ' ) ; return ( prefix === 'query' ? '' : prefix + ' ' ) + node . selectionSet ; } , } , VariableDefinition : { leave : ( { variable , type , defaultValue , directives } ) => variable + ': ' + type + wrap ( ' = ' , defaultValue ) + wrap ( ' ' , join ( directives , ' ' ) ) , } , SelectionSet : { leave : ( { selections } ) => block ( selections ) } , Field : { leave ( { alias , name , arguments : args , directives , selectionSet } ) { const prefix = wrap ( '' , alias , ': ' ) + name ; let argsLine = prefix + wrap ( '(' , join ( args , ', ' ) , ')' ) ; if ( argsLine . length > MAX_LINE_LENGTH ) { argsLine = prefix + wrap ( '(\n' , indent ( join ( args , '\n' ) ) , '\n)' ) ; } return join ( [ argsLine , join ( directives , ' ' ) , selectionSet ] , ' ' ) ; } , } , Argument : { leave : ( { name , value } ) => name + ': ' + value } , FragmentSpread : { leave : ( { name , directives } ) => '...' + name + wrap ( ' ' , join ( directives , ' ' ) ) , } , InlineFragment : { leave : ( { typeCondition , directives , selectionSet } ) => join ( [ '...' , wrap ( 'on ' , typeCondition ) , join ( directives , ' ' ) , selectionSet , ] , ' ' ) , } , FragmentDefinition : { leave : ( { name , typeCondition , variableDefinitions , directives , selectionSet , } ) => `fragment ${ name }${ wrap ( '(' , join ( variableDefinitions , ', ' ) , ')' ) } ` + `on ${ typeCondition } ${ wrap ( '' , join ( directives , ' ' ) , ' ' ) }` + selectionSet , } , IntValue : { leave : ( { value } ) => value } , FloatValue : { leave : ( { value } ) => value } , StringValue : { leave : ( { value , block : isBlockString } ) => isBlockString ? printBlockString ( value ) : printString ( value ) , } , BooleanValue : { leave : ( { value } ) => ( value ? 'true' : 'false' ) } , NullValue : { leave : ( ) => 'null' } , EnumValue : { leave : ( { value } ) => value } , ListValue : { leave : ( { values } ) => '[' + join ( values , ', ' ) + ']' } , ObjectValue : { leave : ( { fields } ) => '{' + join ( fields , ', ' ) + '}' } , ObjectField : { leave : ( { name , value } ) => name + ': ' + value } , Directive : { leave : ( { name , arguments : args } ) => '@' + name + wrap ( '(' , join ( args , ', ' ) , ')' ) , } , NamedType : { leave : ( { name } ) => name } , ListType : { leave : ( { type } ) => '[' + type + ']' } , NonNullType : { leave : ( { type } ) => type + '!' } , SchemaDefinition : { leave : ( { description , directives , operationTypes } ) => wrap ( '' , description , '\n' ) + join ( [ 'schema' , join ( directives , ' ' ) , block ( operationTypes ) ] , ' ' ) , } , OperationTypeDefinition : { leave : ( { operation , type } ) => operation + ': ' + type , } , ScalarTypeDefinition : { leave : ( { description , name , directives } ) => wrap ( '' , description , '\n' ) + join ( [ 'scalar' , name , join ( directives , ' ' ) ] , ' ' ) , } , ObjectTypeDefinition : { leave : ( { description , name , interfaces , directives , fields } ) => wrap ( '' , description , '\n' ) + join ( [ 'type' , name , wrap ( 'implements ' , join ( interfaces , ' & ' ) ) , join ( directives , ' ' ) , block ( fields ) , ] , ' ' ) , } , FieldDefinition : { leave : ( { description , name , arguments : args , type , directives } ) => wrap ( '' , description , '\n' ) + name + ( hasMultilineItems ( args ) ? wrap ( '(\n' , indent ( join ( args , '\n' ) ) , '\n)' ) : wrap ( '(' , join ( args , ', ' ) , ')' ) ) + ': ' + type + wrap ( ' ' , join ( directives , ' ' ) ) , } , InputValueDefinition : { leave : ( { description , name , type , defaultValue , directives } ) => wrap ( '' , description , '\n' ) + join ( [ name + ': ' + type , wrap ( '= ' , defaultValue ) , join ( directives , ' ' ) ] , ' ' ) , } , InterfaceTypeDefinition : { leave : ( { description , name , interfaces , directives , fields } ) => wrap ( '' , description , '\n' ) + join ( [ 'interface' , name , wrap ( 'implements ' , join ( interfaces , ' & ' ) ) , join ( directives , ' ' ) , block ( fields ) , ] , ' ' ) , } , UnionTypeDefinition : { leave : ( { description , name , directives , types } ) => wrap ( '' , description , '\n' ) + join ( [ 'union' , name , join ( directives , ' ' ) , wrap ( '= ' , join ( types , ' | ' ) ) ] , ' ' ) , } , EnumTypeDefinition : { leave : ( { description , name , directives , values } ) => wrap ( '' , description , '\n' ) + join ( [ 'enum' , name , join ( directives , ' ' ) , block ( values ) ] , ' ' ) , } , EnumValueDefinition : { leave : ( { description , name , directives } ) => wrap ( '' , description , '\n' ) + join ( [ name , join ( directives , ' ' ) ] , ' ' ) , } , InputObjectTypeDefinition : { leave : ( { description , name , directives , fields } ) => wrap ( '' , description , '\n' ) + join ( [ 'input' , name , join ( directives , ' ' ) , block ( fields ) ] , ' ' ) , } , DirectiveDefinition : { leave : ( { description , name , arguments : args , repeatable , locations } ) => wrap ( '' , description , '\n' ) + 'directive @' + name + ( hasMultilineItems ( args ) ? wrap ( '(\n' , indent ( join ( args , '\n' ) ) , '\n)' ) : wrap ( '(' , join ( args , ', ' ) , ')' ) ) + ( repeatable ? ' repeatable' : '' ) + ' on ' + join ( locations , ' | ' ) , } , SchemaExtension : { leave : ( { directives , operationTypes } ) => join ( [ 'extend schema' , join ( directives , ' ' ) , block ( operationTypes ) ] , ' ' ) , } , ScalarTypeExtension : { leave : ( { name , directives } ) => join ( [ 'extend scalar' , name , join ( directives , ' ' ) ] , ' ' ) , } , ObjectTypeExtension : { leave : ( { name , interfaces , directives , fields } ) => join ( [ 'extend type' , name , wrap ( 'implements ' , join ( interfaces , ' & ' ) ) , join ( directives , ' ' ) , block ( fields ) , ] , ' ' ) , } , InterfaceTypeExtension : { leave : ( { name , interfaces , directives , fields } ) => join ( [ 'extend interface' , name , wrap ( 'implements ' , join ( interfaces , ' & ' ) ) , join ( directives , ' ' ) , block ( fields ) , ] , ' ' ) , } , UnionTypeExtension : { leave : ( { name , directives , types } ) => join ( [ 'extend union' , name , join ( directives , ' ' ) , wrap ( '= ' , join ( types , ' | ' ) ) , ] , ' ' ) , } , EnumTypeExtension : { leave : ( { name , directives , values } ) => join ( [ 'extend enum' , name , join ( directives , ' ' ) , block ( values ) ] , ' ' ) , } , InputObjectTypeExtension : { leave : ( { name , directives , fields } ) => join ( [ 'extend input' , name , join ( directives , ' ' ) , block ( fields ) ] , ' ' ) , } , } ; /**
 * 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 ?. filter ( ( x ) => x ) . join ( separator ) ?? '' ; } /**
 * Given array, print each item on its own line, wrapped in an indented `{ }` block.
 */ function block ( array ) { return wrap ( '{\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 != null && maybeString !== '' ? start + maybeString + end : '' ; } function indent ( str ) { return wrap ( '  ' , str . replace ( /\n/g , '\n  ' ) ) ; } function hasMultilineItems ( maybeArray ) { return maybeArray ?. some ( ( str ) => str . includes ( '\n' ) ) ?? false ; }