@nestjs/swagger
Version:
Nest - modern, fast, powerful node.js web framework (@swagger)
605 lines (604 loc) • 31.4 kB
JavaScript
import { compact, flatten, head } from 'es-toolkit/compat';
import { posix } from 'path';
import * as ts from 'typescript';
import { factory } from 'typescript';
import { ApiHideProperty, ApiProperty } from '../../decorators/index.js';
import { decoratorsProperties, decoratorsPropertiesMappingType } from '../../services/decorators-properties.js';
import { METADATA_FACTORY_NAME } from '../plugin-constants.js';
import { pluginDebugLogger } from '../plugin-debug-logger.js';
import { createBooleanLiteral, createLiteralFromAnyValue, createPrimitiveLiteral, getDecoratorArguments, getMainCommentOfNode, getText, getTsDocTagsOfNode, isEnum } from '../utils/ast-utils.js';
import { canReferenceNode, convertPath, createAdditionalPropertiesValueSchema, extractTypeArgumentIfArray, getDecoratorOrUndefinedByNames, getRecordValueType, getStringLiteralUnionValues, getTypeReferenceAsString, hasPropertyKey, isAutoGeneratedEnumUnion, isAutoGeneratedTypeUnion } from '../utils/plugin-utils.js';
import { resolvePluginOptionsForFile } from '../utils/module-format.util.js';
import { typeReferenceToIdentifier } from '../utils/type-reference-to-identifier.util.js';
import { AbstractFileVisitor } from './abstract.visitor.js';
export class ModelClassVisitor extends AbstractFileVisitor {
constructor() {
super(...arguments);
this._typeImports = {};
this._collectedMetadata = {};
}
get typeImports() {
return this._typeImports;
}
collectedMetadata() {
return this.buildMetadataImports(this._collectedMetadata);
}
visit(sourceFile, ctx, program, options) {
options = resolvePluginOptionsForFile(options, sourceFile, program.getCompilerOptions());
const typeChecker = program.getTypeChecker();
this._hoistedTypeImports.clear();
sourceFile = this.updateImports(sourceFile, ctx.factory, program, options);
const propertyNodeVisitorFactory = (metadata) => (node) => {
const visit = () => {
if (ts.isPropertyDeclaration(node)) {
this.visitPropertyNodeDeclaration(node, ctx, typeChecker, options, sourceFile, metadata);
}
else if (options.parameterProperties &&
ts.isConstructorDeclaration(node)) {
this.visitConstructorDeclarationNode(node, typeChecker, options, sourceFile, metadata);
}
return node;
};
const visitedNode = visit();
if (!options.readonly) {
return visitedNode;
}
};
const visitClassNode = (node) => {
if (ts.isClassDeclaration(node)) {
const metadata = {};
const isExported = node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
if (options.readonly) {
if (isExported) {
ts.forEachChild(node, propertyNodeVisitorFactory(metadata));
}
else {
if (options.debug) {
pluginDebugLogger.debug(`Skipping class "${node.name.getText()}" because it's not exported.`);
}
}
}
else {
node = ts.visitEachChild(node, propertyNodeVisitorFactory(metadata), ctx);
}
if ((isExported && options.readonly) || !options.readonly) {
const declaration = this.addMetadataFactory(ctx.factory, node, metadata, sourceFile, options);
if (!options.readonly) {
return declaration;
}
}
}
if (options.readonly) {
ts.forEachChild(node, visitClassNode);
}
else {
return ts.visitEachChild(node, visitClassNode, ctx);
}
};
const visitedSourceFile = ts.visitNode(sourceFile, visitClassNode);
if (options.readonly) {
return visitedSourceFile;
}
return this.insertHoistedTypeImports(visitedSourceFile, ctx.factory);
}
visitPropertyNodeDeclaration(node, ctx, typeChecker, options, sourceFile, metadata) {
const isPropertyStatic = (node.modifiers || []).some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword);
if (isPropertyStatic) {
return node;
}
const isPrivateProperty = ts.isPrivateIdentifier(node.name);
if (isPrivateProperty) {
return node;
}
const decorators = ts.canHaveDecorators(node) && ts.getDecorators(node);
const classTransformerShim = options.classTransformerShim;
const hidePropertyDecoratorExists = getDecoratorOrUndefinedByNames(classTransformerShim
? [ApiHideProperty.name, 'Exclude']
: [ApiHideProperty.name], decorators, factory);
const annotatePropertyDecoratorExists = getDecoratorOrUndefinedByNames(classTransformerShim ? [ApiProperty.name, 'Expose'] : [ApiProperty.name], decorators, factory);
if (!annotatePropertyDecoratorExists &&
(hidePropertyDecoratorExists || classTransformerShim === 'exclusive')) {
return node;
}
else if (annotatePropertyDecoratorExists && hidePropertyDecoratorExists) {
if (options.debug) {
pluginDebugLogger.debug(`"${node.parent.name.getText()}->${node.name.getText()}" has conflicting decorators, excluding as @ApiHideProperty() takes priority.`);
}
return node;
}
try {
this.inspectPropertyDeclaration(ctx.factory, node, typeChecker, options, sourceFile.fileName, sourceFile, metadata);
}
catch (err) {
return node;
}
}
visitConstructorDeclarationNode(constructorNode, typeChecker, options, sourceFile, metadata) {
constructorNode.forEachChild((node) => {
if (ts.isParameter(node) &&
node.modifiers != null &&
node.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ReadonlyKeyword ||
modifier.kind === ts.SyntaxKind.PrivateKeyword ||
modifier.kind === ts.SyntaxKind.PublicKeyword ||
modifier.kind === ts.SyntaxKind.ProtectedKeyword)) {
const objectLiteralExpr = this.createDecoratorObjectLiteralExpr(factory, node, typeChecker, factory.createNodeArray(), options, sourceFile.fileName, sourceFile);
const propertyName = node.name.getText();
metadata[propertyName] = objectLiteralExpr;
}
});
}
addMetadataFactory(factory, node, classMetadata, sourceFile, options) {
const returnValue = factory.createObjectLiteralExpression(Object.keys(classMetadata).map((key) => factory.createPropertyAssignment(factory.createIdentifier(key), classMetadata[key])));
if (options.readonly) {
const filePath = this.normalizeImportPath(options.pathToSource, sourceFile.fileName);
this.registerOutputExtension(filePath, sourceFile, options);
if (!this._collectedMetadata[filePath]) {
this._collectedMetadata[filePath] = {};
}
const attributeKey = node.name.getText();
this._collectedMetadata[filePath][attributeKey] = returnValue;
return;
}
const method = factory.createMethodDeclaration([factory.createModifier(ts.SyntaxKind.StaticKeyword)], undefined, factory.createIdentifier(METADATA_FACTORY_NAME), undefined, undefined, [], undefined, factory.createBlock([factory.createReturnStatement(returnValue)], true));
return factory.updateClassDeclaration(node, node.modifiers, node.name, node.typeParameters, node.heritageClauses, [...node.members, method]);
}
inspectPropertyDeclaration(factory, compilerNode, typeChecker, options, hostFilename, sourceFile, metadata) {
const objectLiteralExpr = this.createDecoratorObjectLiteralExpr(factory, compilerNode, typeChecker, factory.createNodeArray(), options, hostFilename, sourceFile);
this.addClassMetadata(compilerNode, objectLiteralExpr, sourceFile, metadata);
}
createDecoratorObjectLiteralExpr(factory, node, typeChecker, existingProperties = factory.createNodeArray(), options = {}, hostFilename = '', sourceFile) {
const isRequired = !node.questionToken;
const properties = [
...existingProperties,
!hasPropertyKey('required', existingProperties) &&
factory.createPropertyAssignment('required', createBooleanLiteral(factory, isRequired)),
...this.createTypePropertyAssignments(factory, node.type, typeChecker, existingProperties, hostFilename, options),
...this.createDescriptionAndTsDocTagPropertyAssignments(factory, node, typeChecker, existingProperties, options, sourceFile),
this.createDefaultPropertyAssignment(factory, node, existingProperties, options),
this.createEnumPropertyAssignment(factory, node, typeChecker, existingProperties, hostFilename, options)
];
if ((ts.isPropertyDeclaration(node) || ts.isPropertySignature(node)) &&
options.classValidatorShim) {
properties.push(this.createValidationPropertyAssignments(factory, node, options));
}
return factory.createObjectLiteralExpression(compact(flatten(properties)));
}
createRecordTypePropertyAssignment(factory, recordValueType, typeChecker) {
const additionalPropertiesExpr = createAdditionalPropertiesValueSchema(recordValueType, typeChecker, factory);
return [
factory.createPropertyAssignment('type', factory.createStringLiteral('object')),
factory.createPropertyAssignment('additionalProperties', additionalPropertiesExpr)
];
}
createTypePropertyAssignments(factory, node, typeChecker, existingProperties, hostFilename, options) {
const key = 'type';
if (hasPropertyKey(key, existingProperties)) {
return [];
}
if (node) {
if (ts.isArrayTypeNode(node) && ts.isTypeLiteralNode(node.elementType)) {
const initializer = this.createInitializerForArrayLiteralTypeNode(node, factory, typeChecker, existingProperties, hostFilename, options);
return [factory.createPropertyAssignment(key, initializer)];
}
if (ts.isTypeLiteralNode(node) &&
node.members.length > 0 &&
node.members.every(ts.isIndexSignatureDeclaration)) {
const literalType = typeChecker.getTypeAtLocation(node);
const recordValueType = getRecordValueType(literalType, typeChecker);
if (recordValueType) {
return this.createRecordTypePropertyAssignment(factory, recordValueType, typeChecker);
}
}
if (ts.isTypeLiteralNode(node)) {
const initializer = this.createInitializerForTypeLiteralNode(node, factory, typeChecker, existingProperties, hostFilename, options);
return [factory.createPropertyAssignment(key, initializer)];
}
if (ts.isUnionTypeNode(node)) {
const { nullableType, isNullable } = this.isNullableUnion(node);
const remainingTypes = node.types.filter((t) => t !== nullableType);
if (remainingTypes.length === 1) {
const nonNullishNode = remainingTypes[0];
const resolved = typeChecker.getTypeAtLocation(nonNullishNode);
let candidateType = resolved;
const arrayTuple = extractTypeArgumentIfArray(candidateType);
if (arrayTuple) {
candidateType = arrayTuple.type;
}
let isEnumType = false;
if (candidateType) {
if (isEnum(candidateType)) {
isEnumType = true;
}
else {
const maybeEnum = isAutoGeneratedEnumUnion(candidateType, typeChecker);
if (maybeEnum || getStringLiteralUnionValues(candidateType)) {
isEnumType = true;
}
}
}
if (isEnumType) {
return isNullable
? [
factory.createPropertyAssignment('nullable', createBooleanLiteral(factory, true))
]
: [];
}
const propertyAssignments = this.createTypePropertyAssignments(factory, nonNullishNode, typeChecker, existingProperties, hostFilename, options);
if (!isNullable) {
return propertyAssignments;
}
return [
...propertyAssignments,
factory.createPropertyAssignment('nullable', createBooleanLiteral(factory, true))
];
}
}
}
const type = typeChecker.getTypeAtLocation(node);
if (!type) {
return [];
}
const stringLiteralUnion = getStringLiteralUnionValues(type);
if (stringLiteralUnion) {
if (stringLiteralUnion.isNullable &&
!hasPropertyKey('nullable', existingProperties)) {
return [
factory.createPropertyAssignment('nullable', createBooleanLiteral(factory, true))
];
}
return [];
}
const recordValueType = getRecordValueType(type, typeChecker);
if (recordValueType) {
return this.createRecordTypePropertyAssignment(factory, recordValueType, typeChecker);
}
const typeReferenceDescriptor = getTypeReferenceAsString(type, typeChecker);
if (!typeReferenceDescriptor.typeName) {
return [];
}
const identifier = typeReferenceToIdentifier(typeReferenceDescriptor, hostFilename, options, factory, type, this._typeImports, this._hoistedTypeImports);
const initializer = factory.createArrowFunction(undefined, undefined, [], undefined, undefined, identifier);
return [factory.createPropertyAssignment(key, initializer)];
}
createInitializerForArrayLiteralTypeNode(node, factory, typeChecker, existingProperties, hostFilename, options) {
const elementType = node.elementType;
const propertyAssignments = Array.from(elementType.members || []).map((member) => {
const literalExpr = this.createDecoratorObjectLiteralExpr(factory, member, typeChecker, existingProperties, options, hostFilename);
return factory.createPropertyAssignment(factory.createIdentifier(member.name.getText()), literalExpr);
});
const initializer = factory.createArrowFunction(undefined, undefined, [], undefined, undefined, factory.createArrayLiteralExpression([
factory.createParenthesizedExpression(factory.createObjectLiteralExpression(propertyAssignments))
]));
return initializer;
}
createInitializerForTypeLiteralNode(node, factory, typeChecker, existingProperties, hostFilename, options) {
const propertyAssignments = Array.from(node.members || []).map((member) => {
const literalExpr = this.createDecoratorObjectLiteralExpr(factory, member, typeChecker, existingProperties, options, hostFilename);
return factory.createPropertyAssignment(factory.createIdentifier(member.name.getText()), literalExpr);
});
const initializer = factory.createArrowFunction(undefined, undefined, [], undefined, undefined, factory.createParenthesizedExpression(factory.createObjectLiteralExpression(propertyAssignments)));
return initializer;
}
isNullableUnion(node) {
const nullableType = node.types.find((type) => type.kind === ts.SyntaxKind.NullKeyword ||
(ts.SyntaxKind.LiteralType && type.getText() === 'null'));
const isNullable = !!nullableType;
return { nullableType, isNullable };
}
createEnumPropertyAssignment(factory, node, typeChecker, existingProperties, hostFilename, options) {
const key = 'enum';
if (hasPropertyKey(key, existingProperties)) {
return undefined;
}
let type;
try {
if (node.type) {
type = typeChecker.getTypeFromTypeNode(node.type);
}
}
catch (e) {
}
if (!type) {
type = typeChecker.getTypeAtLocation(node);
}
if (!type) {
return undefined;
}
if ((type.flags & ts.TypeFlags.Union) !== 0) {
const union = type;
const nonNullish = union.types.filter((t) => (t.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0);
if (nonNullish.length === 1) {
type = nonNullish[0];
}
}
if (isAutoGeneratedTypeUnion(type)) {
const types = type.types;
const nonUndefined = types.find((t) => t.intrinsicName !== 'undefined');
if (nonUndefined) {
type = nonUndefined;
}
}
if (isAutoGeneratedTypeUnion(type)) {
const types = type.types;
type = types[types.length - 1];
}
const typeIsArrayTuple = extractTypeArgumentIfArray(type);
if (!typeIsArrayTuple) {
return undefined;
}
const isArrayType = typeIsArrayTuple.isArray;
type = typeIsArrayTuple.type;
const stringLiteralUnion = getStringLiteralUnionValues(type);
if (stringLiteralUnion) {
const enumProperty = factory.createPropertyAssignment(key, factory.createArrayLiteralExpression(stringLiteralUnion.values.map((v) => typeof v === 'number'
? factory.createNumericLiteral(v)
: factory.createStringLiteral(v))));
const result = compact([
enumProperty,
isArrayType &&
factory.createPropertyAssignment('isArray', factory.createIdentifier('true'))
]);
return result.length === 1 ? result[0] : result;
}
const isEnumMember = type.symbol && type.symbol.flags === ts.SymbolFlags.EnumMember;
if (!isEnum(type) || isEnumMember) {
if (!isEnumMember) {
type = isAutoGeneratedEnumUnion(type, typeChecker);
}
if (!type) {
return undefined;
}
const typeIsArrayTuple = extractTypeArgumentIfArray(type);
if (!typeIsArrayTuple) {
return undefined;
}
type = typeIsArrayTuple.type;
}
const typeReferenceDescriptor = { typeName: getText(type, typeChecker) };
const enumIdentifier = typeReferenceToIdentifier(typeReferenceDescriptor, hostFilename, options, factory, type, this._typeImports, this._hoistedTypeImports);
const enumProperty = factory.createPropertyAssignment(key, enumIdentifier);
const extraProperties = [];
if (options.autoFillEnumName &&
!hasPropertyKey('enumName', existingProperties)) {
const fullTypeName = typeReferenceDescriptor.typeName;
const enumTypeName = fullTypeName.includes('.')
? fullTypeName.slice(fullTypeName.lastIndexOf('.') + 1)
: fullTypeName;
extraProperties.push(factory.createPropertyAssignment('enumName', factory.createStringLiteral(enumTypeName)));
}
if (isArrayType) {
const isArrayKey = 'isArray';
const isArrayProperty = factory.createPropertyAssignment(isArrayKey, factory.createIdentifier('true'));
return [enumProperty, isArrayProperty, ...extraProperties];
}
return extraProperties.length > 0
? [enumProperty, ...extraProperties]
: enumProperty;
}
createDefaultPropertyAssignment(factory, node, existingProperties, options) {
const key = 'default';
if (options.skipDefaultValues) {
return undefined;
}
if (hasPropertyKey(key, existingProperties)) {
return undefined;
}
if (ts.isPropertySignature(node)) {
return undefined;
}
if (node.initializer == null) {
return undefined;
}
let initializer = node.initializer;
if (ts.isAsExpression(initializer)) {
initializer = initializer.expression;
}
initializer =
this.clonePrimitiveLiteral(factory, initializer) ?? initializer;
if (!canReferenceNode(initializer, options)) {
if (options.debug) {
const parentFilePath = node.getSourceFile().fileName;
const propertyName = node.name.getText();
pluginDebugLogger.debug(`Skipping registering default value for "${propertyName}" property in "${parentFilePath}" file because it is not a referenceable value ("${initializer.getText()}").`);
}
return undefined;
}
return factory.createPropertyAssignment(key, initializer);
}
createValidationPropertyAssignments(factory, node, options) {
const assignments = [];
const decorators = ts.canHaveDecorators(node) && ts.getDecorators(node);
if (!options.readonly) {
this.addPropertiesByValidationDecorator(factory, 'IsIn', decorators, assignments, (decoratorRef) => {
const decoratorArguments = getDecoratorArguments(decoratorRef);
const result = [];
const argumentValue = head(decoratorArguments);
if (!canReferenceNode(argumentValue, options)) {
return result;
}
const assignment = this.clonePrimitiveLiteral(factory, argumentValue) ?? argumentValue;
result.push(factory.createPropertyAssignment('enum', assignment));
if (this.isEachOptionEnabled(decoratorArguments[1])) {
result.push(factory.createPropertyAssignment('isArray', factory.createIdentifier('true')));
}
return result;
});
}
decoratorsProperties.forEach((decoratorProperty) => {
if (decoratorProperty.mappingType === decoratorsPropertiesMappingType.DIRECT) {
this.addPropertyByValidationDecorator(factory, decoratorProperty.decorator, decoratorProperty.property, decorators, assignments, options);
}
else if (decoratorProperty.mappingType ===
decoratorsPropertiesMappingType.INDIRECT_VALUE) {
this.addPropertiesByValidationDecorator(factory, decoratorProperty.decorator, decorators, assignments, () => {
return [
factory.createPropertyAssignment(decoratorProperty.property, createPrimitiveLiteral(factory, decoratorProperty.value))
];
});
}
else if (decoratorProperty.mappingType ===
decoratorsPropertiesMappingType.INDIRECT_ARGUMENT) {
this.addPropertiesByValidationDecorator(factory, decoratorProperty.decorator, decorators, assignments, (decoratorRef) => {
const decoratorArguments = getDecoratorArguments(decoratorRef);
const result = [];
const argumentValue = head(decoratorArguments);
if (!canReferenceNode(argumentValue, options)) {
return result;
}
const clonedArgumentValue = this.clonePrimitiveLiteral(factory, argumentValue);
if (clonedArgumentValue) {
result.push(factory.createPropertyAssignment(decoratorProperty.property, clonedArgumentValue));
}
return result;
});
}
});
this.addPropertiesByValidationDecorator(factory, 'Length', decorators, assignments, (decoratorRef) => {
const decoratorArguments = getDecoratorArguments(decoratorRef);
const result = [];
const minLength = head(decoratorArguments);
if (!canReferenceNode(minLength, options)) {
return result;
}
const clonedMinLength = this.clonePrimitiveLiteral(factory, minLength) ?? minLength;
if (clonedMinLength) {
result.push(factory.createPropertyAssignment('minLength', clonedMinLength));
}
if (decoratorArguments.length > 1) {
const maxLength = decoratorArguments[1];
if (!canReferenceNode(maxLength, options)) {
return result;
}
const clonedMaxLength = this.clonePrimitiveLiteral(factory, maxLength) ?? maxLength;
if (clonedMaxLength) {
result.push(factory.createPropertyAssignment('maxLength', clonedMaxLength));
}
}
return result;
});
this.addPropertiesByValidationDecorator(factory, 'Matches', decorators, assignments, (decoratorRef) => {
const decoratorArguments = getDecoratorArguments(decoratorRef);
const firstArg = head(decoratorArguments);
if (!firstArg) {
return [];
}
let patternText;
if (ts.isRegularExpressionLiteral(firstArg)) {
const match = firstArg.text.match(/^\/(.*)\/([gimsuy]*)$/);
if (match) {
patternText = match[1];
}
}
else if (ts.isStringLiteral(firstArg)) {
patternText = firstArg.text;
}
if (patternText === undefined) {
return [];
}
return [
factory.createPropertyAssignment('pattern', createPrimitiveLiteral(factory, patternText))
];
});
return assignments;
}
addPropertyByValidationDecorator(factory, decoratorName, propertyKey, decorators, assignments, options) {
this.addPropertiesByValidationDecorator(factory, decoratorName, decorators, assignments, (decoratorRef) => {
const argument = head(getDecoratorArguments(decoratorRef));
const assignment = this.clonePrimitiveLiteral(factory, argument) ?? argument;
if (!canReferenceNode(assignment, options)) {
return [];
}
return [factory.createPropertyAssignment(propertyKey, assignment)];
});
}
addPropertiesByValidationDecorator(factory, decoratorName, decorators, assignments, addPropertyAssignments) {
const decoratorRef = getDecoratorOrUndefinedByNames([decoratorName], decorators, factory);
if (!decoratorRef) {
return;
}
assignments.push(...addPropertyAssignments(decoratorRef));
}
isEachOptionEnabled(optionsArgument) {
if (!optionsArgument || !ts.isObjectLiteralExpression(optionsArgument)) {
return false;
}
return optionsArgument.properties.some((property) => {
if (!ts.isPropertyAssignment(property) ||
!ts.isIdentifier(property.name) ||
property.name.text !== 'each') {
return false;
}
return property.initializer.kind === ts.SyntaxKind.TrueKeyword;
});
}
addClassMetadata(node, objectLiteral, sourceFile, metadata) {
const hostClass = node.parent;
const className = hostClass.name && hostClass.name.getText();
if (!className) {
return;
}
const propertyName = node.name && node.name.getText(sourceFile);
if (!propertyName ||
(node.name && node.name.kind === ts.SyntaxKind.ComputedPropertyName)) {
return;
}
metadata[propertyName] = objectLiteral;
}
createDescriptionAndTsDocTagPropertyAssignments(factory, node, typeChecker, existingProperties = factory.createNodeArray(), options = {}, sourceFile) {
if (!options.introspectComments || !sourceFile) {
return [];
}
const propertyAssignments = [];
const comments = getMainCommentOfNode(node);
const tags = getTsDocTagsOfNode(node, typeChecker);
const keyOfComment = options.dtoKeyOfComment;
if (!hasPropertyKey(keyOfComment, existingProperties) && comments) {
const descriptionPropertyAssignment = factory.createPropertyAssignment(keyOfComment, factory.createStringLiteral(comments));
propertyAssignments.push(descriptionPropertyAssignment);
}
const hasExampleOrExamplesKey = hasPropertyKey('example', existingProperties) ||
hasPropertyKey('examples', existingProperties);
if (!hasExampleOrExamplesKey && tags.example?.length) {
if (tags.example.length === 1) {
const examplePropertyAssignment = factory.createPropertyAssignment('example', createLiteralFromAnyValue(factory, tags.example[0]));
propertyAssignments.push(examplePropertyAssignment);
}
else {
const examplesPropertyAssignment = factory.createPropertyAssignment('examples', createLiteralFromAnyValue(factory, tags.example));
propertyAssignments.push(examplesPropertyAssignment);
}
}
const hasDeprecatedKey = hasPropertyKey('deprecated', existingProperties);
if (!hasDeprecatedKey && tags.deprecated) {
const deprecatedPropertyAssignment = factory.createPropertyAssignment('deprecated', createLiteralFromAnyValue(factory, tags.deprecated));
propertyAssignments.push(deprecatedPropertyAssignment);
}
return propertyAssignments;
}
normalizeImportPath(pathToSource, path) {
let relativePath = posix.relative(convertPath(pathToSource), convertPath(path));
relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath;
return relativePath;
}
clonePrimitiveLiteral(factory, node) {
const primitiveTypeName = this.getInitializerPrimitiveTypeName(node);
if (!primitiveTypeName) {
return undefined;
}
const text = node.text ?? node.getText();
return createPrimitiveLiteral(factory, text, primitiveTypeName);
}
getInitializerPrimitiveTypeName(node) {
if (ts.isIdentifier(node) &&
(node.text === 'true' || node.text === 'false')) {
return 'boolean';
}
if (ts.isNumericLiteral(node) || ts.isPrefixUnaryExpression(node)) {
return 'number';
}
if (ts.isStringLiteral(node)) {
return 'string';
}
return undefined;
}
}