typedoc
Version: 
Create api documentation for TypeScript projects.
219 lines (218 loc) • 8.4 kB
JavaScript
import { DeclarationReflection, ProjectReflection, ReferenceReflection, ReflectionKind, SignatureReflection, } from "../../models/index.js";
import { DefaultMap, filterMap, JSX } from "#utils";
export function stringify(data) {
    if (typeof data === "bigint") {
        return data.toString() + "n";
    }
    return JSON.stringify(data);
}
export function getDisplayName(refl) {
    let version = "";
    if ((refl instanceof DeclarationReflection || refl instanceof ProjectReflection) && refl.packageVersion) {
        version = ` - v${refl.packageVersion}`;
    }
    return `${refl.name}${version}`;
}
export function toStyleClass(str) {
    return str.replace(/(\w)([A-Z])/g, (_m, m1, m2) => m1 + "-" + m2).toLowerCase();
}
export function getKindClass(refl) {
    if (refl instanceof ReferenceReflection) {
        return getKindClass(refl.getTargetReflectionDeep());
    }
    return ReflectionKind.classString(refl.kind);
}
/**
 * Insert word break tags ``<wbr>`` into the given string.
 *
 * Breaks the given string at ``_``, ``-`` and capital letters.
 *
 * @param str The string that should be split.
 * @return The original string containing ``<wbr>`` tags where possible.
 */
export function wbr(str) {
    // Keep this in sync with the same helper in Navigation.ts
    // We use lookahead/lookbehind to indicate where the string should
    // be split without consuming a character.
    // (?<=[^A-Z])(?=[A-Z]) -- regular camel cased text
    // (?<=[A-Z])(?=[A-Z][a-z]) -- acronym
    // (?<=[_-])(?=[^_-]) -- snake
    const parts = str.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/);
    return parts.flatMap(p => [p, JSX.createElement("wbr", null)]).slice(0, -1);
}
export function join(joiner, list, cb) {
    const result = [];
    for (const item of list) {
        if (result.length > 0) {
            result.push(joiner);
        }
        result.push(cb(item));
    }
    return JSX.createElement(JSX.Fragment, null, result);
}
export function classNames(names, extraCss) {
    const css = Object.keys(names)
        .filter((key) => names[key])
        .concat(extraCss || "")
        .join(" ")
        .trim()
        .replace(/\s+/g, " ");
    return css.length ? css : undefined;
}
export function hasTypeParameters(reflection) {
    return ((reflection instanceof DeclarationReflection || reflection instanceof SignatureReflection) &&
        reflection.typeParameters != null &&
        reflection.typeParameters.length > 0);
}
export function renderTypeParametersSignature(context, typeParameters) {
    if (!typeParameters || typeParameters.length === 0)
        return JSX.createElement(JSX.Fragment, null);
    return (JSX.createElement(JSX.Fragment, null,
        JSX.createElement("span", { class: "tsd-signature-symbol" }, "<"),
        join(JSX.createElement("span", { class: "tsd-signature-symbol" }, ", "), typeParameters, (item) => (JSX.createElement(JSX.Fragment, null,
            (item.flags.isConst || item.varianceModifier) && (JSX.createElement("span", { class: "tsd-signature-keyword" },
                item.flags.isConst && "const ",
                item.varianceModifier && `${item.varianceModifier} `)),
            JSX.createElement("span", { class: "tsd-signature-type tsd-kind-type-parameter" }, item.name),
            !!item.type && (JSX.createElement(JSX.Fragment, null,
                " ",
                JSX.createElement("span", { class: "tsd-signature-keyword" }, "extends"),
                " ",
                context.type(item.type)))))),
        JSX.createElement("span", { class: "tsd-signature-symbol" }, ">")));
}
/**
 * Renders the reflection name with an additional `?` if optional.
 */
