UNPKG

16 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const path = require("path");
4const t = require("@babel/types");
5const common_tags_1 = require("common-tags");
6const graphql_1 = require("graphql");
7const typeCase_1 = require("apollo-codegen-core/lib/compiler/visitors/typeCase");
8const collectAndMergeFields_1 = require("apollo-codegen-core/lib/compiler/visitors/collectAndMergeFields");
9const language_1 = require("./language");
10const printer_1 = require("./printer");
11const graphql_2 = require("graphql");
12const array_1 = require("apollo-codegen-core/lib/utilities/array");
13class TypescriptGeneratedFile {
14 constructor(fileContents) {
15 this.fileContents = fileContents;
16 }
17 get output() {
18 return this.fileContents;
19 }
20}
21function printEnumsAndInputObjects(generator, typesUsed) {
22 generator.printer.enqueue(common_tags_1.stripIndent `
23 //==============================================================
24 // START Enums and Input Objects
25 //==============================================================
26 `);
27 typesUsed
28 .filter(type => type instanceof graphql_1.GraphQLEnumType)
29 .sort()
30 .forEach(enumType => {
31 generator.typeAliasForEnumType(enumType);
32 });
33 typesUsed
34 .filter(type => type instanceof graphql_1.GraphQLInputObjectType)
35 .sort()
36 .forEach(inputObjectType => {
37 generator.typeAliasForInputObjectType(inputObjectType);
38 });
39 generator.printer.enqueue(common_tags_1.stripIndent `
40 //==============================================================
41 // END Enums and Input Objects
42 //==============================================================
43 `);
44}
45function printGlobalImport(generator, typesUsed, outputPath, globalSourcePath) {
46 if (typesUsed.length > 0) {
47 const relative = path.relative(path.dirname(outputPath), path.join(path.dirname(globalSourcePath), path.basename(globalSourcePath, ".ts")));
48 generator.printer.enqueue(generator.import(typesUsed, "./" + relative));
49 }
50}
51function generateSource(context) {
52 const generator = new TypescriptAPIGenerator(context);
53 const generatedFiles = [];
54 Object.values(context.operations).forEach(operation => {
55 generator.fileHeader();
56 generator.interfacesForOperation(operation);
57 const output = generator.printer.printAndClear();
58 generatedFiles.push({
59 sourcePath: operation.filePath,
60 fileName: `${operation.operationName}.ts`,
61 content: new TypescriptGeneratedFile(output)
62 });
63 });
64 Object.values(context.fragments).forEach(fragment => {
65 generator.fileHeader();
66 generator.interfacesForFragment(fragment);
67 const output = generator.printer.printAndClear();
68 generatedFiles.push({
69 sourcePath: fragment.filePath,
70 fileName: `${fragment.fragmentName}.ts`,
71 content: new TypescriptGeneratedFile(output)
72 });
73 });
74 generator.fileHeader();
75 printEnumsAndInputObjects(generator, context.typesUsed);
76 const common = generator.printer.printAndClear();
77 return {
78 generatedFiles,
79 common
80 };
81}
82exports.generateSource = generateSource;
83function generateLocalSource(context) {
84 const generator = new TypescriptAPIGenerator(context);
85 const operations = Object.values(context.operations).map(operation => ({
86 sourcePath: operation.filePath,
87 fileName: `${operation.operationName}.ts`,
88 content: (options) => {
89 generator.fileHeader();
90 if (options && options.outputPath && options.globalSourcePath) {
91 printGlobalImport(generator, generator.getGlobalTypesUsedForOperation(operation), options.outputPath, options.globalSourcePath);
92 }
93 generator.interfacesForOperation(operation);
94 const output = generator.printer.printAndClear();
95 return new TypescriptGeneratedFile(output);
96 }
97 }));
98 const fragments = Object.values(context.fragments).map(fragment => ({
99 sourcePath: fragment.filePath,
100 fileName: `${fragment.fragmentName}.ts`,
101 content: (options) => {
102 generator.fileHeader();
103 if (options && options.outputPath && options.globalSourcePath) {
104 printGlobalImport(generator, generator.getGlobalTypesUsedForFragment(fragment), options.outputPath, options.globalSourcePath);
105 }
106 generator.interfacesForFragment(fragment);
107 const output = generator.printer.printAndClear();
108 return new TypescriptGeneratedFile(output);
109 }
110 }));
111 return operations.concat(fragments);
112}
113exports.generateLocalSource = generateLocalSource;
114function generateGlobalSource(context) {
115 const generator = new TypescriptAPIGenerator(context);
116 generator.fileHeader();
117 printEnumsAndInputObjects(generator, context.typesUsed);
118 const output = generator.printer.printAndClear();
119 return new TypescriptGeneratedFile(output);
120}
121exports.generateGlobalSource = generateGlobalSource;
122class TypescriptAPIGenerator extends language_1.default {
123 constructor(context) {
124 super(context.options);
125 this.getGlobalTypesUsedForOperation = (doc) => {
126 const typesUsed = doc.variables.reduce((acc, { type }) => {
127 const t = this.getUnderlyingType(type);
128 if (this.isGlobalType(t)) {
129 return array_1.maybePush(acc, t);
130 }
131 return acc;
132 }, []);
133 return doc.selectionSet.selections.reduce(this.reduceSelection, typesUsed);
134 };
135 this.getGlobalTypesUsedForFragment = (doc) => {
136 return doc.selectionSet.selections.reduce(this.reduceSelection, []);
137 };
138 this.reduceSelection = (acc, selection) => {
139 if (selection.kind === "Field" || selection.kind === "TypeCondition") {
140 const type = this.getUnderlyingType(selection.type);
141 if (this.isGlobalType(type)) {
142 acc = array_1.maybePush(acc, type);
143 }
144 }
145 if (selection.selectionSet) {
146 return selection.selectionSet.selections.reduce(this.reduceSelection, acc);
147 }
148 return acc;
149 };
150 this.isGlobalType = (type) => {
151 return (type instanceof graphql_1.GraphQLEnumType || type instanceof graphql_1.GraphQLInputObjectType);
152 };
153 this.getUnderlyingType = (type) => {
154 if (type instanceof graphql_2.GraphQLNonNull) {
155 return this.getUnderlyingType(graphql_2.getNullableType(type));
156 }
157 if (type instanceof graphql_2.GraphQLList) {
158 return this.getUnderlyingType(type.ofType);
159 }
160 return type;
161 };
162 this.reduceTypesUsed = (acc, type) => {
163 if (type instanceof graphql_2.GraphQLNonNull) {
164 type = graphql_2.getNullableType(type);
165 }
166 if (type instanceof graphql_2.GraphQLList) {
167 type = type.ofType;
168 }
169 if (type instanceof graphql_1.GraphQLInputObjectType ||
170 type instanceof graphql_2.GraphQLObjectType) {
171 acc = array_1.maybePush(acc, type);
172 const fields = type.getFields();
173 acc = Object.keys(fields)
174 .map(key => fields[key] && fields[key].type)
175 .reduce(this.reduceTypesUsed, acc);
176 }
177 else {
178 acc = array_1.maybePush(acc, type);
179 }
180 return acc;
181 };
182 this.context = context;
183 this.printer = new printer_1.default();
184 this.scopeStack = [];
185 }
186 fileHeader() {
187 this.printer.enqueue(common_tags_1.stripIndent `
188 /* tslint:disable */
189 // This file was automatically generated and should not be edited.
190 `);
191 }
192 typeAliasForEnumType(enumType) {
193 this.printer.enqueue(this.enumerationDeclaration(enumType));
194 }
195 typeAliasForInputObjectType(inputObjectType) {
196 this.printer.enqueue(this.inputObjectDeclaration(inputObjectType));
197 }
198 interfacesForOperation(operation) {
199 const { operationType, operationName, variables, selectionSet } = operation;
200 this.scopeStackPush(operationName);
201 this.printer.enqueue(common_tags_1.stripIndent `
202 // ====================================================
203 // GraphQL ${operationType} operation: ${operationName}
204 // ====================================================
205 `);
206 const variants = this.getVariantsForSelectionSet(selectionSet);
207 const variant = variants[0];
208 const properties = this.getPropertiesForVariant(variant);
209 const exportedTypeAlias = this.exportDeclaration(this.interface(operationName, properties));
210 this.printer.enqueue(exportedTypeAlias);
211 this.scopeStackPop();
212 if (variables.length > 0) {
213 const interfaceName = operationName + "Variables";
214 this.scopeStackPush(interfaceName);
215 this.printer.enqueue(this.exportDeclaration(this.interface(interfaceName, variables.map(variable => ({
216 name: variable.name,
217 type: this.typeFromGraphQLType(variable.type)
218 })), { keyInheritsNullability: true })));
219 this.scopeStackPop();
220 }
221 }
222 interfacesForFragment(fragment) {
223 const { fragmentName, selectionSet } = fragment;
224 this.scopeStackPush(fragmentName);
225 this.printer.enqueue(common_tags_1.stripIndent `
226 // ====================================================
227 // GraphQL fragment: ${fragmentName}
228 // ====================================================
229 `);
230 const variants = this.getVariantsForSelectionSet(selectionSet);
231 if (variants.length === 1) {
232 const properties = this.getPropertiesForVariant(variants[0]);
233 const name = this.nameFromScopeStack(this.scopeStack);
234 const exportedTypeAlias = this.exportDeclaration(this.interface(name, properties));
235 this.printer.enqueue(exportedTypeAlias);
236 }
237 else {
238 const unionMembers = [];
239 variants.forEach(variant => {
240 this.scopeStackPush(variant.possibleTypes[0].toString());
241 const properties = this.getPropertiesForVariant(variant);
242 const name = this.nameFromScopeStack(this.scopeStack);
243 const exportedTypeAlias = this.exportDeclaration(this.interface(name, properties));
244 this.printer.enqueue(exportedTypeAlias);
245 unionMembers.push(t.identifier(this.nameFromScopeStack(this.scopeStack)));
246 this.scopeStackPop();
247 });
248 this.printer.enqueue(this.exportDeclaration(this.typeAliasGenericUnion(this.nameFromScopeStack(this.scopeStack), unionMembers.map(id => t.TSTypeReference(id)))));
249 }
250 this.scopeStackPop();
251 }
252 getTypesUsedForOperation(doc, context) {
253 let docTypesUsed = [];
254 if (doc.hasOwnProperty("operationName")) {
255 const operation = doc;
256 docTypesUsed = operation.variables.map(({ type }) => type);
257 }
258 const reduceTypesForDocument = (nestDoc, acc) => {
259 const { selectionSet: { possibleTypes, selections } } = nestDoc;
260 acc = possibleTypes.reduce(array_1.maybePush, acc);
261 acc = selections.reduce((selectionAcc, selection) => {
262 switch (selection.kind) {
263 case "Field":
264 case "TypeCondition":
265 selectionAcc = array_1.maybePush(selectionAcc, selection.type);
266 break;
267 case "FragmentSpread":
268 selectionAcc = reduceTypesForDocument(selection, selectionAcc);
269 break;
270 default:
271 break;
272 }
273 return selectionAcc;
274 }, acc);
275 return acc;
276 };
277 docTypesUsed = reduceTypesForDocument(doc, docTypesUsed).reduce(this.reduceTypesUsed, []);
278 return context.typesUsed.filter(type => {
279 return docTypesUsed.find(typeUsed => type === typeUsed);
280 });
281 }
282 getVariantsForSelectionSet(selectionSet) {
283 return this.getTypeCasesForSelectionSet(selectionSet).exhaustiveVariants;
284 }
285 getTypeCasesForSelectionSet(selectionSet) {
286 return typeCase_1.typeCaseForSelectionSet(selectionSet, this.context.options.mergeInFieldsFromFragmentSpreads);
287 }
288 getPropertiesForVariant(variant) {
289 const fields = collectAndMergeFields_1.collectAndMergeFields(variant, this.context.options.mergeInFieldsFromFragmentSpreads);
290 return fields.map(field => {
291 const fieldName = field.alias !== undefined ? field.alias : field.name;
292 this.scopeStackPush(fieldName);
293 let res;
294 if (field.selectionSet) {
295 res = this.handleFieldSelectionSetValue(t.identifier(this.nameFromScopeStack(this.scopeStack)), field);
296 }
297 else {
298 res = this.handleFieldValue(field, variant);
299 }
300 this.scopeStackPop();
301 return res;
302 });
303 }
304 handleFieldSelectionSetValue(generatedIdentifier, field) {
305 const { selectionSet } = field;
306 const type = this.typeFromGraphQLType(field.type, generatedIdentifier.name);
307 const typeCase = this.getTypeCasesForSelectionSet(selectionSet);
308 const variants = typeCase.exhaustiveVariants;
309 let exportedTypeAlias;
310 if (variants.length === 1) {
311 const variant = variants[0];
312 const properties = this.getPropertiesForVariant(variant);
313 exportedTypeAlias = this.exportDeclaration(this.interface(this.nameFromScopeStack(this.scopeStack), properties));
314 }
315 else {
316 const identifiers = variants.map(variant => {
317 this.scopeStackPush(variant.possibleTypes[0].toString());
318 const properties = this.getPropertiesForVariant(variant);
319 const identifierName = this.nameFromScopeStack(this.scopeStack);
320 this.printer.enqueue(this.exportDeclaration(this.interface(identifierName, properties)));
321 this.scopeStackPop();
322 return t.identifier(identifierName);
323 });
324 exportedTypeAlias = this.exportDeclaration(this.typeAliasGenericUnion(generatedIdentifier.name, identifiers.map(i => t.TSTypeReference(i))));
325 }
326 this.printer.enqueue(exportedTypeAlias);
327 return {
328 name: field.alias ? field.alias : field.name,
329 description: field.description,
330 type
331 };
332 }
333 handleFieldValue(field, variant) {
334 let res;
335 if (field.name === "__typename") {
336 const types = variant.possibleTypes.map(type => {
337 return t.TSLiteralType(t.stringLiteral(type.toString()));
338 });
339 res = {
340 name: field.alias ? field.alias : field.name,
341 description: field.description,
342 type: t.TSUnionType(types)
343 };
344 }
345 else {
346 res = {
347 name: field.alias ? field.alias : field.name,
348 description: field.description,
349 type: this.typeFromGraphQLType(field.type)
350 };
351 }
352 return res;
353 }
354 get output() {
355 return this.printer.print();
356 }
357 scopeStackPush(name) {
358 this.scopeStack.push(name);
359 }
360 scopeStackPop() {
361 const popped = this.scopeStack.pop();
362 return popped;
363 }
364}
365exports.TypescriptAPIGenerator = TypescriptAPIGenerator;
366//# sourceMappingURL=codeGeneration.js.map
\No newline at end of file