UNPKG

10.1 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5const graphql = require('graphql');
6const pluginHelpers = require('@graphql-codegen/plugin-helpers');
7const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common');
8const javaCommon = require('@graphql-codegen/java-common');
9const path = require('path');
10
11const KOTLIN_SCALARS = {
12 ID: 'Any',
13 String: 'String',
14 Boolean: 'Boolean',
15 Int: 'Int',
16 Float: 'Float',
17};
18class KotlinResolversVisitor extends visitorPluginCommon.BaseVisitor {
19 constructor(rawConfig, _schema, defaultPackageName) {
20 super(rawConfig, {
21 enumValues: rawConfig.enumValues || {},
22 listType: rawConfig.listType || 'Iterable',
23 withTypes: rawConfig.withTypes || false,
24 package: rawConfig.package || defaultPackageName,
25 scalars: visitorPluginCommon.buildScalarsFromConfig(_schema, rawConfig, KOTLIN_SCALARS),
26 });
27 this._schema = _schema;
28 }
29 getPackageName() {
30 return `package ${this.config.package}\n`;
31 }
32 getEnumValue(enumName, enumOption) {
33 if (this.config.enumValues[enumName] &&
34 typeof this.config.enumValues[enumName] === 'object' &&
35 this.config.enumValues[enumName][enumOption]) {
36 return this.config.enumValues[enumName][enumOption];
37 }
38 return enumOption;
39 }
40 EnumValueDefinition(node) {
41 return (enumName) => {
42 return visitorPluginCommon.indent(`${this.convertName(node, { useTypesPrefix: false, transformUnderscore: true })}("${this.getEnumValue(enumName, node.name.value)}")`);
43 };
44 }
45 EnumTypeDefinition(node) {
46 const comment = visitorPluginCommon.transformComment(node.description, 0);
47 const enumName = this.convertName(node.name);
48 const enumValues = visitorPluginCommon.indentMultiline(node.values.map(enumValue => enumValue(node.name.value)).join(',\n') + ';', 2);
49 return `${comment}enum class ${enumName}(val label: String) {
50${enumValues}
51
52 companion object {
53 @JvmStatic
54 fun valueOfLabel(label: String): ${enumName}? {
55 return values().find { it.label == label }
56 }
57 }
58}`;
59 }
60 resolveInputFieldType(typeNode) {
61 const innerType = visitorPluginCommon.getBaseTypeNode(typeNode);
62 const schemaType = this._schema.getType(innerType.name.value);
63 const isArray = typeNode.kind === graphql.Kind.LIST_TYPE ||
64 (typeNode.kind === graphql.Kind.NON_NULL_TYPE && typeNode.type.kind === graphql.Kind.LIST_TYPE);
65 let result = null;
66 const nullable = typeNode.kind !== graphql.Kind.NON_NULL_TYPE;
67 if (graphql.isScalarType(schemaType)) {
68 if (this.config.scalars[schemaType.name]) {
69 result = {
70 baseType: this.scalars[schemaType.name],
71 typeName: this.scalars[schemaType.name],
72 isScalar: true,
73 isArray,
74 nullable: nullable,
75 };
76 }
77 else {
78 result = { isArray, baseType: 'Any', typeName: 'Any', isScalar: true, nullable: nullable };
79 }
80 }
81 else if (graphql.isInputObjectType(schemaType)) {
82 const convertedName = this.convertName(schemaType.name);
83 const typeName = convertedName.endsWith('Input') ? convertedName : `${convertedName}Input`;
84 result = {
85 baseType: typeName,
86 typeName: typeName,
87 isScalar: false,
88 isArray,
89 nullable: nullable,
90 };
91 }
92 else if (graphql.isEnumType(schemaType) || graphql.isObjectType(schemaType)) {
93 result = {
94 isArray,
95 baseType: this.convertName(schemaType.name),
96 typeName: this.convertName(schemaType.name),
97 isScalar: true,
98 nullable: nullable,
99 };
100 }
101 else {
102 result = { isArray, baseType: 'Any', typeName: 'Any', isScalar: true, nullable: nullable };
103 }
104 if (result) {
105 result.typeName = javaCommon.wrapTypeWithModifiers(result.typeName, typeNode, this.config.listType);
106 }
107 return result;
108 }
109 buildInputTransfomer(name, inputValueArray) {
110 const classMembers = inputValueArray
111 .map(arg => {
112 const typeToUse = this.resolveInputFieldType(arg.type);
113 const initialValue = this.initialValue(typeToUse.typeName, arg.defaultValue);
114 const initial = initialValue ? ` = ${initialValue}` : typeToUse.nullable ? ' = null' : '';
115 return visitorPluginCommon.indent(`val ${arg.name.value}: ${typeToUse.typeName}${typeToUse.nullable ? '?' : ''}${initial}`, 2);
116 })
117 .join(',\n');
118 let suppress = '';
119 const ctorSet = inputValueArray
120 .map(arg => {
121 const typeToUse = this.resolveInputFieldType(arg.type);
122 const initialValue = this.initialValue(typeToUse.typeName, arg.defaultValue);
123 const fallback = initialValue ? ` ?: ${initialValue}` : '';
124 if (typeToUse.isArray && !typeToUse.isScalar) {
125 suppress = '@Suppress("UNCHECKED_CAST")\n ';
126 return visitorPluginCommon.indent(`args["${arg.name.value}"]${typeToUse.nullable || fallback ? '?' : '!!'}.let { ${arg.name.value} -> (${arg.name.value} as List<Map<String, Any>>).map { ${typeToUse.baseType}(it) } }${fallback}`, 3);
127 }
128 else if (typeToUse.isScalar) {
129 return visitorPluginCommon.indent(`args["${arg.name.value}"] as ${typeToUse.typeName}${typeToUse.nullable || fallback ? '?' : ''}${fallback}`, 3);
130 }
131 else if (typeToUse.nullable || fallback) {
132 suppress = '@Suppress("UNCHECKED_CAST")\n ';
133 return visitorPluginCommon.indent(`args["${arg.name.value}"]?.let { ${typeToUse.typeName}(it as Map<String, Any>) }${fallback}`, 3);
134 }
135 else {
136 suppress = '@Suppress("UNCHECKED_CAST")\n ';
137 return visitorPluginCommon.indent(`${typeToUse.typeName}(args["${arg.name.value}"] as Map<String, Any>)`, 3);
138 }
139 })
140 .join(',\n');
141 // language=kotlin
142 return `data class ${name}(
143${classMembers}
144) {
145 ${suppress}constructor(args: Map<String, Any>) : this(
146${ctorSet}
147 )
148}`;
149 }
150 buildTypeTransfomer(name, typeValueArray) {
151 const classMembers = typeValueArray
152 .map(arg => {
153 if (!arg.type) {
154 return '';
155 }
156 const typeToUse = this.resolveInputFieldType(arg.type);
157 return visitorPluginCommon.indent(`val ${arg.name.value}: ${typeToUse.typeName}${typeToUse.nullable ? '?' : ''}`, 2);
158 })
159 .join(',\n');
160 // language=kotlin
161 return `data class ${name}(
162${classMembers}
163)`;
164 }
165 initialValue(typeName, defaultValue) {
166 if (defaultValue) {
167 if (defaultValue.kind === 'IntValue' ||
168 defaultValue.kind === 'FloatValue' ||
169 defaultValue.kind === 'BooleanValue') {
170 return `${defaultValue.value}`;
171 }
172 else if (defaultValue.kind === 'StringValue') {
173 return `"""${defaultValue.value}""".trimIndent()`;
174 }
175 else if (defaultValue.kind === 'EnumValue') {
176 return `${typeName}.${defaultValue.value}`;
177 }
178 else if (defaultValue.kind === 'ListValue') {
179 const list = defaultValue.values
180 .map(value => {
181 return this.initialValue(typeName, value);
182 })
183 .join(', ');
184 return `listOf(${list})`;
185 }
186 // Variable
187 // ObjectValue
188 // ObjectField
189 }
190 return undefined;
191 }
192 FieldDefinition(node) {
193 if (node.arguments.length > 0) {
194 const inputTransformer = (typeName) => {
195 const transformerName = `${this.convertName(typeName, { useTypesPrefix: true })}${this.convertName(node.name.value, { useTypesPrefix: false })}Args`;
196 return this.buildInputTransfomer(transformerName, node.arguments);
197 };
198 return { node, inputTransformer };
199 }
200 return { node };
201 }
202 InputObjectTypeDefinition(node) {
203 const convertedName = this.convertName(node);
204 const name = convertedName.endsWith('Input') ? convertedName : `${convertedName}Input`;
205 return this.buildInputTransfomer(name, node.fields);
206 }
207 ObjectTypeDefinition(node) {
208 const name = this.convertName(node);
209 const fields = node.fields;
210 const fieldNodes = [];
211 const argsTypes = [];
212 fields.forEach(({ node, inputTransformer }) => {
213 if (node) {
214 fieldNodes.push(node);
215 }
216 if (inputTransformer) {
217 argsTypes.push(inputTransformer);
218 }
219 });
220 let types = argsTypes.map(f => f(node.name.value)).filter(r => r);
221 if (this.config.withTypes) {
222 types = types.concat([this.buildTypeTransfomer(name, fieldNodes)]);
223 }
224 return types.join('\n');
225 }
226}
227
228const plugin = async (schema, documents, config, { outputFile }) => {
229 const relevantPath = path.dirname(path.normalize(outputFile));
230 const defaultPackageName = javaCommon.buildPackageNameFromPath(relevantPath);
231 const visitor = new KotlinResolversVisitor(config, schema, defaultPackageName);
232 const astNode = pluginHelpers.getCachedDocumentNodeFromSchema(schema);
233 const visitorResult = graphql.visit(astNode, { leave: visitor });
234 const packageName = visitor.getPackageName();
235 const blockContent = visitorResult.definitions.filter(d => typeof d === 'string').join('\n\n');
236 return [packageName, blockContent].join('\n');
237};
238
239exports.plugin = plugin;