Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
hayes committed Mar 22, 2023
1 parent 3d14bfd commit fb9eb16
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 8 deletions.
8 changes: 4 additions & 4 deletions packages/plugin-add-graphql/src/global-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ declare global {
addGraphQLUnion: <Shape>(
type: GraphQLUnionType,
options: AddGraphQLUnionTypeOptions<Types, ObjectRef<Shape>>,
) => UnionRef<unknown>;
) => UnionRef<Shape>;

addGraphQLEnum: <Shape>(
addGraphQLEnum: <Shape extends string | number>(
options: AddGraphQLEnumTypeOptions<Types, EnumValuesWithShape<Types, Shape>>,
) => EnumRef<unknown>;
) => EnumRef<Shape>;

addGraphQLInput: <Shape extends {}>(
options: AddGraphQLInputTypeOptions<Types, Shape>,
) => InputTypeRef<unknown>;
) => InputTypeRef<Shape>;
}
}
}
217 changes: 213 additions & 4 deletions packages/plugin-add-graphql/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
import './global-types';
import { GraphQLObjectType } from 'graphql';
import SchemaBuilder, { BasePlugin, SchemaTypes } from '@pothos/core';
import { AddGraphQLObjectTypeOptions } from './types';
import {
getNamedType,
GraphQLEnumType,
GraphQLInputType,
GraphQLInterfaceType,
GraphQLNamedInputType,
GraphQLNamedOutputType,
GraphQLObjectType,
GraphQLOutputType,
isListType,
isNonNullType,
} from 'graphql';
import SchemaBuilder, {
BasePlugin,
InputFieldRef,
InputType,
InputTypeParam,
OutputType,
SchemaTypes,
TypeParam,
} from '@pothos/core';
import {
AddGraphQLEnumTypeOptions,
AddGraphQLInterfaceTypeOptions,
AddGraphQLObjectTypeOptions,
EnumValuesWithShape,
} from './types';

const pluginName = 'simpleObjects' as const;

Expand All @@ -13,11 +37,196 @@ SchemaBuilder.registerPlugin(pluginName, PothosSimpleObjectsPlugin);

const proto = SchemaBuilder.prototype as PothosSchemaTypes.SchemaBuilder<SchemaTypes>;

function resolveOutputTypeRef(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLNamedOutputType,
) {
return type.name as OutputType<SchemaTypes>;
}

function resolveInputTypeRef(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLNamedInputType,
) {
return type.name as InputType<SchemaTypes>;
}

function resolveOutputType(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLOutputType,
): { type: TypeParam<SchemaTypes>; nullable: boolean } {
const namedType = getNamedType(type);
const isNullable = isNonNullType(type) ? false : true;
const nonNullable = isNonNullType(type) ? type.ofType : type;
const isList = isListType(nonNullable);
const typeRef = resolveOutputTypeRef(builder, namedType);

if (!isList) {
return {
type: typeRef,
nullable: isNullable,
};
}

return {
type: [typeRef],
nullable: {
list: isNullable,
items: isNonNullType(nonNullable.ofType) ? false : true,
} as never,
};
}

function resolveInputType(
builder: PothosSchemaTypes.SchemaBuilder<SchemaTypes>,
type: GraphQLInputType,
): { type: InputTypeParam<SchemaTypes>; required: boolean } {
const namedType = getNamedType(type);
const isNullable = isNonNullType(type) ? false : true;
const nonNullable = isNonNullType(type) ? type.ofType : type;
const isList = isListType(nonNullable);
const typeRef = resolveInputTypeRef(builder, namedType);

if (!isList) {
return {
type: typeRef,
required: !isNullable,
};
}

return {
type: [typeRef],
required: {
list: !isNullable,
items: isNonNullType(nonNullable.ofType) ? true : false,
} as never,
};
}

