UNPKG

typedoc-plugin-markdown

Version:

A plugin for TypeDoc that enables TypeScript API documentation to be generated in Markdown.

304 lines (303 loc) 13.2 kB
import { isQuoted } from '../../libs/utils/index.js'; import { getHierarchyRoots, isNoneSection, sortNoneSectionFirst, } from '../../theme/lib/index.js'; import { DeclarationReflection, EntryPointStrategy, i18n, ReflectionKind, } from 'typedoc'; export class NavigationBuilder { router; theme; project; options; packagesMeta; navigationOptions; navigation = []; isPackages; includeHierarchySummary; fileExtension; constructor(router, theme, project) { this.router = router; this.theme = theme; this.project = project; this.options = theme.application.options; this.navigationOptions = this.getNavigationOptions(); this.packagesMeta = {}; this.packagesMeta = theme.application.renderer.packagesMeta; this.navigation = []; this.isPackages = this.options.getValue('entryPointStrategy') === EntryPointStrategy.Packages; this.includeHierarchySummary = this.options.isSet('includeHierarchySummary') && this.options.getValue('includeHierarchySummary'); this.fileExtension = this.options.getValue('fileExtension'); } getNavigation() { if (this.isPackages) { if (Object.keys(this.packagesMeta)?.length === 1) { this.buildNavigationFromProject(this.project); } else { this.project.children?.forEach((projectChild) => { this.buildNavigationFromPackage(projectChild); }); } } else { this.buildNavigationFromProject(this.project); } this.removeEmptyChildren(this.navigation); return this.navigation; } getNavigationOptions() { if (this.options.isSet('navigation')) { const navigationOptions = this.options.getValue('navigation'); return { excludeCategories: !navigationOptions.includeCategories, excludeGroups: !navigationOptions.includeGroups, excludeFolders: !navigationOptions.includeFolders, }; } return this.options.getValue('navigationModel'); } removeEmptyChildren(navigation) { navigation.forEach((navItem) => { if (navItem.children) { this.removeEmptyChildren(navItem.children); navItem.children = navItem.children.filter((child) => Object.keys(child).length > 0); if (navItem.children.length === 0) { delete navItem.children; } } }); } buildNavigationFromPackage(projectChild) { const packageOptions = this.packagesMeta[projectChild.name]?.options; const entryModule = packageOptions?.isSet('entryModule') ? packageOptions.getValue('entryModule') : this.options.getValue('entryModule'); const projectChildUrl = this.router.getFullUrl(projectChild); const children = []; const childGroups = this.getReflectionGroups(projectChild); if (childGroups) { children.push(...childGroups.filter((child) => child.title !== entryModule)); } this.navigation.push({ title: projectChild.name, kind: projectChild.kind, children, ...(projectChildUrl && { path: projectChildUrl }), }); } buildNavigationFromProject(project) { const entryModule = this.options.getValue('entryModule'); if (this.includeHierarchySummary && getHierarchyRoots(project)?.length) { this.navigation.push({ title: i18n.theme_hierarchy(), path: `hierarchy${this.fileExtension}`, }); } if (!this.navigationOptions.excludeCategories && project.categories?.length) { this.navigation.push(...project.categories.map((category) => { return { title: category.title, ...(this.router.hasUrl(category) && { path: this.router.getFullUrl(category), }), children: this.getCategoryGroupChildren(category), }; })); } else { if (project.groups?.length) { const isOnlyModules = project.children?.every((child) => child.kind === ReflectionKind.Module); if (isOnlyModules) { project.groups?.forEach((projectGroup) => { const children = this.getGroupChildren(projectGroup); if (projectGroup.title === i18n.kind_plural_module()) { if (children?.length) { this.navigation.push(...children.filter((child) => child.title !== entryModule)); } } else { if (this.navigationOptions.excludeGroups) { if (children?.length) { this.navigation.push(...children.filter((child) => child.title !== entryModule)); } } else { if (projectGroup.owningReflection.kind === ReflectionKind.Document) { this.navigation.push({ title: projectGroup.title, children: projectGroup.children.map((child) => { return { title: child.name, kind: child.kind, path: this.router.getFullUrl(child), isDeprecated: child.isDeprecated(), }; }), }); } else { this.navigation.push({ title: projectGroup.title, children: children?.filter((child) => child.title !== entryModule), }); } } } }); } else { project.groups?.forEach((projectGroup) => { const children = this.getGroupChildren(projectGroup); const indexModule = projectGroup.children.find((child) => child.name === entryModule); if (children?.length) { this.navigation.push({ title: projectGroup.title, children: children.filter((child) => child.title !== entryModule), }); } if (indexModule) { const children = indexModule instanceof DeclarationReflection ? this.getReflectionGroups(indexModule) : []; if (children) { this.navigation.push(...children); } } }); } } } } getCategoryGroupChildren(group) { return group.children ?.filter((child) => this.router.hasOwnDocument(child)) .map((child) => { const children = child instanceof DeclarationReflection ? this.getReflectionGroups(child) : []; return { title: child.name, kind: child.kind, path: this.router.getFullUrl(child), isDeprecated: child.isDeprecated(), ...(children && { children }), }; }); } getGroupChildren(group) { if (!this.navigationOptions.excludeCategories && group?.categories?.length) { return group.categories?.map((category) => { return { title: category.title, children: this.getCategoryGroupChildren(category), }; }); } return group.children ?.filter((child) => this.router.hasOwnDocument(child)) .reduce((acc, child) => { const children = child instanceof DeclarationReflection && !this.navigationOptions.excludeCategories && child.categories?.length ? child.categories .sort(sortNoneSectionFirst) ?.map((category) => { const catChildren = this.getCategoryGroupChildren(category); return catChildren.length ? isNoneSection(category) ? [...catChildren] : { title: category.title, ...(this.router.hasUrl(category) && { path: this.router.getFullUrl(category), }), children: catChildren, } : null; }) .filter((cat) => Boolean(cat)) : this.getReflectionGroups(child); return this.processChildren(acc, child, children); }, []); } getReflectionGroups(reflection, outputFileStrategy) { if (reflection instanceof DeclarationReflection) { if (this.navigationOptions.excludeGroups) { return reflection.childrenIncludingDocuments ?.filter((child) => this.router.hasOwnDocument(child)) .reduce((acc, child) => { const children = this.getReflectionGroups(child, outputFileStrategy); return this.processChildren(acc, child, children); }, []); } const groupsWithoutDocs = reflection.groups?.filter((group) => group.title !== ReflectionKind.pluralString(ReflectionKind.Document)); const isModulesGroup = groupsWithoutDocs?.length && groupsWithoutDocs[0].children.every((child) => child.kind === ReflectionKind.Module); if (isModulesGroup) { return this.getGroupChildren(groupsWithoutDocs[0]) || null; } return reflection.groups ?.sort(sortNoneSectionFirst) ?.map((group) => { const groupChildren = this.getGroupChildren(group); if (groupChildren?.length) { if (group.owningReflection.kind === ReflectionKind.Document) { return groupChildren[0]; } if (isNoneSection(group)) { return [...groupChildren]; } return { title: group.title, children: groupChildren, }; } return null; }) .filter((group) => Boolean(group)); } return null; } processChildren(acc, child, children) { if (!isQuoted(child.name) && !this.navigationOptions.excludeFolders) { const titleParts = child.name.split('/'); if (!child.name.startsWith('@') && titleParts.length > 1) { let currentLevel = acc; let currentItem; for (let i = 0; i < titleParts.length - 1; i++) { currentItem = currentLevel?.find((item) => item.title === titleParts[i]); if (!currentItem) { currentItem = { title: titleParts[i], children: [], }; currentLevel.push(currentItem); } if (currentItem) { currentLevel = currentItem.children || []; } } currentLevel.push({ title: titleParts[titleParts.length - 1], kind: child.kind, path: this.router.getFullUrl(child), isDeprecated: child.isDeprecated(), ...(children && { children }), }); return acc; } } acc.push({ title: child.name, kind: child.kind, path: this.router.getFullUrl(child), isDeprecated: child.isDeprecated(), ...(children && { children }), }); return acc; } }