stripIgnoredCharacters.mjs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { Source, isSource } from "../language/source.mjs";
  2. import { TokenKind } from "../language/tokenKind.mjs";
  3. import { Lexer, isPunctuatorTokenKind } from "../language/lexer.mjs";
  4. import { dedentBlockStringValue, getBlockStringIndentation } from "../language/blockString.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. * query SomeQuery($foo: String!, $bar: String) {
  28. * someField(foo: $foo, bar: $bar) {
  29. * a
  30. * b {
  31. * c
  32. * d
  33. * }
  34. * }
  35. * }
  36. *
  37. * Becomes:
  38. *
  39. * query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}}
  40. *
  41. * SDL example:
  42. *
  43. * """
  44. * Type description
  45. * """
  46. * type Foo {
  47. * """
  48. * Field description
  49. * """
  50. * bar: String
  51. * }
  52. *
  53. * Becomes:
  54. *
  55. * """Type description""" type Foo{"""Field description""" bar:String}
  56. */
  57. export function stripIgnoredCharacters(source) {
  58. var sourceObj = isSource(source) ? source : new Source(source);
  59. var body = sourceObj.body;
  60. var lexer = new Lexer(sourceObj);
  61. var strippedBody = '';
  62. var wasLastAddedTokenNonPunctuator = false;
  63. while (lexer.advance().kind !== TokenKind.EOF) {
  64. var currentToken = lexer.token;
  65. var tokenKind = currentToken.kind;
  66. /**
  67. * Every two non-punctuator tokens should have space between them.
  68. * Also prevent case of non-punctuator token following by spread resulting
  69. * in invalid token (e.g. `1...` is invalid Float token).
  70. */
  71. var isNonPunctuator = !isPunctuatorTokenKind(currentToken.kind);
  72. if (wasLastAddedTokenNonPunctuator) {
  73. if (isNonPunctuator || currentToken.kind === TokenKind.SPREAD) {
  74. strippedBody += ' ';
  75. }
  76. }
  77. var tokenBody = body.slice(currentToken.start, currentToken.end);
  78. if (tokenKind === TokenKind.BLOCK_STRING) {
  79. strippedBody += dedentBlockString(tokenBody);
  80. } else {
  81. strippedBody += tokenBody;
  82. }
  83. wasLastAddedTokenNonPunctuator = isNonPunctuator;
  84. }
  85. return strippedBody;
  86. }
  87. function dedentBlockString(blockStr) {
  88. // skip leading and trailing triple quotations
  89. var rawStr = blockStr.slice(3, -3);
  90. var body = dedentBlockStringValue(rawStr);
  91. if (getBlockStringIndentation(body) > 0) {
  92. body = '\n' + body;
  93. }
  94. var lastChar = body[body.length - 1];
  95. var hasTrailingQuote = lastChar === '"' && body.slice(-4) !== '\\"""';
  96. if (hasTrailingQuote || lastChar === '\\') {
  97. body += '\n';
  98. }
  99. return '"""' + body + '"""';
  100. }