1 | import { SelectionSet, Selection } from "apollo-codegen-core/lib/compiler";
|
2 |
|
3 | import {
|
4 | InlineSelection,
|
5 | FragmentReference,
|
6 | OutputType,
|
7 | FragmentOrSelection,
|
8 | InputType,
|
9 | Typename
|
10 | } from "./intermediates";
|
11 |
|
12 | import { GraphQLType, GraphQLInputType } from "graphql";
|
13 |
|
14 | import { Set } from "immutable";
|
15 | import compact from "./compact";
|
16 |
|
17 | type Dependency = {
|
18 | type: "fragment-type" | "fragment-string" | "global";
|
19 | name: string;
|
20 | };
|
21 |
|
22 | export type FragmentDependency = {
|
23 | name: string;
|
24 | importType: boolean;
|
25 | importString: boolean;
|
26 | };
|
27 |
|
28 | type Dependencies = {
|
29 | global: string[];
|
30 | fragments: FragmentDependency[];
|
31 | };
|
32 |
|
33 | const globalDependency = (name: string): Dependency => ({
|
34 | type: "global",
|
35 | name
|
36 | });
|
37 |
|
38 | const outputTypeDependencies = (type: OutputType | Typename): Dependency[] => {
|
39 | switch (type.kind) {
|
40 | case "Maybe":
|
41 | return [
|
42 | globalDependency("Maybe"),
|
43 | ...outputTypeDependencies(type.ofType)
|
44 | ];
|
45 | case "List":
|
46 | return outputTypeDependencies(type.ofType);
|
47 | case "FragmentReference":
|
48 | return [fragmentReferenceDependency(type)];
|
49 | case "InlineSelection":
|
50 | return inlineSelectionDependencies(type);
|
51 | case "Enum":
|
52 | return [globalDependency(type.name)];
|
53 | case "Scalar":
|
54 | return [];
|
55 | case "Typename":
|
56 | return [];
|
57 | }
|
58 | };
|
59 |
|
60 | const inputTypeDependencies = (type: InputType): Dependency[] => {
|
61 | switch (type.kind) {
|
62 | case "Maybe":
|
63 | return [
|
64 | globalDependency("Optional"),
|
65 | ...inputTypeDependencies(type.ofType)
|
66 | ];
|
67 | case "List":
|
68 | return inputTypeDependencies(type.ofType);
|
69 | case "InputObject":
|
70 | return [globalDependency(type.name)];
|
71 | case "Enum":
|
72 | return [globalDependency(type.name)];
|
73 | case "Scalar":
|
74 | return [];
|
75 | }
|
76 | };
|
77 |
|
78 | const fragmentReferenceDependency = (
|
79 | reference: FragmentReference
|
80 | ): Dependency => ({
|
81 | type: "fragment-type",
|
82 | name: reference.name
|
83 | });
|
84 |
|
85 | const fragmentStringDependency = (name: string): Dependency => ({
|
86 | type: "fragment-string",
|
87 | name
|
88 | });
|
89 |
|
90 | const inlineSelectionDependencies = (
|
91 | selection: InlineSelection
|
92 | ): Dependency[] =>
|
93 | compact(
|
94 | ...selection.intersections.map(fragmentReferenceDependency),
|
95 | ...selection.fields.flatMap(field => outputTypeDependencies(field.type)),
|
96 | ...selection.booleanConditions.flatMap(anyObjectDependencies),
|
97 | ...selection.typeConditions.flatMap(anyObjectDependencies),
|
98 | selection.typeConditions.length > 0 && globalDependency("If")
|
99 | );
|
100 |
|
101 | const selectionFragmentStringDependencies = (
|
102 | selection: Selection
|
103 | ): Dependency[] =>
|
104 | compact(
|
105 | selection.kind == "FragmentSpread" &&
|
106 | fragmentStringDependency(selection.fragmentName),
|
107 | ...(selection.selectionSet
|
108 | ? selection.selectionSet.selections.flatMap(
|
109 | selectionFragmentStringDependencies
|
110 | )
|
111 | : [])
|
112 | );
|
113 |
|
114 | const anyObjectDependencies = (object: FragmentOrSelection): Dependency[] =>
|
115 | object.kind == "InlineSelection"
|
116 | ? inlineSelectionDependencies(object)
|
117 | : [fragmentReferenceDependency(object)];
|
118 |
|
119 | const dedupedDependenciesOfType = (
|
120 | dependencies: Dependency[],
|
121 | type: "fragment-type" | "fragment-string" | "global"
|
122 | ): string[] =>
|
123 | Set(
|
124 | dependencies
|
125 | .filter(dependency => dependency.type == type)
|
126 | .map(dependency => dependency.name)
|
127 | ).toArray();
|
128 |
|
129 | const variableDependencies = (variable: {
|
130 | name: string;
|
131 | type: GraphQLType;
|
132 | }): Dependency[] =>
|
133 | inputTypeDependencies(InputType(variable.type as GraphQLInputType));
|
134 |
|
135 | const allDependencies = (
|
136 | selectionSet: SelectionSet,
|
137 | variables?: { name: string; type: GraphQLType }[]
|
138 | ): Dependency[] => [
|
139 | ...inlineSelectionDependencies(InlineSelection(selectionSet)),
|
140 | ...(variables
|
141 | ? [
|
142 | ...variables.flatMap(variableDependencies),
|
143 | ...selectionSet.selections.flatMap(selectionFragmentStringDependencies),
|
144 | globalDependency("Operation")
|
145 | ]
|
146 | : [])
|
147 | ];
|
148 |
|
149 | const fragmentDependencies = (
|
150 | dependencies: Dependency[]
|
151 | ): FragmentDependency[] => {
|
152 | const fragmentTypes = Set(
|
153 | dependencies
|
154 | .filter(dependency => dependency.type == "fragment-type")
|
155 | .map(dependency => dependency.name)
|
156 | );
|
157 | let fragmentStrings = Set(
|
158 | dependencies
|
159 | .filter(dependency => dependency.type == "fragment-string")
|
160 | .map(dependency => dependency.name)
|
161 | );
|
162 | return fragmentTypes
|
163 | .map(fragmentType => {
|
164 | const importString = fragmentStrings.contains(fragmentType!);
|
165 | fragmentStrings = fragmentStrings.remove(fragmentType!);
|
166 | return { name: fragmentType!, importType: true, importString };
|
167 | })
|
168 | .toArray()
|
169 | .concat(
|
170 | fragmentStrings
|
171 | .map(fragmentString => ({
|
172 | name: fragmentString!,
|
173 | importType: false,
|
174 | importString: true
|
175 | }))
|
176 | .toArray()
|
177 | );
|
178 | };
|
179 |
|
180 | const mapDependencies = (dependencies: Dependency[]): Dependencies => ({
|
181 | global: dedupedDependenciesOfType(dependencies, "global"),
|
182 | fragments: fragmentDependencies(dependencies)
|
183 | });
|
184 |
|
185 | const Dependencies = (
|
186 | selectionSet: SelectionSet,
|
187 | variables?: { name: string; type: GraphQLType }[]
|
188 | ): Dependencies => mapDependencies(allDependencies(selectionSet, variables));
|
189 |
|
190 | export default Dependencies;
|