export function renderName(refl) {
    if (refl.flags.isOptional) {
        return JSX.createElement(JSX.Fragment, null,
            wbr(refl.name),
            "?");
    }
    return wbr(refl.name);
}
// This is cached to avoid slowness with large projects.
const rootsCache = new WeakMap();
export function getHierarchyRoots(project) {
    const cached = rootsCache.get(project);
    if (cached)
        return cached;
    const allClasses = project.getReflectionsByKind(ReflectionKind.ClassOrInterface);
    const roots = allClasses.filter((refl) => {
        // If nobody extends this class, there's no possible hierarchy to display.
        if (!refl.implementedBy && !refl.extendedBy) {
            return false;
        }
        // If we don't extend anything, then we are a root
        if (!refl.implementedTypes && !refl.extendedTypes) {
            return true;
        }
        // We might still be a root, if our extended/implemented types are not included
        // in the documentation.
        const types = [...(refl.implementedTypes || []), ...(refl.extendedTypes || [])];
        return types.every((type) => !type.visit({
            reference(ref) {
                return ref.reflection !== undefined;
            },
        }));
    });
    const result = roots.sort((a, b) => a.name.localeCompare(b.name));
    rootsCache.set(project, result);
    return result;
}
export function isNoneSection(section) {
    return section.title.toLocaleLowerCase() === "none";
}
function sortNoneSectionFirst(a, b) {
    if (isNoneSection(a)) {
        return -1;
    }
    if (isNoneSection(b)) {
        return 1;
    }
    return 0;
}
export function getMemberSections(parent, childFilter = () => true) {
    if (parent.categories?.length) {
        return filterMap(parent.categories, (cat) => {
            const children = cat.children.filter(childFilter);
            if (!children.length)
                return;
            return {
                title: cat.title,
                description: cat.description,
                children,
            };
        }).sort(sortNoneSectionFirst);
    }
    if (parent.groups?.length) {
        return parent.groups.flatMap((group) => {
            if (group.categories?.length) {
                return filterMap(group.categories.slice().sort(sortNoneSectionFirst), (cat) => {
                    const children = cat.children.filter(childFilter);
                    if (!children.length)
                        return;
                    return {
                        title: isNoneSection(cat) ? group.title : `${group.title} - ${cat.title}`,
                        description: cat.description,
                        children,
                    };
                });
            }
            const children = group.children.filter(childFilter);
            if (!children.length)
                return [];
            return {
                title: group.title,
                description: group.description,
                children,
            };
        }).sort(sortNoneSectionFirst);
    }
    if (parent.children?.length) {
        return [{
                title: "none",
                children: parent.children || [],
            }];
    }
    return [];
}
const nameCollisionCache = new WeakMap();
function getNameCollisionCount(project, name) {
    let collisions = nameCollisionCache.get(project);
    if (collisions === undefined) {
        collisions = new DefaultMap(() => 0);
        for (const reflection of project.getReflectionsByKind(ReflectionKind.SomeExport)) {
            collisions.set(reflection.name, collisions.get(reflection.name) + 1);
        }
        nameCollisionCache.set(project, collisions);
    }
    return collisions.get(name);
}
function getNamespacedPath(reflection) {
    const path = [reflection];
    let parent = reflection.parent;
    while (parent?.kindOf(ReflectionKind.Namespace)) {
        path.unshift(parent);
        parent = parent.parent;
    }
    return path;
}
/**
 * Returns a (hopefully) globally unique path for the given reflection.
 *
 * This only works for exportable symbols, so e.g. methods are not affected by this.
 *
 * If the given reflection has a globally unique name already, then it will be returned as is. If the name is
 * ambiguous (i.e. there are two classes with the same name in different namespaces), then the namespaces path of the
 * reflection will be returned.
 */
export function getUniquePath(reflection) {
    if (reflection.kindOf(ReflectionKind.SomeExport)) {
        if (getNameCollisionCount(reflection.project, reflection.name) >= 2) {
            return getNamespacedPath(reflection);
        }
    }
    return [reflection];
}