stripIgnoredCharacters.mjs 2.7 KB

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