stripIgnoredCharacters.js.flow 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. // @flow strict
  2. import inspect from '../jsutils/inspect';
  3. import { Source } from '../language/source';
  4. import { TokenKind } from '../language/tokenKind';
  5. import { createLexer, isPunctuatorToken } from '../language/lexer';
  6. import {
  7. dedentBlockStringValue,
  8. getBlockStringIndentation,
  9. } from '../language/blockString';
  10. /**
  11. * Strips characters that are not significant to the validity or execution
  12. * of a GraphQL document:
  13. * - UnicodeBOM
  14. * - WhiteSpace
  15. * - LineTerminator
  16. * - Comment
  17. * - Comma
  18. * - BlockString indentation
  19. *
  20. * Note: It is required to have a delimiter character between neighboring
  21. * non-punctuator tokens and this function always uses single space as delimiter.
  22. *
  23. * It is guaranteed that both input and output documents if parsed would result
  24. * in the exact same AST except for nodes location.
  25. *
  26. * Warning: It is guaranteed that this function will always produce stable results.
  27. * However, it's not guaranteed that it will stay the same between different
  28. * releases due to bugfixes or changes in the GraphQL specification.
  29. *
  30. * Query example:
  31. *
  32. * query SomeQuery($foo: String!, $bar: String) {
  33. * someField(foo: $foo, bar: $bar) {
  34. * a
  35. * b {
  36. * c
  37. * d
  38. * }
  39. * }
  40. * }
  41. *
  42. * Becomes:
  43. *
  44. * query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}}
  45. *
  46. * SDL example:
  47. *
  48. * """
  49. * Type description
  50. * """
  51. * type Foo {
  52. * """
  53. * Field description
  54. * """
  55. * bar: String
  56. * }
  57. *
  58. * Becomes:
  59. *
  60. * """Type description""" type Foo{"""Field description""" bar:String}
  61. */
  62. export function stripIgnoredCharacters(source: string | Source): string {
  63. const sourceObj = typeof source === 'string' ? new Source(source) : source;
  64. if (!(sourceObj instanceof Source)) {
  65. throw new TypeError(
  66. `Must provide string or Source. Received: ${inspect(sourceObj)}`,
  67. );
  68. }
  69. const body = sourceObj.body;
  70. const lexer = createLexer(sourceObj);
  71. let strippedBody = '';
  72. let wasLastAddedTokenNonPunctuator = false;
  73. while (lexer.advance().kind !== TokenKind.EOF) {
  74. const currentToken = lexer.token;
  75. const tokenKind = currentToken.kind;
  76. /**
  77. * Every two non-punctuator tokens should have space between them.
  78. * Also prevent case of non-punctuator token following by spread resulting
  79. * in invalid token (e.g. `1...` is invalid Float token).
  80. */
  81. const isNonPunctuator = !isPunctuatorToken(currentToken);
  82. if (wasLastAddedTokenNonPunctuator) {
  83. if (isNonPunctuator || currentToken.kind === TokenKind.SPREAD) {
  84. strippedBody += ' ';
  85. }
  86. }
  87. const tokenBody = body.slice(currentToken.start, currentToken.end);
  88. if (tokenKind === TokenKind.BLOCK_STRING) {
  89. strippedBody += dedentBlockString(tokenBody);
  90. } else {
  91. strippedBody += tokenBody;
  92. }
  93. wasLastAddedTokenNonPunctuator = isNonPunctuator;
  94. }
  95. return strippedBody;
  96. }
  97. function dedentBlockString(blockStr) {
  98. // skip leading and trailing triple quotations
  99. const rawStr = blockStr.slice(3, -3);
  100. let body = dedentBlockStringValue(rawStr);
  101. const lines = body.split(/\r\n|[\n\r]/g);
  102. if (getBlockStringIndentation(lines) > 0) {
  103. body = '\n' + body;
  104. }
  105. const lastChar = body[body.length - 1];
  106. const hasTrailingQuote = lastChar === '"' && body.slice(-4) !== '\\"""';
  107. if (hasTrailingQuote || lastChar === '\\') {
  108. body += '\n';
  109. }
  110. return '"""' + body + '"""';
  111. }