UNPKG

@elsikora/nestjs-crud-automator

Version:

A library for automating the creation of CRUD operations in NestJS.

86 lines (83 loc) 3.92 kB
import { CONTEXTUAL_MANUAL_DTO_CONSTANT } from '../../../constant/dto/contextual-manual.constant.js'; import { CamelCaseString } from '../../camel-case-string.utility.js'; import { DtoAutoContextPop } from '../auto/context/pop.utility.js'; import { DtoAutoContextPush } from '../auto/context/push.utility.js'; import { GetManualDtoPropertyMetadata } from '../manual/property-metadata/get.utility.js'; const contextualManualDtoCache = new WeakMap(); /** * Generates a path-aware DTO wrapper for a nested manual DTO inside an auto-generated CRUD DTO. * The wrapper replays all original `ApiProperty*` decorators so Swagger, validation, and * class-transformer semantics remain intact while the schema name becomes context-specific. * @param {Type<unknown>} sourceDto - Source manual DTO class. * @param {EApiRouteType} method - Current auto DTO route method. * @param {EApiDtoType} dtoType - Current auto DTO type. * @param {string} parentDtoName - Parent generated DTO class name. * @param {string} propertyPathSegment - Current nested path segment to append. * @returns {Type<unknown>} Context-specific wrapper or the original class when replay metadata is unavailable. */ function DtoGenerateContextualManualDto(sourceDto, method, dtoType, parentDtoName, propertyPathSegment) { if (sourceDto === Object || IsContextualManualDto(sourceDto)) { return sourceDto; } const sourcePrototype = sourceDto.prototype; const propertyMetadata = GetManualDtoPropertyMetadata(sourcePrototype); if (propertyMetadata.size === 0) { return sourceDto; } // eslint-disable-next-line @elsikora/typescript/no-magic-numbers const parentDtoNameBase = parentDtoName.endsWith("DTO") ? parentDtoName.slice(0, -3) : parentDtoName; const className = `${parentDtoNameBase}${CamelCaseString(propertyPathSegment)}DTO`; const cached = contextualManualDtoCache.get(sourceDto)?.get(className); if (cached) { return cached; } class GeneratedDTO { constructor() { for (const propertyKey of propertyMetadata.keys()) { Object.defineProperty(this, propertyKey, { // eslint-disable-next-line @elsikora/typescript/naming-convention configurable: true, // eslint-disable-next-line @elsikora/typescript/naming-convention enumerable: true, value: undefined, // eslint-disable-next-line @elsikora/typescript/naming-convention writable: true, }); } } } Object.defineProperty(GeneratedDTO, "name", { value: className, }); Reflect.defineMetadata?.(CONTEXTUAL_MANUAL_DTO_CONSTANT.METADATA_KEY, { dtoType, method, source: sourceDto, }, GeneratedDTO); let dtoCache = contextualManualDtoCache.get(sourceDto); if (!dtoCache) { dtoCache = new Map(); contextualManualDtoCache.set(sourceDto, dtoCache); } dtoCache.set(className, GeneratedDTO); DtoAutoContextPush(GeneratedDTO.prototype, method, dtoType); try { for (const [propertyKey, metadata] of propertyMetadata.entries()) { metadata.apply(GeneratedDTO.prototype, propertyKey); } } finally { DtoAutoContextPop(GeneratedDTO.prototype); } return GeneratedDTO; } /** * Returns whether a class was materialized as a context-specific wrapper around a manual DTO. * @param {unknown} value - Constructor candidate. * @returns {boolean} Whether the candidate is a generated contextual manual DTO. */ function IsContextualManualDto(value) { return typeof value === "function" && Boolean(Reflect.getMetadata?.(CONTEXTUAL_MANUAL_DTO_CONSTANT.METADATA_KEY, value)); } export { DtoGenerateContextualManualDto, IsContextualManualDto }; //# sourceMappingURL=manual-child.utility.js.map