|
| 1 | +/// The main schema for objects and inputs |
| 2 | + |
| 3 | +import * as graphql from "graphql" |
| 4 | +import ts from "typescript" |
| 5 | + |
| 6 | +import { AppContext } from "./context.js" |
| 7 | +import { formatDTS } from "./formatDTS.js" |
| 8 | +import { typeMapper } from "./typeMap.js" |
| 9 | + |
| 10 | +export async function createSharedExternalSchemaFileViaTSC(context: AppContext) { |
| 11 | + const gql = context.gql |
| 12 | + const types = gql.getTypeMap() |
| 13 | + const knownPrimitives = ["String", "Boolean", "Int"] |
| 14 | + |
| 15 | + const { prisma, fieldFacts } = context |
| 16 | + const mapper = typeMapper(context, {}) |
| 17 | + |
| 18 | + const statements = [] as ts.Statement[] |
| 19 | + |
| 20 | + console.time("") |
| 21 | + |
| 22 | + Object.keys(types).forEach((name) => { |
| 23 | + if (name.startsWith("__")) { |
| 24 | + return |
| 25 | + } |
| 26 | + |
| 27 | + if (knownPrimitives.includes(name)) { |
| 28 | + return |
| 29 | + } |
| 30 | + |
| 31 | + const type = types[name] |
| 32 | + const pType = prisma.get(name) |
| 33 | + |
| 34 | + if (graphql.isObjectType(type) || graphql.isInterfaceType(type) || graphql.isInputObjectType(type)) { |
| 35 | + // This is slower than it could be, use the add many at once api |
| 36 | + const docs = [] |
| 37 | + if (pType?.leadingComments) { |
| 38 | + docs.push(pType.leadingComments) |
| 39 | + } |
| 40 | + |
| 41 | + if (type.description) { |
| 42 | + docs.push(type.description) |
| 43 | + } |
| 44 | + |
| 45 | + const properties = [ |
| 46 | + ts.factory.createPropertySignature( |
| 47 | + undefined, |
| 48 | + "__typename", |
| 49 | + ts.factory.createToken(ts.SyntaxKind.QuestionToken), |
| 50 | + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(type.name)) |
| 51 | + ), |
| 52 | + ] |
| 53 | + |
| 54 | + Object.entries(type.getFields()).forEach(([fieldName, obj]: [string, graphql.GraphQLField<object, object>]) => { |
| 55 | + const docs = [] |
| 56 | + const prismaField = pType?.properties.get(fieldName) |
| 57 | + const type = obj.type as graphql.GraphQLType |
| 58 | + |
| 59 | + if (prismaField?.leadingComments.length) { |
| 60 | + docs.push(prismaField.leadingComments.trim()) |
| 61 | + } |
| 62 | + |
| 63 | + // if (obj.description) docs.push(obj.description); |
| 64 | + const hasResolverImplementation = fieldFacts.get(name)?.[fieldName]?.hasResolverImplementation |
| 65 | + const isOptionalInSDL = !graphql.isNonNullType(type) |
| 66 | + const doesNotExistInPrisma = false // !prismaField; |
| 67 | + |
| 68 | + const hasQuestionToken = hasResolverImplementation ?? (isOptionalInSDL || doesNotExistInPrisma) |
| 69 | + const mappedType = mapper.map(type, { preferNullOverUndefined: true }) |
| 70 | + if (mappedType) { |
| 71 | + properties.push( |
| 72 | + ts.factory.createPropertySignature( |
| 73 | + undefined, |
| 74 | + fieldName, |
| 75 | + hasQuestionToken ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, |
| 76 | + ts.factory.createTypeReferenceNode(mappedType) |
| 77 | + ) |
| 78 | + ) |
| 79 | + } |
| 80 | + }) |
| 81 | + |
| 82 | + const interfaceD = ts.factory.createInterfaceDeclaration( |
| 83 | + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], |
| 84 | + ts.factory.createIdentifier(name), |
| 85 | + undefined, |
| 86 | + undefined, |
| 87 | + properties |
| 88 | + ) |
| 89 | + |
| 90 | + statements.push(interfaceD) |
| 91 | + } |
| 92 | + |
| 93 | + if (graphql.isEnumType(type)) { |
| 94 | + const values = type.getValues().map((m) => (m as { value: string }).value) |
| 95 | + const typeKind = `"${values.join('" | "')}"` |
| 96 | + |
| 97 | + statements.push(ts.factory.createTypeAliasDeclaration(undefined, type.name, [], ts.factory.createTypeReferenceNode(typeKind))) |
| 98 | + } |
| 99 | + |
| 100 | + if (graphql.isUnionType(type)) { |
| 101 | + const types = type.getTypes().map((t) => t.name) |
| 102 | + const typeKind = types.join(" | ") |
| 103 | + statements.push( |
| 104 | + ts.factory.createTypeAliasDeclaration( |
| 105 | + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], |
| 106 | + type.name, |
| 107 | + [], |
| 108 | + ts.factory.createTypeReferenceNode(typeKind) |
| 109 | + ) |
| 110 | + ) |
| 111 | + } |
| 112 | + }) |
| 113 | + |
| 114 | + const { scalars } = mapper.getReferencedGraphQLThingsInMapping() |
| 115 | + if (scalars.length) { |
| 116 | + statements.push( |
| 117 | + ts.factory.createTypeAliasDeclaration( |
| 118 | + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], |
| 119 | + "Scalars", |
| 120 | + [], |
| 121 | + ts.factory.createTypeReferenceNode(`{ ${scalars.join(", ")} }`) |
| 122 | + ) |
| 123 | + ) |
| 124 | + } |
| 125 | + |
| 126 | + const sourceFile = ts.factory.createSourceFile(statements, ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), ts.NodeFlags.None) |
| 127 | + const printer = ts.createPrinter({}) |
| 128 | + const result = printer.printNode(ts.EmitHint.Unspecified, sourceFile, sourceFile) |
| 129 | + |
| 130 | + const fullPath = context.join(context.pathSettings.typesFolderRoot, context.pathSettings.sharedFilename) |
| 131 | + |
| 132 | + const prior = context.sys.readFile(fullPath) |
| 133 | + if (prior !== result) context.sys.writeFile(fullPath, result) |
| 134 | +} |
0 commit comments