From 2f5831a4805e55c2ad0cb03f45c62fa59e3ad970 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 5 May 2024 16:37:10 -0600 Subject: [PATCH] Support `@document` on more reflections Also fix missing edge case where we didn't warn on unknown block tags --- .config/typedoc.json | 3 +- CHANGELOG.md | 5 +- internal-docs/custom-themes.md | 4 ++ internal-docs/plugins.md | 4 ++ src/lib/converter/comments/parser.ts | 4 ++ src/lib/converter/context.ts | 4 ++ src/lib/converter/converter.ts | 11 ++--- src/lib/converter/factories/signature.ts | 6 +-- src/lib/converter/symbols.ts | 1 - src/lib/internationalization/translatable.ts | 1 + src/lib/models/reflections/kind.ts | 1 + src/lib/output/renderer.ts | 2 + .../output/themes/default/DefaultTheme.tsx | 12 +++-- src/lib/utils/options/readers/typedoc.ts | 2 +- src/lib/utils/options/tsdoc-defaults.ts | 46 ++++++++++--------- tsdoc.json | 9 ++++ 16 files changed, 77 insertions(+), 38 deletions(-) diff --git a/.config/typedoc.json b/.config/typedoc.json index a1578fd53..609bee4b7 100644 --- a/.config/typedoc.json +++ b/.config/typedoc.json @@ -13,7 +13,8 @@ ], "sort": ["kind", "instance-first", "required-first", "alphabetical"], "entryPoints": ["../src/index.ts"], - "projectDocuments": ["../CHANGELOG.md"], + "alwaysCreateEntryPointModule": false, + "projectDocuments": ["../internal-docs/plugins.md"], "excludeExternals": true, "excludeInternal": false, "excludePrivate": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index ac03136a0..7352aa639 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,12 @@ ### To Do -- Handle `@document` tag to add documents to the tree for classes, interfaces, enums, functions, variables - Handle image and relative markdown links within documents - Update website docs - consider if reworking website to just be a TypeDoc generated site is a good idea - `@license`, `@import`, sitemapBaseUrl, markedOptions -> markdownItOptions, markdownItLoader, navigation + `@license`, `@import`, `@hideGroups` `@hideCategories` sitemapBaseUrl, markedOptions -> markdownItOptions, markdownItLoader, navigation sort - documents-first, documents-last, alphabetical-ignoring-documents searchInDocuments +- Review JSDoc tags for other tags we should add to the default list of known block tags ### Breaking Changes @@ -43,6 +43,7 @@ - New option, `--customFooterHtml` to add custom HTML to the generated page footer, #2559. - TypeDoc will now copy modifier tags to children if specified in the `--cascadedModifierTags` option, #2056. - TypeDoc will now warn if mutually exclusive modifier tags are specified for a comment (e.g. both `@alpha` and `@beta`), #2056. +- TypeDoc will now warn if a block tag is used which is not defined by the `--blockTags` option. - Added three new sort strategies `documents-first`, `documents-last`, and `alphabetical-ignoring-documents` to order markdown documents. - Added new `--alwaysCreateEntryPointModule` option. When set, TypeDoc will always create a `Module` for entry points, even if only one is provided. If `--projectDocuments` is used to add documents, this option defaults to `true`, otherwise, defaults to `false`. diff --git a/internal-docs/custom-themes.md b/internal-docs/custom-themes.md index 77249f6cb..1f855ea03 100644 --- a/internal-docs/custom-themes.md +++ b/internal-docs/custom-themes.md @@ -1,3 +1,7 @@ +--- +title: Custom Themes +--- + # Custom Themes TypeDoc 0.22 changed how themes are defined, necessarily breaking compatibility with all Handlebars based themes diff --git a/internal-docs/plugins.md b/internal-docs/plugins.md index 61752f7e4..295d98dac 100644 --- a/internal-docs/plugins.md +++ b/internal-docs/plugins.md @@ -1,3 +1,7 @@ +--- +title: Plugins +--- + # Writing a TypeDoc Plugin TypeDoc supports plugins which can modify how projects are converted, how converted symbols diff --git a/src/lib/converter/comments/parser.ts b/src/lib/converter/comments/parser.ts index bd0a7def9..d2aa51576 100644 --- a/src/lib/converter/comments/parser.ts +++ b/src/lib/converter/comments/parser.ts @@ -351,6 +351,10 @@ function blockTag( "blockTag called not at the start of a block tag.", ); // blockContent is broken if this fails. + if (!config.blockTags.has(blockTag.text)) { + warning(i18n.unknown_block_tag_0(blockTag.text), blockTag); + } + const tagName = aliasedTags.get(blockTag.text) || blockTag.text; let content: CommentDisplayPart[]; diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index 9ab84949c..2aed43d5a 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -227,6 +227,10 @@ export class Context { this, reflection, ); + + if (reflection.kindOf(ReflectionKind.MayContainDocuments)) { + this.converter.processDocumentTags(reflection, reflection); + } } addChild(reflection: DeclarationReflection | DocumentReflection) { diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 477fe3189..fe759adc0 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -321,7 +321,6 @@ export class Converter extends ChildableComponent< * reference passed will have the `moduleSource` set and the `symbolReference` will navigate via `.`) * and user defined \{\@link\} tags which cannot be resolved. If the link being resolved is inferred * from a type, then no `part` will be passed to the resolver function. - * @since 0.22.14 */ addUnknownSymbolResolver(resolver: ExternalSymbolResolver): void { this._externalSymbolResolvers.push(resolver); @@ -428,7 +427,7 @@ export class Converter extends ChildableComponent< context.project.comment = symbol ? context.getComment(symbol, context.project.kind) : context.getFileComment(node); - this.processDocumentTags(context.project); + this.processDocumentTags(context.project, context.project); context.trigger( Converter.EVENT_CREATE_DECLARATION, context.project, @@ -539,7 +538,7 @@ export class Converter extends ChildableComponent< ); } - processDocumentTags(reflection: ContainerReflection) { + processDocumentTags(reflection: Reflection, parent: ContainerReflection) { let relativeTo = reflection.comment?.sourcePath; if (relativeTo) { relativeTo = dirname(relativeTo); @@ -565,12 +564,12 @@ export class Converter extends ChildableComponent< const { content, frontmatter } = this.parseRawComment(file); const docRefl = new DocumentReflection( basename(file.fileName).replace(/\.[^.]+$/, ""), - reflection, + parent, content, frontmatter, ); - reflection.addChild(docRefl); - reflection.project.registerReflection(docRefl); + parent.addChild(docRefl); + parent.project.registerReflection(docRefl); } } } diff --git a/src/lib/converter/factories/signature.ts b/src/lib/converter/factories/signature.ts index 1f4f8009b..d09b47513 100644 --- a/src/lib/converter/factories/signature.ts +++ b/src/lib/converter/factories/signature.ts @@ -56,9 +56,6 @@ export function createSignature( ); } - // If we are creating signatures for a variable or property and it has a comment associated with it - // then we should prefer that comment over any comment on the signature. The comment plugin - // will copy the comment down if this signature doesn't have one, so don't set one. let parentReflection = context.scope; if ( parentReflection.kindOf(ReflectionKind.TypeLiteral) && @@ -71,6 +68,9 @@ export function createSignature( const sigComment = context.getSignatureComment(declaration); if (parentReflection.comment?.discoveryId !== sigComment?.discoveryId) { sigRef.comment = sigComment; + if (parentReflection.kindOf(ReflectionKind.MayContainDocuments)) { + context.converter.processDocumentTags(sigRef, parentReflection); + } } } diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index e1ad56487..1f56e82e6 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -292,7 +292,6 @@ function convertNamespace( exportSymbol, ); context.finalizeDeclarationReflection(reflection); - context.converter.processDocumentTags(reflection); } convertSymbols( diff --git a/src/lib/internationalization/translatable.ts b/src/lib/internationalization/translatable.ts index 379890620..6317b846f 100644 --- a/src/lib/internationalization/translatable.ts +++ b/src/lib/internationalization/translatable.ts @@ -79,6 +79,7 @@ export const translatable = { treating_unrecognized_tag_0_as_modifier: `Treating unrecognized tag {0} as a modifier tag.`, unmatched_closing_brace: `Unmatched closing brace.`, unescaped_open_brace_without_inline_tag: `Encountered an unescaped open brace without an inline tag.`, + unknown_block_tag_0: `Encountered an unknown block tag {0}.`, unknown_inline_tag_0: `Encountered an unknown inline tag {0}.`, open_brace_within_inline_tag: `Encountered an open brace within an inline tag, this is likely a mistake.`, inline_tag_not_closed: `Inline tag is not closed.`, diff --git a/src/lib/models/reflections/kind.ts b/src/lib/models/reflections/kind.ts index d7439a07a..39b82254d 100644 --- a/src/lib/models/reflections/kind.ts +++ b/src/lib/models/reflections/kind.ts @@ -89,6 +89,7 @@ export namespace ReflectionKind { ReflectionKind.Interface | ReflectionKind.TypeAlias | ReflectionKind.Reference; + export const MayContainDocuments = SomeExport | ReflectionKind.Project; /** @internal */ export const ExportContainer = ReflectionKind.SomeModule | ReflectionKind.Project; diff --git a/src/lib/output/renderer.ts b/src/lib/output/renderer.ts index 0a09e2e2a..ac1bbd496 100644 --- a/src/lib/output/renderer.ts +++ b/src/lib/output/renderer.ts @@ -124,6 +124,8 @@ export interface RendererHooks { * * {@link Renderer.EVENT_PREPARE_INDEX}
* Triggered when the JavascriptIndexPlugin is preparing the search index. Listeners receive * an instance of {@link IndexEvent}. + * + * @document ../../../internal-docs/custom-themes.md */ @Component({ name: "renderer", internal: true, childClass: RendererComponent }) export class Renderer extends ChildableComponent< diff --git a/src/lib/output/themes/default/DefaultTheme.tsx b/src/lib/output/themes/default/DefaultTheme.tsx index 46f27cfac..207e42869 100644 --- a/src/lib/output/themes/default/DefaultTheme.tsx +++ b/src/lib/output/themes/default/DefaultTheme.tsx @@ -1,11 +1,11 @@ import { Theme } from "../../theme"; import type { Renderer } from "../../renderer"; import { - type Reflection, ReflectionKind, ProjectReflection, type ContainerReflection, DeclarationReflection, + type Reflection, SignatureReflection, ReflectionCategory, ReflectionGroup, @@ -176,7 +176,7 @@ export class DefaultTheme extends Theme { }, { kind: [ReflectionKind.Document], - directory: "variables", + directory: "documents", template: this.documentTemplate, }, ]; @@ -359,10 +359,16 @@ export class DefaultTheme extends Theme { return; } - if (!parent.kindOf(ReflectionKind.SomeModule | ReflectionKind.Project) || parent.isDocument()) { + if (!parent.kindOf(ReflectionKind.MayContainDocuments) || parent.isDocument()) { return; } + if (!parent.kindOf(ReflectionKind.SomeModule | ReflectionKind.Project)) { + // Tricky: Non-module children don't show up in the navigation pane, + // but any documents added by them should. + return parent.documents?.map(toNavigation); + } + if (parent.categories && shouldShowCategories(parent, opts)) { return parent.categories.map(toNavigation); } diff --git a/src/lib/utils/options/readers/typedoc.ts b/src/lib/utils/options/readers/typedoc.ts index aa5b0bb77..8c0b0a5af 100644 --- a/src/lib/utils/options/readers/typedoc.ts +++ b/src/lib/utils/options/readers/typedoc.ts @@ -161,7 +161,7 @@ export class TypeDocReader implements OptionsReader { * * @param path Path to the typedoc.(js|json) file. If path is a directory * typedoc file will be attempted to be found at the root of this path - * @return the typedoc.(js|json) file path or undefined + * @returns the typedoc.(js|json) file path or undefined */ private findTypedocFile(path: string): string | undefined { path = resolve(path); diff --git a/src/lib/utils/options/tsdoc-defaults.ts b/src/lib/utils/options/tsdoc-defaults.ts index c09ba21cf..5873558af 100644 --- a/src/lib/utils/options/tsdoc-defaults.ts +++ b/src/lib/utils/options/tsdoc-defaults.ts @@ -1,24 +1,29 @@ // If updating these lists, also update tsdoc.json export const tsdocBlockTags = [ + "@defaultValue", "@deprecated", + "@example", "@param", + "@privateRemarks", "@remarks", "@returns", + "@see", "@throws", - "@privateRemarks", - "@defaultValue", "@typeParam", ] as const; export const blockTags = [ ...tsdocBlockTags, - "@module", - "@inheritDoc", - "@group", - "@groupDescription", "@category", "@categoryDescription", + "@document", + "@group", + "@groupDescription", + "@inheritDoc", + "@license", + "@module", + "@return", // Alias for @typeParam "@template", // Because TypeScript is important! @@ -28,7 +33,6 @@ export const blockTags = [ "@prop", "@property", "@satisfies", - "@license", "@import", ] as const; @@ -36,33 +40,33 @@ export const tsdocInlineTags = ["@link", "@inheritDoc", "@label"] as const; export const inlineTags = [...tsdocInlineTags, "@linkcode", "@linkplain"]; export const tsdocModifierTags = [ - "@public", - "@private", - "@protected", - "@internal", - "@readonly", - "@packageDocumentation", - "@eventProperty", "@alpha", "@beta", + "@eventProperty", "@experimental", - "@sealed", + "@internal", "@override", + "@packageDocumentation", + "@private", + "@protected", + "@public", + "@readonly", + "@sealed", "@virtual", ] as const; export const modifierTags = [ ...tsdocModifierTags, - "@hidden", - "@ignore", "@class", "@enum", "@event", - "@overload", - "@namespace", + "@hidden", + "@hideCategories", + "@hideGroups", + "@ignore", "@interface", + "@namespace", + "@overload", "@showCategories", - "@hideCategories", "@showGroups", - "@hideGroups", ] as const; diff --git a/tsdoc.json b/tsdoc.json index 977281fbe..889013625 100644 --- a/tsdoc.json +++ b/tsdoc.json @@ -59,6 +59,15 @@ "tagName": "@class", "syntaxKind": "modifier" }, + { + "tagName": "@document", + "syntaxKind": "block" + }, + { + // TSDoc defines @returns, we also recognize @return for JSDoc compat + "tagName": "@return", + "syntaxKind": "block" + }, { "tagName": "@enum", "syntaxKind": "modifier"