UNPKG

5.13 kBPlain TextView Raw
1import { SelectionSet, Selection } from "apollo-codegen-core/lib/compiler";
2
3import {
4 InlineSelection,
5 FragmentReference,
6 OutputType,
7 FragmentOrSelection,
8 InputType,
9 Typename
10} from "./intermediates";
11
12import { GraphQLType, GraphQLInputType } from "graphql";
13
14import { Set } from "immutable";
15import compact from "./compact";
16
17type Dependency = {
18 type: "fragment-type" | "fragment-string" | "global";
19 name: string;
20};
21
22export type FragmentDependency = {
23 name: string;
24 importType: boolean;
25 importString: boolean;
26};
27
28type Dependencies = {
29 global: string[];
30 fragments: FragmentDependency[];
31};
32
33const globalDependency = (name: string): Dependency => ({
34 type: "global",
35 name
36});
37
38const 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
60const 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
78const fragmentReferenceDependency = (
79 reference: FragmentReference
80): Dependency => ({
81 type: "fragment-type",
82 name: reference.name
83});
84
85const fragmentStringDependency = (name: string): Dependency => ({
86 type: "fragment-string",
87 name
88});
89
90const 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
101const 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
114const anyObjectDependencies = (object: FragmentOrSelection): Dependency[] =>
115 object.kind == "InlineSelection"
116 ? inlineSelectionDependencies(object)
117 : [fragmentReferenceDependency(object)];
118
119const 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
129const variableDependencies = (variable: {
130 name: string;
131 type: GraphQLType;
132}): Dependency[] =>
133 inputTypeDependencies(InputType(variable.type as GraphQLInputType));
134
135const 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
149const 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
180const mapDependencies = (dependencies: Dependency[]): Dependencies => ({
181 global: dedupedDependenciesOfType(dependencies, "global"),
182 fragments: fragmentDependencies(dependencies)
183});
184
185const Dependencies = (
186 selectionSet: SelectionSet,
187 variables?: { name: string; type: GraphQLType }[]
188): Dependencies => mapDependencies(allDependencies(selectionSet, variables));
189
190export default Dependencies;