@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
JavaScript
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