UNPKG

3.35 kBJavaScriptView Raw
1import inspect from '../jsutils/inspect';
2import { Source } from '../language/source';
3import { TokenKind } from '../language/tokenKind';
4import { createLexer, isPunctuatorToken } from '../language/lexer';
5import { 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
59export function stripIgnoredCharacters(source) {
60 var sourceObj = typeof source === 'string' ? new Source(source) : source;
61
62 if (!(sourceObj instanceof Source)) {
63 throw new TypeError("Must provide string or Source. Received: ".concat(inspect(sourceObj)));
64 }
65
66 var body = sourceObj.body;
67 var lexer = createLexer(sourceObj);
68 var strippedBody = '';
69 var wasLastAddedTokenNonPunctuator = false;
70
71 while (lexer.advance().kind !== TokenKind.EOF) {
72 var currentToken = lexer.token;
73 var 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
80 var isNonPunctuator = !isPunctuatorToken(currentToken);
81
82 if (wasLastAddedTokenNonPunctuator) {
83 if (isNonPunctuator || currentToken.kind === TokenKind.SPREAD) {
84 strippedBody += ' ';
85 }
86 }
87
88 var tokenBody = body.slice(currentToken.start, currentToken.end);
89
90 if (tokenKind === TokenKind.BLOCK_STRING) {
91 strippedBody += dedentBlockString(tokenBody);
92 } else {
93 strippedBody += tokenBody;
94 }
95
96 wasLastAddedTokenNonPunctuator = isNonPunctuator;
97 }
98
99 return strippedBody;
100}
101
102function dedentBlockString(blockStr) {
103 // skip leading and trailing triple quotations
104 var rawStr = blockStr.slice(3, -3);
105 var body = dedentBlockStringValue(rawStr);
106 var lines = body.split(/\r\n|[\n\r]/g);
107
108 if (getBlockStringIndentation(lines) > 0) {
109 body = '\n' + body;
110 }
111
112 var lastChar = body[body.length - 1];
113 var hasTrailingQuote = lastChar === '"' && body.slice(-4) !== '\\"""';
114
115 if (hasTrailingQuote || lastChar === '\\') {
116 body += '\n';
117 }
118
119 return '"""' + body + '"""';
120}