proto.addGraphQLObject = function addGraphQLObject<Shape>(
type: GraphQLObjectType<Shape>,
options: AddGraphQLObjectTypeOptions<SchemaTypes, Shape>,
{ fields, extensions, ...options }: AddGraphQLObjectTypeOptions<SchemaTypes, Shape>,
) {
const ref = this.objectRef<Shape>(options?.name ?? type.name);

ref.implement({
...options,
description: type.description ?? undefined,
isTypeOf: type.isTypeOf as never,
extensions: { ...type.extensions, ...extensions },
interfaces: () => type.getInterfaces().map((i) => resolveOutputTypeRef(this, i)) as [],
fields: (t) => {
const existingFields = type.getFields();
const newFields = fields?.(t) ?? {};
const combinedFields = {} as typeof newFields;

Object.entries(existingFields).forEach(([fieldName, field]) => {
if (typeof newFields[fieldName] !== 'undefined') {
if (newFields[fieldName] !== null) {
combinedFields[fieldName] = newFields[fieldName];
}
return;
}

const args: Record<string, InputFieldRef> = {};

for (const [argName, arg] of Object.entries(field.args)) {
args[argName] = t.arg({
...resolveInputType(this, arg.type),
description: arg.description ?? undefined,
deprecationReason: arg.deprecationReason ?? undefined,
defaultValue: arg.defaultValue,
extensions: arg.extensions,
});
}

combinedFields[fieldName] = t.field({
...resolveOutputType(this, field.type),
args,
description: field.description ?? undefined,
deprecationReason: field.deprecationReason ?? undefined,
resolve: field.resolve as never,
});
});

return combinedFields as {};
},
});

return ref;
};

proto.addGraphQLInterface = function addGraphQLInterface<Shape = unknown>(
type: GraphQLInterfaceType,
{ fields, extensions, ...options }: AddGraphQLInterfaceTypeOptions<SchemaTypes, Shape>,
) {
const ref = this.interfaceRef<Shape>(options?.name ?? type.name);

ref.implement({
...options,
description: type.description ?? undefined,
resolveType: type.resolveType as never,
extensions: { ...type.extensions, ...extensions },
interfaces: () => type.getInterfaces().map((i) => resolveOutputTypeRef(this, i)) as [],
fields: (t) => {
const existingFields = type.getFields();
const newFields = fields?.(t) ?? {};
const combinedFields = {} as typeof newFields;

Object.entries(existingFields).forEach(([fieldName, field]) => {
if (typeof newFields[fieldName] !== 'undefined') {
if (newFields[fieldName] !== null) {
combinedFields[fieldName] = newFields[fieldName];
}
return;
}

const args: Record<string, InputFieldRef> = {};

for (const [argName, arg] of Object.entries(field.args)) {
args[argName] = t.arg({
...resolveInputType(this, arg.type),
description: arg.description ?? undefined,
deprecationReason: arg.deprecationReason ?? undefined,
defaultValue: arg.defaultValue,
extensions: arg.extensions,
});
}

combinedFields[fieldName] = t.field({
...resolveOutputType(this, field.type),
args,
description: field.description ?? undefined,
deprecationReason: field.deprecationReason ?? undefined,
resolve: field.resolve as never,
});
});

return combinedFields as {};
},
});

return ref;
};

proto.addGraphQLEnum = function addGraphQLEnum<Shape extends string | number>(
type: GraphQLEnumType,
{
values,
extensions,
...options
}: AddGraphQLEnumTypeOptions<SchemaTypes, EnumValuesWithShape<SchemaTypes, Shape>>,
) {
const newValues = values ?? {};
const ref = this.enumType<never, EnumValuesWithShape<SchemaTypes, Shape>>(
(options?.name ?? type.name) as never,
{
...options,
description: type.description ?? undefined,
extensions: { ...type.extensions, ...extensions },
values: newValues,
},
);

return ref;
};

0 comments on commit fb9eb16

Please sign in to comment.