@weverson_na/prisma-generator-nestjs-dto
Version:
Advanced Prisma Generator with Smart Merge v2: Creates DTO and Entity classes with AST-based preservation, intelligent import management, and modular architecture for NestJS
476 lines (475 loc) • 19.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SmartMergeContentV2 = void 0;
const ts_morph_1 = require("ts-morph");
class SmartMergeContentV2 {
constructor() {
this.SCHEMA_MARKER = '// @generated from prisma schema';
this.IGNORE = '// @ignore';
this.IMPORTS = new Map();
this.CLASSES = new Map();
this.ENUMS = new Map();
this.TYPES = new Map();
this.INTERFACES = new Map();
this.FUNCTIONS = new Map();
this.CONSTRUCTORS = new Map();
this.CLASS_EXTENDS = new Map();
this.CLASS_IMPLEMENTS = new Map();
this.INTERFACE_EXTENDS = new Map();
this.CLASS_DECORATORS = new Map();
}
clearAllData() {
this.IMPORTS.clear();
this.CLASSES.clear();
this.ENUMS.clear();
this.TYPES.clear();
this.INTERFACES.clear();
this.FUNCTIONS.clear();
this.CONSTRUCTORS.clear();
this.CLASS_EXTENDS.clear();
this.CLASS_IMPLEMENTS.clear();
this.INTERFACE_EXTENDS.clear();
this.CLASS_DECORATORS.clear();
}
merge(existingText, generatedText) {
this.clearAllData();
if (!existingText.trim()) {
return generatedText;
}
const project = new ts_morph_1.Project({ useInMemoryFileSystem: true });
const existingFile = project.createSourceFile('existing.ts', existingText);
const generatedFile = project.createSourceFile('generated.ts', generatedText);
if (existingFile.getClasses().length === 0 &&
generatedFile.getClasses().length === 0) {
return generatedText;
}
this.setFileImports(existingFile);
this.setFileImports(generatedFile);
this.setEnums(existingFile);
this.setEnums(generatedFile);
this.setInterfaces(existingFile);
this.setInterfaces(generatedFile);
this.setFunctions(existingFile);
this.setFunctions(generatedFile);
this.setConstructors(existingFile);
this.setConstructors(generatedFile);
this.setTypes(existingFile);
this.setTypes(generatedFile);
this.setClasses(existingFile);
this.setClasses(generatedFile);
this.setClassDecorators(existingFile);
this.setClassDecorators(generatedFile);
const newContent = this.generateFileFromCollectedData();
return newContent;
}
setClassDecorators(file) {
const classes = file.getClasses();
classes.forEach((classDeclaration) => {
const className = classDeclaration.getName() || '';
const decorators = classDeclaration.getDecorators();
if (!this.CLASS_DECORATORS.has(className)) {
this.CLASS_DECORATORS.set(className, new Map());
}
const classDecorators = this.CLASS_DECORATORS.get(className);
decorators.forEach((decorator) => {
const decoratorName = decorator.getName();
const args = decorator.getArguments().map((arg) => arg.getText());
if (!classDecorators.has(decoratorName)) {
classDecorators.set(decoratorName, {
hasArgs: args.length > 0,
args: new Set(),
});
}
const decoratorInfo = classDecorators.get(decoratorName);
if (args.length === 0) {
decoratorInfo.hasArgs = false;
}
else {
decoratorInfo.hasArgs = true;
args.forEach((arg) => {
decoratorInfo.args.add(arg);
});
}
});
});
}
setTypes(existingFile) {
const typeAliases = existingFile.getTypeAliases();
typeAliases.forEach((typeAlias) => {
var _a;
const typeName = typeAlias.getName() || '';
const typeDefinition = ((_a = typeAlias.getTypeNode()) === null || _a === void 0 ? void 0 : _a.getText()) || '';
this.TYPES.set(typeName, typeDefinition);
});
}
setInterfaces(file) {
const interfaces = file.getInterfaces();
interfaces.forEach((interfaceDeclaration) => {
const interfaceName = interfaceDeclaration.getName() || '';
const properties = interfaceDeclaration.getProperties();
const extendsTypes = interfaceDeclaration
.getExtends()
.map((extend) => extend.getText());
if (extendsTypes.length > 0) {
this.INTERFACE_EXTENDS.set(interfaceName, extendsTypes);
}
const newProperties = new Map();
properties.forEach((property) => {
var _a;
const propertyName = property.getName();
const propertyType = ((_a = property.getTypeNode()) === null || _a === void 0 ? void 0 : _a.getText()) || 'any';
const hasQuestionToken = property.hasQuestionToken();
const leadingComments = property.getLeadingCommentRanges();
const decorators = leadingComments.map((comment) => comment.getText().trim());
const key = propertyName + (hasQuestionToken ? '?' : '');
newProperties.set(key, {
decorators,
type: propertyType,
generated: this.isSchemaGenerated(property),
optional: hasQuestionToken,
});
});
this.mergeInterfaceProperties(interfaceName, newProperties);
});
}
mergeInterfaceProperties(interfaceName, newProperties) {
const existingProperties = this.INTERFACES.get(interfaceName) || new Map();
if (existingProperties.size === 0) {
this.INTERFACES.set(interfaceName, newProperties);
return;
}
const isProcessingGenerated = this.isProcessingGeneratedProperties(newProperties);
if (isProcessingGenerated) {
this.updateGeneratedInterfaceProperties(existingProperties, newProperties);
}
else {
this.updateExistingInterfaceProperties(existingProperties, newProperties);
}
this.INTERFACES.set(interfaceName, existingProperties);
}
isProcessingGeneratedProperties(properties) {
let generatedCount = 0;
let totalCount = 0;
properties.forEach((metadata) => {
totalCount++;
if (metadata.generated) {
generatedCount++;
}
});
return totalCount > 0 && generatedCount === totalCount;
}
updateGeneratedInterfaceProperties(existingProperties, newGeneratedProperties) {
const keysToRemove = [];
existingProperties.forEach((metadata, key) => {
if (metadata.generated && !newGeneratedProperties.has(key)) {
keysToRemove.push(key);
}
});
keysToRemove.forEach((key) => {
existingProperties.delete(key);
});
newGeneratedProperties.forEach((newMetadata, key) => {
existingProperties.set(key, newMetadata);
});
}
updateExistingInterfaceProperties(existingProperties, newProperties) {
newProperties.forEach((newValue, key) => {
const existingValue = existingProperties.get(key);
if (!existingValue) {
existingProperties.set(key, newValue);
return;
}
const combinedDecorators = [
...new Set([...existingValue.decorators, ...newValue.decorators]),
];
existingProperties.set(key, {
decorators: combinedDecorators,
type: newValue.type || existingValue.type,
generated: newValue.generated || existingValue.generated,
optional: newValue.optional || existingValue.optional,
});
});
}
setFunctions(file) {
const functions = file.getFunctions();
functions.forEach((func) => {
const functionName = func.getName() || '';
const functionText = func.getText();
this.FUNCTIONS.set(functionName, functionText);
});
}
setConstructors(file) {
const classes = file.getClasses();
classes.forEach((cls) => {
const className = cls.getName() || '';
const constructors = cls.getConstructors();
const constructorTexts = constructors.map((constructor) => constructor.getText());
if (constructorTexts.length > 0) {
this.CONSTRUCTORS.set(className, constructorTexts);
}
});
}
setEnums(file) {
const enums = file.getEnums();
enums.forEach((enumDeclaration) => {
const enumName = enumDeclaration.getName() || '';
const enumMembers = enumDeclaration
.getMembers()
.map((member) => member.getName());
this.ENUMS.set(enumName, enumMembers);
});
}
setClasses(file) {
const classes = file.getClasses();
classes.forEach((cls) => {
const className = cls.getName() || '';
const properties = cls.getProperties();
const extendsClause = cls.getExtends();
if (extendsClause) {
this.CLASS_EXTENDS.set(className, extendsClause.getText());
}
const implementsTypes = cls.getImplements().map((impl) => impl.getText());
if (implementsTypes.length > 0) {
this.CLASS_IMPLEMENTS.set(className, implementsTypes);
}
const newProperties = new Map();
properties.forEach((property) => {
const key = this.getPropertyName(property);
const decorators = this.extractDecorators(property);
const type = this.getPropertyType(property);
const hasQuestionToken = property.hasQuestionToken();
newProperties.set(key, {
decorators,
type,
generated: this.isSchemaGenerated(property),
optional: hasQuestionToken,
});
});
this.mergeClassProperties(className, newProperties);
});
}
mergeClassProperties(className, newProperties) {
const existingProperties = this.CLASSES.get(className) || new Map();
if (existingProperties.size === 0) {
this.CLASSES.set(className, newProperties);
return;
}
const isProcessingGenerated = this.isProcessingGeneratedProperties(newProperties);
if (isProcessingGenerated) {
this.updateGeneratedClassProperties(existingProperties, newProperties);
}
else {
this.updateExistingClassProperties(existingProperties, newProperties);
}
this.CLASSES.set(className, existingProperties);
}
updateGeneratedClassProperties(existingProperties, newGeneratedProperties) {
const keysToRemove = [];
existingProperties.forEach((metadata, key) => {
if (metadata.generated && !newGeneratedProperties.has(key)) {
keysToRemove.push(key);
}
});
keysToRemove.forEach((key) => {
existingProperties.delete(key);
});
newGeneratedProperties.forEach((newMetadata, key) => {
existingProperties.set(key, newMetadata);
});
}
updateExistingClassProperties(existingProperties, newProperties) {
newProperties.forEach((newValue, key) => {
const existingValue = existingProperties.get(key);
if (!existingValue) {
existingProperties.set(key, newValue);
return;
}
const combinedDecorators = [
...new Set([...existingValue.decorators, ...newValue.decorators]),
];
existingProperties.set(key, {
decorators: combinedDecorators,
type: newValue.type || existingValue.type,
generated: newValue.generated || existingValue.generated,
optional: newValue.optional || existingValue.optional,
});
});
}
setFileImports(file) {
const imports = file.getImportDeclarations();
imports.forEach((imp) => {
var _a, _b;
const moduleSpecifier = imp.getModuleSpecifierValue();
const namedImports = imp.getNamedImports().map((ni) => ni.getName());
const defaultImport = (_a = imp.getDefaultImport()) === null || _a === void 0 ? void 0 : _a.getText();
const namespaceImport = (_b = imp.getNamespaceImport()) === null || _b === void 0 ? void 0 : _b.getText();
const existingImports = new Set(this.IMPORTS.get(moduleSpecifier) || []);
namedImports.forEach((importName) => existingImports.add(importName));
if (defaultImport) {
existingImports.add(defaultImport);
}
if (namespaceImport) {
existingImports.add(namespaceImport);
}
this.IMPORTS.set(moduleSpecifier, Array.from(existingImports));
});
}
getPropertyName(property) {
if (ts_morph_1.Node.isPropertySignature(property)) {
return property.getName();
}
else if (ts_morph_1.Node.isPropertyDeclaration(property)) {
return property.getName();
}
return '';
}
getPropertyType(property) {
const typeNode = property.getTypeNode();
return typeNode ? typeNode.getText() : 'any';
}
extractDecorators(property) {
const decorators = [];
if (ts_morph_1.Node.isPropertyDeclaration(property)) {
for (const decorator of property.getDecorators()) {
decorators.push(decorator.getText());
}
}
return decorators;
}
isSchemaGenerated(property) {
const leadingComments = property.getLeadingCommentRanges();
return leadingComments.some((comment) => comment.getText().includes(this.SCHEMA_MARKER));
}
generateFileFromCollectedData() {
const sections = [];
sections.push(this.generateImports());
sections.push(this.generateTypes());
sections.push(this.generateEnums());
sections.push(this.generateInterfaces());
sections.push(this.generateClasses());
sections.push(this.generateFunctions());
return sections.filter((section) => section.trim()).join('\n\n');
}
generateImports() {
if (this.IMPORTS.size === 0)
return '';
const importLines = [];
this.IMPORTS.forEach((imports, module) => {
const uniqueImports = [...new Set(imports)];
if (uniqueImports.length > 0) {
importLines.push(`import { ${uniqueImports.join(', ')} } from '${module}';`);
}
});
return importLines.join('\n');
}
generateTypes() {
if (this.TYPES.size === 0)
return '';
const typeLines = [];
this.TYPES.forEach((definition, name) => {
typeLines.push(`type ${name} = ${definition};`);
});
return typeLines.join('\n');
}
generateEnums() {
if (this.ENUMS.size === 0)
return '';
const enumLines = [];
this.ENUMS.forEach((members, name) => {
enumLines.push(`enum ${name} {`);
members.forEach((member) => {
enumLines.push(` ${member},`);
});
enumLines.push('}');
});
return enumLines.join('\n');
}
generateInterfaces() {
if (this.INTERFACES.size === 0)
return '';
const interfaceLines = [];
this.INTERFACES.forEach((properties, name) => {
const extendsTypes = this.INTERFACE_EXTENDS.get(name);
const extendsClause = extendsTypes && extendsTypes.length > 0
? ` extends ${extendsTypes.join(', ')}`
: '';
interfaceLines.push(`export interface ${name}${extendsClause} {`);
properties.forEach((metadata, propertyName) => {
const cleanPropertyName = propertyName.replace('?', '');
const optional = metadata.optional ? '?' : '';
const generatedComment = metadata.generated
? ' // @generated from prisma schema\n'
: '';
interfaceLines.push(`${generatedComment} ${cleanPropertyName}${optional}: ${metadata.type};`);
});
interfaceLines.push('}');
});
return interfaceLines.join('\n');
}
generateClasses() {
if (this.CLASSES.size === 0)
return '';
const classLines = [];
this.CLASSES.forEach((properties, className) => {
const classDecorators = this.CLASS_DECORATORS.get(className);
if (classDecorators && classDecorators.size > 0) {
classDecorators.forEach((decoratorInfo, decoratorName) => {
if (decoratorInfo.hasArgs && decoratorInfo.args.size > 0) {
const allArgs = Array.from(decoratorInfo.args).join(', ');
classLines.push(`@${decoratorName}(${allArgs})`);
}
else {
classLines.push(`@${decoratorName}()`);
}
});
}
const extendsClause = this.CLASS_EXTENDS.get(className);
const implementsTypes = this.CLASS_IMPLEMENTS.get(className);
let classDeclaration = `export class ${className}`;
if (extendsClause) {
classDeclaration += ` extends ${extendsClause}`;
}
if (implementsTypes && implementsTypes.length > 0) {
classDeclaration += ` implements ${implementsTypes.join(', ')}`;
}
classDeclaration += ' {';
classLines.push(classDeclaration);
const constructors = this.CONSTRUCTORS.get(className);
if (constructors && constructors.length > 0) {
constructors.forEach((constructor) => {
classLines.push(` ${constructor}`);
});
classLines.push('');
}
properties.forEach((metadata, propertyName) => {
const optional = metadata.optional ? '?' : '';
const generatedComment = metadata.generated
? ' // @generated from prisma schema'
: '';
if (generatedComment) {
classLines.push(generatedComment);
}
metadata.decorators.forEach((decorator) => {
classLines.push(` ${decorator}`);
});
classLines.push(` ${propertyName}${optional}: ${metadata.type};`);
classLines.push('');
});
if (classLines[classLines.length - 1] === '') {
classLines.pop();
}
classLines.push('}');
});
return classLines.join('\n');
}
generateFunctions() {
if (this.FUNCTIONS.size === 0)
return '';
const functionLines = [];
this.FUNCTIONS.forEach((functionText) => {
functionLines.push(functionText);
});
return functionLines.join('\n\n');
}
}
exports.SmartMergeContentV2 = SmartMergeContentV2;