stripIgnoredCharacters.mjs 3.3 KB

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