UNPKG

@adocasts.com/dto

Version:

Easily make and generate DTOs from Lucid Models

97 lines (96 loc) 4 kB
import FileService from './file_service.js'; import { generators } from '@adonisjs/core/app'; import string from '@adonisjs/core/helpers/string'; import { slash } from '@adonisjs/core/helpers'; import { fsReadAll } from '@adonisjs/core/helpers'; import UtilService from './util_service.js'; export default class ModelService { app; #relationTypes = ['BelongsTo', 'HasOne', 'HasMany', 'ManyToMany', 'HasManyThrough']; #relationTypesPlural = ['HasMany', 'ManyToMany', 'HasManyThrough']; constructor(app) { this.app = app; } async getFromFiles() { const modelsPath = this.app.modelsPath(); const modelFilePaths = await fsReadAll(modelsPath, { pathType: 'absolute' }); const promises = modelFilePaths.map(async (filePath) => { const name = generators.modelName(slash(filePath).split('/').pop()); return this.getModelInfo({ name, filePath }, name); }); return await Promise.all(promises); } /** * Get model's validity, file, class, and property information * @param modelName * @param dtoName */ async getModelInfo(modelName, dtoName) { const name = (typeof modelName === 'object' ? modelName.name : modelName) || dtoName; const fileName = generators.modelFileName(name); const filePath = typeof modelName === 'object' ? modelName.filePath : this.app.modelsPath(fileName); const isReadable = await FileService.canRead(filePath); const data = { name: generators.modelName(name), variable: string.camelCase(name), fileName, filePath, isReadable, properties: [], }; if (!isReadable) return { model: data, modelFileLines: [] }; const { definitions, fileLines } = await FileService.readDeclarations(filePath); data.properties = await this.#getModelProperties(definitions); return { model: data, modelFileLines: fileLines }; } /** * Get model property, relationship, and type info * @param definitions * @private */ async #getModelProperties(definitions) { return definitions.map((definition) => { const propertyTypeString = definition.replace('declare', '').replace('public', ''); const [propertyLeft, propertyRight = ''] = propertyTypeString.split(':'); const name = UtilService.cleanDefinition(propertyLeft); let { typeString, valueString } = UtilService.getTypeAndValue(propertyRight); if (!typeString) { typeString = UtilService.getDefaultType(name); } const typesRaw = typeString.split('|').map((type) => type.trim()); const types = typesRaw.map((type) => this.#parseRelationType(type)); const defaultValue = valueString?.trim(); const relation = types.find((type) => type.isRelationship); const isOptionallyModified = propertyLeft.trim().endsWith('?') && !types.some((item) => item.type === 'undefined'); return { name, types, defaultValue, relation, isOptionallyModified, }; }); } /** * Gets relationship type information from raw type string * @param typeRaw * @private */ #parseRelationType(typeRaw) { if (!this.#relationTypes.some((type) => typeRaw.includes(type))) { return { type: typeRaw }; } const isPlural = this.#relationTypesPlural.some((type) => typeRaw.includes(type)); const model = typeRaw.split('typeof').at(1)?.split('>').at(0)?.trim(); const dto = model?.endsWith('Dto') ? model : model + 'Dto'; return { type: typeRaw, dtoType: `${dto}${isPlural ? '[]' : ''}`, model, dto, isPlural, isRelationship: true, }; } }