2023-03-08 12:26:10 +01:00

110 lines
2.7 KiB
TypeScript

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)
);
}
}