diff --git a/CHANGELOG.md b/CHANGELOG.md index 454023f25..6b3334ad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Added `Application.EVENT_VALIDATE_PROJECT` event for plugins which implement custom validation, #2183. - Plugins may now return an object from external symbol resolvers, #2066. +- Expose `Comment.displayPartsToMarkdown` on for themes overwriting the `comment` helper, #2115. ### Bug Fixes diff --git a/src/lib/models/comments/comment.ts b/src/lib/models/comments/comment.ts index 37079324c..c39e2f8e4 100644 --- a/src/lib/models/comments/comment.ts +++ b/src/lib/models/comments/comment.ts @@ -121,6 +121,65 @@ export class Comment { return result; } + /** + * Helper function to convert an array of comment display parts into markdown suitable for + * passing into Marked. `urlTo` will be used to resolve urls to any reflections linked to with + * `@link` tags. + */ + static displayPartsToMarkdown( + parts: readonly CommentDisplayPart[], + urlTo: (ref: Reflection) => string + ) { + const result: string[] = []; + + for (const part of parts) { + switch (part.kind) { + case "text": + case "code": + result.push(part.text); + break; + case "inline-tag": + switch (part.tag) { + case "@label": + case "@inheritdoc": // Shouldn't happen + break; // Not rendered. + case "@link": + case "@linkcode": + case "@linkplain": { + if (part.target) { + const url = + typeof part.target === "string" + ? part.target + : urlTo(part.target); + const text = + part.tag === "@linkcode" + ? `${part.text}` + : part.text; + result.push( + url + ? `${text}` + : part.text + ); + } else { + result.push(part.text); + } + break; + } + default: + // Hmm... probably want to be able to render these somehow, so custom inline tags can be given + // special rendering rules. Future capability. For now, just render their text. + result.push(`{${part.tag} ${part.text}}`); + break; + } + break; + default: + assertNever(part); + } + } + + return result.join(""); + } + /** * Helper utility to clone {@link Comment.summary} or {@link CommentTag.content} */ diff --git a/src/lib/output/themes/default/DefaultThemeRenderContext.ts b/src/lib/output/themes/default/DefaultThemeRenderContext.ts index 6c72ccee5..208255a92 100644 --- a/src/lib/output/themes/default/DefaultThemeRenderContext.ts +++ b/src/lib/output/themes/default/DefaultThemeRenderContext.ts @@ -1,11 +1,11 @@ import type { RendererHooks } from "../.."; -import type { +import { + Comment, CommentDisplayPart, ReferenceType, Reflection, } from "../../../models"; import type { NeverIfInternal, Options } from "../../../utils"; -import { displayPartsToMarkdown } from "../lib"; import type { DefaultTheme } from "./DefaultTheme"; import { defaultLayout } from "./layouts/default"; import { index } from "./partials"; @@ -62,14 +62,14 @@ export class DefaultThemeRenderContext { return url ? this.theme.markedPlugin.getRelativeUrl(url) : url; }; - urlTo = (reflection: Reflection) => this.relativeURL(reflection.url); + urlTo = (reflection: Reflection) => this.relativeURL(reflection.url)!; markdown = ( md: readonly CommentDisplayPart[] | NeverIfInternal ) => { if (md instanceof Array) { return this.theme.markedPlugin.parseMarkdown( - displayPartsToMarkdown(md, this.urlTo) + Comment.displayPartsToMarkdown(md, this.urlTo) ); } return md ? this.theme.markedPlugin.parseMarkdown(md) : ""; diff --git a/src/lib/output/themes/lib.tsx b/src/lib/output/themes/lib.tsx index 3b01121fb..e38397be9 100644 --- a/src/lib/output/themes/lib.tsx +++ b/src/lib/output/themes/lib.tsx @@ -1,14 +1,12 @@ import { Comment, - CommentDisplayPart, DeclarationReflection, Reflection, ReflectionFlags, SignatureReflection, TypeParameterReflection, } from "../../models"; -import { assertNever, JSX } from "../../utils"; -import type { DefaultThemeRenderContext } from "./default/DefaultThemeRenderContext"; +import { JSX } from "../../utils"; export function stringify(data: unknown) { if (typeof data === "bigint") { @@ -117,50 +115,6 @@ export function camelToTitleCase(text: string) { return text.substring(0, 1).toUpperCase() + text.substring(1).replace(/[a-z][A-Z]/g, (x) => `${x[0]} ${x[1]}`); } -export function displayPartsToMarkdown( - parts: readonly CommentDisplayPart[], - urlTo: DefaultThemeRenderContext["urlTo"] -) { - const result: string[] = []; - - for (const part of parts) { - switch (part.kind) { - case "text": - case "code": - result.push(part.text); - break; - case "inline-tag": - switch (part.tag) { - case "@label": - case "@inheritdoc": // Shouldn't happen - break; // Not rendered. - case "@link": - case "@linkcode": - case "@linkplain": { - if (part.target) { - const url = typeof part.target === "string" ? part.target : urlTo(part.target); - const text = part.tag === "@linkcode" ? `${part.text}` : part.text; - result.push(url ? `${text}` : part.text); - } else { - result.push(part.text); - } - break; - } - default: - // Hmm... probably want to be able to render these somehow, so custom inline tags can be given - // special rendering rules. Future capability. For now, just render their text. - result.push(`{${part.tag} ${part.text}}`); - break; - } - break; - default: - assertNever(part); - } - } - - return result.join(""); -} - /** * Renders the reflection name with an additional `?` if optional. */