UNPKG

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