Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 023aa09

Browse files
committedJun 4, 2019
add mergeAST from GraphiQL, refactor using visit()
1 parent c3dd670 commit 023aa09

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed
 
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*
9+
*/
10+
11+
export const fixtures = [
12+
{
13+
desc: 'does not modify query with no fragments',
14+
query: `
15+
query Test {
16+
id
17+
}`,
18+
mergedQuery: `
19+
query Test {
20+
id
21+
}`,
22+
},
23+
{
24+
desc: 'inlines simple nested fragment',
25+
query: `
26+
query Test {
27+
...Fragment1
28+
}
29+
30+
fragment Fragment1 on Test {
31+
id
32+
}`,
33+
mergedQuery: `
34+
query Test {
35+
...on Test {
36+
id
37+
}
38+
}`,
39+
},
40+
{
41+
desc: 'inlines triple nested fragment',
42+
query: `
43+
query Test {
44+
...Fragment1
45+
}
46+
47+
fragment Fragment1 on Test {
48+
...Fragment2
49+
}
50+
51+
fragment Fragment2 on Test {
52+
...Fragment3
53+
}
54+
55+
fragment Fragment3 on Test {
56+
id
57+
}`,
58+
mergedQuery: `
59+
query Test {
60+
...on Test {
61+
...on Test {
62+
...on Test {
63+
id
64+
}
65+
}
66+
}
67+
}`,
68+
},
69+
{
70+
desc: 'inlines multiple fragments',
71+
query: `
72+
query Test {
73+
...Fragment1
74+
...Fragment2
75+
...Fragment3
76+
}
77+
78+
fragment Fragment1 on Test {
79+
id
80+
}
81+
82+
fragment Fragment2 on Test {
83+
id
84+
}
85+
86+
fragment Fragment3 on Test {
87+
id
88+
}`,
89+
mergedQuery: `
90+
query Test {
91+
...on Test {
92+
id
93+
}
94+
...on Test {
95+
id
96+
}
97+
...on Test {
98+
id
99+
}
100+
}`,
101+
},
102+
{
103+
desc: 'removes duplicate fragment spreads',
104+
query: `
105+
query Test {
106+
...Fragment1
107+
...Fragment1
108+
}
109+
110+
fragment Fragment1 on Test {
111+
id
112+
}`,
113+
mergedQuery: `
114+
query Test {
115+
...on Test {
116+
id
117+
}
118+
}`,
119+
},
120+
];
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*
9+
*/
10+
11+
import { expect } from 'chai';
12+
import { describe, it } from 'mocha';
13+
14+
import { parse, print } from '../../index';
15+
16+
import { mergeAST } from '../mergeAST';
17+
18+
import { fixtures } from './mergeAST-fixture';
19+
20+
describe.only('MergeAST', () => {
21+
fixtures.forEach(fixture => {
22+
it(fixture.desc, () => {
23+
const result = print(mergeAST(parse(fixture.query))).replace(/\s/g, '');
24+
expect(result).to.equal(fixture.mergedQuery.replace(/\s/g, ''));
25+
});
26+
});
27+
});

‎src/utilities/mergeAST.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict
8+
*/
9+
10+
import { visit } from '../language/visitor';
11+
import { print } from '../';
12+
import {
13+
type DocumentNode,
14+
type FragmentDefinitionNode,
15+
type FragmentSpreadNode,
16+
} from '../language/ast';
17+
18+
/**
19+
* Given a document AST, merge all fragments definitions into the
20+
* operations using inline fragments.
21+
*/
22+
23+
export function mergeAST(ast: DocumentNode): DocumentNode {
24+
const fragmentDefinitions: { [name: string]: FragmentDefinitionNode } = {};
25+
const inlinedFragments: { [name: string]: FragmentSpreadNode } = {};
26+
27+
visit(ast, {
28+
FragmentDefinition(node) {
29+
// collect the existing FragmentDefinition nodes
30+
fragmentDefinitions[node.name.value] = node;
31+
},
32+
});
33+
34+
return visit(ast, {
35+
FragmentSpread(node) {
36+
37+
// no repeats
38+
if (inlinedFragments[node.name.value]) {
39+
return null;
40+
}
41+
42+
// otherwise, we are inlining a new fragment definition
43+
inlinedFragments[node.name.value] = node;
44+
45+
// retrieve the original definition, and convert
46+
// all FragmentSpread nodes to InlineFragment nodes
47+
return {
48+
...fragmentDefinitions[node.name.value],
49+
kind: 'InlineFragment',
50+
};
51+
},
52+
FragmentDefinition(node) {
53+
// remove the original FragmentDefinition nodes
54+
return null;
55+
},
56+
});
57+
}

0 commit comments

Comments
 (0)
Please sign in to comment.