@giraphql/converter
Version:
A converter for generating GiraphQL SchemaBuilder code from GraphQL SDL
616 lines • 24.5 kB
JavaScript
;
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