UNPKG

@giraphql/converter

Version:

A converter for generating GiraphQL SchemaBuilder code from GraphQL SDL

616 lines 24.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const graphql_1 = require("graphql"); const ts_morph_1 = require("ts-morph"); const builtins = ['Boolean', 'Int', 'Float', 'String', 'ID']; function unwrap(type) { if (type instanceof graphql_1.GraphQLNonNull) { return unwrap(type.ofType); } if (type instanceof graphql_1.GraphQLList) { return unwrap(type.ofType); } return type; } function isRecursive(type, seen = []) { if (!(type instanceof graphql_1.GraphQLObjectType || type instanceof graphql_1.GraphQLInputObjectType || type instanceof graphql_1.GraphQLInterfaceType)) { return false; } const fieldMap = type.getFields(); const fields = Object.keys(fieldMap).map((name) => fieldMap[name]); return fields.some((field) => { const fieldType = unwrap(field.type); if (fieldType.name === type.name) { return true; } if (seen.includes(fieldType.name)) { return true; } return isRecursive(fieldType, [...seen, type.name]); }); } class GiraphQLConverter { constructor(schema, { types } = {}) { this.project = new ts_morph_1.Project(); this.schema = schema; this.sourcefile = this.project.createSourceFile('./codegen/schema.ts'); this.types = types !== null && types !== void 0 ? types : null; this.createSchemaTypes(); } createSchemaTypes() { const typeMap = this.schema.getTypeMap(); const gqlTypes = Object.keys(typeMap).map((typeName) => typeMap[typeName]); if (!this.types) { this.sourcefile.addImportDeclaration({ kind: ts_morph_1.StructureKind.ImportDeclaration, moduleSpecifier: '@giraphql/core', defaultImport: 'SchemaBuilder', }); this.sourcefile.addStatements((writer) => writer.blankLine()); this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: 'builder', initializer: (writer) => { writer.writeLine('new SchemaBuilder<{'); writer.indent(() => { this.writeTypeInfo(writer); }); writer.writeLine('}>({})'); }, }, ], }); } gqlTypes.forEach((type) => { if (type.name.startsWith('__') || builtins.includes(type.name)) { return; } if (this.types && !this.types.includes(type.name)) { return; } if (type instanceof graphql_1.GraphQLUnionType) { this.unionType(type); } else if (type instanceof graphql_1.GraphQLEnumType) { this.enumType(type); } else if (type instanceof graphql_1.GraphQLScalarType) { this.scalarType(type); } }); gqlTypes.forEach((type) => { if (this.types && !this.types.includes(type.name)) { return; } if (type instanceof graphql_1.GraphQLInputObjectType) { this.inputType(type); } }); gqlTypes.forEach((type) => { if (this.types && !this.types.includes(type.name)) { return; } if (this.types && !this.types.includes(type.name)) { return; } if (type instanceof graphql_1.GraphQLInterfaceType) { this.interfaceType(type); } }); gqlTypes.forEach((type) => { if (this.types && !this.types.includes(type.name)) { return; } if (type.name.startsWith('__')) { return; } if (type instanceof graphql_1.GraphQLObjectType) { switch (type.name) { case 'Query': { this.queryType(type); break; } case 'Mutation': { this.mutationType(type); break; } case 'Subscription': { this.subscriptionType(type); break; } default: { this.objectType(type); } } } }); if (!this.types) { this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, isExported: true, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: 'schema', initializer: (writer) => { writer.writeLine('builder.toSchema({})'); }, }, ], }); } } queryType(type) { this.sourcefile.addStatements((writer) => { writer.writeLine('builder.queryType({'); writer.indent(() => { this.writeDescription(writer, type); this.writeObjectShape(writer, type); }); writer.writeLine('})'); }); } mutationType(type) { this.sourcefile.addStatements((writer) => { writer.writeLine('builder.mutationType({'); writer.indent(() => { this.writeDescription(writer, type); this.writeObjectShape(writer, type); }); writer.writeLine('})'); }); } subscriptionType(type) { this.sourcefile.addStatements((writer) => { writer.writeLine('builder.subscriptionType({'); writer.indent(() => { this.writeDescription(writer, type); this.writeObjectShape(writer, type); }); writer.writeLine('})'); }); } objectType(type) { this.sourcefile.addStatements((writer) => { writer.writeLine(`builder.objectType('${type.name}', {`); writer.indent(() => { this.writeDescription(writer, type); if (type.getInterfaces().length > 0) { writer.writeLine(`interfaces: [${type .getInterfaces() .map((i) => i.name) .join(', ')}],`); writer.writeLine(`isTypeOf: (obj, context, info) => { throw new Error('Not implemented') },`); } this.writeObjectShape(writer, type); }); writer.writeLine('})'); }); } interfaceType(type) { this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: type.name, initializer: (writer) => { writer.writeLine(`builder.interfaceType('${type.name}', {`); writer.indent(() => { this.writeDescription(writer, type); this.writeObjectShape(writer, type); }); writer.writeLine('})'); }, }, ], }); } unionType(type) { this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: type.name, initializer: (writer) => { writer.writeLine(`builder.unionType('${type.name}', {`); writer.indent(() => { this.writeDescription(writer, type); writer.writeLine(`types: [${type.getTypes().map((t) => `'${t.name}'`)}],`); writer.writeLine(`resolveType: (parent, context, info) => throw new Error('Not implemented')`); }); writer.writeLine('})'); }, }, ], }); } scalarType(type) { this.sourcefile.addStatements((writer) => { writer.writeLine(`builder.scalarType('${type.name}', {`); writer.indent(() => { writer.writeLine(`serialize: () => { throw new Error('Not implemented') },`); }); writer.writeLine('})'); }); } inputType(type) { const recursive = isRecursive(type); if (recursive) { this.inputTypeShape(type); this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: type.name, initializer: (writer) => { writer.writeLine(`builder.inputRef<${type.name}Shape>('${type.name}')`); }, }, ], }); this.sourcefile.addStatements((writer) => { writer.newLine(); writer.writeLine(`${type.name}.implement({`); writer.indent(() => { this.writeDescription(writer, type); this.writeInputShape(writer, type); }); writer.writeLine('})'); }); } else { this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: type.name, initializer: (writer) => { writer.writeLine(`builder.inputType('${type.name}', {`); writer.indent(() => { this.writeDescription(writer, type); this.writeInputShape(writer, type); }); writer.writeLine('})'); }, }, ], }); } } inputTypeShape(type) { const fieldMap = type.getFields(); const fields = Object.keys(fieldMap).map((name) => fieldMap[name]); this.sourcefile.addInterface({ kind: ts_morph_1.StructureKind.Interface, name: `${type.name}Shape`, properties: fields.map((field) => ({ name: field.name, type: (writer) => void this.writeInputFieldShape(writer, field.type, type), })), }); } enumType(type) { this.sourcefile.addVariableStatement({ kind: ts_morph_1.StructureKind.VariableStatement, declarationKind: ts_morph_1.VariableDeclarationKind.Const, declarations: [ { kind: ts_morph_1.StructureKind.VariableDeclaration, name: type.name, initializer: (writer) => { writer.writeLine(`builder.enumType('${type.name}', {`); writer.indent(() => { this.writeDescription(writer, type); writer.writeLine('values: {'); writer.indent(() => { type.getValues().forEach((value) => { writer.writeLine(`${value.name}: {`); writer.indent(() => { this.writeDescription(writer, value); writer.write(`value: `); if (value.value) { writer.write(typeof value.value === 'number' ? `${value.value} as const,` : `'${value.value}' as const,`); } else { writer.write(value.name); } writer.newLine(); }); writer.writeLine('},'); }); }); writer.writeLine('},'); }); writer.writeLine('})'); }, }, ], }); } writeObjectShape(writer, type) { const fieldMap = type.getFields(); const inheritedFields = type instanceof graphql_1.GraphQLObjectType ? type.getInterfaces().flatMap((i) => Object.keys(i.getFields())) : []; const fields = Object.keys(fieldMap).map((f) => fieldMap[f]); writer.writeLine('fields: t => ({'); writer.indent(() => { fields.forEach((field) => { if (inheritedFields.includes(field.name)) { return; } writer.writeLine(`${field.name}: t.field({`); writer.indent(() => { this.writeDescription(writer, field); this.writeArgs(writer, field); writer.write('type: '); this.writeType(writer, field.type); writer.writeLine(','); this.writeNullability(writer, field.type); writer.writeLine(`resolve: (parent, args, context, info) => { throw new Error('Not implemented') },`); if (type.name === 'Subscription') { writer.writeLine(`subscribe: (parent, args, context, info) => { throw new Error('Not implemented') },`); } }); writer.writeLine('})'); }); }); writer.writeLine('}),'); } writeInputShape(writer, type) { writer.writeLine('fields: t => ({'); writer.indent(() => { const fieldMap = type.getFields(); const fields = Object.keys(fieldMap).map((name) => fieldMap[name]); fields.forEach((field) => { writer.writeLine(`${field.name}: t.field({`); writer.indent(() => { writer.write('type: '); this.writeType(writer, field.type); writer.write(','); writer.newLine(); this.writeDescription(writer, field); writer.write('required: '); this.writeRequiredness(writer, field.type); writer.write(','); writer.newLine(); }); writer.writeLine('}),'); }); }); writer.writeLine('}),'); } writeDescription(writer, type) { if (type.description) { writer.write('description:'); writer.quote(type.description); writer.writeLine(','); } } writeType(writer, type) { if (type instanceof graphql_1.GraphQLNonNull) { this.writeType(writer, type.ofType); return; } if (type instanceof graphql_1.GraphQLList) { writer.write('['); this.writeType(writer, type.ofType); writer.write(']'); return; } if (type instanceof graphql_1.GraphQLScalarType || type instanceof graphql_1.GraphQLObjectType || type instanceof graphql_1.GraphQLInterfaceType) { writer.write(`'${type.name}'`); return; } writer.write(type.name); } writeInputFieldShape(writer, wrappedType, rootType) { const type = unwrap(wrappedType); if (wrappedType instanceof graphql_1.GraphQLNonNull && wrappedType.ofType instanceof graphql_1.GraphQLList) { this.writeInputFieldShape(writer, wrappedType.ofType.ofType, rootType); writer.write('[]'); } else if (wrappedType instanceof graphql_1.GraphQLList) { this.writeInputFieldShape(writer, wrappedType.ofType, rootType); writer.write('[]'); } else if (type instanceof graphql_1.GraphQLScalarType) { switch (type.name) { case 'String': writer.write('string'); break; case 'Int': writer.write('number'); break; case 'Float': writer.write('number'); break; case 'ID': writer.write('(string | number)'); break; case 'Boolean': writer.write('boolean'); break; default: writer.write('unknown'); } } else if (type.name === rootType.name) { writer.write(`${rootType.name}Shape`); } else if (type instanceof graphql_1.GraphQLInputObjectType) { if (isRecursive(type)) { writer.write(`${rootType.name}Shape`); throw new Error(type.toString()); } else { writer.write('{'); writer.newLine(); writer.indent(() => { const fieldMap = type.getFields(); const fields = Object.keys(fieldMap).map((name) => fieldMap[name]); fields.forEach((field) => { writer.write(`${field.name}: `); this.writeInputFieldShape(writer, field.type, rootType); writer.write(';'); writer.newLine(); }); }); writer.newLine(); writer.write('}'); } } else if (type instanceof graphql_1.GraphQLEnumType) { writer.write(type .getValues() .map(({ value }) => (typeof value === 'string' ? `'${value}'` : `${value}`)) .join(' | ')); } else { writer.write(rootType.name); } if (!(wrappedType instanceof graphql_1.GraphQLNonNull)) { writer.write('| null | undefined'); } } writeNullability(writer, type) { if (type instanceof graphql_1.GraphQLNonNull) { if (type.ofType instanceof graphql_1.GraphQLList && !(type.ofType.ofType instanceof graphql_1.GraphQLNonNull)) { writer.write('nullable: { list: false, items: true },'); } } else if (type instanceof graphql_1.GraphQLList) { if (type.ofType instanceof graphql_1.GraphQLNonNull) { writer.write('nullable: true,'); } else { writer.write(`nullable: { list: true, items: true },`); } } else { writer.write('nullable: true,'); } } writeRequiredness(writer, type) { if (type instanceof graphql_1.GraphQLNonNull) { if (type.ofType instanceof graphql_1.GraphQLList) { if (type.ofType.ofType instanceof graphql_1.GraphQLNonNull) { writer.write(`{ list: true, items: true }`); } else { writer.write(`{ list: true, items: false }`); } } else { writer.write('true'); } } else if (type instanceof graphql_1.GraphQLList) { if (type.ofType instanceof graphql_1.GraphQLNonNull) { writer.write(`{ list: false, items: true }`); } else { writer.write(`{ list: false, items: false }`); } } else { writer.write('false'); } } writeArgs(writer, type) { if (type.args.length > 0) { writer.write('args: {'); writer.indent(() => { type.args.forEach((arg) => { writer.writeLine(`${arg.name}: t.arg({`); writer.indent(() => { writer.write('type: '); this.writeType(writer, arg.type); writer.write(','); writer.newLine(); this.writeDescription(writer, arg); writer.write('required: '); this.writeRequiredness(writer, arg.type); writer.write(','); writer.newLine(); if (arg.defaultValue != null) { writer.write(`defaultValue: ${JSON.stringify(arg.defaultValue)}`); writer.write(','); writer.newLine(); } }); writer.newLine(); writer.writeLine('}),'); }); }); writer.writeLine('},'); } } writeTypeInfo(writer) { const typeMap = this.schema.getTypeMap(); const gqlTypes = Object.keys(typeMap) .map((typeName) => typeMap[typeName]) .filter((type) => !type.name.startsWith('__') && !builtins.includes(type.name)); writer.writeLine('Context: {}'); const objects = gqlTypes.filter((type) => type instanceof graphql_1.GraphQLObjectType && type.name !== 'Query' && type.name !== 'Mutation' && type.name !== 'Subscription'); if (objects.length > 0) { writer.writeLine('Objects: {'); writer.indent(() => { objects.forEach((type) => { writer.writeLine(`${type.name}: unknown,`); }); }); writer.writeLine('},'); } const interfaces = gqlTypes.filter((type) => type instanceof graphql_1.GraphQLInterfaceType); if (interfaces.length > 0) { writer.writeLine('Interfaces: {'); writer.indent(() => { interfaces.forEach((type) => { writer.writeLine(`${type.name}: unknown,`); }); }); writer.writeLine('},'); } const scalars = gqlTypes.filter((type) => type instanceof graphql_1.GraphQLScalarType); if (scalars.length > 0) { writer.writeLine('Scalars: {'); writer.indent(() => { scalars.forEach((type) => { writer.writeLine(`${type.name}: { Input: unknown, Output: unknown },`); }); }); writer.writeLine('},'); } } toString() { return this.sourcefile.print(); } async saveAs(filePath) { await this.sourcefile.copyImmediately(filePath); } } exports.default = GiraphQLConverter; //# sourceMappingURL=index.js.map