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 |
|
66 | export function stripIgnoredCharacters(source) {
|
67 | const sourceObj = isSource(source) ? source : new Source(source);
|
68 | const body = sourceObj.body;
|
69 | const lexer = new Lexer(sourceObj);
|
70 | let strippedBody = '';
|
71 | let wasLastAddedTokenNonPunctuator = false;
|
72 |
|
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 |
|
82 | const isNonPunctuator = !isPunctuatorTokenKind(currentToken.kind);
|
83 |
|
84 | if (wasLastAddedTokenNonPunctuator) {
|
85 | if (isNonPunctuator || currentToken.kind === TokenKind.SPREAD) {
|
86 | strippedBody += ' ';
|
87 | }
|
88 | }
|
89 |
|
90 | const tokenBody = body.slice(currentToken.start, currentToken.end);
|
91 |
|
92 | if (tokenKind === TokenKind.BLOCK_STRING) {
|
93 | strippedBody += printBlockString(currentToken.value, {
|
94 | minimize: true,
|
95 | });
|
96 | } else {
|
97 | strippedBody += tokenBody;
|
98 | }
|
99 |
|
100 | wasLastAddedTokenNonPunctuator = isNonPunctuator;
|
101 | }
|
102 |
|
103 | return strippedBody;
|
104 | }
|