UNPKG

4.38 kBPlain TextView Raw
1import {
2 GraphQLEnumType,
3 GraphQLInputObjectType,
4 GraphQLType
5} from 'graphql';
6
7import {
8 CompilerOptions
9} from 'apollo-codegen-core/lib/compiler';
10
11import { commentBlockContent } from 'apollo-codegen-core/lib/utilities/printing';
12
13import {
14 sortEnumValues
15} from 'apollo-codegen-core/lib/utilities/graphql';
16
17import { createTypeFromGraphQLTypeFunction, } from './helpers';
18
19import * as t from '@babel/types';
20
21export type ObjectProperty = {
22 name: string,
23 description?: string | null | undefined,
24 type: t.TSType
25}
26
27export interface TypescriptCompilerOptions extends CompilerOptions {
28 // Leaving this open for Typescript only compiler options
29}
30
31export default class TypescriptGenerator {
32 options: TypescriptCompilerOptions
33 typeFromGraphQLType: Function
34
35 constructor(compilerOptions: TypescriptCompilerOptions) {
36 this.options = compilerOptions;
37
38 this.typeFromGraphQLType = createTypeFromGraphQLTypeFunction(compilerOptions);
39 }
40
41 public enumerationDeclaration(type: GraphQLEnumType) {
42 const { name, description } = type;
43 const enumMembers = sortEnumValues(type.getValues()).map(({ value }) => {
44 return t.TSEnumMember(
45 t.identifier(value),
46 t.stringLiteral(value)
47 );
48 });
49
50 const typeAlias = t.exportNamedDeclaration(
51 t.TSEnumDeclaration(
52 t.identifier(name),
53 enumMembers
54 ),
55 []
56 );
57
58 if (description) {
59 typeAlias.leadingComments = [{
60 type: 'CommentBlock',
61 value: commentBlockContent(description)
62 } as t.CommentBlock];
63 }
64
65 return typeAlias;
66 }
67
68 public inputObjectDeclaration(inputObjectType: GraphQLInputObjectType) {
69 const { name, description } = inputObjectType;
70
71 const fieldMap = inputObjectType.getFields();
72 const fields: ObjectProperty[] = Object.keys(inputObjectType.getFields())
73 .map((fieldName: string) => {
74 const field = fieldMap[fieldName];
75 return {
76 name: fieldName,
77 type: this.typeFromGraphQLType(field.type)
78 }
79 });
80
81 const inputType = t.exportNamedDeclaration(this.interface(name, fields, {
82 keyInheritsNullability: true
83 }), []);
84
85 if (description) {
86 inputType.leadingComments = [{
87 type: 'CommentBlock',
88 value: commentBlockContent(description)
89 } as t.CommentBlock]
90 }
91
92 return inputType;
93 }
94
95 public typesForProperties(fields: ObjectProperty[], {
96 keyInheritsNullability = false
97 } : {
98 keyInheritsNullability?: boolean
99 } = {}) {
100
101 return fields.map(({name, description, type}) => {
102 const propertySignatureType = t.TSPropertySignature(
103 t.identifier(name),
104 t.TSTypeAnnotation(type)
105 );
106
107 // TODO: Check if this works
108 propertySignatureType.optional = keyInheritsNullability && this.isNullableType(type);
109
110 if (description) {
111 propertySignatureType.leadingComments = [{
112 type: 'CommentBlock',
113 value: commentBlockContent(description)
114 } as t.CommentBlock]
115 }
116
117 return propertySignatureType;
118 });
119 }
120
121 public interface(name: string, fields: ObjectProperty[], {
122 keyInheritsNullability = false
123 }: {
124 keyInheritsNullability?: boolean
125 } = {}) {
126
127 return t.TSInterfaceDeclaration(
128 t.identifier(name),
129 undefined,
130 undefined,
131 t.TSInterfaceBody(
132 this.typesForProperties(fields, {
133 keyInheritsNullability
134 })
135 )
136 );
137 }
138
139 public typeAliasGenericUnion(name: string, members: t.TSType[]) {
140 return t.TSTypeAliasDeclaration(
141 t.identifier(name),
142 undefined,
143 t.TSUnionType(
144 members
145 )
146 );
147 }
148
149 public exportDeclaration(declaration: t.Declaration) {
150 return t.exportNamedDeclaration(declaration, []);
151 }
152
153 public nameFromScopeStack(scope: string[]) {
154 return scope.join('_');
155 }
156
157 public makeNullableType(type: t.TSType) {
158 return t.TSUnionType([
159 type,
160 t.TSNullKeyword()
161 ])
162 }
163
164 public isNullableType(type: t.TSType) {
165 return t.isTSUnionType(type) && type.types.some(type => t.isTSNullKeyword(type));
166 }
167
168 public import(types: GraphQLType[], source: string) {
169 return t.importDeclaration(
170 types.map((type) => t.importSpecifier(
171 t.identifier(type.toString()),
172 t.identifier(type.toString()),
173 )),
174 t.stringLiteral(source)
175 );
176 }
177}