Skip to content

Commit

Permalink
Make jsDoc @description tags available on more reflection classes and…
Browse files Browse the repository at this point in the history
… types (#463)
  • Loading branch information
jefflaporte committed Oct 1, 2023
1 parent 138a11e commit 8aee9a1
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 0 deletions.
14 changes: 14 additions & 0 deletions packages/type-compiler/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const {
isSourceFile,
isStringLiteral,
isTypeAliasDeclaration,
isTypeLiteralNode,
isTypeParameterDeclaration,
isTypeQueryNode,
isTypeReferenceNode,
Expand Down Expand Up @@ -1246,6 +1247,8 @@ export class ReflectionTransformer implements CustomTransformer {

if (node) {
const members: ClassElement[] = [];
const description = extractJSDocAttribute(narrowed, 'description');
if (description) program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));

if (narrowed.typeParameters) {
for (const typeParameter of narrowed.typeParameters) {
Expand Down Expand Up @@ -1288,6 +1291,7 @@ export class ReflectionTransformer implements CustomTransformer {
}

program.pushOp(ReflectionOp.class);
if (description) program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));

if (narrowed.heritageClauses && narrowed.heritageClauses[0] && narrowed.heritageClauses[0].types[0]) {
const first = narrowed.heritageClauses[0].types[0];
Expand Down Expand Up @@ -1375,6 +1379,7 @@ export class ReflectionTransformer implements CustomTransformer {
case SyntaxKind.InterfaceDeclaration: {
//TypeScript does not narrow types down
const narrowed = node as TypeLiteralNode | InterfaceDeclaration;
let descriptionNode: Node = narrowed;
program.pushFrame();

//first all extend expressions
Expand All @@ -1392,6 +1397,11 @@ export class ReflectionTransformer implements CustomTransformer {
this.extractPackStructOfType(member, program);
}
program.pushOp(ReflectionOp.objectLiteral);
if (isTypeLiteralNode(narrowed)) {
descriptionNode = narrowed.parent;
}
const description = extractJSDocAttribute(descriptionNode, 'description');
if (description) program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
program.popFrameImplicit();
break;
}
Expand Down Expand Up @@ -1622,6 +1632,8 @@ export class ReflectionTransformer implements CustomTransformer {
if (hasModifier(narrowed, SyntaxKind.AbstractKeyword)) program.pushOp(ReflectionOp.abstract);
if (hasModifier(narrowed, SyntaxKind.StaticKeyword)) program.pushOp(ReflectionOp.static);
}
const description = extractJSDocAttribute(narrowed, 'description');
if (description) program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
program.popFrameImplicit();
break;
}
Expand Down Expand Up @@ -1691,6 +1703,8 @@ export class ReflectionTransformer implements CustomTransformer {
}
}
program.pushOp(ReflectionOp.enum);
const description = extractJSDocAttribute(narrowed, 'description');
if (description) program.pushOp(ReflectionOp.description, program.findOrAddStackEntry(description));
program.popFrameImplicit();
break;
}
Expand Down
13 changes: 13 additions & 0 deletions packages/type/src/reflection/reflection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,15 @@ export class ReflectionParameter {

export class ReflectionFunction {
parameters: ReflectionParameter[] = [];
description: string = '';

constructor(
public readonly type: TypeMethod | TypeMethodSignature | TypeFunction,
) {
for (const p of this.type.parameters) {
this.parameters.push(new ReflectionParameter(p, this));
}
if (this.type.description) this.description = this.type.description;
}

static from(fn: Function): ReflectionFunction {
Expand Down Expand Up @@ -395,6 +397,10 @@ export class ReflectionFunction {
return this.type.name || 'anonymous';
}

getDescription(): string {
return this.description;
}

get name(): string {
return memberNameToString(this.getName());
}
Expand Down Expand Up @@ -858,6 +864,7 @@ export class ReflectionClass<T> {
this.name = parent.name;
this.collectionName = parent.collectionName;
this.databaseSchemaName = parent.databaseSchemaName;
this.description = parent.description;

for (const member of parent.getProperties()) {
this.registerProperty(member.clone(this));
Expand All @@ -875,6 +882,7 @@ export class ReflectionClass<T> {
if (entityOptions) {
applyEntityOptions(this, entityOptions);
}
this.description = this.type.description || this.description;

//apply decorators
if (type.kind === ReflectionKind.class && isWithDeferredDecorators(type.classType)) {
Expand Down Expand Up @@ -911,6 +919,7 @@ export class ReflectionClass<T> {
reflection.indexes = this.indexes.slice();
reflection.subClasses = this.subClasses.slice();
reflection.data = { ...this.data };
reflection.description = this.description;

return reflection;
}
Expand Down Expand Up @@ -954,6 +963,10 @@ export class ReflectionClass<T> {
return this.name || this.getClassName();
}

getDescription(): string {
return this.description;
}

getCollectionName(): string {
return this.collectionName || this.getName();
}
Expand Down
6 changes: 6 additions & 0 deletions packages/type/src/reflection/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ export interface TypeMethod extends TypeBaseMember {
kind: ReflectionKind.method,
parent: TypeClass;
name: number | string | symbol;
description?: string;
parameters: TypeParameter[];
return: Type;
}
Expand All @@ -291,6 +292,7 @@ export interface TypeFunction extends TypeAnnotations {
kind: ReflectionKind.function,
parent?: Type;
name?: number | string | symbol,
description?: string;
function?: Function; //reference to the real function if available
parameters: TypeParameter[];
return: Type;
Expand All @@ -313,6 +315,7 @@ export interface TypeClass extends TypeAnnotations {
kind: ReflectionKind.class,
parent?: Type;
classType: ClassType;
description?: string;

/**
* When the class extends another class and uses on it generic type arguments, then those arguments
Expand All @@ -339,6 +342,7 @@ export interface TypeEnum extends TypeAnnotations {
enum: { [name: string]: string | number | undefined | null };
values: (string | number | undefined | null)[];
indexType: Type;
description?: string;
}

export interface TypeEnumMember extends TypeAnnotations {
Expand Down Expand Up @@ -387,6 +391,7 @@ export interface TypeMethodSignature extends TypeAnnotations {
parent: TypeObjectLiteral;
name: number | string | symbol;
optional?: true;
description?: string;
parameters: TypeParameter[];
return: Type;
}
Expand All @@ -397,6 +402,7 @@ export interface TypeMethodSignature extends TypeAnnotations {
export interface TypeObjectLiteral extends TypeAnnotations {
kind: ReflectionKind.objectLiteral,
parent?: Type;
description?: string;
types: (TypeIndexSignature | TypePropertySignature | TypeMethodSignature | TypeCallSignature)[];
}

Expand Down
76 changes: 76 additions & 0 deletions packages/type/tests/jsdoc.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { expect, test } from '@jest/globals';
import { TypeEnum } from '../src/reflection/type.js';
import { ReflectionClass, ReflectionMethod, ReflectionFunction, typeOf } from '../src/reflection/reflection.js';

test('description available on Interface and Type alias', () => {
/** @description user interface */
interface IUser {
username: string;
}

/** @description user type declaration */
type IUser2 = {
username: string;
}

const reflectTypeInterface = ReflectionClass.from(typeOf<IUser>());
expect(reflectTypeInterface.description).toEqual('user interface');
expect(reflectTypeInterface.type.description).toEqual('user interface');

const reflectTypeObjectLiteral = ReflectionClass.from(typeOf<IUser2>());
expect(reflectTypeObjectLiteral.description).toEqual('user type declaration');
expect(reflectTypeObjectLiteral.type.description).toEqual('user type declaration');
});

test('description available on ReflectionClass', () => {
class MyDate {}

/** @description user class */
class User {
myDate?: MyDate;
created: Date = new Date;
}
const reflection = ReflectionClass.from(typeOf<User>());
expect(reflection.description).toEqual('user class');
});

test('description available on ReflectionFunction and ReflectionMethod', () => {
class MyDate {}
/** @description user class */
class User {
myDate?: MyDate;
created: Date = new Date;
}
/** @description getUser function */
function getUser(): User {
return new User()
}

class FunctionContainer {
/** @description getUser member */
getUser(): User {
return new User()
}
}

const reflectionFunction = ReflectionFunction.from(getUser);
expect(reflectionFunction.getDescription()).toEqual('getUser function');

const fc = new FunctionContainer();
const reflection = ReflectionClass.from(typeOf<FunctionContainer>());
const method:ReflectionMethod = reflection.getMethod('getUser')
expect(method.description).toEqual('getUser member');
});

test('description available on TypeEnum', () => {
/** @description results enum */
enum RESULTS {
SUCCESS = 'success',
FAILURE = 'failure'
}
const type = typeOf<RESULTS>() as TypeEnum;
expect(type.description).toEqual('results enum');

});


0 comments on commit 8aee9a1

Please sign in to comment.