UNPKG

@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
"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;