From 0117c999cda90f32005237c2db43ec69c005b317 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Dec 2023 18:08:32 -0700 Subject: [PATCH] Add --navigation.includeFolders Closes #2388 --- CHANGELOG.md | 3 + .../output/themes/default/DefaultTheme.tsx | 87 +++++++++++++++---- .../output/themes/default/partials/icon.tsx | 11 ++- src/lib/utils/options/declaration.ts | 1 + src/lib/utils/options/sources/typedoc.ts | 1 + 5 files changed, 85 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a87666a8f..1ff5d2f9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Features +- Added a `--navigation.includeFolders` (default: `true`) option to create nested navigation for projects which include many entry points, #2388. - Type parameters on functions/classes can will now link to the "Type Parameters" section, #2322. Type parameters have also been changed to have a distinct color from type aliases when rendering, which can be changed with custom CSS. - TypeDoc now provides warnings if a signature comment is directly specified on a signature and contains `@param` tags which do not apply, #2368. @@ -29,6 +30,8 @@ - `@group` and `@category` organization is now applied later to allow inherited comments to create groups/categories, #2459. - Conversion order should no longer affect link resolution for classes with properties whose type does not rely on `this`, #2466. - Keyword syntax highlighting introduced in 0.25.4 was not always applied to keywords. +- Module reflections now have a custom `M` icon rather than sharing with the namespace icon. + Note: The default CSS still colors both modules and namespaces the same, as it is generally uncommon to have both in a generated site. - If all members in a group are hidden from the page, the group will be hidden in the page index on page load. ## v0.25.4 (2023-11-26) diff --git a/src/lib/output/themes/default/DefaultTheme.tsx b/src/lib/output/themes/default/DefaultTheme.tsx index da0b6754a..b0900d44f 100644 --- a/src/lib/output/themes/default/DefaultTheme.tsx +++ b/src/lib/output/themes/default/DefaultTheme.tsx @@ -305,27 +305,70 @@ export class DefaultTheme extends Theme { return parent.groups.map(toNavigation); } + if ( + opts.includeFolders && + parent.children?.every((child) => child.kindOf(ReflectionKind.Module)) && + parent.children.some((child) => child.name.includes("/")) + ) { + return deriveModuleFolders(parent.children); + } + return parent.children?.map(toNavigation); } - function shouldShowCategories( - reflection: Reflection, - opts: { includeCategories: boolean; includeGroups: boolean }, - ) { - if (opts.includeCategories) { - return !reflection.comment?.hasModifier("@hideCategories"); + function deriveModuleFolders(children: DeclarationReflection[]) { + const result: NavigationElement[] = []; + + const resolveOrCreateParents = ( + path: string[], + root: NavigationElement[] = result, + ): NavigationElement[] => { + if (path.length > 1) { + const inner = root.find((el) => el.text === path[0]); + if (inner) { + inner.children ||= []; + return resolveOrCreateParents(path.slice(1), inner.children); + } else { + root.push({ + text: path[0], + children: [], + }); + return resolveOrCreateParents(path.slice(1), root[root.length - 1].children); + } + } + + return root; + }; + + // Note: This might end up putting a module within another module if we document + // both foo/index.ts and foo/bar.ts. + for (const child of children) { + const parts = child.name.split("/"); + const collection = resolveOrCreateParents(parts); + const nav = toNavigation(child); + nav.text = parts[parts.length - 1]; + collection.push(nav); } - return reflection.comment?.hasModifier("@showCategories") === true; - } - function shouldShowGroups( - reflection: Reflection, - opts: { includeCategories: boolean; includeGroups: boolean }, - ) { - if (opts.includeGroups) { - return !reflection.comment?.hasModifier("@hideGroups"); + // Now merge single-possible-paths together so we don't have folders in our navigation + // which contain only another single folder. + const queue = [...result]; + while (queue.length) { + const review = queue.shift()!; + queue.push(...(review.children || [])); + if (review.kind || review.path) continue; + + if (review.children?.length === 1) { + const copyFrom = review.children[0]; + const fullName = `${review.text}/${copyFrom.text}`; + delete review.children; + Object.assign(review, copyFrom); + review.text = fullName; + queue.push(review); + } } - return reflection.comment?.hasModifier("@showGroups") === true; + + return result; } } @@ -401,3 +444,17 @@ function getReflectionClasses(reflection: DeclarationReflection, filters: Record return classes.join(" "); } + +function shouldShowCategories(reflection: Reflection, opts: { includeCategories: boolean; includeGroups: boolean }) { + if (opts.includeCategories) { + return !reflection.comment?.hasModifier("@hideCategories"); + } + return reflection.comment?.hasModifier("@showCategories") === true; +} + +function shouldShowGroups(reflection: Reflection, opts: { includeCategories: boolean; includeGroups: boolean }) { + if (opts.includeGroups) { + return !reflection.comment?.hasModifier("@hideGroups"); + } + return reflection.comment?.hasModifier("@showGroups") === true; +} diff --git a/src/lib/output/themes/default/partials/icon.tsx b/src/lib/output/themes/default/partials/icon.tsx index ebc417352..dc26cec98 100644 --- a/src/lib/output/themes/default/partials/icon.tsx +++ b/src/lib/output/themes/default/partials/icon.tsx @@ -123,9 +123,14 @@ export const icons: Record< true, ), [ReflectionKind.Module]() { - return this[ReflectionKind.Namespace](); + return kindIcon( + , + "var(--color-ts-module)", + ); }, - [ReflectionKind.Namespace]: () => kindIcon( kindIcon( diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 4b77c63ff..6bfb0f880 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -144,6 +144,7 @@ export interface TypeDocOptionMap { navigation: { includeCategories: boolean; includeGroups: boolean; + includeFolders: boolean; fullTree: boolean; }; visibilityFilters: ManuallyValidatedOption<{ diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 493674cd9..0116d7070 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -464,6 +464,7 @@ export function addTypeDocOptions(options: Pick) { defaults: { includeCategories: false, includeGroups: false, + includeFolders: true, fullTree: false, }, });