UNPKG

typedoc

Version:

Create api documentation for TypeScript projects.

157 lines (156 loc) 6.18 kB
import { Reflection, TraverseProperty } from "./Reflection.js"; import { ReflectionCategory } from "./ReflectionCategory.js"; import { ReflectionGroup } from "./ReflectionGroup.js"; import { ReflectionKind } from "./kind.js"; import { assertNever, removeIfPresent } from "#utils"; /** * @category Reflections */ export class ContainerReflection extends Reflection { /** * The children of this reflection. Do not add reflections to this array * manually. Instead call {@link addChild}. */ children; /** * Documents associated with this reflection. * * These are not children as including them as children requires code handle both * types, despite being mostly unrelated and handled separately. * * Including them here in a separate array neatly handles that problem, but also * introduces another one for rendering. When rendering, documents should really * actually be considered part of the "children" of a reflection. For this reason, * we also maintain a list of child declarations with child documents which is used * when rendering. */ documents; /** * Union of the {@link children} and {@link documents} arrays which dictates the * sort order for rendering. */ childrenIncludingDocuments; /** * All children grouped by their kind. */ groups; /** * All children grouped by their category. */ categories; /** * Return a list of all children of a certain kind. * * @param kind The desired kind of children. * @returns An array containing all children with the desired kind. */ getChildrenByKind(kind) { return (this.children || []).filter((child) => child.kindOf(kind)); } addChild(child) { if (child.isDeclaration()) { this.children ||= []; this.children.push(child); this.childrenIncludingDocuments ||= []; this.childrenIncludingDocuments.push(child); } else if (child.isDocument()) { this.documents ||= []; this.documents.push(child); this.childrenIncludingDocuments ||= []; this.childrenIncludingDocuments.push(child); } else if (this.isDeclaration() && child.isSignature()) { switch (child.kind) { case ReflectionKind.CallSignature: case ReflectionKind.ConstructorSignature: this.signatures ||= []; this.signatures.push(child); break; case ReflectionKind.IndexSignature: this.indexSignatures ||= []; this.indexSignatures.push(child); break; case ReflectionKind.GetSignature: case ReflectionKind.SetSignature: throw new Error("Unsupported child type: " + ReflectionKind[child.kind]); default: assertNever(child.kind); } } else { throw new Error("Unsupported child type: " + ReflectionKind[child.kind]); } } removeChild(child) { if (child.isDeclaration()) { removeIfPresent(this.children, child); if (this.children?.length === 0) { delete this.children; } } else { removeIfPresent(this.documents, child); if (this.documents?.length === 0) { delete this.documents; } } removeIfPresent(this.childrenIncludingDocuments, child); if (this.childrenIncludingDocuments?.length === 0) { delete this.childrenIncludingDocuments; } } traverse(callback) { for (const child of this.children?.slice() || []) { if (callback(child, TraverseProperty.Children) === false) { return; } } for (const child of this.documents?.slice() || []) { if (callback(child, TraverseProperty.Documents) === false) { return; } } } toObject(serializer) { return { ...super.toObject(serializer), children: serializer.toObjectsOptional(this.children), documents: serializer.toObjectsOptional(this.documents), // If we only have one type of child, don't bother writing the duplicate info about // ordering with documents to the serialized file. childrenIncludingDocuments: this.children?.length && this.documents?.length ? this.childrenIncludingDocuments?.map((refl) => refl.id) : undefined, groups: serializer.toObjectsOptional(this.groups), categories: serializer.toObjectsOptional(this.categories), }; } fromObject(de, obj) { super.fromObject(de, obj); this.children = de.reviveMany(obj.children, (child) => de.constructReflection(child)); this.documents = de.reviveMany(obj.documents, (child) => de.constructReflection(child)); const byId = new Map(); for (const child of this.children || []) { byId.set(child.id, child); } for (const child of this.documents || []) { byId.set(child.id, child); } for (const id of obj.childrenIncludingDocuments || []) { const child = byId.get(de.oldIdToNewId[id] ?? -1); if (child) { this.childrenIncludingDocuments ||= []; this.childrenIncludingDocuments.push(child); byId.delete(de.oldIdToNewId[id] ?? -1); } } if (byId.size) { // Anything left in byId wasn't included in the childrenIncludingDocuments array. this.childrenIncludingDocuments ||= []; this.childrenIncludingDocuments.push(...byId.values()); } this.groups = de.reviveMany(obj.groups, (group) => new ReflectionGroup(group.title, this)); this.categories = de.reviveMany(obj.categories, (cat) => new ReflectionCategory(cat.title)); } }