UNPKG

kapix-typegraphql-prisma

Version:

![integration logo](https://raw.githubusercontent.com/MichalLytek/typegraphql-prisma/main/img/integration.png)

500 lines (479 loc) 24.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const ts_morph_1 = require("ts-morph"); const path_1 = tslib_1.__importDefault(require("path")); const node_child_process_1 = require("node:child_process"); const node_util_1 = require("node:util"); const execa = (0, node_util_1.promisify)(node_child_process_1.exec); const helpers_1 = require("./helpers"); const enum_1 = tslib_1.__importDefault(require("./enum")); const model_type_class_1 = tslib_1.__importDefault(require("./model-type-class")); const relations_1 = tslib_1.__importDefault(require("./resolvers/relations")); const type_class_1 = require("./type-class"); const full_crud_1 = tslib_1.__importDefault(require("./resolvers/full-crud")); const config_1 = require("./config"); const imports_1 = require("./imports"); const dmmf_document_1 = require("./dmmf/dmmf-document"); const args_class_1 = tslib_1.__importDefault(require("./args-class")); const separate_action_1 = tslib_1.__importDefault(require("./resolvers/separate-action")); const prisma_version_1 = require("../utils/prisma-version"); const generate_enhance_1 = require("./generate-enhance"); const generate_scalars_1 = require("./generate-scalars"); const generate_helpers_1 = require("./generate-helpers"); const emit_block_1 = require("./emit-block"); const baseCompilerOptions = { target: ts_morph_1.ScriptTarget.ES2019, module: ts_morph_1.ModuleKind.CommonJS, emitDecoratorMetadata: true, experimentalDecorators: true, esModuleInterop: true, }; async function generateCode(dmmf, baseOptions, log = helpers_1.noop) { var _a, _b, _c, _d, _e; (0, prisma_version_1.ensureInstalledCorrectPrismaPackage)(); const options = { ...baseOptions, blocksToEmit: (0, emit_block_1.getBlocksToEmit)(baseOptions.emitOnly), contextPrismaKey: (_a = baseOptions.contextPrismaKey) !== null && _a !== void 0 ? _a : "prisma", relativePrismaOutputPath: (0, helpers_1.toUnixPath)(path_1.default.relative(baseOptions.outputDirPath, baseOptions.prismaClientPath)), absolutePrismaOutputPath: !baseOptions.customPrismaImportPath && baseOptions.prismaClientPath.includes("node_modules") ? "@prisma/client" : undefined, formatGeneratedCode: (_b = baseOptions.formatGeneratedCode) !== null && _b !== void 0 ? _b : "tsc", queryActions: (_c = baseOptions.queryActions) !== null && _c !== void 0 ? _c : [ "findUnique", "findFirst", "findMany", // "groupBy", ], mutationActions: (_d = baseOptions.mutationActions) !== null && _d !== void 0 ? _d : [ "createOne", "createMany", "updateOne", "deleteMany", "updateMany", "upsertOne", ], }; const baseDirPath = options.outputDirPath; const emitTranspiledCode = (_e = options.emitTranspiledCode) !== null && _e !== void 0 ? _e : options.outputDirPath.includes("node_modules"); const project = new ts_morph_1.Project({ compilerOptions: { ...baseCompilerOptions, ...(emitTranspiledCode && { declaration: true, importHelpers: true, }), }, }); log("Transforming dmmfDocument..."); const dmmfDocument = new dmmf_document_1.DmmfDocument(dmmf, options); const hasAggregate = options.queryActions.includes("aggregate"); const hasNoMutations = options.mutationActions.length === 0; // const hasCreateOne = options.mutationActions.includes('createOne') // const hasCreateMany = options.mutationActions.includes('createMany') // const hasUpdateOne = options.mutationActions.includes('updateOne') // const hasUpdateMany = options.mutationActions.includes('updateMany') const inputTypes = dmmfDocument.schema.inputTypes.filter(t => { if (!hasAggregate && !t.typeName.includes("OrderByRelationAggregateInput") && (t.typeName.includes("WithAggreg") || t.typeName.includes("AggregateInput"))) { return false; } if (hasNoMutations) { if (t.typeName.includes('CreateInput')) { return false; } if (t.typeName.includes('CreateMany')) { return false; } if (t.typeName.includes('UpdateInput')) { return false; } if (t.typeName.includes('UpdateMany')) { return false; } } // if (!hasCreateOne && t.typeName.includes('CreateInput')) { // return false // } // if (!hasCreateMany && t.typeName.includes('CreateMany')) { // return false // } // if (!hasUpdateOne && t.typeName.includes('UpdateInput')) { // return false // } // if (!hasUpdateMany && t.typeName.includes('UpdateMany')) { // return false // } return true; }); // debugger if (dmmfDocument.shouldGenerateBlock("enums")) { log("Generating enums..."); const datamodelEnumNames = dmmfDocument.datamodel.enums.map(enumDef => enumDef.typeName); dmmfDocument.datamodel.enums.forEach(enumDef => (0, enum_1.default)(project, baseDirPath, enumDef)); dmmfDocument.schema.enums // skip enums from datamodel .filter(enumDef => !datamodelEnumNames.includes(enumDef.typeName)) .forEach(enumDef => (0, enum_1.default)(project, baseDirPath, enumDef)); const emittedEnumNames = [ ...new Set([ ...dmmfDocument.schema.enums.map(it => it.typeName), ...dmmfDocument.datamodel.enums.map(it => it.typeName), ]), ]; const enumsBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.enumsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateEnumsBarrelFile)(enumsBarrelExportSourceFile, emittedEnumNames); } if (dmmfDocument.shouldGenerateBlock("models")) { log("Generating models..."); dmmfDocument.datamodel.models.forEach(model => { const modelOutputType = dmmfDocument.schema.outputTypes.find(type => type.name === model.name); return (0, model_type_class_1.default)(project, baseDirPath, model, modelOutputType, dmmfDocument); }); const modelsBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.modelsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateModelsBarrelFile)(modelsBarrelExportSourceFile, dmmfDocument.datamodel.models.map(it => it.typeName)); } const resolversDirPath = path_1.default.resolve(baseDirPath, config_1.resolversFolderName); let outputTypesToGenerate = []; if (dmmfDocument.shouldGenerateBlock("outputs")) { log("Generating output types..."); const rootTypes = dmmfDocument.schema.outputTypes.filter(type => ["Query", "Mutation"].includes(type.name)); const modelNames = dmmfDocument.datamodel.models.map(model => model.name); outputTypesToGenerate = dmmfDocument.schema.outputTypes.filter( // skip generating models and root resolvers type => !modelNames.includes(type.name) && !rootTypes.includes(type)); const outputTypesFieldsArgsToGenerate = outputTypesToGenerate .map(it => it.fields) .reduce((a, b) => a.concat(b), []) .filter(it => it.argsTypeName); outputTypesToGenerate.forEach(type => (0, type_class_1.generateOutputTypeClassFromType)(project, resolversDirPath, type, dmmfDocument, options)); const outputsBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.outputsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateOutputsBarrelFile)(outputsBarrelExportSourceFile, outputTypesToGenerate.map(it => it.typeName), outputTypesFieldsArgsToGenerate.length > 0); if (outputTypesFieldsArgsToGenerate.length > 0) { log("Generating output types args..."); outputTypesFieldsArgsToGenerate.forEach(async (field) => { (0, args_class_1.default)(project, path_1.default.resolve(resolversDirPath, config_1.outputsFolderName), field.args, field.argsTypeName, dmmfDocument, 2); }); const outputsArgsBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.outputsFolderName, config_1.argsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateArgsBarrelFile)(outputsArgsBarrelExportSourceFile, outputTypesFieldsArgsToGenerate.map(it => it.argsTypeName)); } } if (dmmfDocument.shouldGenerateBlock("inputs")) { log("Generating input types..."); // if (dmmfDocument.schema.inputTypes.some(type => type.name.toLowerCase().includes("enum"))) { // debugger // } const geometryInputContent = `import * as TypeGraphQL from "type-graphql"; import * as GraphQLScalars from "graphql-scalars"; import { Prisma } from "@prisma/client"; @TypeGraphQL.InputType("Coordinate", {}) export class Coordinate { @TypeGraphQL.Field(_type => Number) lat: number @TypeGraphQL.Field(_type => Number) lng: number } @TypeGraphQL.InputType("GeometryInput", {}) export class GeometryInput { @TypeGraphQL.Field(_type => String) type: string @TypeGraphQL.Field(_type => Coordinate) coord: Coordinate } @TypeGraphQL.InputType("GeometryListInput", {}) export class GeometryListInput { @TypeGraphQL.Field(_type => String) type: string @TypeGraphQL.Field(_type => [Coordinate]) coords: Coordinate[] } @TypeGraphQL.InputType("DistanceFilter", {}) export class DistanceFilter { @TypeGraphQL.Field(_type => Coordinate, { nullable:true }) centerPoint?: Coordinate; @TypeGraphQL.Field(_type => TypeGraphQL.Float, { nullable: true }) lt?: number | undefined; @TypeGraphQL.Field(_type => [GeometryListInput], { nullable: true }) polygons?: GeometryListInput[]; } `; project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.inputsFolderName, "GeometryInput.ts"), geometryInputContent, { overwrite: true, }); inputTypes.forEach(type => { (0, type_class_1.generateInputTypeClassFromType)(project, resolversDirPath, type, options); }); const inputsBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.inputsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateInputsBarrelFile)(inputsBarrelExportSourceFile, inputTypes.map(it => it.typeName)); } if (dmmfDocument.relationModels.length > 0 && dmmfDocument.shouldGenerateBlock("relationResolvers")) { log("Generating relation resolvers..."); dmmfDocument.relationModels.forEach(relationModel => (0, relations_1.default)(project, baseDirPath, dmmfDocument, relationModel, options)); const relationResolversBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.relationsResolversFolderName, "resolvers.index.ts"), undefined, { overwrite: true }); (0, imports_1.generateResolversBarrelFile)(relationResolversBarrelExportSourceFile, dmmfDocument.relationModels.map(relationModel => ({ resolverName: relationModel.resolverName, modelName: relationModel.model.typeName, }))); log("Generating relation resolver args..."); dmmfDocument.relationModels.forEach(async (relationModelData) => { const resolverDirPath = path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.relationsResolversFolderName, relationModelData.model.typeName); relationModelData.relationFields .filter(field => field.argsTypeName) .forEach(async (field) => { (0, args_class_1.default)(project, resolverDirPath, field.outputTypeField.args, field.argsTypeName, dmmfDocument); }); const argTypeNames = relationModelData.relationFields .filter(it => it.argsTypeName !== undefined) .map(it => it.argsTypeName); if (argTypeNames.length) { const barrelExportSourceFile = project.createSourceFile(path_1.default.resolve(resolverDirPath, config_1.argsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateArgsBarrelFile)(barrelExportSourceFile, argTypeNames); } }); const relationModelsWithArgs = dmmfDocument.relationModels.filter(relationModelData => relationModelData.relationFields.some(it => it.argsTypeName !== undefined)); if (relationModelsWithArgs.length > 0) { const relationResolversArgsIndexSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.relationsResolversFolderName, "args.index.ts"), undefined, { overwrite: true }); (0, imports_1.generateArgsIndexFile)(relationResolversArgsIndexSourceFile, relationModelsWithArgs.map(relationModelData => relationModelData.model.typeName)); } const relationResolversIndexSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.relationsResolversFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateResolversIndexFile)(relationResolversIndexSourceFile, "relations", relationModelsWithArgs.length > 0); } if (dmmfDocument.shouldGenerateBlock("crudResolvers")) { log("Generating crud resolvers..."); dmmfDocument.modelMappings.forEach(async (mapping) => { const model = dmmfDocument.datamodel.models.find(model => model.name === mapping.modelName); (0, full_crud_1.default)(project, baseDirPath, mapping, model, dmmfDocument, options); mapping.actions.forEach(async (action) => { const model = dmmfDocument.datamodel.models.find(model => model.name === mapping.modelName); (0, separate_action_1.default)(project, baseDirPath, model, action, mapping, dmmfDocument, options); }); }); const generateMappingData = dmmfDocument.modelMappings.map(mapping => { const model = dmmfDocument.datamodel.models.find(model => model.name === mapping.modelName); return { modelName: model.typeName, resolverName: mapping.resolverName, actionResolverNames: mapping.actions.map(it => it.actionResolverName), }; }); const crudResolversBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.crudResolversFolderName, "resolvers-crud.index.ts"), undefined, { overwrite: true }); (0, imports_1.generateResolversBarrelFile)(crudResolversBarrelExportSourceFile, generateMappingData); const crudResolversActionsBarrelExportSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.crudResolversFolderName, "resolvers-actions.index.ts"), undefined, { overwrite: true }); generatePostgisQuery(project, baseDirPath); (0, imports_1.generateResolversActionsBarrelFile)(crudResolversActionsBarrelExportSourceFile, generateMappingData); const crudResolversIndexSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.crudResolversFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateResolversIndexFile)(crudResolversIndexSourceFile, "crud", true); log("Generating crud resolvers args..."); dmmfDocument.modelMappings.forEach(async (mapping) => { const actionsWithArgs = mapping.actions.filter(it => it.argsTypeName !== undefined); if (actionsWithArgs.length) { const model = dmmfDocument.datamodel.models.find(model => model.name === mapping.modelName); const resolverDirPath = path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.crudResolversFolderName, model.typeName); actionsWithArgs.forEach(async (action) => { (0, args_class_1.default)(project, resolverDirPath, action.method.args, action.argsTypeName, dmmfDocument); }); const barrelExportSourceFile = project.createSourceFile(path_1.default.resolve(resolverDirPath, config_1.argsFolderName, "index.ts"), undefined, { overwrite: true }); (0, imports_1.generateArgsBarrelFile)(barrelExportSourceFile, actionsWithArgs.map(it => it.argsTypeName)); } }); const crudResolversArgsIndexSourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.crudResolversFolderName, "args.index.ts"), undefined, { overwrite: true }); (0, imports_1.generateArgsIndexFile)(crudResolversArgsIndexSourceFile, dmmfDocument.modelMappings .filter(mapping => mapping.actions.some(it => it.argsTypeName !== undefined)) .map(mapping => mapping.modelTypeName)); } log("Generate enhance map"); const enhanceSourceFile = project.createSourceFile(baseDirPath + "/enhance.ts", undefined, { overwrite: true }); (0, generate_enhance_1.generateEnhanceMap)(enhanceSourceFile, dmmfDocument, dmmfDocument.modelMappings, dmmfDocument.relationModels, dmmfDocument.datamodel.models, dmmfDocument.schema.inputTypes, outputTypesToGenerate, options); log("Generate custom scalars"); const scalarsSourceFile = project.createSourceFile(baseDirPath + "/scalars.ts", undefined, { overwrite: true }); (0, generate_scalars_1.generateCustomScalars)(scalarsSourceFile, dmmfDocument.options); log("Generate custom helpers"); const helpersSourceFile = project.createSourceFile(baseDirPath + "/helpers.ts", undefined, { overwrite: true }); (0, generate_helpers_1.generateHelpersFile)(helpersSourceFile, dmmfDocument.options); log("Generating index file"); const indexSourceFile = project.createSourceFile(baseDirPath + "/index.ts", undefined, { overwrite: true }); (0, imports_1.generateIndexFile)(indexSourceFile, dmmfDocument.relationModels.length > 0, dmmfDocument.options.blocksToEmit); log("Emitting generated code files"); if (emitTranspiledCode) { await project.emit(); } else { if (options.formatGeneratedCode === "tsc") { for (const file of project.getSourceFiles()) { file.formatText({ indentSize: 2 }); } } await project.save(); if (options.formatGeneratedCode === "prettier") { await execa(`npx prettier --write ${baseDirPath}`); } } } exports.default = generateCode; function generatePostgisQuery(project, baseDirPath) { const sourceFile = project.createSourceFile(path_1.default.resolve(baseDirPath, config_1.resolversFolderName, config_1.crudResolversFolderName, "PostgisQuery.ts"), undefined, { overwrite: true }); sourceFile.addFunction({ name: "updateGeomFields", isExported: true, isAsync: true, parameters: [ { name: "id", type: "string", }, { name: "args", type: "any", }, { name: "prisma", type: "any", }, { name: "tabName", type: "string", }, { name: "geomFieldsList", type: "string[]", }, ], statements: 'const updates: string[] = [] \n\ geomFieldsList.forEach((field) => { \n\ const geometry = args.data[field] as any \n\ if(!geometry) return \n\ const { coord } = geometry \n\ const point = `ST_GeomFromText(\'POINT(${coord.lng} ${coord.lat})\',4326)` \n\ updates.push(`"${field}Geom" = ${point}`) \n\ }) \n\ if(updates.length > 0) \n\ await prisma.$executeRawUnsafe(`UPDATE "${tabName}" \n\ SET ${updates.join(", ")} \n\ WHERE id = \'${id}\'`)', }); sourceFile.addFunction({ name: "filterGeomByIds", isExported: true, isAsync: false, parameters: [ { name: "idWithDistance", type: "undefined | { id: string }[]", }, { name: "result", type: "any", }, { name: "args", type: "any", }, { name: "take", type: "undefined | number", }, ], statements: ` if (idWithDistance) { const idToKeep = idWithDistance .filter(idWithDistanceItem => result.some(obj => obj.id === idWithDistanceItem.id)) .slice((args.skip ?? 0) * (take ?? 0), take); result = result.filter(obj => idToKeep.some(idWithDistanceItem => idWithDistanceItem.id === obj.id)); } return result;`, }); sourceFile.addFunction({ name: "applyGeomPointFilter", isExported: true, isAsync: true, returnType: `Promise<[ undefined | Array<{ id: string; }>, undefined | number ]>`, parameters: [ { name: "geomFieldsList", type: "string[]", }, { name: "args", type: "any", }, { name: "tableName", type: "string", }, { name: "pkName", type: "string", }, { name: "prisma", type: "any", }, ], statements: ` const geomPointToFilter = geomFieldsList.filter((field) => { const geom = args.where && args.where[field] if (geom && geom.centerPoint) return true }) if (geomPointToFilter.length > 0) { const whereGeomPoint = Array<{ fieldName: string, centerPoint: { lat: number, lng: number }, lt: number }>() for (const field of geomPointToFilter) { const { centerPoint, lt } = args.where[field] whereGeomPoint.push({ fieldName: field, centerPoint, lt }) delete args.where[field] } const stringGeomPoint = whereGeomPoint.map((point) => { return { postgisString: \`ST_SetSRID(ST_MakePoint(\${point.centerPoint.lng}, \${point.centerPoint.lat}), 4326)::geography\`, fieldName: point.fieldName, lt: point.lt } }) let query= \`SELECT r.\${pkName}, RANK() OVER ( ORDER BY r.distanceFromCenter ASC ) as distanceOrder FROM ( SELECT \${pkName}, ST_Distance("\${stringGeomPoint[0].fieldName}Geom", \${stringGeomPoint[0].postgisString}) as distanceFromCenter FROM "\${tableName}" WHERE \${stringGeomPoint.map((point) => { return \`ST_DWithin("\${point.fieldName}Geom", \${point.postgisString}, \${point.lt * 1000})\` // for km; 1000m = 1km }).join(' AND ')} ) r;\` const rawResult = (await prisma.$queryRawUnsafe(query)) let take = args.take if (take) { delete args.take } const bigIntList: any[] = [] const tempList = rawResult.values() for (const current of tempList) { bigIntList.push(current[pkName]) } args.where[pkName] = { in: bigIntList } return [rawResult, take] } return [undefined, undefined]`, }); } //# sourceMappingURL=generate-code.js.map