Skip to content

Commit

Permalink
rewrite schema type printer
Browse files Browse the repository at this point in the history
  • Loading branch information
dcousens committed Aug 24, 2022
1 parent 6f2439b commit f0b488e
Showing 1 changed file with 110 additions and 67 deletions.
177 changes: 110 additions & 67 deletions packages/core/src/lib/schema-type-printer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,93 +14,136 @@ import { InitialisedList } from './core/types-for-lists';

const introspectionTypesSet = new Set(introspectionTypes);

let printEnumTypeDefinition = (type: GraphQLEnumType) => {
return `export type ${type.name} =\n${type
.getValues()
.map(x => ` | ${JSON.stringify(x.name)}`)
.join('\n')};`;
const printEnumTypeDefinition = (type: GraphQLEnumType) => {
return [
`export type ${type.name} =`,
type
.getValues()
.map(x => ` | ${JSON.stringify(x.name)}`)
.join('\n') + ';',
].join('\n');
};

function printInputTypesFromSchema(schema: GraphQLSchema, scalars: Record<string, string>) {
let printTypeReferenceWithoutNullable = (
type: GraphQLNamedType | GraphQLList<GraphQLType>
): string => {
if (type instanceof GraphQLList) {
return `ReadonlyArray<${printTypeReference(type.ofType)}> | ${printTypeReference(
type.ofType
)}`;
}
let name = type.name;
if (type instanceof GraphQLScalarType) {
if (scalars[name] === undefined) {
return 'any';
}
return `Scalars[${JSON.stringify(name)}]`;
}
return name;
};
let printTypeReference = (type: GraphQLType): string => {
if (type instanceof GraphQLNonNull) {
return printTypeReferenceWithoutNullable(type.ofType);
}
return `${printTypeReferenceWithoutNullable(type)} | null`;
};
let printInputObjectTypeDefinition = (type: GraphQLInputObjectType) => {
let str = `export type ${type.name} = {\n`;
for (const field of Object.values(type.getFields())) {
str += ` readonly ${field.name}${
field.type instanceof GraphQLNonNull && field.defaultValue === undefined ? '' : '?'
}: ${printTypeReference(field.type)};\n`;
}
function printTypeReference(type: GraphQLType, scalars: Record<string, string>): string {
if (type instanceof GraphQLNonNull) {
return printTypeReferenceWithoutNullable(type.ofType, scalars);
}
return `${printTypeReferenceWithoutNullable(type, scalars)} | null`;
}

str += '};';
return str;
};
let typeString = 'type Scalars = {\n';
for (let scalar in scalars) {
typeString += ` readonly ${scalar}: ${scalars[scalar]};\n`;
function printTypeReferenceWithoutNullable(
type: GraphQLNamedType | GraphQLList<GraphQLType>,
scalars: Record<string, string>
): string {
if (type instanceof GraphQLList) {
return `ReadonlyArray<${printTypeReference(type.ofType, scalars)}> | ${printTypeReference(
type.ofType,
scalars
)}`;
}
typeString += '};';

const name = type.name;
if (type instanceof GraphQLScalarType) {
if (scalars[name] === undefined) return 'any';
return `Scalars[${JSON.stringify(name)}]`;
}

return name;
}

function printInputObjectTypeDefinition(
type: GraphQLInputObjectType,
scalars: Record<string, string>
) {
return [
`export type ${type.name} = {`,
...Object.values(type.getFields()).map(field => {
const maybe = field.type instanceof GraphQLNonNull && field.defaultValue === undefined ? '' : '?';
return ` readonly ${field.name}${maybe}: ${printTypeReference(field.type, scalars)};`;
}),
'};',
].join('\n');
}

function printInputTypesFromSchema(schema: GraphQLSchema, scalars: Record<string, string>) {
const output = [
'type Scalars = {',
...Object.keys(scalars).map(scalar => ` readonly ${scalar}: ${scalars[scalar]};`),
'};',
];

for (const type of Object.values(schema.getTypeMap())) {
// We don't want to print TS types for the built-in GraphQL introspection types
// they won't be used for anything we want to print here.
if (introspectionTypesSet.has(type)) continue;
if (type instanceof GraphQLInputObjectType) {
typeString += '\n\n' + printInputObjectTypeDefinition(type);
output.push('', printInputObjectTypeDefinition(type, scalars));
}
if (type instanceof GraphQLEnumType) {
typeString += '\n\n' + printEnumTypeDefinition(type);
output.push('', printEnumTypeDefinition(type));
}
}
return typeString + '\n\n';

return output.join('\n') + '\n\n';
}

function printInterimFieldType({
listKey,
fieldKey,
operation,
}: {
listKey: string;
fieldKey: string;
operation: string;
}) {
return ` ${fieldKey}?: import('.prisma/client').Prisma.${listKey}${operation}Input["${fieldKey}"];`;
}

function printInterimMultiFieldType({
listKey,
fieldKey,
operation,
fields,
}: {
listKey: string;
fieldKey: string;
operation: string;
fields: { [key: string]: unknown };
}) {
return [
` ${fieldKey}: {`,
...Object.keys(fields).map(subFieldKey => {
return ` ${subFieldKey}?: import('.prisma/client').Prisma.${listKey}${operation}Input["${fieldKey}_${subFieldKey}"];`;
}),
` };`,
].join('\n');
}

function printInterimType<L extends InitialisedList>(
list: L,
listKey: string,
typename: string,
type: 'Create' | 'Update'
operation: 'Create' | 'Update'
) {
let resolvedTypeString = `type Resolved${typename} = {\n`;
for (let [fieldKey, { dbField }] of Object.entries(list.fields)) {
if (dbField.kind === 'multi') {
resolvedTypeString +=
` ${fieldKey}: {\n` +
Object.keys(dbField.fields)
.map(
key =>
` ${key}?: import('.prisma/client').Prisma.${listKey}${type}Input["${fieldKey}_${key}"];`
)
.join('\n') +
'\n }\n';
} else if (dbField.kind === 'none') {
resolvedTypeString += ` ${fieldKey}?: undefined\n`;
} else {
resolvedTypeString += ` ${fieldKey}?: import('.prisma/client').Prisma.${listKey}${type}Input["${fieldKey}"];\n`;
}
}
resolvedTypeString += '};\n\n';
return resolvedTypeString;
return (
[
`type Resolved${typename} = {`,
...Object.entries(list.fields).map(([fieldKey, { dbField }]) => {
if (dbField.kind === 'none') return ` ${fieldKey}?: undefined\n`;
if (dbField.kind === 'multi') {
return printInterimMultiFieldType({
listKey,
fieldKey,
operation,
fields: dbField.fields,
});
}

return printInterimFieldType({ listKey, fieldKey, operation });
}),
`};`,
].join('\n') + '\n'
);
}

export function printGeneratedTypes(
Expand Down

0 comments on commit f0b488e

Please sign in to comment.