diff --git a/CHANGELOG.md b/CHANGELOG.md index 649c31c6f..5040d1ab4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Bug Fixes - `@property` now works as expected if used to override a method's documentation. +- Deprecated functions/methods are now correctly rendered with a struck-out name. - `--watch` mode works again, #2378. - Improved support for optional names within JSDoc types, #2384. - Fixed duplicate rendering of reflection flags on signature parameters, #2385. diff --git a/internal-docs/components-and-events.md b/internal-docs/components-and-events.md index 4f297e451..c09cd6948 100644 --- a/internal-docs/components-and-events.md +++ b/internal-docs/components-and-events.md @@ -27,7 +27,7 @@ Components are slowly being removed from TypeDoc. `isPropagationStopped` `isDefaultPrevented` -## `@BindOption` +## `@Option` -`@BindOption` decorator can be placed on any class which has `application` or `options` fields. -It turns the field into a getter which gets the value from `this.options` or `this.application.options` +`@Option` decorator can be placed on any class which has `application` or `options` accessor. +It turns the accessor into a getter which gets the value from `this.options` or `this.application.options` diff --git a/src/index.ts b/src/index.ts index 250789f44..f1425b96b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,7 @@ export type { RenderTemplate, RendererHooks } from "./lib/output"; export { ArgumentsReader, + Option, BindOption, CommentStyle, JSX, diff --git a/src/lib/application.ts b/src/lib/application.ts index 87be27df8..b1be20478 100644 --- a/src/lib/application.ts +++ b/src/lib/application.ts @@ -21,7 +21,7 @@ import { ChildableComponent, Component, } from "./utils/component"; -import { Options, BindOption } from "./utils"; +import { Options, Option } from "./utils"; import type { TypeDocOptions } from "./utils/options/declaration"; import { unique } from "./utils/array"; import { ok } from "assert"; @@ -111,16 +111,16 @@ export class Application extends ChildableComponent< options = new Options(); /** @internal */ - @BindOption("skipErrorChecking") - readonly skipErrorChecking!: boolean; + @Option("skipErrorChecking") + accessor skipErrorChecking!: boolean; /** @internal */ - @BindOption("entryPointStrategy") - readonly entryPointStrategy!: EntryPointStrategy; + @Option("entryPointStrategy") + accessor entryPointStrategy!: EntryPointStrategy; /** @internal */ - @BindOption("entryPoints") - readonly entryPoints!: string[]; + @Option("entryPoints") + accessor entryPoints!: string[]; /** * The version number of TypeDoc. diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 4ffc9e80b..0f792cb62 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -13,7 +13,7 @@ import { import { Context } from "./context"; import { ConverterComponent } from "./components"; import { Component, ChildableComponent } from "../utils/component"; -import { BindOption, MinimalSourceFile, readFile } from "../utils"; +import { Option, MinimalSourceFile, readFile } from "../utils"; import { convertType } from "./types"; import { ConverterEvents } from "./converter-events"; import { convertSymbol } from "./symbols"; @@ -49,50 +49,53 @@ export class Converter extends ChildableComponent< ConverterComponent > { /** @internal */ - @BindOption("externalPattern") - externalPattern!: string[]; + @Option("externalPattern") + accessor externalPattern!: string[]; private externalPatternCache?: Minimatch[]; private excludeCache?: Minimatch[]; /** @internal */ - @BindOption("excludeExternals") - excludeExternals!: boolean; + @Option("excludeExternals") + accessor excludeExternals!: boolean; /** @internal */ - @BindOption("excludeNotDocumented") - excludeNotDocumented!: boolean; + @Option("excludeNotDocumented") + accessor excludeNotDocumented!: boolean; /** @internal */ - @BindOption("excludePrivate") - excludePrivate!: boolean; + @Option("excludePrivate") + accessor excludePrivate!: boolean; /** @internal */ - @BindOption("excludeProtected") - excludeProtected!: boolean; + @Option("excludeProtected") + accessor excludeProtected!: boolean; /** @internal */ - @BindOption("excludeReferences") - excludeReferences!: boolean; + @Option("excludeReferences") + accessor excludeReferences!: boolean; /** @internal */ - @BindOption("commentStyle") - commentStyle!: CommentStyle; + @Option("commentStyle") + accessor commentStyle!: CommentStyle; /** @internal */ - @BindOption("validation") - validation!: ValidationOptions; + @Option("validation") + accessor validation!: ValidationOptions; /** @internal */ - @BindOption("externalSymbolLinkMappings") - externalSymbolLinkMappings!: Record>; + @Option("externalSymbolLinkMappings") + accessor externalSymbolLinkMappings!: Record< + string, + Record + >; /** @internal */ - @BindOption("useTsLinkResolution") - useTsLinkResolution!: boolean; + @Option("useTsLinkResolution") + accessor useTsLinkResolution!: boolean; /** @internal */ - @BindOption("preserveLinkText") - preserveLinkText!: boolean; + @Option("preserveLinkText") + accessor preserveLinkText!: boolean; private _config?: CommentParserConfig; private _externalSymbolResolvers: Array = []; diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index 035b224bd..b8243326d 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 { BindOption, getSortFunction, removeIf } from "../../utils"; +import { Option, getSortFunction, removeIf } from "../../utils"; /** * A handler that sorts and categorizes the found reflections in the resolving phase. @@ -19,17 +19,17 @@ import { BindOption, getSortFunction, removeIf } from "../../utils"; export class CategoryPlugin extends ConverterComponent { sortFunction!: (reflections: DeclarationReflection[]) => void; - @BindOption("defaultCategory") - defaultCategory!: string; + @Option("defaultCategory") + accessor defaultCategory!: string; - @BindOption("categoryOrder") - categoryOrder!: string[]; + @Option("categoryOrder") + accessor categoryOrder!: string[]; - @BindOption("categorizeByGroup") - categorizeByGroup!: boolean; + @Option("categorizeByGroup") + accessor categorizeByGroup!: boolean; - @BindOption("searchCategoryBoosts") - boosts!: Record; + @Option("searchCategoryBoosts") + accessor boosts!: Record; usedBoosts = new Set(); diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index cca3ba3ba..4a2c9ec42 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -16,7 +16,7 @@ import { CommentTag, } from "../../models"; import { - BindOption, + Option, filterMap, removeIfPresent, unique, @@ -103,20 +103,20 @@ const NEVER_RENDERED = [ */ @Component({ name: "comment" }) export class CommentPlugin extends ConverterComponent { - @BindOption("excludeTags") - excludeTags!: `@${string}`[]; + @Option("excludeTags") + accessor excludeTags!: `@${string}`[]; - @BindOption("excludeInternal") - excludeInternal!: boolean; + @Option("excludeInternal") + accessor excludeInternal!: boolean; - @BindOption("excludePrivate") - excludePrivate!: boolean; + @Option("excludePrivate") + accessor excludePrivate!: boolean; - @BindOption("excludeProtected") - excludeProtected!: boolean; + @Option("excludeProtected") + accessor excludeProtected!: boolean; - @BindOption("excludeNotDocumented") - excludeNotDocumented!: boolean; + @Option("excludeNotDocumented") + accessor excludeNotDocumented!: boolean; private _excludeKinds: number | undefined; private get excludeNotDocumentedKinds(): number { diff --git a/src/lib/converter/plugins/GroupPlugin.ts b/src/lib/converter/plugins/GroupPlugin.ts index d84265c56..a9f781c5b 100644 --- a/src/lib/converter/plugins/GroupPlugin.ts +++ b/src/lib/converter/plugins/GroupPlugin.ts @@ -9,7 +9,7 @@ import { Component, ConverterComponent } from "../components"; import { Converter } from "../converter"; import type { Context } from "../context"; import { getSortFunction } from "../../utils/sort"; -import { BindOption, removeIf } from "../../utils"; +import { Option, removeIf } from "../../utils"; import { Comment } from "../../models"; /** @@ -21,11 +21,11 @@ import { Comment } from "../../models"; export class GroupPlugin extends ConverterComponent { sortFunction!: (reflections: DeclarationReflection[]) => void; - @BindOption("searchGroupBoosts") - boosts!: Record; + @Option("searchGroupBoosts") + accessor boosts!: Record; - @BindOption("groupOrder") - groupOrder!: string[]; + @Option("groupOrder") + accessor groupOrder!: string[]; usedBoosts = new Set(); diff --git a/src/lib/converter/plugins/InheritDocPlugin.ts b/src/lib/converter/plugins/InheritDocPlugin.ts index 4819b1d64..abb7bf606 100644 --- a/src/lib/converter/plugins/InheritDocPlugin.ts +++ b/src/lib/converter/plugins/InheritDocPlugin.ts @@ -10,7 +10,7 @@ import { Component, ConverterComponent } from "../components"; import { Converter } from "../converter"; import type { Context } from "../context"; import type { Reflection } from "../../models/reflections/abstract"; -import { BindOption, DefaultMap, ValidationOptions } from "../../utils"; +import { Option, DefaultMap, ValidationOptions } from "../../utils"; import { zip } from "../../utils/array"; import { parseDeclarationReference } from "../comments/declarationReference"; import { resolveDeclarationReference } from "../comments/declarationReferenceResolver"; @@ -31,8 +31,8 @@ import { ApplicationEvents } from "../../application-events"; */ @Component({ name: "inheritDoc" }) export class InheritDocPlugin extends ConverterComponent { - @BindOption("validation") - validation!: ValidationOptions; + @Option("validation") + accessor validation!: ValidationOptions; // Key is depended on by Values private dependencies = new DefaultMap(() => []); diff --git a/src/lib/converter/plugins/LinkResolverPlugin.ts b/src/lib/converter/plugins/LinkResolverPlugin.ts index cff8a77fb..148118161 100644 --- a/src/lib/converter/plugins/LinkResolverPlugin.ts +++ b/src/lib/converter/plugins/LinkResolverPlugin.ts @@ -1,7 +1,7 @@ import { Component, ConverterComponent } from "../components"; import type { Context, ExternalResolveResult } from "../../converter"; import { ConverterEvents } from "../converter-events"; -import { BindOption, ValidationOptions } from "../../utils"; +import { Option, ValidationOptions } from "../../utils"; import { DeclarationReflection, ProjectReflection } from "../../models"; import { discoverAllReferenceTypes } from "../../utils/reflections"; import { ApplicationEvents } from "../../application-events"; @@ -11,8 +11,8 @@ import { ApplicationEvents } from "../../application-events"; */ @Component({ name: "link-resolver" }) export class LinkResolverPlugin extends ConverterComponent { - @BindOption("validation") - validation!: ValidationOptions; + @Option("validation") + accessor validation!: ValidationOptions; override initialize() { super.initialize(); diff --git a/src/lib/converter/plugins/PackagePlugin.ts b/src/lib/converter/plugins/PackagePlugin.ts index 395c7c5eb..4685aad89 100644 --- a/src/lib/converter/plugins/PackagePlugin.ts +++ b/src/lib/converter/plugins/PackagePlugin.ts @@ -3,7 +3,7 @@ import * as Path from "path"; import { Component, ConverterComponent } from "../components"; import { Converter } from "../converter"; import type { Context } from "../context"; -import { BindOption, EntryPointStrategy, readFile } from "../../utils"; +import { Option, EntryPointStrategy, readFile } from "../../utils"; import { deriveRootDir, discoverInParentDir, @@ -21,17 +21,17 @@ import { join } from "path"; */ @Component({ name: "package" }) export class PackagePlugin extends ConverterComponent { - @BindOption("readme") - readme!: string; + @Option("readme") + accessor readme!: string; - @BindOption("entryPointStrategy") - entryPointStrategy!: EntryPointStrategy; + @Option("entryPointStrategy") + accessor entryPointStrategy!: EntryPointStrategy; - @BindOption("entryPoints") - entryPoints!: string[]; + @Option("entryPoints") + accessor entryPoints!: string[]; - @BindOption("includeVersion") - includeVersion!: boolean; + @Option("includeVersion") + accessor includeVersion!: boolean; /** * The file name of the found readme.md file. diff --git a/src/lib/converter/plugins/SourcePlugin.ts b/src/lib/converter/plugins/SourcePlugin.ts index 44ca1d61c..2954369ce 100644 --- a/src/lib/converter/plugins/SourcePlugin.ts +++ b/src/lib/converter/plugins/SourcePlugin.ts @@ -7,7 +7,7 @@ import { import { Component, ConverterComponent } from "../components"; import { Converter } from "../converter"; import type { Context } from "../context"; -import { BindOption, normalizePath, getCommonDirectory } from "../../utils"; +import { Option, normalizePath, getCommonDirectory } from "../../utils"; import { isNamedNode } from "../utils/nodes"; import { dirname, relative } from "path"; import { SourceReference } from "../../models"; @@ -24,23 +24,23 @@ import { BasePath } from "../utils/base-path"; */ @Component({ name: "source" }) export class SourcePlugin extends ConverterComponent { - @BindOption("disableSources") - readonly disableSources!: boolean; + @Option("disableSources") + accessor disableSources!: boolean; - @BindOption("gitRevision") - readonly gitRevision!: string; + @Option("gitRevision") + accessor gitRevision!: string; - @BindOption("gitRemote") - readonly gitRemote!: string; + @Option("gitRemote") + accessor gitRemote!: string; - @BindOption("disableGit") - readonly disableGit!: boolean; + @Option("disableGit") + accessor disableGit!: boolean; - @BindOption("sourceLinkTemplate") - readonly sourceLinkTemplate!: string; + @Option("sourceLinkTemplate") + accessor sourceLinkTemplate!: string; - @BindOption("basePath") - readonly basePath!: string; + @Option("basePath") + accessor basePath!: string; /** * All file names to find the base path from. diff --git a/src/lib/models/reflections/abstract.ts b/src/lib/models/reflections/abstract.ts index dbe7ec2a2..78c5c7dc5 100644 --- a/src/lib/models/reflections/abstract.ts +++ b/src/lib/models/reflections/abstract.ts @@ -232,6 +232,10 @@ export interface TraverseCallback { ): boolean | NeverIfInternal; } +export type ReflectionVisitor = { + [K in keyof ReflectionVariant]?: (refl: ReflectionVariant[K]) => void; +}; + /** * Base class for all reflection classes. * @@ -473,7 +477,21 @@ export abstract class Reflection { * Check if this reflection or any of its parents have been marked with the `@deprecated` tag. */ isDeprecated(): boolean { - if (this.comment?.getTag("@deprecated")) { + let signaturesDeprecated = false; + this.visit({ + declaration(decl) { + if ( + decl.signatures && + decl.signatures.every( + (sig) => sig.comment?.getTag("@deprecated"), + ) + ) { + signaturesDeprecated = true; + } + }, + }); + + if (signaturesDeprecated || this.comment?.getTag("@deprecated")) { return true; } @@ -493,6 +511,10 @@ export abstract class Reflection { */ abstract traverse(callback: TraverseCallback): void; + visit(visitor: ReflectionVisitor) { + visitor[this.variant]?.(this as never); + } + /** * Return a string representation of this reflection. */ diff --git a/src/lib/models/reflections/index.ts b/src/lib/models/reflections/index.ts index d95e34882..28cce65fe 100644 --- a/src/lib/models/reflections/index.ts +++ b/src/lib/models/reflections/index.ts @@ -4,7 +4,7 @@ export { ReflectionFlags, TraverseProperty, } from "./abstract"; -export type { TraverseCallback } from "./abstract"; +export type { TraverseCallback, ReflectionVisitor } from "./abstract"; export { ContainerReflection } from "./container"; export { DeclarationReflection, ConversionFlags } from "./declaration"; export type { DeclarationHierarchy } from "./declaration"; diff --git a/src/lib/output/plugins/AssetsPlugin.ts b/src/lib/output/plugins/AssetsPlugin.ts index 7dcc9c5e3..98fa1a97b 100644 --- a/src/lib/output/plugins/AssetsPlugin.ts +++ b/src/lib/output/plugins/AssetsPlugin.ts @@ -3,7 +3,7 @@ import { RendererEvent } from "../events"; import { copySync, writeFileSync } from "../../utils/fs"; import { DefaultTheme } from "../themes/default/DefaultTheme"; import { getStyles } from "../../utils/highlighter"; -import { BindOption } from "../../utils"; +import { Option } from "../../utils"; import { existsSync } from "fs"; import { join } from "path"; @@ -14,8 +14,8 @@ import { join } from "path"; @Component({ name: "assets" }) export class AssetsPlugin extends RendererComponent { /** @internal */ - @BindOption("customCss") - customCss!: string; + @Option("customCss") + accessor customCss!: string; /** * Create a new AssetsPlugin instance. diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 2e3697a5b..a72129682 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -8,7 +8,7 @@ import { } from "../../models"; import { Component, RendererComponent } from "../components"; import { IndexEvent, RendererEvent } from "../events"; -import { BindOption, writeFileSync } from "../../utils"; +import { Option, writeFileSync } from "../../utils"; import { DefaultTheme } from "../themes/default/DefaultTheme"; /** @@ -29,8 +29,8 @@ interface SearchDocument { */ @Component({ name: "javascript-index" }) export class JavascriptIndexPlugin extends RendererComponent { - @BindOption("searchInComments") - searchComments!: boolean; + @Option("searchInComments") + accessor searchComments!: boolean; /** * Create a new JavascriptIndexPlugin instance. diff --git a/src/lib/output/renderer.ts b/src/lib/output/renderer.ts index 19a8cda0e..0a3ab1335 100644 --- a/src/lib/output/renderer.ts +++ b/src/lib/output/renderer.ts @@ -18,7 +18,7 @@ import { writeFileSync } from "../utils/fs"; import { DefaultTheme } from "./themes/default/DefaultTheme"; import { RendererComponent } from "./components"; import { Component, ChildableComponent } from "../utils/component"; -import { BindOption, EventHooks } from "../utils"; +import { Option, EventHooks } from "../utils"; import { loadHighlighter } from "../utils/highlighter"; import type { Theme as ShikiTheme } from "shiki"; import { Reflection } from "../models"; @@ -175,36 +175,36 @@ export class Renderer extends ChildableComponent< hooks = new EventHooks(); /** @internal */ - @BindOption("theme") - themeName!: string; + @Option("theme") + accessor themeName!: string; /** @internal */ - @BindOption("cleanOutputDir") - cleanOutputDir!: boolean; + @Option("cleanOutputDir") + accessor cleanOutputDir!: boolean; /** @internal */ - @BindOption("cname") - cname!: string; + @Option("cname") + accessor cname!: string; /** @internal */ - @BindOption("githubPages") - githubPages!: boolean; + @Option("githubPages") + accessor githubPages!: boolean; /** @internal */ - @BindOption("cacheBust") - cacheBust!: boolean; + @Option("cacheBust") + accessor cacheBust!: boolean; /** @internal */ - @BindOption("lightHighlightTheme") - lightTheme!: ShikiTheme; + @Option("lightHighlightTheme") + accessor lightTheme!: ShikiTheme; /** @internal */ - @BindOption("darkHighlightTheme") - darkTheme!: ShikiTheme; + @Option("darkHighlightTheme") + accessor darkTheme!: ShikiTheme; /** @internal */ - @BindOption("pretty") - pretty!: boolean; + @Option("pretty") + accessor pretty!: boolean; renderStartTime = -1; diff --git a/src/lib/output/themes/MarkedPlugin.ts b/src/lib/output/themes/MarkedPlugin.ts index c297f21e6..f314b500f 100644 --- a/src/lib/output/themes/MarkedPlugin.ts +++ b/src/lib/output/themes/MarkedPlugin.ts @@ -4,7 +4,7 @@ import * as Marked from "marked"; import { Component, ContextAwareRendererComponent } from "../components"; import { RendererEvent, MarkdownEvent, PageEvent } from "../events"; -import { BindOption, readFile, copySync, isFile } from "../../utils"; +import { Option, readFile, copySync, isFile } from "../../utils"; import { highlight, isSupportedLanguage } from "../../utils/highlighter"; import type { Theme } from "shiki"; import { escapeHtml, getTextContent } from "../../utils/html"; @@ -15,17 +15,17 @@ import { escapeHtml, getTextContent } from "../../utils/html"; */ @Component({ name: "marked" }) export class MarkedPlugin extends ContextAwareRendererComponent { - @BindOption("includes") - includeSource!: string; + @Option("includes") + accessor includeSource!: string; - @BindOption("media") - mediaSource!: string; + @Option("media") + accessor mediaSource!: string; - @BindOption("lightHighlightTheme") - lightTheme!: Theme; + @Option("lightHighlightTheme") + accessor lightTheme!: Theme; - @BindOption("darkHighlightTheme") - darkTheme!: Theme; + @Option("darkHighlightTheme") + accessor darkTheme!: Theme; /** * The path referenced files are located in. diff --git a/src/lib/output/themes/default/partials/header.tsx b/src/lib/output/themes/default/partials/header.tsx index 38e41315b..9e434b68c 100644 --- a/src/lib/output/themes/default/partials/header.tsx +++ b/src/lib/output/themes/default/partials/header.tsx @@ -1,4 +1,4 @@ -import { getDisplayName, hasTypeParameters, join } from "../../lib"; +import { classNames, getDisplayName, hasTypeParameters, join } from "../../lib"; import { JSX } from "../../../../utils"; import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext"; import type { PageEvent } from "../../../events"; @@ -9,7 +9,7 @@ export const header = (context: DefaultThemeRenderContext, props: PageEvent {!!props.model.parent &&
    {context.breadcrumb(props.model)}
} - + {props.model.kind !== ReflectionKind.Project && `${ReflectionKind.singularString(props.model.kind)} `} {getDisplayName(props.model)} {hasTypeParameters(props.model) && ( diff --git a/src/lib/utils/component.ts b/src/lib/utils/component.ts index 800e73c55..4c84a55a3 100644 --- a/src/lib/utils/component.ts +++ b/src/lib/utils/component.ts @@ -35,8 +35,10 @@ const childMappings: { /** * Class decorator applied to Components */ -export function Component(options: ComponentOptions): ClassDecorator { - return (target: Function) => { +export function Component(options: ComponentOptions) { + // _context is ClassDecoratorContext, but that then requires a public constructor + // which Application does not have. + return (target: Function, _context: unknown) => { const proto = target.prototype; if (!(proto instanceof AbstractComponent)) { throw new Error( diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 129a3ca9f..650c89df2 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -27,6 +27,7 @@ export { ConsoleLogger, Logger, LogLevel } from "./loggers"; export { DefaultMap } from "./map"; export { ArgumentsReader, + Option, BindOption, CommentStyle, Options, diff --git a/src/lib/utils/options/index.ts b/src/lib/utils/options/index.ts index 3f7df5155..770fa44a6 100644 --- a/src/lib/utils/options/index.ts +++ b/src/lib/utils/options/index.ts @@ -1,4 +1,4 @@ -export { Options, BindOption } from "./options"; +export { Options, Option, BindOption } from "./options"; export type { OptionsReader } from "./options"; export { ArgumentsReader, diff --git a/src/lib/utils/options/options.ts b/src/lib/utils/options/options.ts index b3e546f94..c773ef2b1 100644 --- a/src/lib/utils/options/options.ts +++ b/src/lib/utils/options/options.ts @@ -431,12 +431,43 @@ export class Options { } } +/** + * Binds an option to an accessor. Does not register the option. + * + * Note: This is a standard ES decorator. It will not work with pre-TS 5.0 experimental decorators enabled. + */ +export function Option(name: K) { + return ( + _: unknown, + _context: ClassAccessorDecoratorContext< + { application: Application } | { options: Options }, + TypeDocOptionValues[K] + >, + ) => { + return { + get(this: { application: Application } | { options: Options }) { + const options = + "options" in this ? this.options : this.application.options; + const value = options.getValue(name as keyof TypeDocOptions); + + return value as TypeDocOptionValues[K]; + }, + set(_value: never) { + throw new Error( + "Options may not be set via the Option decorator", + ); + }, + }; + }; +} + /** * Binds an option to the given property. Does not register the option. * * Note: This is a legacy experimental decorator, and will not work with TS 5.0 decorators * * @since v0.16.3 + * @deprecated Will be removed in 0.26, use {@link Option | `@Option`} instead. */ export function BindOption( name: K, @@ -453,6 +484,7 @@ export function BindOption( * Note: This is a legacy experimental decorator, and will not work with TS 5.0 decorators * * @since v0.16.3 + * @deprecated Will be removed in 0.26, use {@link Option | `@Option`} instead * * @privateRemarks * This overload is intended for plugin use only with looser type checks. Do not use internally. diff --git a/src/lib/utils/perf.ts b/src/lib/utils/perf.ts index 29bc6b29b..b1e7407b8 100644 --- a/src/lib/utils/perf.ts +++ b/src/lib/utils/perf.ts @@ -4,7 +4,7 @@ import { performance } from "perf_hooks"; const benchmarks: { name: string; calls: number; time: number }[] = []; -export function bench(fn: T, name = fn.name): T { +export function bench(fn: T, name: string = fn.name): T { const matching = benchmarks.find((b) => b.name === name); const timer = matching || { name, @@ -43,11 +43,17 @@ export function bench(fn: T, name = fn.name): T { } as any; } -export function Bench(): MethodDecorator { - return function (target: any, key, descriptor) { - const rawMethod = descriptor.value as unknown as Function; - const name = `${target.name ?? target.constructor.name}.${String(key)}`; - descriptor.value = bench(rawMethod, name) as any; +export function Bench( + value: T, + context: ClassMethodDecoratorContext, +) { + let runner: T | undefined; + return function (this: any, ...args: any) { + if (!runner) { + const className = Object.getPrototypeOf(this).constructor.name; + runner = bench(value, `${className}.${String(context.name)}`); + } + return runner.apply(this, args); }; } @@ -68,7 +74,7 @@ export function measure(cb: () => T): T { return result; } -process.on("beforeExit", () => { +process.on("exit", () => { if (!benchmarks.length) return; const width = benchmarks.reduce((a, b) => Math.max(a, b.name.length), 11); diff --git a/src/test/utils/options/options.test.ts b/src/test/utils/options/options.test.ts index f53143a0d..5b4f17afd 100644 --- a/src/test/utils/options/options.test.ts +++ b/src/test/utils/options/options.test.ts @@ -1,9 +1,9 @@ import { LogLevel, Options, ParameterType } from "../../../lib/utils"; import { - BindOption, + Option, MapDeclarationOption, NumberDeclarationOption, -} from "../../../lib/utils/options"; +} from "../../../lib/utils"; import { deepStrictEqual as equal, throws } from "assert"; import type { DeclarationOption, @@ -196,12 +196,12 @@ describe("Options", () => { }); }); -describe("BindOption", () => { +describe("Option", () => { class Container { constructor(public options: Options) {} - @BindOption("emit") - emit!: EmitStrategy; + @Option("emit") + accessor emit!: EmitStrategy; } it("Supports fetching options", () => { diff --git a/static/style.css b/static/style.css index 258146fc0..f29b14450 100644 --- a/static/style.css +++ b/static/style.css @@ -1134,7 +1134,7 @@ img { } .deprecated { - text-decoration: line-through; + text-decoration: line-through !important; } .warning { diff --git a/tsconfig.json b/tsconfig.json index c1e6cf629..5de076553 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,6 @@ "tsBuildInfoFile": "dist/.tsbuildinfo", // "skipLibCheck": true, - "experimentalDecorators": true, "strict": true, "alwaysStrict": true, // For tests