@rxap/ts-morph
Version:
Provides utilities for manipulating TypeScript code using the ts-morph library. It offers a fluent API to add, modify, and remove code elements such as classes, decorators, imports, and properties in both Angular and NestJS projects. This package simplifi
232 lines • 10 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CoerceDtoClass = CoerceDtoClass;
const utilities_1 = require("@rxap/utilities");
const path_1 = require("path");
const ts_morph_1 = require("ts-morph");
const dto_class_property_1 = require("./dto-class-property");
require("colors");
const coerce_source_file_1 = require("../coerce-source-file");
const coerce_class_1 = require("../coerce-class");
const type_import_1 = require("../type-import");
const coerce_property_declaration_1 = require("../coerce-property-declaration");
const write_type_1 = require("../write-type");
const coerce_decorator_1 = require("../coerce-decorator");
const coerce_imports_1 = require("../coerce-imports");
function CoerceDtoClass(options) {
const { project, propertyList = [], tsMorphTransform = utilities_1.noop, } = options;
let { name, } = options;
name = (0, utilities_1.dasherize)(name);
const className = (0, utilities_1.CoerceSuffix)((0, utilities_1.classify)(name), 'Dto');
const fileName = (0, utilities_1.CoerceSuffix)(name, '.dto');
const sourceFile = (0, coerce_source_file_1.CoerceSourceFile)(project, (0, path_1.join)('dtos', fileName + '.ts'));
const classDeclaration = (0, coerce_class_1.CoerceClass)(sourceFile, className);
classDeclaration.setIsExported(true);
for (const property of propertyList.map(dto_class_property_1.NormalizeDataClassProperty)) {
// create a clone of the property type to avoid modifying the original type
// if the property type is a self reference we need to replace the self reference with the class name
// if the property type is a deferred reference we need to replace the deferred reference with the class name
const propertyType = Object.assign({}, property.type);
let subTypeOutput;
switch (propertyType.name) {
case type_import_1.TypeNames.Self:
propertyType.name = className;
propertyType.isTypeOnly = false;
propertyType.moduleSpecifier = null;
propertyType.namedImport = null;
propertyType.namespaceImport = null;
propertyType.defaultImport = null;
property.isType = true;
break;
case type_import_1.TypeNames.Deferred:
subTypeOutput = CoerceDtoClass({
project,
name: [name, (0, utilities_1.dasherize)(property.name)].join('-'),
propertyList: property.memberList,
});
propertyType.name = subTypeOutput.className;
propertyType.moduleSpecifier = './' + (0, utilities_1.dasherize)(subTypeOutput.className.replace(/Dto$/, '')) + '.dto';
propertyType.isTypeOnly = false;
propertyType.namedImport = null;
propertyType.namespaceImport = null;
propertyType.defaultImport = null;
property.isType = true;
break;
}
let propertyName = (0, utilities_1.camelize)(property.name);
const prefixMatch = property.name.match(/^(_+)/);
if (prefixMatch) {
propertyName = (0, utilities_1.camelize)(property.name.replace(/^_+/, ''));
propertyName = prefixMatch[0] + propertyName;
}
const propertyDeclaration = (0, coerce_property_declaration_1.CoercePropertyDeclaration)(classDeclaration, propertyName).set({
type: (0, write_type_1.WriteType)({
isArray: property.isArray,
type: propertyType,
}, sourceFile),
hasQuestionToken: property.isOptional,
hasExclamationToken: !property.isOptional,
});
addExposeDecorator(propertyDeclaration);
if (property.isArray) {
addClassValidatorDecoratorForIsArray(propertyDeclaration);
}
if (property.isType) {
addClassValidatorDecoratorForNestedDto(propertyDeclaration, propertyType, property.isArray);
}
if (property.isOptional) {
addIsOptionalDecorator(propertyDeclaration);
}
addClassValidatorDecoratorForType(propertyDeclaration, propertyType);
if (propertyType.name !== 'unknown') {
cleanupUnknownApiPropertyDecorator(propertyDeclaration);
}
}
tsMorphTransform(project, sourceFile, classDeclaration);
return {
className,
filePath: '.' + (0, path_1.join)(sourceFile.getDirectoryPath(), sourceFile.getBaseNameWithoutExtension()),
sourceFile,
classDeclaration,
};
}
function addIsOptionalDecorator(propertyDeclaration) {
const sourceFile = propertyDeclaration.getSourceFile();
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsOptional', {
arguments: [],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsOptional'],
moduleSpecifier: 'class-validator',
});
}
function addExposeDecorator(propertyDeclaration) {
const sourceFile = propertyDeclaration.getSourceFile();
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'Expose', {
arguments: [],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['Expose'],
moduleSpecifier: 'class-transformer',
});
}
function addClassValidatorDecoratorForIsArray(propertyDeclaration) {
const sourceFile = propertyDeclaration.getSourceFile();
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsArray', {
arguments: [],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsArray'],
moduleSpecifier: 'class-validator',
});
}
function addClassValidatorDecoratorForNestedDto(propertyDeclaration, propertyType, isArray) {
const sourceFile = propertyDeclaration.getSourceFile();
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'Type', {
arguments: [
w => {
w.write('() => ');
(0, write_type_1.WriteType)({
type: propertyType,
isArray: false,
}, sourceFile)(w);
},
],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['Type'],
moduleSpecifier: 'class-transformer',
});
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsInstance', {
arguments: [
(0, write_type_1.WriteType)({
type: propertyType,
isArray: false,
}, sourceFile),
w => {
if (isArray) {
ts_morph_1.Writers.object({ each: 'true' })(w);
}
},
],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsInstance'],
moduleSpecifier: 'class-validator',
});
}
function addClassValidatorDecoratorForType(propertyDeclaration, propertyType) {
var _a;
const sourceFile = propertyDeclaration.getSourceFile();
switch (propertyType.name) {
case 'date':
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsDate', {
arguments: [],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsDate'],
moduleSpecifier: 'class-validator',
});
break;
case 'number':
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsNumber', {
arguments: [],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsNumber'],
moduleSpecifier: 'class-validator',
});
break;
case 'string':
if ((_a = propertyDeclaration.getName()) === null || _a === void 0 ? void 0 : _a.match(/^uuid|Uuid$/)) {
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsUUID'],
moduleSpecifier: 'class-validator',
});
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsUUID', {
arguments: [],
});
}
else {
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsString'],
moduleSpecifier: 'class-validator',
});
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsString', {
arguments: [],
});
}
break;
case 'boolean':
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'IsBoolean', {
arguments: [],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['IsBoolean'],
moduleSpecifier: 'class-validator',
});
break;
case 'unknown':
(0, coerce_decorator_1.CoerceDecorator)(propertyDeclaration, 'ApiProperty', {
arguments: [ts_morph_1.Writers.object({ type: w => w.quote('unknown') })],
});
(0, coerce_imports_1.CoerceImports)(sourceFile, {
namedImports: ['ApiProperty'],
moduleSpecifier: '@nestjs/swagger',
});
break;
}
}
function cleanupUnknownApiPropertyDecorator(propertyDeclaration) {
var _a;
const apiProperty = propertyDeclaration.getDecorators().find(d => d.getName() === 'ApiProperty');
if (apiProperty) {
const args = apiProperty.getArguments()[0];
if (args.isKind(ts_morph_1.SyntaxKind.ObjectLiteralExpression)) {
if ((_a = args.getProperty('type')) === null || _a === void 0 ? void 0 : _a.getText().includes('unknown')) {
apiProperty.remove();
}
}
}
}
//# sourceMappingURL=coerce-dto-class.js.map