diff --git a/CHANGELOG.md b/CHANGELOG.md index 0678e9aa6..99f92dcd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ### Features -- Added `stripYamlFrontmatter` config option, #2381. +- Added `stripYamlFrontmatter` config option to remove YAML frontmatter from README.md, #2381. +- Added `--excludeCategories` config option to remove reflections present in any excluded category, #1407. - Navigation is now written to a JS file and built dynamically, which significantly decreases document generation time with large projects and also provides large space benefits. Themes may now override `DefaultTheme.buildNavigation` to customize the displayed navigation tree, #2287. diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index b8243326d..e1d07635e 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -8,7 +8,7 @@ import { ReflectionCategory } from "../../models"; import { Component, ConverterComponent } from "../components"; import { Converter } from "../converter"; import type { Context } from "../context"; -import { Option, getSortFunction, removeIf } from "../../utils"; +import { Option, getSortFunction } from "../../utils"; /** * A handler that sorts and categorizes the found reflections in the resolving phase. @@ -157,13 +157,13 @@ export class CategoryPlugin extends ConverterComponent { * relevance boost to be used when searching * @returns An array containing all children of the given reflection categorized */ - getReflectionCategories( + private getReflectionCategories( reflections: DeclarationReflection[], ): ReflectionCategory[] { const categories = new Map(); for (const child of reflections) { - const childCategories = this.getCategories(child); + const childCategories = this.extractCategories(child); if (childCategories.size === 0) { childCategories.add(CategoryPlugin.defaultCategory); } @@ -197,31 +197,18 @@ export class CategoryPlugin extends ConverterComponent { * @privateRemarks * If you change this, also update getGroups in GroupPlugin accordingly. */ - getCategories(reflection: DeclarationReflection) { - const categories = new Set(); - function extractCategoryTags(comment: Comment | undefined) { - if (!comment) return; - removeIf(comment.blockTags, (tag) => { - if (tag.tag === "@category") { - categories.add( - Comment.combineDisplayParts(tag.content).trim(), - ); - - return true; - } - return false; - }); - } + private extractCategories(reflection: DeclarationReflection) { + const categories = CategoryPlugin.getCategories(reflection); - extractCategoryTags(reflection.comment); + reflection.comment?.removeTags("@category"); for (const sig of reflection.getNonIndexSignatures()) { - extractCategoryTags(sig.comment); + sig.comment?.removeTags("@category"); } if (reflection.type?.type === "reflection") { - extractCategoryTags(reflection.type.declaration.comment); + reflection.type.declaration.comment?.removeTags("@category"); for (const sig of reflection.type.declaration.getNonIndexSignatures()) { - extractCategoryTags(sig.comment); + sig.comment?.removeTags("@category"); } } @@ -245,7 +232,7 @@ export class CategoryPlugin extends ConverterComponent { * @param b The right reflection to sort. * @returns The sorting weight. */ - static sortCatCallback( + private static sortCatCallback( a: ReflectionCategory, b: ReflectionCategory, ): number { @@ -268,4 +255,34 @@ export class CategoryPlugin extends ConverterComponent { } return aWeight - bWeight; } + + static getCategories(reflection: DeclarationReflection) { + const categories = new Set(); + function discoverCategories(comment: Comment | undefined) { + if (!comment) return; + for (const tag of comment.blockTags) { + if (tag.tag === "@category") { + categories.add( + Comment.combineDisplayParts(tag.content).trim(), + ); + } + } + } + + discoverCategories(reflection.comment); + for (const sig of reflection.getNonIndexSignatures()) { + discoverCategories(sig.comment); + } + + if (reflection.type?.type === "reflection") { + discoverCategories(reflection.type.declaration.comment); + for (const sig of reflection.type.declaration.getNonIndexSignatures()) { + discoverCategories(sig.comment); + } + } + + categories.delete(""); + + return categories; + } } diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index 4a2c9ec42..a127533ae 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -22,6 +22,7 @@ import { unique, partition, } from "../../utils"; +import { CategoryPlugin } from "./CategoryPlugin"; /** * These tags are not useful to display in the generated documentation. @@ -118,6 +119,12 @@ export class CommentPlugin extends ConverterComponent { @Option("excludeNotDocumented") accessor excludeNotDocumented!: boolean; + @Option("excludeCategories") + accessor excludeCategories!: string[]; + + @Option("defaultCategory") + accessor defaultCategory!: string; + private _excludeKinds: number | undefined; private get excludeNotDocumentedKinds(): number { this._excludeKinds ??= this.application.options @@ -478,6 +485,10 @@ export class CommentPlugin extends ConverterComponent { return true; } + if (this.excludedByCategory(reflection)) { + return true; + } + if (!comment) { // We haven't moved comments from the parent for signatures without a direct // comment, so don't exclude those due to not being documented. @@ -545,6 +556,26 @@ export class CommentPlugin extends ConverterComponent { return isHidden; } + + private excludedByCategory(reflection: Reflection): boolean { + const excludeCategories = this.excludeCategories; + + let target: DeclarationReflection | undefined; + if (reflection instanceof DeclarationReflection) { + target = reflection; + } else if (reflection instanceof SignatureReflection) { + target = reflection.parent; + } + + if (!target || !excludeCategories.length) return false; + + const categories = CategoryPlugin.getCategories(target); + if (categories.size === 0) { + categories.add(this.defaultCategory); + } + + return excludeCategories.some((cat) => categories.has(cat)); + } } function inTypeLiteral(refl: Reflection | undefined) { diff --git a/src/lib/converter/plugins/GroupPlugin.ts b/src/lib/converter/plugins/GroupPlugin.ts index a9f781c5b..eac9c8a4c 100644 --- a/src/lib/converter/plugins/GroupPlugin.ts +++ b/src/lib/converter/plugins/GroupPlugin.ts @@ -99,7 +99,7 @@ export class GroupPlugin extends ConverterComponent { * Extracts the groups for a given reflection. * * @privateRemarks - * If you change this, also update getCategories in CategoryPlugin accordingly. + * If you change this, also update extractCategories in CategoryPlugin accordingly. */ getGroups(reflection: DeclarationReflection) { const groups = new Set(); diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index c74904af2..80983a955 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -105,6 +105,7 @@ export interface TypeDocOptionMap { excludePrivate: boolean; excludeProtected: boolean; excludeReferences: boolean; + excludeCategories: string[]; name: string; includeVersion: boolean; disableSources: boolean; diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index ab6bd7753..1d3c02754 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -142,6 +142,12 @@ export function addTypeDocOptions(options: Pick) { help: "Prevent symbols that are marked with @internal from being documented.", type: ParameterType.Boolean, }); + options.addDeclaration({ + name: "excludeCategories", + help: "Exclude symbols within this category from the documentation.", + type: ParameterType.Array, + defaultValue: [], + }); options.addDeclaration({ name: "excludePrivate", help: "Ignore private variables and methods.", diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index df8250791..af8ec5dca 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -347,6 +347,13 @@ describe("Behavior Tests", () => { logger.expectNoOtherMessages(); }); + it("Handles excludeCategories", () => { + app.options.setValue("excludeCategories", ["A", "Default"]); + app.options.setValue("defaultCategory", "Default"); + const project = convert("excludeCategories"); + equal(project.children?.map((c) => c.name), ["c"]); + }); + it("Handles excludeNotDocumentedKinds", () => { app.options.setValue("excludeNotDocumented", true); app.options.setValue("excludeNotDocumentedKinds", ["Property"]); diff --git a/src/test/converter2/behavior/excludeCategories.ts b/src/test/converter2/behavior/excludeCategories.ts new file mode 100644 index 000000000..b2b312c3e --- /dev/null +++ b/src/test/converter2/behavior/excludeCategories.ts @@ -0,0 +1,14 @@ +/** + * @category A + */ +export const a = 1; +/** + * @category A + * @category B + */ +export const b = 1; +/** + * @category C + */ +export const c = 1; +export const d = 1;