@brakebein/prisma-generator-nestjs-dto
Version:
Generates DTO and Entity classes from Prisma Schema for NestJS
185 lines (184 loc) • 9.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generate = void 0;
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const make_dir_1 = __importDefault(require("make-dir"));
const slash_1 = __importDefault(require("slash"));
const generator_helper_1 = require("@prisma/generator-helper");
const prettier_1 = __importDefault(require("prettier"));
const utils_1 = require("./utils");
const generator_1 = require("./generator");
const field_classifiers_1 = require("./generator/field-classifiers");
const annotations_1 = require("./generator/annotations");
const stringToBoolean = (input, defaultValue = false) => {
if (input === 'true') {
return true;
}
if (input === 'false') {
return false;
}
return defaultValue;
};
const generate = async (options) => {
var _a;
const output = (0, utils_1.parseEnvValue)(options.generator.output);
if (!output) {
throw new Error('Failed to parse output path');
}
const { connectDtoPrefix = 'Connect', createDtoPrefix = 'Create', updateDtoPrefix = 'Update', dtoSuffix = 'Dto', entityPrefix = '', entitySuffix = '', fileNamingStyle = 'camel', outputType = 'class', generateFileTypes = 'all', } = options.generator.config;
const exportRelationModifierClasses = stringToBoolean(options.generator.config.exportRelationModifierClasses, true);
const outputToNestJsResourceStructure = stringToBoolean(options.generator.config.outputToNestJsResourceStructure, false);
const flatResourceStructure = stringToBoolean(options.generator.config.flatResourceStructure, false);
const reExport = stringToBoolean(options.generator.config.reExport, false);
const supportedFileNamingStyles = ['kebab', 'camel', 'pascal', 'snake'];
const isSupportedFileNamingStyle = (style) => supportedFileNamingStyles.includes(style);
if (!isSupportedFileNamingStyle(fileNamingStyle)) {
throw new Error(`'${fileNamingStyle}' is not a valid file naming style. Valid options are ${supportedFileNamingStyles
.map((s) => `'${s}'`)
.join(', ')}.`);
}
const classValidation = stringToBoolean(options.generator.config.classValidation, false);
const supportedOutputTypes = ['class', 'interface'];
if (!supportedOutputTypes.includes(outputType)) {
throw new Error(`'${outputType}' is not a valid output type. Valid options are 'class' and 'interface'.`);
}
const noDependencies = stringToBoolean(options.generator.config.noDependencies, false);
if (classValidation && outputType !== 'class') {
throw new Error(`To use 'classValidation' decorators, 'outputType' must be 'class'.`);
}
if (classValidation && noDependencies) {
throw new Error(`To use 'classValidation' decorators, 'noDependencies' cannot be false.`);
}
const definiteAssignmentAssertion = stringToBoolean(options.generator.config.definiteAssignmentAssertion, false);
if (definiteAssignmentAssertion && outputType !== 'class') {
throw new Error(`To use 'definiteAssignmentAssertion', 'outputType' must be 'class'.`);
}
const requiredResponseApiProperty = stringToBoolean(options.generator.config.requiredResponseApiProperty, true);
const prismaClientGenerator = options.otherGenerators.find((config) => config.name === 'client');
const prismaClientOutputPath = (_a = prismaClientGenerator === null || prismaClientGenerator === void 0 ? void 0 : prismaClientGenerator.output) === null || _a === void 0 ? void 0 : _a.value;
let prismaClientImportPath = options.generator.config.prismaClientImportPath || '@prisma/client';
if (!options.generator.config.prismaClientImportPath &&
prismaClientOutputPath &&
!prismaClientOutputPath.endsWith(['node_modules', '@prisma', 'client'].join(node_path_1.default.sep))) {
const withStructure = outputToNestJsResourceStructure
? flatResourceStructure
? '../'
: '../../'
: '';
prismaClientImportPath = (0, slash_1.default)(withStructure + node_path_1.default.relative(output, prismaClientOutputPath));
if (!prismaClientImportPath.startsWith('.')) {
prismaClientImportPath = './' + prismaClientImportPath;
}
}
const outputApiPropertyType = stringToBoolean(options.generator.config.outputApiPropertyType, true);
if (!outputApiPropertyType) {
(0, utils_1.warn)('`outputApiPropertyType = "false"` is deprecated. Please use `wrapRelationsAsType = "true"` instead and report any issues.');
}
const wrapRelationsAsType = stringToBoolean(options.generator.config.wrapRelationsAsType, false);
const showDefaultValues = stringToBoolean(options.generator.config.showDefaultValues, false);
const results = (0, generator_1.run)({
output,
dmmf: options.dmmf,
exportRelationModifierClasses,
outputToNestJsResourceStructure,
flatResourceStructure,
connectDtoPrefix: connectDtoPrefix,
createDtoPrefix: createDtoPrefix,
updateDtoPrefix: updateDtoPrefix,
dtoSuffix: dtoSuffix,
entityPrefix: entityPrefix,
entitySuffix: entitySuffix,
fileNamingStyle: fileNamingStyle,
classValidation,
outputType: outputType,
noDependencies,
definiteAssignmentAssertion,
requiredResponseApiProperty,
prismaClientImportPath: prismaClientImportPath,
outputApiPropertyType,
generateFileTypes: generateFileTypes,
wrapRelationsAsType,
showDefaultValues,
});
const deprecatedCastTypeAnnotation = [
...options.dmmf.datamodel.models,
...options.dmmf.datamodel.types,
].some((model) => model.fields.some((field) => (0, field_classifiers_1.isAnnotatedWith)(field, annotations_1.DTO_CAST_TYPE)));
if (deprecatedCastTypeAnnotation) {
(0, utils_1.warn)('@DtoCastType annotation is deprecated. Please use DtoOverrideType instead.');
}
const indexCollections = {};
if (reExport) {
results.forEach(({ fileName }) => {
const dirName = node_path_1.default.dirname(fileName);
const { [dirName]: fileSpec } = indexCollections;
indexCollections[dirName] = {
fileName: (fileSpec === null || fileSpec === void 0 ? void 0 : fileSpec.fileName) || node_path_1.default.join(dirName, 'index.ts'),
content: [
(fileSpec === null || fileSpec === void 0 ? void 0 : fileSpec.content) || '',
`export * from './${node_path_1.default.basename(fileName, '.ts')}';`,
].join('\n'),
};
});
if (outputToNestJsResourceStructure) {
const content = [];
Object.entries(indexCollections)
.sort((a, b) => a[0].localeCompare(b[0]))
.forEach(([dirName, file]) => {
if (output === dirName) {
content.push(file.content);
}
else {
const base = dirName
.split(/[\\\/]/)
.slice(flatResourceStructure ? -1 : -2);
content.push(`export * from './${base[0]}${base[1] ? '/' + base[1] : ''}';`);
}
});
indexCollections[output] = {
fileName: node_path_1.default.join(output, 'index.ts'),
content: content.join('\n'),
};
}
}
const applyPrettier = stringToBoolean(options.generator.config.prettier, false);
let prettierConfig = {};
if (applyPrettier) {
const prettierConfigFile = await prettier_1.default.resolveConfigFile();
if (!prettierConfigFile) {
(0, utils_1.logger)('Stylizing output DTOs with the default Prettier config.');
}
else {
(0, utils_1.logger)(`Stylizing output DTOs with found Prettier config. (${prettierConfigFile})`);
}
if (prettierConfigFile) {
const resolvedConfig = await prettier_1.default.resolveConfig(prettierConfigFile, {
config: prettierConfigFile,
});
if (resolvedConfig)
prettierConfig = resolvedConfig;
}
prettierConfig.parser = 'typescript';
}
return Promise.all(results
.concat(Object.values(indexCollections))
.map(async ({ fileName, content }) => {
await (0, make_dir_1.default)(node_path_1.default.dirname(fileName));
if (applyPrettier) {
content = await prettier_1.default.format(content, prettierConfig);
}
return promises_1.default.writeFile(fileName, content);
}));
};
exports.generate = generate;
(0, generator_helper_1.generatorHandler)({
onManifest: () => ({
defaultOutput: '../src/generated/nestjs-dto',
prettyName: 'NestJS DTO Generator',
}),
onGenerate: exports.generate,
});