@adocasts.com/dto
Version:
Easily make and generate DTOs from Lucid Models
97 lines (96 loc) • 4 kB
JavaScript
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,
};
}
}