import ts from "typescript"; import { TypeRegistry } from "../registry.ts"; import { ParameterNode } from "./function.ts"; import { TypeReference } from "./reference.ts"; export class ClassNode { private constructor( public readonly name: string, public readonly fields: FieldNode[], public readonly methods: MethodNode[] ) {} public static create( registry: TypeRegistry, node: ts.ClassDeclaration, symbol?: ts.Symbol ) { const fields: FieldNode[] = []; const methods: MethodNode[] = []; symbol?.members?.forEach((value) => { const decl = value.declarations?.[0]; if (!decl) { return; } if (ts.isPropertyDeclaration(decl) || ts.isParameter(decl)) { fields.push(FieldNode.create(registry, decl)); } else if ( ts.isConstructorDeclaration(decl) || ts.isMethodDeclaration(decl) ) { methods.push(MethodNode.create(registry, decl)); } }); return new ClassNode( symbol?.escapedName ?? node?.name?.escapedText ?? "", fields, methods ); } } export type MemberVisibility = "public" | "private" | "protected"; /** Retrieve the visibility of a member based on the modifier flags */ function visibilityFrom(decl: ts.Declaration): MemberVisibility { const modifiers = ts.getCombinedModifierFlags(decl); if ((ts.ModifierFlags.Private & modifiers) === ts.ModifierFlags.Private) { return "private"; } if ((ts.ModifierFlags.Protected & modifiers) === ts.ModifierFlags.Protected) { return "protected"; } return "public"; } export class FieldNode { private constructor( public readonly name: string, public readonly type: TypeReference, public readonly visibility: MemberVisibility, public readonly optional: boolean ) {} public static create( registry: TypeRegistry, decl: ts.PropertyDeclaration | ts.ParameterDeclaration ) { return new FieldNode( decl.name.getText(), registry.getOrCreate(decl.type)!, visibilityFrom(decl), !!decl.questionToken // FIXME: or union with undefined ); } } export class MethodNode { static readonly ctorName = "__constructor"; public readonly isConstructor: boolean; private constructor( public readonly name: string, public readonly visibility: MemberVisibility, public readonly parameters: ParameterNode[], public readonly returnType?: TypeReference ) { this.isConstructor = name === MethodNode.ctorName; } public static create( registry: TypeRegistry, decl: ts.MethodDeclaration | ts.ConstructorDeclaration ) { return new MethodNode( decl?.name?.getText() ?? MethodNode.ctorName, visibilityFrom(decl), [], registry.getOrCreate(decl.type) ); } }