Skip to content

Commit

Permalink
Add support for an @expand tag.
Browse files Browse the repository at this point in the history
Closes #2303
  • Loading branch information
Gerrit0 committed Jul 1, 2024
1 parent 6e76611 commit c1bf00b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 45 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
- Removed the `hideParameterTypesInTitle` option, this was originally added as a workaround for many signatures overflowing
the available horizontal space in rendered pages. TypeDoc now has logic to wrap types/signatures smartly, so this option is
no longer necessary.
- Add support for an `@expand` tag which can be placed on type aliases and interfaces.
When a type with `@expand` is referenced and TypeDoc has a place to include additional details about the type,
the properties of the type will be included in the page where `@expand` is found. Note that use of this tag can
_significantly_ increase the size of your generated documentation if it is applied to commonly used types as
it will result in inlining the comments for those types everywhere they are referenced, #2303.

TODO:

- Add an option for controlling print width.
- Add an option for non-rendered tags, add `@expand` to it by default.
- Finish cleaning up formatter TODO comments, tests
- Write docs for `@expand`

# Unreleased

Expand Down
18 changes: 10 additions & 8 deletions src/lib/models/comments/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,18 @@ export type CommentDisplayPart =
* that TypeDoc knows to skip it when parsing relative links and inline tags.
**/
| { kind: "code"; text: string }
/**
* Represents an inline tag like `{@link Foo}`
*/
| InlineTagDisplayPart
/**
* Represents a reference to a path relative to where the comment resides.
* This is used to detect and copy relative image links.
* Use {@link FileRegistry} to determine what path on disc this refers to.
*/
| RelativeLinkDisplayPart;

/**
* Represents an inline tag like `{@link Foo}`
*
* The `@link`, `@linkcode`, and `@linkplain` tags may have a `target`
* property set indicating which reflection/url they link to. They may also
* have a `tsLinkText` property which includes the part of the `text` which
* TypeScript thinks should be displayed as the link text.
* @category Comments
* @expand
*/
export interface InlineTagDisplayPart {
kind: "inline-tag";
Expand All @@ -51,9 +46,16 @@ export interface InlineTagDisplayPart {
}

/**
* Represents a reference to a path relative to where the comment resides.
* This is used to detect and copy relative image links.
*
* Use {@link FileRegistry} to determine what path on disc this refers to.
*
* This is used for relative links within comments/documents.
* It is used to mark pieces of text which need to be replaced
* to make links work properly.
* @category Comments
* @expand
*/
export interface RelativeLinkDisplayPart {
kind: "relative-link";
Expand Down
99 changes: 62 additions & 37 deletions src/lib/output/themes/default/partials/typeDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { DeclarationReflection, SignatureReflection } from "../../../../models/index.js";
import type { SomeType, TypeVisitor } from "../../../../models/types.js";
import {
type Reflection,
ReflectionKind,
type DeclarationReflection,
type SignatureReflection,
} from "../../../../models/index.js";
import type { ReferenceType, SomeType, TypeVisitor } from "../../../../models/types.js";
import { JSX, Raw } from "../../../../utils/index.js";
import { classNames, getKindClass } from "../../lib.js";
import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js";
Expand All @@ -17,6 +22,9 @@ const isUsefulVisitor: Partial<TypeVisitor<boolean>> = {
reflection(type) {
return renderingChildIsUseful(type.declaration);
},
reference(type) {
return shouldExpandReference(type);
},
};

function renderingTypeDetailsIsUseful(type: SomeType) {
Expand All @@ -35,6 +43,15 @@ export function typeDeclaration(context: DefaultThemeRenderContext, type: SomeTy
return null;
}

const expanded = new Set<Reflection>();
function shouldExpandReference(reference: ReferenceType) {
const target = reference.reflection;
if (!target?.kindOf(ReflectionKind.TypeAlias | ReflectionKind.Interface)) return false;
if (!target.comment?.hasModifier("@expand")) return false;

return expanded.has(target) === false;
}

export function typeDetails(context: DefaultThemeRenderContext, type: SomeType): JSX.Children {
return type.visit<JSX.Children>({
array(type) {
Expand All @@ -58,42 +75,18 @@ export function typeDetails(context: DefaultThemeRenderContext, type: SomeType):
},
reflection(type) {
const declaration = type.declaration;

return (
<ul class="tsd-parameters">
{declaration.signatures && (
<li class="tsd-parameter-signature">
<ul
class={classNames(
{ "tsd-signatures": true },
context.getReflectionClasses(declaration),
)}
>
{declaration.signatures.map((item) => (
<>
<li class="tsd-signature" id={item.anchor}>
{context.memberSignatureTitle(item, {
hideName: true,
})}
</li>
<li class="tsd-description">
{context.memberSignatureBody(item, {
hideSources: true,
})}
</li>
</>
))}
</ul>
</li>
)}
{declaration.indexSignatures?.map((index) => renderIndexSignature(context, index))}
{declaration.children?.map((child) => renderChild(context, child))}
</ul>
);
return declarationDetails(context, declaration);
},
reference() {
// TODO: Check for @expand
return null;
reference(reference) {
if (shouldExpandReference(reference)) {
const target = reference.reflection as DeclarationReflection;

// Ensure we don't go into an infinite loop here
expanded.add(target);
const details = target.type ? typeDetails(context, target.type) : declarationDetails(context, target);
expanded.delete(target);
return details;
}
},
// tuple??
});
Expand All @@ -105,6 +98,38 @@ export function typeDetailsIfUseful(context: DefaultThemeRenderContext, type: So
}
}

function declarationDetails(context: DefaultThemeRenderContext, declaration: DeclarationReflection): JSX.Children {
return (
<>
{context.commentSummary(declaration)}
<ul class="tsd-parameters">
{declaration.signatures && (
<li class="tsd-parameter-signature">
<ul class={classNames({ "tsd-signatures": true }, context.getReflectionClasses(declaration))}>
{declaration.signatures.map((item) => (
<>
<li class="tsd-signature" id={item.anchor}>
{context.memberSignatureTitle(item, {
hideName: true,
})}
</li>
<li class="tsd-description">
{context.memberSignatureBody(item, {
hideSources: true,
})}
</li>
</>
))}
</ul>
</li>
)}
{declaration.indexSignatures?.map((index) => renderIndexSignature(context, index))}
{declaration.children?.map((child) => renderChild(context, child))}
</ul>
</>
);
}

function renderChild(context: DefaultThemeRenderContext, child: DeclarationReflection) {
if (child.signatures) {
return (
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils/options/tsdoc-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const modifierTags = [
"@class",
"@enum",
"@event",
"@expand",
"@hidden",
"@hideCategories",
"@hideconstructor",
Expand Down
4 changes: 4 additions & 0 deletions tsdoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@
"tagName": "@event",
"syntaxKind": "modifier"
},
{
"tagName": "@expand",
"syntaxKind": "modifier"
},
{
"tagName": "@template",
"syntaxKind": "block",
Expand Down

0 comments on commit c1bf00b

Please sign in to comment.