1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | const graphql = require('graphql');
|
6 | const pluginHelpers = require('@graphql-codegen/plugin-helpers');
|
7 | const visitorPluginCommon = require('@graphql-codegen/visitor-plugin-common');
|
8 | const javaCommon = require('@graphql-codegen/java-common');
|
9 | const path = require('path');
|
10 |
|
11 | const KOTLIN_SCALARS = {
|
12 | ID: 'Any',
|
13 | String: 'String',
|
14 | Boolean: 'Boolean',
|
15 | Int: 'Int',
|
16 | Float: 'Float',
|
17 | };
|
18 | class 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 |
|
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 |
|
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 |
|
187 |
|
188 |
|
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 |
|
228 | const 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 |
|
239 | exports.plugin = plugin;
|