@ladislaogarcia/prisma-trpc-generator
Version:
Prisma 2+ generator to emit fully implemented tRPC routers
159 lines (132 loc) • 4.55 kB
text/typescript
import { DMMF, EnvValue, GeneratorOptions } from '@prisma/generator-helper';
import { getDMMF, parseEnvValue } from '@prisma/internals';
import { promises as fs } from 'fs';
import path from 'path';
import pluralize from 'pluralize';
import { generate as PrismaTrpcShieldGenerator } from 'prisma-trpc-shield-generator/lib/prisma-generator';
import { generate as PrismaZodGenerator } from '@ladislaogarcia/prisma-zod-generator/lib/prisma-generator';
import { configSchema } from './config';
import {
generateBaseRouter,
generateCreateRouterImport,
generateProcedure,
generateRouterImport,
generateRouterSchemaImports,
generateShieldImport,
generatetRPCImport,
getInputTypeByOpName,
resolveModelsComments,
} from './helpers';
import { project } from './project';
import removeDir from './utils/removeDir';
export async function generate(options: GeneratorOptions) {
const outputDir = parseEnvValue(options.generator.output as EnvValue);
const results = configSchema.safeParse(options.generator.config);
if (!results.success) throw new Error('Invalid options passed');
const config = results.data;
await fs.mkdir(outputDir, { recursive: true });
await removeDir(outputDir, true);
if (config.withZod) {
await PrismaZodGenerator(options);
}
if (config.withShield === true) {
const shieldOutputPath = path.join(outputDir, './shield');
await PrismaTrpcShieldGenerator({
...options,
generator: {
...options.generator,
output: {
...options.generator.output,
value: shieldOutputPath,
},
config: {
...options.generator.config,
contextPath: config.contextPath,
},
},
});
}
const prismaClientProvider = options.otherGenerators.find(
(it) => parseEnvValue(it.provider) === 'prisma-client-js',
);
const prismaClientDmmf = await getDMMF({
datamodel: options.datamodel,
previewFeatures: prismaClientProvider.previewFeatures,
});
const modelOperations = prismaClientDmmf.mappings.modelOperations;
const models = prismaClientDmmf.datamodel.models;
const hiddenModels: string[] = [];
resolveModelsComments(models, hiddenModels);
const createRouter = project.createSourceFile(
path.resolve(outputDir, 'routers', 'helpers', 'createRouter.ts'),
undefined,
{ overwrite: true },
);
generatetRPCImport(createRouter);
if (config.withShield) {
generateShieldImport(createRouter, options, config.withShield);
}
generateBaseRouter(createRouter, config, options);
createRouter.formatText({
indentSize: 2,
});
const appRouter = project.createSourceFile(
path.resolve(outputDir, 'routers', `index.ts`),
undefined,
{ overwrite: true },
);
generateCreateRouterImport({
sourceFile: appRouter,
});
const routerStatements = [];
for (const modelOperation of modelOperations) {
const { model, ...operations } = modelOperation;
if (hiddenModels.includes(model)) continue;
const modelActions = Object.keys(operations).filter<DMMF.ModelAction>(
(opType): opType is DMMF.ModelAction =>
config.generateModelActions.includes(
opType.replace('One', '') as DMMF.ModelAction,
),
);
if (!modelActions.length) continue;
const plural = pluralize(model.toLowerCase());
generateRouterImport(appRouter, plural, model);
const modelRouter = project.createSourceFile(
path.resolve(outputDir, 'routers', `${model}.router.ts`),
undefined,
{ overwrite: true },
);
generateCreateRouterImport({
sourceFile: modelRouter,
config,
});
if (config.withZod) {
generateRouterSchemaImports(modelRouter, model, modelActions);
}
modelRouter.addStatements(/* ts */ `
export const ${plural}Router = t.router({`);
for (const opType of modelActions) {
const opNameWithModel = operations[opType];
const baseOpType = opType.replace('OrThrow', '');
generateProcedure(
modelRouter,
opNameWithModel,
getInputTypeByOpName(baseOpType, model),
model,
opType,
baseOpType,
config,
);
}
modelRouter.addStatements(/* ts */ `
})`);
modelRouter.formatText({ indentSize: 2 });
routerStatements.push(/* ts */ `
${model.toLowerCase()}: ${plural}Router`);
}
appRouter.addStatements(/* ts */ `
export const appRouter = t.router({${routerStatements}})
`);
appRouter.formatText({ indentSize: 2 });
await project.save();
}