UNPKG

@stryke/prisma-trpc-generator

Version:

A fork of the prisma-trpc-generator code to work in ESM with Prisma v6.

184 lines (181 loc) 8.88 kB
import { lowerCaseFirst } from "./string-format/src/lower-case-first.mjs"; import { configSchema } from "./config.mjs"; import { project } from "./project.mjs"; import { getPrismaInternals } from "./utils/get-prisma-internals.mjs"; import { constructDefaultOptions, constructShield, constructZodModels, generateCreateRouterImport, generateProcedure, generateRouterImport, generateRouterSchemaImports, generateTRPCExports, getInputTypeByOpName, resolveModelsComments } from "./helpers.mjs"; import { writeFileSafely } from "./utils/write-file-safely.mjs"; import { resolveZodAggregateOperationSupport } from "./zod/aggregate-helpers.mjs"; import { hideZodInputObjectTypesAndRelatedFields, resolveZodModelsComments } from "./zod/comments-helpers.mjs"; import Transformer from "./zod/transformer.mjs"; import { generateZodEnumSchemas, generateZodIndex, generateZodModelSchemas, generateZodObjectSchemas } from "./zod/generator-helpers.mjs"; import { addMissingZodInputObjectTypes } from "./zod/helpers.mjs"; import { existsSync } from "@stryke/fs/exists"; import { createDirectory, removeDirectory } from "@stryke/fs/helpers"; import { joinPaths } from "@stryke/path/join-paths"; import path from "node:path"; import pluralize from "pluralize"; //#region src/prisma-generator.ts async function generate(options) { console.log("[STORM]: Running the Storm Software - Prisma tRPC generator \n"); const internals = await getPrismaInternals(); console.log(`[STORM]: Validating configuration options \n`); const outputDir = internals.parseEnvValue(options.generator.output); const results = await configSchema.safeParseAsync(options.generator.config); if (!results.success) throw new Error("Invalid options passed"); const config = results.data; const consoleLog = (message) => { if (config.debug) console.log(`[STORM]: ${message} \n`); }; consoleLog(`Using configuration parameters: \n${JSON.stringify(config)}`); consoleLog(`Preparing output directory: ${outputDir}`); await removeDirectory(outputDir); await createDirectory(outputDir); consoleLog("Finding Prisma Client generator"); const prismaClientProvider = options.otherGenerators.find((it) => internals.parseEnvValue(it.provider) === "prisma-client-js"); if (!prismaClientProvider) throw new Error("No Prisma Client generator found. Please add `prisma-client-js` to your generator list."); consoleLog("Generating Prisma Client DMMF"); const prismaClientDmmf = await internals.getDMMF({ datamodel: options.datamodel, previewFeatures: prismaClientProvider?.previewFeatures }); const modelOperations = prismaClientDmmf.mappings.modelOperations; const inputObjectTypes = prismaClientDmmf.schema.inputObjectTypes.prisma; const outputObjectTypes = prismaClientDmmf.schema.outputObjectTypes.prisma; const enumTypes = prismaClientDmmf.schema.enumTypes; const models = prismaClientDmmf.datamodel.models; const hiddenModels = []; const hiddenFields = []; if (config.withZod !== false) { consoleLog("Generating Zod schemas"); const zodOutputPath = internals.parseEnvValue(options.generator.output); await createDirectory(zodOutputPath); Transformer.setOutputPath(zodOutputPath); if (prismaClientProvider?.isCustomOutput) Transformer.setPrismaClientOutputPath(prismaClientProvider.output?.value); await constructZodModels(models, joinPaths(zodOutputPath, "schemas", "models"), config, options); resolveZodModelsComments(models, modelOperations, enumTypes, hiddenModels, hiddenFields); await generateZodEnumSchemas(enumTypes.prisma, enumTypes.model); const dataSource = options.datasources?.[0]; if (!dataSource) throw new Error("No datasource found"); const previewFeatures = prismaClientProvider?.previewFeatures; Transformer.provider = dataSource.provider; Transformer.previewFeatures = previewFeatures; addMissingZodInputObjectTypes(inputObjectTypes, outputObjectTypes, models, modelOperations, dataSource.provider, { isGenerateSelect: true, isGenerateInclude: true }); const aggregateOperationSupport = resolveZodAggregateOperationSupport(inputObjectTypes); hideZodInputObjectTypesAndRelatedFields(inputObjectTypes, hiddenModels, hiddenFields); await generateZodObjectSchemas(inputObjectTypes); await generateZodModelSchemas(models, modelOperations, aggregateOperationSupport); await generateZodIndex(); } else consoleLog("Skipping Zod schemas generation"); const queries = []; const mutations = []; const subscriptions = []; prismaClientDmmf.mappings.modelOperations.forEach((modelOperation) => { const { model: _model, plural: _plural, ...operations } = modelOperation; for (const [opType, opNameWithModel] of Object.entries(operations)) { if ([ "findUnique", "findUniqueOrThrow", "findFirst", "findFirstOrThrow", "findRaw", "findMany", "aggregateRaw", "count", "aggregate", "groupBy" ].includes(opType)) queries.push(opNameWithModel); if ([ "createOne", "createMany", "createManyAndReturn", "deleteOne", "deleteMany", "updateOne", "updateMany", "updateManyAndReturn", "upsertOne" ].includes(opType)) mutations.push(opNameWithModel); } }); queries.sort(); mutations.sort(); subscriptions.sort(); if (config.withShield && !(typeof config.withShield === "string" && (existsSync(joinPaths(outputDir, config.withShield)) || existsSync(joinPaths(outputDir, `./${config.withShield}.ts`)) || existsSync(joinPaths(outputDir, config.withShield, "./shield.ts"))))) { consoleLog(`Generating tRPC Shield source file to ${outputDir}`); await writeFileSafely(joinPaths(outputDir, "./shield.ts"), await constructShield({ queries, mutations, subscriptions }, config, options, outputDir)); } else consoleLog("Skipping tRPC Shield generation"); consoleLog(`Generating tRPC source code for ${models.length} models`); if (config.trpcOptions && typeof config.trpcOptions === "boolean") { consoleLog(`Generating tRPC options source file to ${outputDir}`); await writeFileSafely(joinPaths(outputDir, "./options.ts"), constructDefaultOptions(config, options, outputDir)); } resolveModelsComments(models, hiddenModels); consoleLog("Generating tRPC export file"); await generateTRPCExports(project.createSourceFile(path.resolve(outputDir, "trpc.ts"), void 0, { overwrite: true }), config, options, outputDir); consoleLog("Generating tRPC app router"); const appRouter = project.createSourceFile(path.resolve(outputDir, "routers", `index.ts`), void 0, { overwrite: true }); consoleLog("Generating tRPC router imports"); generateCreateRouterImport({ sourceFile: appRouter }); const routerStatements = []; for (const modelOperation of modelOperations) { const { model, ...operations } = modelOperation; if (hiddenModels.includes(model)) { consoleLog(`Skipping model ${model} as it is hidden`); continue; } if (!model) { consoleLog(`Skipping model ${model} as it is not defined`); continue; } const modelActions = Object.keys(operations).filter((opType) => config.generateModelActions.some((generateModelAction) => generateModelAction === opType.replace("One", ""))); if (!modelActions.length) { consoleLog(`Skipping model ${model} as it has no actions to generate`); continue; } const plural = pluralize(lowerCaseFirst(model)); consoleLog(`Generating tRPC router for model ${model}`); generateRouterImport(appRouter, plural, model); const modelRouter = project.createSourceFile(path.resolve(outputDir, "routers", `${lowerCaseFirst(model)}.router.ts`), void 0, { overwrite: true }); generateCreateRouterImport({ sourceFile: modelRouter, config }); if (config.withZod) { consoleLog("Generating Zod schemas imports"); generateRouterSchemaImports(modelRouter, model, modelActions); } modelRouter.addStatements(` export const ${plural}Router = t.router({`); for (const opType of modelActions) { const opNameWithModel = operations[opType]; if (opNameWithModel) { const baseOpType = opType.replace("OrThrow", ""); generateProcedure(modelRouter, opNameWithModel, getInputTypeByOpName(baseOpType, model), model, opType, baseOpType, config); } } modelRouter.addStatements(` })`); modelRouter.formatText({ indentSize: 2 }); routerStatements.push(` ${lowerCaseFirst(model)}: ${plural}Router`); consoleLog(`Generated tRPC router for model ${model} with ${modelActions.length} actions`); } consoleLog("Generating tRPC app router"); appRouter.addStatements(` export const appRouter = t.router({${routerStatements.join()}}); export type AppRouter = typeof appRouter;`); appRouter.formatText({ indentSize: 2 }); consoleLog("Saving tRPC router source files to disk"); await project.save(); consoleLog("Storm Software - Prisma tRPC generator completed successfully"); } //#endregion export { generate }; //# sourceMappingURL=prisma-generator.mjs.map