printer.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true,
  4. });
  5. exports.print = print;
  6. var _blockString = require('./blockString.js');
  7. var _printString = require('./printString.js');
  8. var _visitor = require('./visitor.js');
  9. /**
  10. * Converts an AST into a string, using one set of reasonable
  11. * formatting rules.
  12. */
  13. function print(ast) {
  14. return (0, _visitor.visit)(ast, printDocASTReducer);
  15. }
  16. const MAX_LINE_LENGTH = 80;
  17. const printDocASTReducer = {
  18. Name: {
  19. leave: (node) => node.value,
  20. },
  21. Variable: {
  22. leave: (node) => '$' + node.name,
  23. },
  24. // Document
  25. Document: {
  26. leave: (node) => join(node.definitions, '\n\n'),
  27. },
  28. OperationDefinition: {
  29. leave(node) {
  30. const varDefs = wrap('(', join(node.variableDefinitions, ', '), ')');
  31. const prefix = join(
  32. [
  33. node.operation,
  34. join([node.name, varDefs]),
  35. join(node.directives, ' '),
  36. ],
  37. ' ',
  38. ); // Anonymous queries with no directives or variable definitions can use
  39. // the query short form.
  40. return (prefix === 'query' ? '' : prefix + ' ') + node.selectionSet;
  41. },
  42. },
  43. VariableDefinition: {
  44. leave: ({ variable, type, defaultValue, directives }) =>
  45. variable +
  46. ': ' +
  47. type +
  48. wrap(' = ', defaultValue) +
  49. wrap(' ', join(directives, ' ')),
  50. },
  51. SelectionSet: {
  52. leave: ({ selections }) => block(selections),
  53. },
  54. Field: {
  55. leave({ alias, name, arguments: args, directives, selectionSet }) {
  56. const prefix = wrap('', alias, ': ') + name;
  57. let argsLine = prefix + wrap('(', join(args, ', '), ')');
  58. if (argsLine.length > MAX_LINE_LENGTH) {
  59. argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)');
  60. }
  61. return join([argsLine, join(directives, ' '), selectionSet], ' ');
  62. },
  63. },
  64. Argument: {
  65. leave: ({ name, value }) => name + ': ' + value,
  66. },
  67. // Fragments
  68. FragmentSpread: {
  69. leave: ({ name, directives }) =>
  70. '...' + name + wrap(' ', join(directives, ' ')),
  71. },
  72. InlineFragment: {
  73. leave: ({ typeCondition, directives, selectionSet }) =>
  74. join(
  75. [
  76. '...',
  77. wrap('on ', typeCondition),
  78. join(directives, ' '),
  79. selectionSet,
  80. ],
  81. ' ',
  82. ),
  83. },
  84. FragmentDefinition: {
  85. leave: (
  86. { name, typeCondition, variableDefinitions, directives, selectionSet }, // Note: fragment variable definitions are experimental and may be changed
  87. ) =>
  88. // or removed in the future.
  89. `fragment ${name}${wrap('(', join(variableDefinitions, ', '), ')')} ` +
  90. `on ${typeCondition} ${wrap('', join(directives, ' '), ' ')}` +
  91. selectionSet,
  92. },
  93. // Value
  94. IntValue: {
  95. leave: ({ value }) => value,
  96. },
  97. FloatValue: {
  98. leave: ({ value }) => value,
  99. },
  100. StringValue: {
  101. leave: ({ value, block: isBlockString }) =>
  102. isBlockString
  103. ? (0, _blockString.printBlockString)(value)
  104. : (0, _printString.printString)(value),
  105. },
  106. BooleanValue: {
  107. leave: ({ value }) => (value ? 'true' : 'false'),
  108. },
  109. NullValue: {
  110. leave: () => 'null',
  111. },
  112. EnumValue: {
  113. leave: ({ value }) => value,
  114. },
  115. ListValue: {
  116. leave: ({ values }) => '[' + join(values, ', ') + ']',
  117. },
  118. ObjectValue: {
  119. leave: ({ fields }) => '{' + join(fields, ', ') + '}',
  120. },
  121. ObjectField: {
  122. leave: ({ name, value }) => name + ': ' + value,
  123. },
  124. // Directive
  125. Directive: {
  126. leave: ({ name, arguments: args }) =>
  127. '@' + name + wrap('(', join(args, ', '), ')'),
  128. },
  129. // Type
  130. NamedType: {
  131. leave: ({ name }) => name,
  132. },
  133. ListType: {
  134. leave: ({ type }) => '[' + type + ']',
  135. },
  136. NonNullType: {
  137. leave: ({ type }) => type + '!',
  138. },
  139. // Type System Definitions
  140. SchemaDefinition: {
  141. leave: ({ description, directives, operationTypes }) =>
  142. wrap('', description, '\n') +
  143. join(['schema', join(directives, ' '), block(operationTypes)], ' '),
  144. },
  145. OperationTypeDefinition: {
  146. leave: ({ operation, type }) => operation + ': ' + type,
  147. },
  148. ScalarTypeDefinition: {
  149. leave: ({ description, name, directives }) =>
  150. wrap('', description, '\n') +
  151. join(['scalar', name, join(directives, ' ')], ' '),
  152. },
  153. ObjectTypeDefinition: {
  154. leave: ({ description, name, interfaces, directives, fields }) =>
  155. wrap('', description, '\n') +
  156. join(
  157. [
  158. 'type',
  159. name,
  160. wrap('implements ', join(interfaces, ' & ')),
  161. join(directives, ' '),
  162. block(fields),
  163. ],
  164. ' ',
  165. ),
  166. },
  167. FieldDefinition: {
  168. leave: ({ description, name, arguments: args, type, directives }) =>
  169. wrap('', description, '\n') +
  170. name +
  171. (hasMultilineItems(args)
  172. ? wrap('(\n', indent(join(args, '\n')), '\n)')
  173. : wrap('(', join(args, ', '), ')')) +
  174. ': ' +
  175. type +
  176. wrap(' ', join(directives, ' ')),
  177. },
  178. InputValueDefinition: {
  179. leave: ({ description, name, type, defaultValue, directives }) =>
  180. wrap('', description, '\n') +
  181. join(
  182. [name + ': ' + type, wrap('= ', defaultValue), join(directives, ' ')],
  183. ' ',
  184. ),
  185. },
  186. InterfaceTypeDefinition: {
  187. leave: ({ description, name, interfaces, directives, fields }) =>
  188. wrap('', description, '\n') +
  189. join(
  190. [
  191. 'interface',
  192. name,
  193. wrap('implements ', join(interfaces, ' & ')),
  194. join(directives, ' '),
  195. block(fields),
  196. ],
  197. ' ',
  198. ),
  199. },
  200. UnionTypeDefinition: {
  201. leave: ({ description, name, directives, types }) =>
  202. wrap('', description, '\n') +
  203. join(
  204. ['union', name, join(directives, ' '), wrap('= ', join(types, ' | '))],
  205. ' ',
  206. ),
  207. },
  208. EnumTypeDefinition: {
  209. leave: ({ description, name, directives, values }) =>
  210. wrap('', description, '\n') +
  211. join(['enum', name, join(directives, ' '), block(values)], ' '),
  212. },
  213. EnumValueDefinition: {
  214. leave: ({ description, name, directives }) =>
  215. wrap('', description, '\n') + join([name, join(directives, ' ')], ' '),
  216. },
  217. InputObjectTypeDefinition: {
  218. leave: ({ description, name, directives, fields }) =>
  219. wrap('', description, '\n') +
  220. join(['input', name, join(directives, ' '), block(fields)], ' '),
  221. },
  222. DirectiveDefinition: {
  223. leave: ({ description, name, arguments: args, repeatable, locations }) =>
  224. wrap('', description, '\n') +
  225. 'directive @' +
  226. name +
  227. (hasMultilineItems(args)
  228. ? wrap('(\n', indent(join(args, '\n')), '\n)')
  229. : wrap('(', join(args, ', '), ')')) +
  230. (repeatable ? ' repeatable' : '') +
  231. ' on ' +
  232. join(locations, ' | '),
  233. },
  234. SchemaExtension: {
  235. leave: ({ directives, operationTypes }) =>
  236. join(
  237. ['extend schema', join(directives, ' '), block(operationTypes)],
  238. ' ',
  239. ),
  240. },
  241. ScalarTypeExtension: {
  242. leave: ({ name, directives }) =>
  243. join(['extend scalar', name, join(directives, ' ')], ' '),
  244. },
  245. ObjectTypeExtension: {
  246. leave: ({ name, interfaces, directives, fields }) =>
  247. join(
  248. [
  249. 'extend type',
  250. name,
  251. wrap('implements ', join(interfaces, ' & ')),
  252. join(directives, ' '),
  253. block(fields),
  254. ],
  255. ' ',
  256. ),
  257. },
  258. InterfaceTypeExtension: {
  259. leave: ({ name, interfaces, directives, fields }) =>
  260. join(
  261. [
  262. 'extend interface',
  263. name,
  264. wrap('implements ', join(interfaces, ' & ')),
  265. join(directives, ' '),
  266. block(fields),
  267. ],
  268. ' ',
  269. ),
  270. },
  271. UnionTypeExtension: {
  272. leave: ({ name, directives, types }) =>
  273. join(
  274. [
  275. 'extend union',
  276. name,
  277. join(directives, ' '),
  278. wrap('= ', join(types, ' | ')),
  279. ],
  280. ' ',
  281. ),
  282. },
  283. EnumTypeExtension: {
  284. leave: ({ name, directives, values }) =>
  285. join(['extend enum', name, join(directives, ' '), block(values)], ' '),
  286. },
  287. InputObjectTypeExtension: {
  288. leave: ({ name, directives, fields }) =>
  289. join(['extend input', name, join(directives, ' '), block(fields)], ' '),
  290. },
  291. };
  292. /**
  293. * Given maybeArray, print an empty string if it is null or empty, otherwise
  294. * print all items together separated by separator if provided
  295. */
  296. function join(maybeArray, separator = '') {
  297. var _maybeArray$filter$jo;
  298. return (_maybeArray$filter$jo =
  299. maybeArray === null || maybeArray === void 0
  300. ? void 0
  301. : maybeArray.filter((x) => x).join(separator)) !== null &&
  302. _maybeArray$filter$jo !== void 0
  303. ? _maybeArray$filter$jo
  304. : '';
  305. }
  306. /**
  307. * Given array, print each item on its own line, wrapped in an indented `{ }` block.
  308. */
  309. function block(array) {
  310. return wrap('{\n', indent(join(array, '\n')), '\n}');
  311. }
  312. /**
  313. * If maybeString is not null or empty, then wrap with start and end, otherwise print an empty string.
  314. */
  315. function wrap(start, maybeString, end = '') {
  316. return maybeString != null && maybeString !== ''
  317. ? start + maybeString + end
  318. : '';
  319. }
  320. function indent(str) {
  321. return wrap(' ', str.replace(/\n/g, '\n '));
  322. }
  323. function hasMultilineItems(maybeArray) {
  324. var _maybeArray$some;
  325. // FIXME: https://github.com/graphql/graphql-js/issues/2203
  326. /* c8 ignore next */
  327. return (_maybeArray$some =
  328. maybeArray === null || maybeArray === void 0
  329. ? void 0
  330. : maybeArray.some((str) => str.includes('\n'))) !== null &&
  331. _maybeArray$some !== void 0
  332. ? _maybeArray$some
  333. : false;
  334. }