UNPKG

@cheetah.js/orm

Version:
338 lines (337 loc) 17 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OrmService = void 0; const core_1 = require("@cheetah.js/core"); const entities_1 = require("./domain/entities"); const constants_1 = require("./constants"); const ts_morph_1 = require("ts-morph"); const orm_1 = require("./orm"); const globby = __importStar(require("globby")); let OrmService = class OrmService { constructor(orm, storage, entityFile) { this.orm = orm; this.storage = storage; this.allEntities = new Map(); const files = new ts_morph_1.Project({ skipLoadingLibFiles: true }).addSourceFilesAtPaths(entityFile ?? this.getSourceFilePaths()); files.forEach(file => { file.getClasses().forEach(classDeclaration => { if (classDeclaration.getDecorator('Entity')) { const properties = classDeclaration.getProperties(); const nullables = []; const defaults = {}; const extendsClass = classDeclaration.getBaseClass(); if (extendsClass) { const extendsProperties = extendsClass.getProperties(); properties.push(...extendsProperties); } properties.forEach(property => { const propertyName = property.getName(); const isNullable = property.hasQuestionToken(); const initializer = property.getInitializer(); if (isNullable) { nullables.push(propertyName); } if (initializer) { const initializerKind = initializer.getKind(); switch (initializerKind) { case ts_morph_1.SyntaxKind.StringLiteral: defaults[propertyName] = initializer.getText(); break; case ts_morph_1.SyntaxKind.NumericLiteral: defaults[propertyName] = parseFloat(initializer.getText()); break; case ts_morph_1.SyntaxKind.NewExpression: case ts_morph_1.SyntaxKind.CallExpression: break; default: defaults[propertyName] = () => initializer.getText(); break; } } this.allEntities.set(classDeclaration.getName(), { nullables, defaults }); }); } }); }); } discoverRelationshipTypes(files) { const entityNameToClass = new Map(); const processedClasses = new Set(); const entities = core_1.Metadata.get(constants_1.ENTITIES, Reflect) || []; for (const entity of entities) { entityNameToClass.set(entity.target.name, entity.target); } files.forEach(file => { file.getClasses().forEach(classDeclaration => { if (!classDeclaration.getDecorator('Entity')) return; const className = classDeclaration.getName(); const targetClass = entityNameToClass.get(className); if (!targetClass) return; processedClasses.add(className); const relationships = core_1.Metadata.get(constants_1.PROPERTIES_RELATIONS, targetClass) || []; classDeclaration.getProperties().forEach(property => { const propertyName = property.getName(); const relationship = relationships.find(r => r.propertyKey === propertyName); if (relationship && relationship.entity === '__AUTO_DETECT__' && relationship.relation === 'many-to-one') { const typeNode = property.getTypeNode(); if (!typeNode) return; const entityTypeName = typeNode.getText().trim(); if (entityTypeName) { const entityClass = entityNameToClass.get(entityTypeName); if (entityClass) { relationship.entity = () => entityClass; } else { console.warn(`Warning: Could not find entity "${entityTypeName}" for relationship ` + `"${className}.${propertyName}". Please define it explicitly.`); } } } }); core_1.Metadata.set(constants_1.PROPERTIES_RELATIONS, relationships, targetClass); }); }); for (const entity of entities) { if (processedClasses.has(entity.target.name)) continue; const relationships = core_1.Metadata.get(constants_1.PROPERTIES_RELATIONS, entity.target) || []; let updated = false; for (const relationship of relationships) { if (relationship.entity === '__AUTO_DETECT__' && relationship.relation === 'many-to-one') { const propertyKey = relationship.propertyKey; const capitalizedName = propertyKey.charAt(0).toUpperCase() + propertyKey.slice(1); let entityClass = entityNameToClass.get(capitalizedName); if (!entityClass) { for (const [name, cls] of entityNameToClass) { if (name.toLowerCase() === propertyKey.toLowerCase()) { entityClass = cls; break; } } } if (entityClass) { relationship.entity = () => entityClass; updated = true; } else { console.warn(`Warning: Could not auto-detect entity for "${entity.target.name}.${propertyKey}". ` + `Please define it explicitly.`); } } } if (updated) { core_1.Metadata.set(constants_1.PROPERTIES_RELATIONS, relationships, entity.target); } } } discoverEnumTypes(files, entities) { const entityNameToClass = new Map(); for (const entity of entities) { entityNameToClass.set(entity.target.name, entity.target); } files.forEach(file => { file.getClasses().forEach(classDeclaration => { if (!classDeclaration.getDecorator('Entity')) return; const className = classDeclaration.getName(); const targetClass = entityNameToClass.get(className); if (!targetClass) return; const properties = core_1.Metadata.get(constants_1.PROPERTIES_METADATA, targetClass) || {}; classDeclaration.getProperties().forEach(property => { const propertyName = property.getName(); const propertyMetadata = properties[propertyName]; if (propertyMetadata?.options?.enumItems === '__AUTO_DETECT__') { const typeNode = property.getTypeNode(); if (!typeNode) return; const enumTypeName = typeNode.getText().trim(); const enumArrayMatch = enumTypeName.match(/^(.+)\[\]$/); const actualEnumName = enumArrayMatch ? enumArrayMatch[1] : enumTypeName; const sourceFile = file; const enumDeclaration = sourceFile.getEnum(actualEnumName); if (enumDeclaration) { const enumMembers = enumDeclaration.getMembers(); const enumValues = enumMembers.map(member => { const value = member.getValue(); return value !== undefined ? value : member.getName(); }); propertyMetadata.options.enumItems = enumValues; if (enumArrayMatch) { propertyMetadata.options.array = true; } } else { const allSourceFiles = sourceFile.getProject().getSourceFiles(); let foundEnum = false; for (const sf of allSourceFiles) { const importedEnum = sf.getEnum(actualEnumName); if (importedEnum) { const enumMembers = importedEnum.getMembers(); const enumValues = enumMembers.map(member => { const value = member.getValue(); return value !== undefined ? value : member.getName(); }); propertyMetadata.options.enumItems = enumValues; if (enumArrayMatch) { propertyMetadata.options.array = true; } foundEnum = true; break; } } if (!foundEnum) { console.warn(`Warning: Could not find enum "${actualEnumName}" for property ` + `"${className}.${propertyName}". Please define it explicitly.`); } } } }); core_1.Metadata.set(constants_1.PROPERTIES_METADATA, properties, targetClass); }); }); } async onInit(customConfig = {}) { const hasCustomConfig = Object.keys(customConfig).length > 0; let config = null; let setConfig; if (!hasCustomConfig) { const configFile = globby.sync('cheetah.config.ts', { absolute: true }); if (configFile.length === 0) { console.log('No config file found!'); return; } config = await Promise.resolve(`${configFile[0]}`).then(s => __importStar(require(s))); setConfig = config.default; } else { setConfig = customConfig; } this.orm.setConnection(setConfig); await this.orm.connect(); if (config && typeof config.default.entities === 'string') { const files = globby.sync([config.default.entities, '!node_modules'], { gitignore: true, absolute: true }); for (const file of files) { await Promise.resolve(`${file}`).then(s => __importStar(require(s))); } } let entities = core_1.Metadata.get(constants_1.ENTITIES, Reflect); if (!entities) { const entityPaths = this.getSourceFilePaths(); const entityFiles = globby.sync(entityPaths.filter(path => path.includes('.entity.') || path.includes('entities/'))); for (const file of entityFiles) { try { await Promise.resolve(`${file}`).then(s => __importStar(require(s))); } catch (error) { console.warn(`Failed to import entity file ${file}:`, error); } } entities = core_1.Metadata.get(constants_1.ENTITIES, Reflect); } if (!entities) { console.log('No entities found!'); return; } const files = new ts_morph_1.Project({ skipLoadingLibFiles: true }) .addSourceFilesAtPaths(this.getSourceFilePaths()); this.discoverRelationshipTypes(files); this.discoverEnumTypes(files, entities); for (const entity of entities) { const nullableDefaultEntity = this.allEntities.get(entity.target.name); const propertiesFromMetadata = core_1.Metadata.get(constants_1.PROPERTIES_METADATA, entity.target); const relationship = core_1.Metadata.get(constants_1.PROPERTIES_RELATIONS, entity.target); const hooks = core_1.Metadata.get(constants_1.EVENTS_METADATA, entity.target); // Cria uma cópia profunda das propriedades para evitar mutação compartilhada const properties = {}; if (propertiesFromMetadata) { for (const [key, value] of Object.entries(propertiesFromMetadata)) { properties[key] = { type: value.type, options: { ...value.options } }; } } for (const property in properties) { if (nullableDefaultEntity?.nullables.includes(property)) { properties[property].options.nullable = true; } if (nullableDefaultEntity?.defaults[property]) { properties[property].options.default = nullableDefaultEntity?.defaults[property]; } } this.storage.add(entity, properties, relationship, hooks); } } getSourceFilePaths() { const projectRoot = process.cwd(); // Ajuste conforme a estrutura do seu projeto const getAllFiles = (dir) => { const patterns = [`${dir}/**/*.(ts|js)`, "!**/node_modules/**"]; try { return globby.sync(patterns, { gitignore: true }); } catch (error) { console.error('Erro ao obter arquivos:', error); return []; } }; // Filtra os arquivos pelo padrão de nomenclatura return getAllFiles(projectRoot); } }; exports.OrmService = OrmService; __decorate([ (0, core_1.OnApplicationInit)(), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], OrmService.prototype, "onInit", null); exports.OrmService = OrmService = __decorate([ (0, core_1.Service)(), __metadata("design:paramtypes", [orm_1.Orm, entities_1.EntityStorage, String]) ], OrmService);