UNPKG

15.6 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7const pluginHelpers = require('@graphql-codegen/plugin-helpers');
8const graphql = require('graphql');
9const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common');
10const autoBind = _interopDefault(require('auto-bind'));
11const typescript = require('@graphql-codegen/typescript');
12
13const MAYBE_REGEX = /^Maybe<(.*?)>$/;
14const ARRAY_REGEX = /^Array<(.*?)>$/;
15const SCALAR_REGEX = /^Scalars\['(.*?)'\]$/;
16const GRAPHQL_TYPES = ['Query', 'Mutation', 'Subscription'];
17const SCALARS = ['ID', 'String', 'Boolean', 'Int', 'Float'];
18const TYPE_GRAPHQL_SCALARS = ['ID', 'Int', 'Float'];
19function escapeString(str) {
20 return ("'" +
21 String(str || '')
22 .replace(/\\/g, '\\\\')
23 .replace(/\n/g, '\\n')
24 .replace(/'/g, "\\'") +
25 "'");
26}
27function formatDecoratorOptions(options, isFirstArgument = true) {
28 if (!Object.keys(options).length) {
29 return '';
30 }
31 else {
32 return ((isFirstArgument ? '' : ', ') +
33 ('{ ' +
34 Object.entries(options)
35 .map(([key, value]) => `${key}: ${value}`)
36 .join(', ') +
37 ' }'));
38 }
39}
40const FIX_DECORATOR_SIGNATURE = `type FixDecorator<T> = T;`;
41function getTypeGraphQLNullableValue(type) {
42 if (type.isNullable) {
43 if (type.isItemsNullable) {
44 return "'itemsAndList'";
45 }
46 else {
47 return 'true';
48 }
49 }
50 else if (type.isItemsNullable) {
51 return "'items'";
52 }
53 return undefined;
54}
55class TypeGraphQLVisitor extends typescript.TsVisitor {
56 constructor(schema, pluginConfig, additionalConfig = {}) {
57 super(schema, pluginConfig, {
58 avoidOptionals: pluginConfig.avoidOptionals || false,
59 maybeValue: pluginConfig.maybeValue || 'T | null',
60 constEnums: pluginConfig.constEnums || false,
61 enumsAsTypes: pluginConfig.enumsAsTypes || false,
62 immutableTypes: pluginConfig.immutableTypes || false,
63 declarationKind: {
64 type: 'class',
65 interface: 'abstract class',
66 arguments: 'class',
67 input: 'class',
68 scalar: 'type',
69 },
70 decoratorName: {
71 type: 'ObjectType',
72 interface: 'InterfaceType',
73 arguments: 'ArgsType',
74 field: 'Field',
75 input: 'InputType',
76 ...(pluginConfig.decoratorName || {}),
77 },
78 decorateTypes: pluginConfig.decorateTypes || undefined,
79 ...(additionalConfig || {}),
80 });
81 autoBind(this);
82 this.typescriptVisitor = new typescript.TsVisitor(schema, pluginConfig, additionalConfig);
83 const enumNames = Object.values(schema.getTypeMap())
84 .map(type => (type instanceof graphql.GraphQLEnumType ? type.name : undefined))
85 .filter(t => t);
86 this.setArgumentsTransformer(new typescript.TypeScriptOperationVariablesToObject(this.scalars, this.convertName, this.config.avoidOptionals, this.config.immutableTypes, null, enumNames, this.config.enumPrefix, this.config.enumValues));
87 this.setDeclarationBlockConfig({
88 enumNameValueSeparator: ' =',
89 });
90 }
91 getDecoratorOptions(node) {
92 const decoratorOptions = {};
93 if (node.description) {
94 // Add description as TypeGraphQL description instead of comment
95 decoratorOptions.description = escapeString(node.description);
96 node.description = undefined;
97 }
98 return decoratorOptions;
99 }
100 getWrapperDefinitions() {
101 return [...super.getWrapperDefinitions(), this.getFixDecoratorDefinition()];
102 }
103 getFixDecoratorDefinition() {
104 return `${this.getExportPrefix()}${FIX_DECORATOR_SIGNATURE}`;
105 }
106 buildArgumentsBlock(node) {
107 const fieldsWithArguments = node.fields.filter(field => field.arguments && field.arguments.length > 0) || [];
108 return fieldsWithArguments
109 .map(field => {
110 const name = node.name.value +
111 (this.config.addUnderscoreToArgsType ? '_' : '') +
112 this.convertName(field, {
113 useTypesPrefix: false,
114 useTypesSuffix: false,
115 }) +
116 'Args';
117 if (this.hasTypeDecorators(name)) {
118 return this.getArgumentsObjectTypeDefinition(node, name, field);
119 }
120 else {
121 return this.typescriptVisitor.getArgumentsObjectTypeDefinition(node, name, field);
122 }
123 })
124 .join('\n\n');
125 }
126 ObjectTypeDefinition(node, key, parent) {
127 const isGraphQLType = GRAPHQL_TYPES.includes(node.name);
128 if (!isGraphQLType && !this.hasTypeDecorators(node.name)) {
129 return this.typescriptVisitor.ObjectTypeDefinition(node, key, parent);
130 }
131 const typeDecorator = this.config.decoratorName.type;
132 const originalNode = parent[key];
133 const decoratorOptions = this.getDecoratorOptions(node);
134 let declarationBlock;
135 if (isGraphQLType) {
136 declarationBlock = this.typescriptVisitor.getObjectTypeDeclarationBlock(node, originalNode);
137 }
138 else {
139 declarationBlock = this.getObjectTypeDeclarationBlock(node, originalNode);
140 // Add type-graphql ObjectType decorator
141 const interfaces = originalNode.interfaces.map(i => this.convertName(i));
142 if (interfaces.length > 1) {
143 decoratorOptions.implements = `[${interfaces.join(', ')}]`;
144 }
145 else if (interfaces.length === 1) {
146 decoratorOptions.implements = interfaces[0];
147 }
148 declarationBlock = declarationBlock.withDecorator(`@TypeGraphQL.${typeDecorator}(${formatDecoratorOptions(decoratorOptions)})`);
149 }
150 return [declarationBlock.string, this.buildArgumentsBlock(originalNode)].filter(f => f).join('\n\n');
151 }
152 InputObjectTypeDefinition(node) {
153 if (!this.hasTypeDecorators(node.name)) {
154 return this.typescriptVisitor.InputObjectTypeDefinition(node);
155 }
156 const typeDecorator = this.config.decoratorName.input;
157 const decoratorOptions = this.getDecoratorOptions(node);
158 let declarationBlock = this.getInputObjectDeclarationBlock(node);
159 // Add type-graphql InputType decorator
160 declarationBlock = declarationBlock.withDecorator(`@TypeGraphQL.${typeDecorator}(${formatDecoratorOptions(decoratorOptions)})`);
161 return declarationBlock.string;
162 }
163 getArgumentsObjectDeclarationBlock(node, name, field) {
164 return new visitorPluginCommon.DeclarationBlock(this._declarationBlockConfig)
165 .export()
166 .asKind(this._parsedConfig.declarationKind.arguments)
167 .withName(this.convertName(name))
168 .withComment(node.description)
169 .withBlock(field.arguments.map(argument => this.InputValueDefinition(argument)).join('\n'));
170 }
171 getArgumentsObjectTypeDefinition(node, name, field) {
172 const typeDecorator = this.config.decoratorName.arguments;
173 let declarationBlock = this.getArgumentsObjectDeclarationBlock(node, name, field);
174 // Add type-graphql Args decorator
175 declarationBlock = declarationBlock.withDecorator(`@TypeGraphQL.${typeDecorator}()`);
176 return declarationBlock.string;
177 }
178 InterfaceTypeDefinition(node, key, parent) {
179 if (!this.hasTypeDecorators(node.name)) {
180 return this.typescriptVisitor.InterfaceTypeDefinition(node, key, parent);
181 }
182 const interfaceDecorator = this.config.decoratorName.interface;
183 const originalNode = parent[key];
184 const decoratorOptions = this.getDecoratorOptions(node);
185 const declarationBlock = this.getInterfaceTypeDeclarationBlock(node, originalNode).withDecorator(`@TypeGraphQL.${interfaceDecorator}(${formatDecoratorOptions(decoratorOptions)})`);
186 return [declarationBlock.string, this.buildArgumentsBlock(originalNode)].filter(f => f).join('\n\n');
187 }
188 buildTypeString(type) {
189 if (!type.isArray && !type.isScalar && !type.isNullable) {
190 type.type = `FixDecorator<${type.type}>`;
191 }
192 if (type.isScalar) {
193 type.type = `Scalars['${type.type}']`;
194 }
195 if (type.isArray) {
196 type.type = `Array<${type.type}>`;
197 }
198 if (type.isNullable) {
199 type.type = `Maybe<${type.type}>`;
200 }
201 return type.type;
202 }
203 parseType(rawType) {
204 const typeNode = rawType;
205 if (typeNode.kind === 'NamedType') {
206 return {
207 type: typeNode.name.value,
208 isNullable: true,
209 isArray: false,
210 isItemsNullable: false,
211 isScalar: SCALARS.includes(typeNode.name.value),
212 };
213 }
214 else if (typeNode.kind === 'NonNullType') {
215 return {
216 ...this.parseType(typeNode.type),
217 isNullable: false,
218 };
219 }
220 else if (typeNode.kind === 'ListType') {
221 return {
222 ...this.parseType(typeNode.type),
223 isArray: true,
224 isNullable: true,
225 };
226 }
227 const isNullable = !!rawType.match(MAYBE_REGEX);
228 const nonNullableType = rawType.replace(MAYBE_REGEX, '$1');
229 const isArray = !!nonNullableType.match(ARRAY_REGEX);
230 const singularType = nonNullableType.replace(ARRAY_REGEX, '$1');
231 const isSingularTypeNullable = !!singularType.match(MAYBE_REGEX);
232 const singularNonNullableType = singularType.replace(MAYBE_REGEX, '$1');
233 const isScalar = !!singularNonNullableType.match(SCALAR_REGEX);
234 const type = singularNonNullableType.replace(SCALAR_REGEX, (match, type) => {
235 if (TYPE_GRAPHQL_SCALARS.includes(type)) {
236 // This is a TypeGraphQL type
237 return `TypeGraphQL.${type}`;
238 }
239 else if (global[type]) {
240 // This is a JS native type
241 return type;
242 }
243 else if (this.scalars[type]) {
244 // This is a type specified in the configuration
245 return this.scalars[type];
246 }
247 else {
248 throw new Error(`Unknown scalar type ${type}`);
249 }
250 });
251 return { type, isNullable, isArray, isScalar, isItemsNullable: isArray && isSingularTypeNullable };
252 }
253 fixDecorator(type, typeString) {
254 return type.isArray || type.isNullable || type.isScalar ? typeString : `FixDecorator<${typeString}>`;
255 }
256 FieldDefinition(node, key, parent, path, ancestors) {
257 const parentName = ancestors === null || ancestors === void 0 ? void 0 : ancestors[ancestors.length - 1].name.value;
258 if (!this.hasTypeDecorators(parentName)) {
259 return this.typescriptVisitor.FieldDefinition(node, key, parent);
260 }
261 const fieldDecorator = this.config.decoratorName.field;
262 let typeString = node.type;
263 const type = this.parseType(typeString);
264 const decoratorOptions = this.getDecoratorOptions(node);
265 const nullableValue = getTypeGraphQLNullableValue(type);
266 if (nullableValue) {
267 decoratorOptions.nullable = nullableValue;
268 }
269 const decorator = '\n' +
270 visitorPluginCommon.indent(`@TypeGraphQL.${fieldDecorator}(type => ${type.isArray ? `[${type.type}]` : type.type}${formatDecoratorOptions(decoratorOptions, false)})`) +
271 '\n';
272 typeString = this.fixDecorator(type, typeString);
273 return decorator + visitorPluginCommon.indent(`${this.config.immutableTypes ? 'readonly ' : ''}${node.name}!: ${typeString};`);
274 }
275 InputValueDefinition(node, key, parent, path, ancestors) {
276 const parentName = ancestors === null || ancestors === void 0 ? void 0 : ancestors[ancestors.length - 1].name.value;
277 if (parent && !this.hasTypeDecorators(parentName)) {
278 return this.typescriptVisitor.InputValueDefinition(node, key, parent);
279 }
280 const fieldDecorator = this.config.decoratorName.field;
281 const rawType = node.type;
282 const type = this.parseType(rawType);
283 const typeGraphQLType = type.isScalar && TYPE_GRAPHQL_SCALARS.includes(type.type) ? `TypeGraphQL.${type.type}` : type.type;
284 const decoratorOptions = this.getDecoratorOptions(node);
285 const nullableValue = getTypeGraphQLNullableValue(type);
286 if (nullableValue) {
287 decoratorOptions.nullable = nullableValue;
288 }
289 const decorator = '\n' +
290 visitorPluginCommon.indent(`@TypeGraphQL.${fieldDecorator}(type => ${type.isArray ? `[${typeGraphQLType}]` : typeGraphQLType}${formatDecoratorOptions(decoratorOptions, false)})`) +
291 '\n';
292 const nameString = node.name.kind ? node.name.value : node.name;
293 const typeString = rawType.kind
294 ? this.buildTypeString(type)
295 : this.fixDecorator(type, rawType);
296 return decorator + visitorPluginCommon.indent(`${this.config.immutableTypes ? 'readonly ' : ''}${nameString}!: ${typeString};`);
297 }
298 EnumTypeDefinition(node) {
299 if (!this.hasTypeDecorators(node.name)) {
300 return this.typescriptVisitor.EnumTypeDefinition(node);
301 }
302 return (super.EnumTypeDefinition(node) +
303 `TypeGraphQL.registerEnumType(${this.convertName(node)}, { name: '${this.convertName(node)}' });\n`);
304 }
305 clearOptional(str) {
306 if (str.startsWith('Maybe')) {
307 return str.replace(/Maybe<(.*?)>$/, '$1');
308 }
309 return str;
310 }
311 hasTypeDecorators(typeName) {
312 if (GRAPHQL_TYPES.includes(typeName)) {
313 return false;
314 }
315 if (!this.config.decorateTypes) {
316 return true;
317 }
318 return this.config.decorateTypes.some(filter => filter === typeName);
319 }
320}
321
322const TYPE_GRAPHQL_IMPORT = `import * as TypeGraphQL from 'type-graphql';\nexport { TypeGraphQL };`;
323const isDefinitionInterface = (definition) => definition.includes('@TypeGraphQL.InterfaceType()');
324const plugin = (schema, documents, config) => {
325 const visitor = new TypeGraphQLVisitor(schema, config);
326 const astNode = pluginHelpers.getCachedDocumentNodeFromSchema(schema);
327 const visitorResult = graphql.visit(astNode, { leave: visitor });
328 const introspectionDefinitions = typescript.includeIntrospectionDefinitions(schema, documents, config);
329 const scalars = visitor.scalarsDefinition;
330 const definitions = visitorResult.definitions;
331 // Sort output by interfaces first, classes last to prevent TypeScript errors
332 definitions.sort((definition1, definition2) => +isDefinitionInterface(definition2) - +isDefinitionInterface(definition1));
333 return {
334 prepend: [...visitor.getEnumsImports(), ...visitor.getWrapperDefinitions(), TYPE_GRAPHQL_IMPORT],
335 content: [scalars, ...definitions, ...introspectionDefinitions].join('\n'),
336 };
337};
338
339Object.defineProperty(exports, 'TsIntrospectionVisitor', {
340 enumerable: true,
341 get: function () {
342 return typescript.TsIntrospectionVisitor;
343 }
344});
345exports.TypeGraphQLVisitor = TypeGraphQLVisitor;
346exports.plugin = plugin;