UNPKG

@ts-ast-parser/core

Version:

Reflects a simplified version of the TypeScript AST for generating documentation

372 lines 12.5 kB
import { ExpressionWithTypeArgumentsNode } from './expression-with-type-arguments-node.js'; import { getInstanceMembers, getStaticMembers, isAbstract } from '../utils/member.js'; import { isArrowFunction, isFunctionExpression } from '../utils/function.js'; import { DeclarationKind } from '../models/declaration-kind.js'; import { tryAddProperty } from '../utils/try-add-property.js'; import { TypeParameterNode } from './type-parameter-node.js'; import { isCustomElement } from '../utils/heritage.js'; import { getDecorators } from '../utils/decorator.js'; import { getNamespace } from '../utils/namespace.js'; import { SignatureNode } from './signature-node.js'; import { DecoratorNode } from './decorator-node.js'; import { ModifierType } from '../models/member.js'; import { FunctionNode } from './function-node.js'; import { PropertyNode } from './property-node.js'; import { isThirdParty } from '../utils/import.js'; import { RootNodeType } from '../models/node.js'; import { CommentNode } from './comment-node.js'; import ts from 'typescript'; /** * Reflected node that represents a ClassDeclaration or a ClassExpression */ export class ClassNode { constructor(node, context) { Object.defineProperty(this, "_node", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_context", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_instanceMembers", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_staticMembers", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_heritage", { enumerable: true, configurable: true, writable: true, value: [] }); Object.defineProperty(this, "_jsDoc", { enumerable: true, configurable: true, writable: true, value: void 0 }); this._node = node; this._context = context; this._jsDoc = new CommentNode(node); const classNode = this._getClassNode(); if (classNode) { this._instanceMembers = getInstanceMembers(classNode, this._context); this._staticMembers = getStaticMembers(classNode, this._context); this._heritage = (classNode.heritageClauses ?? []).flatMap(h => { return h.types.map(t => new ExpressionWithTypeArgumentsNode(t, this._context)); }); } } /** * The name of the class. * * If it's a class expression that it's assigned to a variable, it will return * the name of the variable * * @returns The name of the class */ getName() { if (ts.isVariableStatement(this._node)) { return this._node.declarationList.declarations[0]?.name.getText() ?? ''; } return this._node.name?.getText() ?? ''; } /** * The reflected node type * * @returns The declaration kind */ getNodeType() { return RootNodeType.Declaration; } /** * The reflected declaration kind * * @returns The class declaration kind */ getKind() { return DeclarationKind.Class; } /** * The context includes useful APIs that are shared across * all the reflected symbols. * * Some APIs include the parsed configuration options, the * system interface, the type checker * * @returns The analyser context */ getContext() { return this._context; } /** * The internal TypeScript node * * @returns The TypeScript AST node related to this reflected node */ getTSNode() { return this._node; } /** * The start line number position * * @returns The start line number position */ getLine() { return this._context.getLinePosition(this._node); } /** * The namespace where the class has been defined. * * @returns The name of the namespace where this declaration is defined. * Will return an empty string if no namespace is found. */ getNamespace() { return getNamespace(this._node); } /** * The reflected JSDoc comment node * * @returns The JSDoc node */ getJSDoc() { return this._jsDoc; } /** * The reflected decorators applied to the class * * @returns An array of reflected decorators */ getDecorators() { return getDecorators(this._node).map(d => new DecoratorNode(d, this._context)); } /** * Finds a reflected decorator based on the name * * @param name - The decorator name to find * * @returns The reflected decorator that matches the given name */ getDecoratorWithName(name) { return this.getDecorators().find(d => d.getName() === name) ?? null; } /** * An array of constructors that can be used to * create an instance of the class * * @returns All the constructor signatures */ getConstructors() { const classNode = this._getClassNode(); if (!classNode) { return []; } const checker = this._context.getTypeChecker(); const symbol = checker.getTypeAtLocation(classNode).getSymbol(); const type = symbol && checker.getTypeOfSymbolAtLocation(symbol, classNode); const signatures = type?.getConstructSignatures() ?? []; const result = []; for (const signature of signatures) { // If there is no declaration for the constructor, don't add it // to the list of constructors. if (!signature.getDeclaration()) { continue; } const node = new SignatureNode(signature, this._context); const path = node.getPath(); if (path && !isThirdParty(path)) { result.push(node); } } return result; } /** * The instance properties * * @returns All the reflected instance properties */ getProperties() { return this._getPropertyMembers(this._instanceMembers); } /** * The static properties * * @returns All the reflected static properties */ getStaticProperties() { return this._getPropertyMembers(this._staticMembers); } /** * Finds an instance property based on the name * * @param name - The property name to find * * @returns The reflected instance property that matches the given name */ getPropertyWithName(name) { return this.getProperties().find(m => m.getName() === name) ?? null; } /** * The instance methods * * @returns All the reflected instance methods */ getMethods() { return this._getMethodMembers(this._instanceMembers); } /** * The static methods * * @returns All the reflected static methods */ getStaticMethods() { return this._getMethodMembers(this._staticMembers); } /** * Finds an instance method based on the name * * @param name - The name of the method to find * * @returns The reflected instance method that matches the given name */ getMethodWithName(name) { return this.getMethods().find(m => m.getName() === name) ?? null; } /** * The type parameters * * @returns All the reflected type parameters */ getTypeParameters() { const classNode = this._getClassNode(); if (!classNode) { return []; } return classNode.typeParameters?.map(tp => new TypeParameterNode(tp, this._context)) ?? []; } /** * The heritage chain. Interfaces that the class implements or * parent classes that it extends. * * @returns The heritage chain */ getHeritage() { return this._heritage; } /** * Whether is a custom element or not * * @returns True if the class declaration is a custom element, otherwise false */ isCustomElement() { const classNode = this._getClassNode(); if (!classNode) { return false; } return isCustomElement(classNode, this._context); } /** * Whether it's an abstract class or not * * @returns True if it's an abstract class, otherwise false */ isAbstract() { const classNode = this._getClassNode(); if (!classNode) { return false; } return isAbstract(classNode); } /** * Serializes the reflected node * * @returns The reflected node as a serializable object */ serialize() { const tmpl = { name: this.getName(), kind: this.getKind(), line: this.getLine(), }; tryAddProperty(tmpl, 'constructors', this.getConstructors().map(c => c.serialize())); tryAddProperty(tmpl, 'decorators', this.getDecorators().map(d => d.serialize())); tryAddProperty(tmpl, 'jsDoc', this.getJSDoc().serialize()); tryAddProperty(tmpl, 'typeParameters', this.getTypeParameters().map(tp => tp.serialize())); tryAddProperty(tmpl, 'heritage', this.getHeritage().map(h => h.serialize())); tryAddProperty(tmpl, 'abstract', this.isAbstract()); tryAddProperty(tmpl, 'customElement', this.isCustomElement()); tryAddProperty(tmpl, 'namespace', this.getNamespace()); tryAddProperty(tmpl, 'properties', this.getProperties().map(p => p.serialize())); tryAddProperty(tmpl, 'staticProperties', this.getStaticProperties().map(p => p.serialize())); tryAddProperty(tmpl, 'methods', this.getMethods().map(m => m.serialize())); tryAddProperty(tmpl, 'staticMethods', this.getStaticMethods().map(m => m.serialize())); return tmpl; } _getPropertyMembers(members) { const result = []; for (const member of members) { const { symbol } = member; const decl = symbol?.getDeclarations()?.[0]; if (!decl) { continue; } const isProperty = ts.isPropertyDeclaration(decl); const isPropertyMethod = ts.isMethodDeclaration(decl) || (isProperty && (isArrowFunction(decl.initializer) || isFunctionExpression(decl.initializer))); if (isPropertyMethod) { continue; } if (isProperty || ts.isGetAccessor(decl) || ts.isSetAccessor(decl)) { const reflectedNode = new PropertyNode(decl, member, this._context); if (reflectedNode.getModifier() === ModifierType.public) { result.push(reflectedNode); } } } return result; } _getMethodMembers(members) { const result = []; for (const member of members) { const { symbol } = member; const decl = symbol?.getDeclarations()?.[0]; if (!decl) { continue; } const isProperty = ts.isPropertyDeclaration(decl); const isPropertyMethod = ts.isMethodDeclaration(decl) || (isProperty && (isArrowFunction(decl.initializer) || isFunctionExpression(decl.initializer))); if (isPropertyMethod) { const reflectedNode = new FunctionNode(decl, member, this._context); if (reflectedNode.getModifier() === ModifierType.public) { result.push(reflectedNode); } } } return result; } _getClassNode() { if (ts.isClassDeclaration(this._node) || ts.isClassExpression(this._node)) { return this._node; } const decl = this._node.declarationList.declarations[0]; const initializer = decl?.initializer; if (!initializer || !ts.isClassExpression(initializer)) { return null; } return initializer; } } //# sourceMappingURL=class-node.js.map