From 42af237eb59789b2935d4570c423f07c5ac8da39 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 11 Apr 2025 15:51:33 -0600 Subject: [PATCH 001/119] Handle `@inline` on tuple types Resolves #2932 --- CHANGELOG.md | 4 ++++ src/lib/converter/types.ts | 10 ++++++++-- src/lib/utils-common/array.ts | 1 + src/lib/utils-common/events.ts | 1 + src/test/converter2/issues/gh2932.ts | 6 ++++++ src/test/issues.c2.test.ts | 6 ++++++ 6 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/test/converter2/issues/gh2932.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a5d49fd7a..80d055193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Bug Fixes + +- `@inline` now functions when referencing tuple types, #2932. + ## v0.28.2 (2025-04-07) ### Features diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 27a060e53..5b1820a22 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -1009,7 +1009,7 @@ const thisConverter: TypeConverter = { }, }; -const tupleConverter: TypeConverter = { +const tupleConverter = { kind: [ts.SyntaxKind.TupleType], convert(context, node) { const elements = node.elements.map((node) => convertType(context, node)); @@ -1061,7 +1061,7 @@ const tupleConverter: TypeConverter = { return new TupleType(elements ?? []); }, -}; +} satisfies TypeConverter; const supportedOperatorNames = { [ts.SyntaxKind.KeyOfKeyword]: "keyof", @@ -1265,6 +1265,12 @@ function convertTypeInlined(context: Context, type: ts.Type): SomeType { const elementType = convertType(context, context.checker.getTypeArguments(type as ts.TypeReference)[0]); return new ArrayType(elementType); } + if (isTypeReference(type) && context.checker.isTupleType(type)) { + const tupleNode = context.checker.typeToTypeNode(type.target, void 0, ts.NodeBuilderFlags.IgnoreErrors)!; + if (ts.isTupleTypeNode(tupleNode)) { + return tupleConverter.convertType(context, type as ts.TupleTypeReference, tupleNode); + } + } return typeLiteralConverter.convertType( context, diff --git a/src/lib/utils-common/array.ts b/src/lib/utils-common/array.ts index 82e85bea9..143ddd4bf 100644 --- a/src/lib/utils-common/array.ts +++ b/src/lib/utils-common/array.ts @@ -3,6 +3,7 @@ export const emptyArray: readonly [] = []; /** * Inserts an item into an array sorted by priority. If two items have the same priority, * the item will be inserted later will be placed later in the array. + * Higher priority is placed earlier in the array. * @param arr modified by inserting item. * @param item */ diff --git a/src/lib/utils-common/events.ts b/src/lib/utils-common/events.ts index 697d97d21..def5a16b9 100644 --- a/src/lib/utils-common/events.ts +++ b/src/lib/utils-common/events.ts @@ -22,6 +22,7 @@ export class EventDispatcher> { * @param event the event to listen to. * @param listener function to be called when an this event is emitted. * @param priority optional priority to insert this hook with. + * Higher priority is placed earlier in the listener array. */ on( event: K, diff --git a/src/test/converter2/issues/gh2932.ts b/src/test/converter2/issues/gh2932.ts new file mode 100644 index 000000000..653cf373b --- /dev/null +++ b/src/test/converter2/issues/gh2932.ts @@ -0,0 +1,6 @@ +/** + * @inline + */ +type Vector2D = [start: number, end: number]; + +export function doStuff([start, end]: Vector2D) {} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 2070065af..3ebed30d1 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2115,4 +2115,10 @@ describe("Issue Tests", () => { const EdgeCases = query(project, "EdgeCases"); equal(EdgeCases.typeParameters?.map(t => t.type?.toString()), ["number", undefined]); }); + + it("#2932 handles @inline on tuple types", () => { + const project = convert(); + const sig = querySig(project, "doStuff"); + equal(sig.parameters?.[0].type?.toString(), "[start: number, end: number]"); + }); }); From fff32ef261b629181de3e2c79506155557e937a2 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 11 Apr 2025 16:06:53 -0600 Subject: [PATCH 002/119] Render links to the current page Resolves #2934 --- CHANGELOG.md | 1 + src/lib/output/themes/MarkedPlugin.tsx | 6 +++ src/test/converter2/renderer/index.ts | 3 ++ src/test/converter2/typedoc.json | 8 +++- .../specs/classes/ModifiersClass.json | 46 ++++++++++++++----- .../renderer/specs/enums/Enumeration.json | 6 +-- src/test/renderer/specs/functions/box.json | 2 +- .../specs/interfaces/DisabledGroups.json | 6 +-- .../specs/interfaces/NoneCategory.json | 8 ++-- .../renderer/specs/interfaces/NoneGroup.json | 6 +-- src/test/renderer/specs/types/Nested.json | 4 +- .../renderer/specs/types/UnionComments.json | 2 +- 12 files changed, 69 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80d055193..fd81c5959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ title: Changelog ### Bug Fixes - `@inline` now functions when referencing tuple types, #2932. +- `@link` links to the current page are now rendered, #2934. ## v0.28.2 (2025-04-07) diff --git a/src/lib/output/themes/MarkedPlugin.tsx b/src/lib/output/themes/MarkedPlugin.tsx index 9df6941df..ba398a23a 100644 --- a/src/lib/output/themes/MarkedPlugin.tsx +++ b/src/lib/output/themes/MarkedPlugin.tsx @@ -195,6 +195,12 @@ export class MarkedPlugin extends ContextAwareRendererComponent { ); } } + + // If the url goes to this page, render as `#` + // to go to the top of the page. + if (url == "") { + url = "#"; + } } if (useHtml) { diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 92b7b987a..8d354b5ea 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -68,6 +68,9 @@ export class ModifiersClass { readonly read = 4; /** @deprecated */ dep = 5; + + /** #2934 same page link {@linkcode ModifiersClass} */ + constructor() {} } /** diff --git a/src/test/converter2/typedoc.json b/src/test/converter2/typedoc.json index 029de8ae6..9e56b27f9 100644 --- a/src/test/converter2/typedoc.json +++ b/src/test/converter2/typedoc.json @@ -15,7 +15,13 @@ "sourceLinkTemplate": "{path}", "excludeExternals": true, "tsconfig": "tsconfig.json", - "validation": true, + "validation": { + "invalidLink": true, + "notDocumented": false, + "notExported": true, + "rewrittenLink": true, + "unusedMergeModuleWith": true + }, "skipErrorChecking": true, "externalSymbolLinkMappings": { // used by {@link !Promise} diff --git a/src/test/renderer/specs/classes/ModifiersClass.json b/src/test/renderer/specs/classes/ModifiersClass.json index d2b69f253..17d479fa2 100644 --- a/src/test/renderer/specs/classes/ModifiersClass.json +++ b/src/test/renderer/specs/classes/ModifiersClass.json @@ -202,18 +202,42 @@ ] }, { - "div.tsd-description": { - "h4.tsd-returns-title": [ - "Returns ", - { - "tag": "a.tsd-signature-type.tsd-kind-class", - "props": { - "href": "" - }, - "children": "ModifiersClass" + "div.tsd-description": [ + { + "div.tsd-comment.tsd-typography": "

#2934 same page link ModifiersClass

\n" + }, + { + "h4.tsd-returns-title": [ + "Returns ", + { + "tag": "a.tsd-signature-type.tsd-kind-class", + "props": { + "href": "" + }, + "children": "ModifiersClass" + } + ] + }, + { + "div.tsd-comment.tsd-typography": [] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "index.ts" + }, + "children": "index.ts:73" + } + ] + } } - ] - } + } + ] } ] } diff --git a/src/test/renderer/specs/enums/Enumeration.json b/src/test/renderer/specs/enums/Enumeration.json index e5b4f9c9f..3c5e62ba9 100644 --- a/src/test/renderer/specs/enums/Enumeration.json +++ b/src/test/renderer/specs/enums/Enumeration.json @@ -61,7 +61,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:77" + "children": "index.ts:80" } ] } @@ -180,7 +180,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:79" + "children": "index.ts:82" } ] } @@ -234,7 +234,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:81" + "children": "index.ts:84" } ] } diff --git a/src/test/renderer/specs/functions/box.json b/src/test/renderer/specs/functions/box.json index b597048fc..17a67cef3 100644 --- a/src/test/renderer/specs/functions/box.json +++ b/src/test/renderer/specs/functions/box.json @@ -197,7 +197,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:110" + "children": "index.ts:113" } ] } diff --git a/src/test/renderer/specs/interfaces/DisabledGroups.json b/src/test/renderer/specs/interfaces/DisabledGroups.json index 187a56a4e..90d525305 100644 --- a/src/test/renderer/specs/interfaces/DisabledGroups.json +++ b/src/test/renderer/specs/interfaces/DisabledGroups.json @@ -101,7 +101,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:129" + "children": "index.ts:132" } ] } @@ -194,7 +194,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:130" + "children": "index.ts:133" } ] } @@ -265,7 +265,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:131" + "children": "index.ts:134" } ] } diff --git a/src/test/renderer/specs/interfaces/NoneCategory.json b/src/test/renderer/specs/interfaces/NoneCategory.json index fbc092f71..f13e3d383 100644 --- a/src/test/renderer/specs/interfaces/NoneCategory.json +++ b/src/test/renderer/specs/interfaces/NoneCategory.json @@ -119,7 +119,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:114" + "children": "index.ts:117" } ] } @@ -248,7 +248,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:119" + "children": "index.ts:122" } ] } @@ -316,7 +316,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:116" + "children": "index.ts:119" } ] } @@ -383,7 +383,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:117" + "children": "index.ts:120" } ] } diff --git a/src/test/renderer/specs/interfaces/NoneGroup.json b/src/test/renderer/specs/interfaces/NoneGroup.json index e83fc182e..8970f3e31 100644 --- a/src/test/renderer/specs/interfaces/NoneGroup.json +++ b/src/test/renderer/specs/interfaces/NoneGroup.json @@ -98,7 +98,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:122" + "children": "index.ts:125" } ] } @@ -206,7 +206,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:124" + "children": "index.ts:127" } ] } @@ -271,7 +271,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:125" + "children": "index.ts:128" } ] } diff --git a/src/test/renderer/specs/types/Nested.json b/src/test/renderer/specs/types/Nested.json index 434656ec1..add5b69d4 100644 --- a/src/test/renderer/specs/types/Nested.json +++ b/src/test/renderer/specs/types/Nested.json @@ -181,7 +181,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:85" + "children": "index.ts:88" } ] } @@ -491,7 +491,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:86" + "children": "index.ts:89" } ] } diff --git a/src/test/renderer/specs/types/UnionComments.json b/src/test/renderer/specs/types/UnionComments.json index 470df7d50..f338513ee 100644 --- a/src/test/renderer/specs/types/UnionComments.json +++ b/src/test/renderer/specs/types/UnionComments.json @@ -88,7 +88,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:99" + "children": "index.ts:102" } ] } From 81c59bcb5818b3234901e0bb854dd8428d7bae4b Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 11 Apr 2025 16:46:40 -0600 Subject: [PATCH 003/119] Fix failing test --- src/lib/models/types.ts | 3 ++- src/test/issues.c2.test.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/models/types.ts b/src/lib/models/types.ts index f1eaac8e5..14e54166e 100644 --- a/src/lib/models/types.ts +++ b/src/lib/models/types.ts @@ -6,7 +6,7 @@ import { ReflectionSymbolId } from "./ReflectionSymbolId.js"; import type { DeclarationReference } from "#utils"; import { ReflectionKind } from "./kind.js"; import { Comment, type CommentDisplayPart } from "./Comment.js"; -import { i18n, joinArray } from "#utils"; +import { i18n, joinArray, NonEnumerable } from "#utils"; import type { SignatureReflection } from "./SignatureReflection.js"; /** @@ -917,6 +917,7 @@ export class ReferenceType extends Type { preferValues = false; private _target: ReflectionSymbolId | number; + @NonEnumerable private _project: ProjectReflection | null; private constructor( diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 3ebed30d1..444762f5d 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -553,6 +553,7 @@ describe("Issue Tests", () => { }); it("#1898", () => { + app.options.setValue("validation", true); const project = convert(); app.validate(project); logger.expectMessage( From 911faff99ce0ad941c683496e5aaba1ccdf88649 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 19 Apr 2025 17:48:27 -0600 Subject: [PATCH 004/119] Resolve aliased symbols before checking exclusion Resolves #2937 --- CHANGELOG.md | 1 + src/lib/converter/context.ts | 4 ++-- src/lib/converter/converter.ts | 18 +++++++++++++----- src/lib/converter/symbols.ts | 3 ++- src/test/converter2/issues/gh2937/index.ts | 2 ++ .../converter2/issues/gh2937/not-excluded.ts | 1 + src/test/issues.c2.test.ts | 6 ++++++ 7 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 src/test/converter2/issues/gh2937/index.ts create mode 100644 src/test/converter2/issues/gh2937/not-excluded.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fd81c5959..76e5bc551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ title: Changelog - `@inline` now functions when referencing tuple types, #2932. - `@link` links to the current page are now rendered, #2934. +- Aliased symbols (re-exports) are now resolved before checking if they are excluded/external, #2937. ## v0.28.2 (2025-04-07) diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index f0a59f187..f745025a5 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -209,7 +209,7 @@ export class Context { this.addChild(reflection); } - if (symbol && this.converter.isExternal(symbol)) { + if (symbol && this.converter.isExternal(symbol, this.checker)) { reflection.setFlag(ReflectionFlag.External); } if (exportSymbol) { @@ -276,7 +276,7 @@ export class Context { } shouldIgnore(symbol: ts.Symbol) { - return this.converter.shouldIgnore(symbol); + return this.converter.shouldIgnore(symbol, this.checker); } /** diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 220dbb5c1..7da84bbcf 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -61,6 +61,7 @@ import { SourcePlugin } from "./plugins/SourcePlugin.js"; import { TypePlugin } from "./plugins/TypePlugin.js"; import { IncludePlugin } from "./plugins/IncludePlugin.js"; import { MergeModuleWithPlugin } from "./plugins/MergeModuleWithPlugin.js"; +import { resolveAliasedSymbol } from "./utils/symbols.js"; export interface ConverterEvents { begin: [Context]; @@ -330,7 +331,13 @@ export class Converter extends AbstractComponent { this.resolve(context); this.trigger(Converter.EVENT_END, context); - this._config = undefined; + + // Delete caches of options so that test usage which changes options + // doesn't have confusing behavior where tests run in isolation work + // but break when run as a batch. + delete this._config; + delete this.excludeCache; + delete this.externalPatternCache; return project; } @@ -613,12 +620,13 @@ export class Converter extends AbstractComponent { * information at this point since comment discovery hasn't happened. * @internal */ - shouldIgnore(symbol: ts.Symbol) { + shouldIgnore(symbol: ts.Symbol, checker: ts.TypeChecker) { + symbol = resolveAliasedSymbol(symbol, checker); if (this.isExcluded(symbol)) { return true; } - return this.excludeExternals && this.isExternal(symbol); + return this.excludeExternals && this.isExternal(symbol, checker); } private isExcluded(symbol: ts.Symbol) { @@ -631,11 +639,11 @@ export class Converter extends AbstractComponent { } /** @internal */ - isExternal(symbol: ts.Symbol) { + isExternal(symbol: ts.Symbol, checker: ts.TypeChecker) { this.externalPatternCache ??= new MinimatchSet(this.externalPattern); const cache = this.externalPatternCache; - const declarations = symbol.getDeclarations(); + const declarations = resolveAliasedSymbol(symbol, checker).getDeclarations(); // `undefined` has no declarations, if someone does `export default undefined` // the symbol ends up as having no declarations (the export symbol does, but diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index cbcff10c9..8ad104e3c 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -23,6 +23,7 @@ import { import { convertJsDocAlias, convertJsDocCallback } from "./jsdoc.js"; import { getHeritageTypes } from "./utils/nodes.js"; import { removeUndefined } from "./utils/reflections.js"; +import { resolveAliasedSymbol } from "./utils/symbols.js"; const symbolConverters: { [K in ts.SymbolFlags]?: ( @@ -105,7 +106,7 @@ assert( ); function _convertSymbolNow(context: Context, symbol: ts.Symbol, exportSymbol: ts.Symbol | undefined) { - if (context.shouldIgnore(symbol)) { + if (context.shouldIgnore(resolveAliasedSymbol(symbol, context.checker))) { return; } diff --git a/src/test/converter2/issues/gh2937/index.ts b/src/test/converter2/issues/gh2937/index.ts new file mode 100644 index 000000000..3b34129fa --- /dev/null +++ b/src/test/converter2/issues/gh2937/index.ts @@ -0,0 +1,2 @@ +export const excluded = true; +export { notExcluded } from "./not-excluded.js"; diff --git a/src/test/converter2/issues/gh2937/not-excluded.ts b/src/test/converter2/issues/gh2937/not-excluded.ts new file mode 100644 index 000000000..c506c4a71 --- /dev/null +++ b/src/test/converter2/issues/gh2937/not-excluded.ts @@ -0,0 +1 @@ +export const notExcluded = true; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 444762f5d..912f4d66a 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2122,4 +2122,10 @@ describe("Issue Tests", () => { const sig = querySig(project, "doStuff"); equal(sig.parameters?.[0].type?.toString(), "[start: number, end: number]"); }); + + it("#2937 resolves symbols before checking if they are excluded/external", () => { + app.options.setValue("exclude", ["!**/not-excluded.ts"]); + const project = convert(); + equal(project.children?.map(c => c.name), ["notExcluded"]); + }); }); From 58246235b3dd0f6df27795763d2f604853554794 Mon Sep 17 00:00:00 2001 From: Casey Occhialini <1508707+littlespex@users.noreply.github.com> Date: Sat, 19 Apr 2025 16:55:12 -0700 Subject: [PATCH 005/119] Add mts and cts file extension aliases (#2936) Resolves #2936 --- CHANGELOG.md | 1 + src/lib/converter/plugins/IncludePlugin.ts | 2 ++ src/lib/utils/highlighter.tsx | 5 +++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76e5bc551..281107068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ title: Changelog - `@inline` now functions when referencing tuple types, #2932. - `@link` links to the current page are now rendered, #2934. +- `@includeCode` now supports regions in TypeScript files with `.mts` and `.cts` file extensions, #2935. - Aliased symbols (re-exports) are now resolved before checking if they are excluded/external, #2937. ## v0.28.2 (2025-04-07) diff --git a/src/lib/converter/plugins/IncludePlugin.ts b/src/lib/converter/plugins/IncludePlugin.ts index 29a4cfea3..4228418e3 100644 --- a/src/lib/converter/plugins/IncludePlugin.ts +++ b/src/lib/converter/plugins/IncludePlugin.ts @@ -428,3 +428,5 @@ regionTagREsByExt["php"] = regionTagREsByExt["cs"]; regionTagREsByExt["ps1"] = regionTagREsByExt["cs"]; regionTagREsByExt["py"] = regionTagREsByExt["cs"]; regionTagREsByExt["js"] = regionTagREsByExt["ts"]; +regionTagREsByExt["mts"] = regionTagREsByExt["ts"]; +regionTagREsByExt["cts"] = regionTagREsByExt["ts"]; diff --git a/src/lib/utils/highlighter.tsx b/src/lib/utils/highlighter.tsx index 0041a2ccc..4ccc0d5b8 100644 --- a/src/lib/utils/highlighter.tsx +++ b/src/lib/utils/highlighter.tsx @@ -2,7 +2,8 @@ import * as shiki from "@gerrit0/mini-shiki"; import { JSX, unique } from "#utils"; import assert from "assert"; -const aliases = new Map(); +const tsAliases: [string, string][] = [["mts", "typescript"], ["cts", "typescript"]]; +const aliases = new Map(tsAliases); for (const lang of shiki.bundledLanguagesInfo) { for (const alias of lang.aliases || []) { aliases.set(alias, lang.id); @@ -175,7 +176,7 @@ export function getSupportedThemes(): string[] { export function isLoadedLanguage(lang: string): boolean { return ( - plaintextLanguages.includes(lang) || ignoredLanguages?.includes(lang) || highlighter?.supports(lang) || false + isSupportedLanguage(lang) || highlighter?.supports(lang) || false ); } From 209acb5b0e7dd65373946898de48d4fc6ae76054 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 19 Apr 2025 19:21:19 -0600 Subject: [PATCH 006/119] Improve error reporting for Windows path separators Resolves #2938 --- CHANGELOG.md | 1 + src/lib/cli.ts | 2 +- src/lib/internationalization/locales/en.cts | 3 +- src/lib/internationalization/locales/ja.cts | 1 - src/lib/internationalization/locales/zh.cts | 1 - src/lib/utils/entry-point.ts | 3 -- src/lib/utils/options/declaration.ts | 14 +++++- src/lib/utils/options/readers/arguments.ts | 48 +++++++++++++-------- src/test/programs.ts | 5 ++- 9 files changed, 51 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 281107068..3c6002630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ title: Changelog - `@link` links to the current page are now rendered, #2934. - `@includeCode` now supports regions in TypeScript files with `.mts` and `.cts` file extensions, #2935. - Aliased symbols (re-exports) are now resolved before checking if they are excluded/external, #2937. +- Improved error reporting when paths including Windows separators are provided as globs, #2938. ## v0.28.2 (2025-04-07) diff --git a/src/lib/cli.ts b/src/lib/cli.ts index d45f4b006..75c3abf7e 100644 --- a/src/lib/cli.ts +++ b/src/lib/cli.ts @@ -26,7 +26,7 @@ async function main() { new td.TypeDocReader(), new td.PackageJsonReader(), new td.TSConfigReader(), - new td.ArgumentsReader(300), + new td.ArgumentsReader(300).ignoreErrors(), ]); const exitCode = await run(app); diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index eed29ca14..6a8e0792a 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -200,7 +200,6 @@ export = { use_expand_or_glob_for_files_in_dir: `If you wanted to include files inside this directory, set --entryPointStrategy to expand or specify a glob`, glob_0_did_not_match_any_files: `The glob {0} did not match any files`, - glob_should_use_posix_slash: `Try replacing Windows path separators (\\) with posix path separators (/)`, entry_point_0_did_not_match_any_files_after_exclude: `The glob {0} did not match any files after applying exclude patterns`, entry_point_0_did_not_exist: `Provided entry point {0} does not exist`, @@ -218,6 +217,8 @@ export = { circular_reference_extends_0: `Circular reference encountered for "extends" field of {0}`, failed_resolve_0_to_file_in_1: `Failed to resolve {0} to a file in {1}`, + glob_0_should_use_posix_slash: + `The glob "{0}" escapes a non-special character. Glob inputs to TypeDoc may not use Windows path separators (\\), try replacing with posix path separators (/)`, option_0_can_only_be_specified_by_config_file: `The '{0}' option can only be specified via a config file`, option_0_expected_a_value_but_none_provided: `--{0} expected a value, but none was given as an argument`, unknown_option_0_may_have_meant_1: `Unknown option: {0}, you may have meant:\n\t{1}`, diff --git a/src/lib/internationalization/locales/ja.cts b/src/lib/internationalization/locales/ja.cts index 6d2a469b4..9f5ab3070 100644 --- a/src/lib/internationalization/locales/ja.cts +++ b/src/lib/internationalization/locales/ja.cts @@ -138,7 +138,6 @@ export = localeUtils.buildIncompleteTranslation({ use_expand_or_glob_for_files_in_dir: "このディレクトリ内のファイルを含める場合は、--entryPointStrategyを設定して展開するか、globを指定します。", glob_0_did_not_match_any_files: "グロブ {0} はどのファイルにも一致しませんでした", - // glob_should_use_posix_slash entry_point_0_did_not_match_any_files_after_exclude: "除外パターンを適用した後、グロブ {0} はどのファイルにも一致しませんでした", entry_point_0_did_not_exist: "指定されたエントリ ポイント {0} は存在しません", diff --git a/src/lib/internationalization/locales/zh.cts b/src/lib/internationalization/locales/zh.cts index 162be15b6..8ac1d1e55 100644 --- a/src/lib/internationalization/locales/zh.cts +++ b/src/lib/internationalization/locales/zh.cts @@ -174,7 +174,6 @@ export = localeUtils.buildIncompleteTranslation({ failed_to_resolve_0_to_ts_path: "无法将 package.json 中的入口点 {0} 解析至 TypeScript 源文件", use_expand_or_glob_for_files_in_dir: "如果要包含此目录中的文件,请设置 --entryPointStrategy 以展开或指定 glob", glob_0_did_not_match_any_files: "glob {0} 与任何文件均不匹配", - glob_should_use_posix_slash: `请将 Windows 路径分隔符(\\)替换为 POSIX 路径分隔符(/)`, entry_point_0_did_not_match_any_files_after_exclude: "应用排除模式后,glob {0} 没有匹配任何文件", entry_point_0_did_not_exist: "提供的入口点 {0} 不存在", entry_point_0_did_not_match_any_packages: "入口点 glob {0} 与任何包含 package.json 的目录不匹配", diff --git a/src/lib/utils/entry-point.ts b/src/lib/utils/entry-point.ts index badb348fe..4a28627e1 100644 --- a/src/lib/utils/entry-point.ts +++ b/src/lib/utils/entry-point.ts @@ -372,9 +372,6 @@ function expandGlobs(globs: GlobString[], exclude: GlobString[], logger: Logger) logger.warn( i18n.glob_0_did_not_match_any_files(entry), ); - if (entry.includes("\\") && !entry.includes("/")) { - logger.info(i18n.glob_should_use_posix_slash()); - } } else if (filtered.length === 0) { logger.warn( i18n.entry_point_0_did_not_match_any_files_after_exclude( diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 6a8c476b4..454bcae84 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -769,7 +769,19 @@ const converters: { return resolved; }, [ParameterType.GlobArray](value, option, configPath) { - const toGlobString = (v: unknown) => createGlobString(configPath, String(v)); + const toGlobString = (v: unknown) => { + const s = String(v); + + // If the string tries to escape a character which isn't a special + // glob character, the user probably provided a Windows style path + // by accident due to shell completion, tell them to either remove + // the useless escape or switch to Unix path separators. + if (/\\[^?*()[\]\\{}]/.test(s)) { + throw new Error(i18n.glob_0_should_use_posix_slash(s)); + } + + return createGlobString(configPath, s); + }; const globs = Array.isArray(value) ? value.map(toGlobString) : [toGlobString(value)]; option.validate?.(globs); return globs; diff --git a/src/lib/utils/options/readers/arguments.ts b/src/lib/utils/options/readers/arguments.ts index bc8ceb2e5..e7a30c73f 100644 --- a/src/lib/utils/options/readers/arguments.ts +++ b/src/lib/utils/options/readers/arguments.ts @@ -18,12 +18,18 @@ export class ArgumentsReader implements OptionsReader { readonly order: number; readonly supportsPackages = false; private args: string[]; + private skipErrorReporting = false; constructor(priority: number, args = process.argv.slice(2)) { this.order = priority; this.args = args; } + ignoreErrors() { + this.skipErrorReporting = true; + return this; + } + read(container: Options, logger: Logger): void { // Make container's type more lax, we do the appropriate checks manually. const options = container as Options & { @@ -38,7 +44,9 @@ export class ArgumentsReader implements OptionsReader { options.setValue(name, value); } catch (err) { ok(err instanceof Error); - logger.error(err.message as TranslatedString); + if (!this.skipErrorReporting) { + logger.error(err.message as TranslatedString); + } } }; @@ -50,11 +58,13 @@ export class ArgumentsReader implements OptionsReader { if (decl) { if (decl.configFileOnly) { - logger.error( - i18n.option_0_can_only_be_specified_by_config_file( - decl.name, - ), - ); + if (!this.skipErrorReporting) { + logger.error( + i18n.option_0_can_only_be_specified_by_config_file( + decl.name, + ), + ); + } continue; } @@ -81,11 +91,13 @@ export class ArgumentsReader implements OptionsReader { } else { if (index === this.args.length) { // Only boolean values have optional values. - logger.warn( - i18n.option_0_expected_a_value_but_none_provided( - decl.name, - ), - ); + if (!this.skipErrorReporting) { + logger.warn( + i18n.option_0_expected_a_value_but_none_provided( + decl.name, + ), + ); + } } trySet(decl.name, this.args[index]); } @@ -115,12 +127,14 @@ export class ArgumentsReader implements OptionsReader { } } - logger.error( - i18n.unknown_option_0_may_have_meant_1( - name, - options.getSimilarOptions(name).join("\n\t"), - ), - ); + if (!this.skipErrorReporting) { + logger.error( + i18n.unknown_option_0_may_have_meant_1( + name, + options.getSimilarOptions(name).join("\n\t"), + ), + ); + } index++; } } diff --git a/src/test/programs.ts b/src/test/programs.ts index 9ccecece5..8c6a51110 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -15,7 +15,7 @@ import { createAppForTesting } from "../lib/application.js"; import { existsSync } from "fs"; import { clearCommentCache } from "../lib/converter/comments/index.js"; import { diagnostics } from "../lib/utils/loggers.js"; -import { readFile } from "#node-utils"; +import { normalizePath, readFile } from "#node-utils"; let converterApp: Application | undefined; let converterProgram: ts.Program | undefined; @@ -163,7 +163,8 @@ export function getConverter2Project(entries: string[], folder: string) { join(base, folder, entry), ].find(existsSync) ) - .filter((x) => x !== undefined); + .filter((x) => x !== undefined) + .map(normalizePath); const files = entryPoints.map((e) => program.getSourceFile(e)); for (const [index, file] of files.entries()) { From 066a6c9e875a52610626778aeb3d5354d6ab0d85 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 19 Apr 2025 19:24:24 -0600 Subject: [PATCH 007/119] Bump version to 0.28.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b05dd0af..d245a6844 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.2", + "version": "0.28.3", "homepage": "https://typedoc.org", "type": "module", "exports": { From e75f47fde35bec0df56d8719c0bc50999953134b Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Sun, 20 Apr 2025 01:25:15 +0000 Subject: [PATCH 008/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c6002630..d00c8081b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.3 (2025-04-20) + ### Bug Fixes - `@inline` now functions when referencing tuple types, #2932. From 0e33e8216735502e4a7448454649c6c7fd962c99 Mon Sep 17 00:00:00 2001 From: Andre Wachsmuth Date: Thu, 24 Apr 2025 05:00:55 +0200 Subject: [PATCH 009/119] Add localization for German (#2941) * Add localization for German * format file --------- Co-authored-by: Andre Wachsmuth --- src/lib/internationalization/locales/de.cts | 572 ++++++++++++++++++++ src/test/internationalization.test.ts | 1 + 2 files changed, 573 insertions(+) create mode 100644 src/lib/internationalization/locales/de.cts diff --git a/src/lib/internationalization/locales/de.cts b/src/lib/internationalization/locales/de.cts new file mode 100644 index 000000000..1bd6e7413 --- /dev/null +++ b/src/lib/internationalization/locales/de.cts @@ -0,0 +1,572 @@ +// Please DO NOT include machine generated translations here. +// If adding a new key, leave it commented out for a native speaker +// to update. + +import localeUtils = require("../locale-utils.cjs"); + +export = localeUtils.buildIncompleteTranslation({ + loaded_multiple_times_0: + "TypeDoc wurde mehrfach geladen. Das wird oft von Plugins verursacht, die auch TypeDoc installiert haben. Die Pfade, von denen TypeDoc geladen wurde, sind:\n\t{0}", + unsupported_ts_version_0: + "Sie verwenden eine Version von TypeScript, die nicht unterstützt wird! Stürzt TypeDoc ab, ist das der Grund. TypeDoc unterstützt {0}", + no_compiler_options_set: + "Keine Compiler-Optionen gesetzt. Das bedeutet wahrscheinlich, dass TypeDoc die tsconfig.json nicht finden konnte. Die generierte Dokumentation wird wahrscheinlich leer sein", + + loaded_plugin_0: "Plugin {0} geladen", + + solution_not_supported_in_watch_mode: + "Die angegebene tsconfig-Datei sieht nach einer Solution-Style-tsconfig aus, die nicht im Watch-Modus unterstützt wird", + strategy_not_supported_in_watch_mode: + "entryPointStrategy muss für den Watch-Modus entweder auf resolve oder expand gesetzt werden", + file_0_changed_restarting: "Konfigurationsdatei {0} wurde verändert: Kompletter Neustart erforderlich...", + file_0_changed_rebuilding: "Datei {0} wurde verändert: Baue Ausgabe neu...", + found_0_errors_and_1_warnings: "{0} Fehler und {1} Warnungen gefunden", + + output_0_could_not_be_generated: "{0}-Ausgabe konnte aufgrund obiger Fehler nicht erstellt werden", + output_0_generated_at_1: "{0} wurde generiert in {1}", + + no_entry_points_for_packages: + "Keine Einstiegspunkte für den packages-Modus angegeben, Dokumentation kann nicht generiert werden", + failed_to_find_packages: + "Konnte keine Packages finden, stellen Sie sicher, dass mindestens ein Verzeichnis mit einer package.json als Einstiegspunkt angegeben wurde", + nested_packages_unsupported_0: + "Projekt unter {0} hat die entryPointStrategy auf packages gesetzt, aber geschachtelte Packages werden nicht unterstützt", + package_option_0_should_be_specified_at_root: + "Die Option packageOptions setzt die Option {0}, welche nur auf Root-Ebene eine Auswirkung hat", + previous_error_occurred_when_reading_options_for_0: + "Der vorangegangene Fehler trat auf, als die Optionen für das Package unter {0} gelesen wurden", + converting_project_at_0: "Konvertiere Projekt unter {0}", + failed_to_convert_packages: + "Konnte ein oder mehrere Packages nicht konvertieren, Ergebnisse werden nicht zusammengeführt", + merging_converted_projects: "Führe konvertierte Projekte zusammen", + + no_entry_points_to_merge: "Keine Einstiegspunkte zum Zusammenführen angegeben", + entrypoint_did_not_match_files_0: "Der Glob {0} für den Einstiegspunkt passte auf keine Dateien", + failed_to_parse_json_0: "Konnte Datei unter {0} nicht als JSON parsen", + + failed_to_read_0_when_processing_document_tag_in_1: + "Fehler beim Einlesen der Datei {0} während der Verarbeitung des @document-Tags vom Kommentar in {1}", + failed_to_read_0_when_processing_project_document: + "Fehler beim Einlesen der Datei {0} während des Hinzufügens des Projekt-Dokuments", + failed_to_read_0_when_processing_document_child_in_1: + "Fehler beim Einlesen der Datei {0} während der Verarbeitung der Dokument-Kindelemente in {1}", + frontmatter_children_0_should_be_an_array_of_strings_or_object_with_string_values: + "Kinder der Frontmatter in {0} sollten entweder ein Array von Strings oder ein Objekt mit String-Werten sein", + converting_union_as_interface: + "Nutzung von @interface auf einem Union-Typ verwirft alle Eigenschaften, die nicht in allen Teilen der Union vorhanden sind. TypeDocs Ausgabe spiegelt möglicherweise den Quellcode nicht korrekt wider.", + converting_0_as_class_requires_value_declaration: + "Konvertierung von {0} als Klasse erfordert eine Klassen-Deklaration, die einen Wert und nicht nur einen Typ darstellt", + converting_0_as_class_without_construct_signatures: + "{0} wird als Klasse konvertiert, hat aber keine Konstruktor-Signaturen", + + comment_for_0_should_not_contain_block_or_modifier_tags: + "Das Kommentar für {0} sollte keine Block- oder Modifier-Tags enthalten", + + symbol_0_has_multiple_declarations_with_comment: + "{0} hat mehrere Deklarationen mit Kommentaren. Ein beliebiges Kommentar wird verwendet werden", + comments_for_0_are_declared_at_1: "Die Kommentare für {0} sind deklariert in:\n\t{1}", + + // comments/parser.ts + multiple_type_parameters_on_template_tag_unsupported: + "TypeDoc unterstützt mehrfache Typenparameter nicht, wenn diese in einem einzelnen @template-Tag mit Kommentar definiert sind", + failed_to_find_jsdoc_tag_for_name_0: + "Konnte JSDoc-Tag für {0} nach dem Parsen der Kommentare nicht finden, bitte erstellen Sie einen Bug-Report", + relative_path_0_is_not_a_file_and_will_not_be_copied_to_output: + "Der relative Pfad {0} ist keine Datei und wird daher nicht mit in das Ausgabeverzeichnis kopiert", + + inline_inheritdoc_should_not_appear_in_block_tag_in_comment_at_0: + "Inline-@inheritDoc-Tag sollte nicht innerhalb eines Block-Tags verwendet werden. Solche Tags im Kommentar unter {0} können nicht verarbeitet werden", + at_most_one_remarks_tag_expected_in_comment_at_0: + "Höchstens ein @remarks-Tag darf in einem Kommentar verwendet werden. Alle außer dem ersten Tag im Kommentar unter {0} werden ignoriert", + at_most_one_returns_tag_expected_in_comment_at_0: + "Höchstens ein @returns-Tag darf in einem Kommentar verwendet werden. Alle außer dem ersten Tag im Kommentar unter {0} werden ignoriert", + at_most_one_inheritdoc_tag_expected_in_comment_at_0: + "Höchstens ein @inheritDoc-Tag darf in einem Kommentar verwendet werden. Alle außer dem ersten Tag im Kommentar unter {0} werden ignoriert", + content_in_summary_overwritten_by_inheritdoc_in_comment_at_0: + "Inhalt in der Zusammenfassung des Kommentars unter {0} wird vom @inheritDoc-Tag überschrieben werden", + content_in_remarks_block_overwritten_by_inheritdoc_in_comment_at_0: + "Inhalt im @remarks-Block des Kommentars unter {0} wird vom @inheritDoc-Tag überschrieben werden", + example_tag_literal_name: + "Die erste Zeile eines @example-Tags wird wortwörtlich als Name des Beispiels interpretiert und sollte nur Text enthalten", + inheritdoc_tag_properly_capitalized: "Der @inheritDoc-Tag sollte korrekte Groß- und Kleinschreibung verwenden", + treating_unrecognized_tag_0_as_modifier: "Behandle unerkannten Tag {0} als Modifier-Tag", + unmatched_closing_brace: "Nicht übereinstimmende schließende Klammern", + unescaped_open_brace_without_inline_tag: "Unmaskierte öffnende Klammer ohne Inline-Tag vorgefunden", + unknown_block_tag_0: "Unbekannter Block-Tag {0} vorgefunden", + unknown_inline_tag_0: "Unbekannter Inline-Tag {0} vorgefunden", + open_brace_within_inline_tag: + "Öffnende Klammer innerhalb eines Inline-Tags vorgefunden, das ist wahrscheinlich ein Fehler", + inline_tag_not_closed: "Inline-Tag wurde nicht geschlossen", + + // validation + failed_to_resolve_link_to_0_in_comment_for_1: `Konnte Link zu "{0}" im Kommentar für {1} nicht auflösen`, + failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2: + `Konnte Link zu "{0}" im Kommentar für {1} nicht auflösen. Meinten Sie vielleicht "{2}"`, + failed_to_resolve_link_to_0_in_readme_for_1: `Konnte Link zu "{0}" in Readme für {1} nicht auflösen`, + failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2: + `Konnte Link zu "{0}" in Readme für {1} nicht auflösen. Meinten Sie vielleicht "{2}"`, + failed_to_resolve_link_to_0_in_document_1: `Konnte Link zu "{0}" im Dokument {1} nicht auflösen`, + failed_to_resolve_link_to_0_in_document_1_may_have_meant_2: + `Konnte Link zu "{0}" im Dokument {1} nicht auflösen. Meinten Sie vielleicht "{2}"`, + type_0_defined_in_1_is_referenced_by_2_but_not_included_in_docs: + "{0}, definiert in {1}, wird referenziert von {2}, ist aber nicht in der Dokumentation enthalten", + reflection_0_kind_1_defined_in_2_does_not_have_any_documentation: + "{0} ({1}), definiert in {2}, hat keinerlei Dokumentation", + invalid_intentionally_not_documented_names_0: + "Die folgenden qualifizierten Reflection-Namen wurden absichtlich als undokumentiert markiert, wurden aber entweder in der Dokumentation nicht referenziert oder werden dokumentiert:\n\t{0}", + invalid_intentionally_not_exported_symbols_0: + "Die folgenden Symbole wurden absichtlich als nicht exportiert markiert, wurden aber entweder in der Dokumentation nicht referenziert oder werden dokumentiert:\n\t{0}", + reflection_0_has_unused_mergeModuleWith_tag: + "{0} hat einen @mergeModuleWith-Tag, der nicht aufgelöst werden konnte", + reflection_0_links_to_1_with_text_2_but_resolved_to_3: + `"{0}" verlinkt auf "{1}" mit Text "{2}", welcher zwar existiert, aber keinen Link in der Dokumentation hat. Verlinke stattdessen auf "{3}"`, + + // conversion plugins + not_all_search_category_boosts_used_0: + "Nicht alle in searchCategoryBoosts angegebenen Kategorien werden in der Dokumentation verwendet. Die unbenutzten Kategorien sind:\n\t{0}", + not_all_search_group_boosts_used_0: + "Nicht alle in searchGroupBoosts angegebenen Gruppen werden in der Dokumentation verwendet. Die unbenutzten Gruppen sind:\n\t{0}", + comment_for_0_includes_categoryDescription_for_1_but_no_child_in_group: + `Kommentar für {0} enthält @categoryDescription für "{1}", aber kein Kind wurde in dieser Kategorie platziert`, + comment_for_0_includes_groupDescription_for_1_but_no_child_in_group: + `Kommentar für {0} enthält @groupDescription für "{1}", aber kein Kind wurde in dieser Gruppe platziert`, + label_0_for_1_cannot_be_referenced: + `Das Label "{0}" für {1} kann nicht mit einer Deklarationsreferenz referenziert werden. Labels dürfen nur A-Z, 0-9 sowie _ enthalten und dürfen nicht mit einer Ziffer beginnen`, + modifier_tag_0_is_mutually_exclusive_with_1_in_comment_for_2: + "Der Modifier-Tag {0} darf nicht gleichzeitig mit {1} verwendet werden im Kommentar für {2}", + signature_0_has_unused_param_with_name_1: + `Die Signatur {0} enthält einen @param mit Namen "{1}", der nicht verwendet wird`, + declaration_reference_in_inheritdoc_for_0_not_fully_parsed: + "Deklarationsreferenz in @inheritDoc für {0} wurde nicht vollständig geparst und wird möglicherweise falsch aufgelöst werden", + failed_to_find_0_to_inherit_comment_from_in_1: + `Konnte "{0}" zum Erben des Kommentars nicht finden. Betrifft Kommentar für {1}`, + reflection_0_tried_to_copy_comment_from_1_but_source_had_no_comment: + "{0} hat versucht, ein Kommentar von {1} mit @inheritDoc zu kopieren, aber die Quelle hat kein zugehöriges Kommentar", + inheritdoc_circular_inheritance_chain_0: "@inheritDoc spezifiziert eine zyklische Vererbungskette: {0}", + provided_readme_at_0_could_not_be_read: "Angegebener README-Pfad {0} konnte nicht gelesen werden", + defaulting_project_name: + 'Die Option --name wurde nicht angegeben und kein package.json wurde gefunden. Verwende "Dokumentation" als Rückfallwert für den Projektnamen', + disable_git_set_but_not_source_link_template: + "disableGit wurde gesetzt, aber sourceLinkTemplate nicht, sodass Links auf die Quellcode-Dateien nicht erstellt werden können. Setzen Sie sourceLinkTemplate oder disableSources, um das Ermitteln der Quellcode-Dateien zu deaktivieren", + disable_git_set_and_git_revision_used: + "disableGit wurde gesetzt und sourceLinkTemplate enthält {gitRevision}, was mit dem Leerstring ersetzt wird, da keine Revision angegeben wurde", + git_remote_0_not_valid: + `Das angegebene Git-Remote "{0}" war nicht gültig. Links auf Quellcode-Dateien werden nicht funktionieren`, + reflection_0_tried_to_merge_into_child_1: + "Die Reflection {0} versuchte mittels @mergeModuleWith, sich in eines ihrer Kinder einzufügen: {1}", + + include_0_in_1_specified_2_resolved_to_3_does_not_exist: + `{0}-Tag im Kommentar für {1} gab "{2}" zum Einbinden an, was zu "{3}" aufgelöst wurde und nicht existiert oder keine Datei ist.`, + include_0_in_1_specified_2_circular_include_3: + `{0}-Tag im Kommentar für {1} gab "{2}" zum Einbinden an, was in einer zyklischen Einbindung resultierte:\n\t{3}`, + include_0_tag_in_1_specified_2_file_3_region_4_region_not_found: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an, aber die Region wurde nicht in der Datei gefunden.`, + include_0_tag_in_1_region_2_region_not_supported: + `{0}-Tag in {1} gab "{2}" an, aber Regionen werden für die Dateierweiterung nicht unterstützt.`, + include_0_tag_in_1_specified_2_file_3_region_4_region_close_not_found: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an, aber das Kommentar zum Schließen der Region wurde nicht in der Datei gefunden.`, + include_0_tag_in_1_specified_2_file_3_region_4_region_open_not_found: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an, aber das Kommentar zum Öffnen einer Region wurde nicht in der Datei gefunden.`, + include_0_tag_in_1_specified_2_file_3_region_4_region_close_found_multiple_times: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an, aber das Kommentar zum Schließen der Region wurde mehrfach in der Datei gefunden.`, + include_0_tag_in_1_specified_2_file_3_region_4_region_open_found_multiple_times: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an, aber das Kommentar zum Öffnen der Region wurde mehrfach in der Datei gefunden.`, + include_0_tag_in_1_specified_2_file_3_region_4_region_found_multiple_times: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an, aber die Region wurde mehrfach in der Datei gefunden.`, + include_0_tag_in_1_specified_2_file_3_region_4_region_empty: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Region mit Label "{4}" aus Datei "{3}" an. Die Region wurde gefunden, ist aber leer oder enthält nur Leerzeichen.`, + include_0_tag_in_1_specified_2_file_3_lines_4_invalid_range: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Zeilen {4} aus Datei "{3}" an, aber ein ungültiges Intervall wurde angegeben.`, + include_0_tag_in_1_specified_2_file_3_lines_4_but_only_5_lines: + `{0}-Tag in {1} gab "{2}" zum Einbinden der Zeilen {4} aus Datei "{3}" an, aber die Datei hat nur {5} Zeilen.`, + + // output plugins + custom_css_file_0_does_not_exist: "Eigene CSS-Datei unter {0} existiert nicht", + custom_js_file_0_does_not_exist: "Eigene JavaScript-Datei unter {0} existiert nicht", + unsupported_highlight_language_0_not_highlighted_in_comment_for_1: + "Sprache {0} unterstützt keine Syntaxhervorhebung und wird im Kommentar {1} nicht hervorgehoben", + unloaded_language_0_not_highlighted_in_comment_for_1: + "Code-Block mit Sprache {0} wird keine Syntaxhervorhebung im Kommentar für {1} erfahren, da diese Sprache nicht in der Option highlightLanguages enthalten ist", + yaml_frontmatter_not_an_object: "Erwartete ein Objekt für die YAML-Frontmatter", + + // renderer + could_not_write_0: "{0} konnte nicht geschrieben werden", + could_not_empty_output_directory_0: "Ausgabeverzeichnis {0} konnte nicht geleert werden", + could_not_create_output_directory_0: "Konnte das Ausgabeverzeichnis {0} nicht erstellen", + theme_0_is_not_defined_available_are_1: `Das Theme '{0}' ist nicht definiert. Verfügbare Themes sind: {1}`, + router_0_is_not_defined_available_are_1: `Der Router '{0}' ist nicht definiert. Verfügbare Router sind: {1}`, + reflection_0_links_to_1_but_anchor_does_not_exist_try_2: + "{0} verlinkt auf {1}, aber der Anker existiert nicht. Meinten Sie vielleicht:\n\t{2}", + + // entry points + no_entry_points_provided: + "Einstiegspunkte wurden weder angegeben noch konnten sie aus den package.json-Exports ermittelt werden. Das ist wahrscheinlich eine Fehlerkonfiguration", + unable_to_find_any_entry_points: "Konnte keine Einstiegspunkte finden. Beachte auch die vorigen Warnmeldungen", + watch_does_not_support_packages_mode: "Watch-Modus unterstützt Einstiegspunkte der Art 'packages' nicht", + watch_does_not_support_merge_mode: "Watch-Modus unterstützt Einstiegspunkte der Art 'merge' nicht", + entry_point_0_not_in_program: + `Der Einstiegspunkt {0} wird nicht von der Option 'files' oder 'include' in der tsconfig referenziert`, + failed_to_resolve_0_to_ts_path: + "Konnte den Einstiegspunktpfad {0} der package.json nicht zu einer TypeScript-Quellcode-Datei auflösen", + use_expand_or_glob_for_files_in_dir: + 'Falls Sie Dateien aus diesem Verzeichnis einbinden wollten, setzen Sie die --entryPointStrategy auf "expand" oder geben Sie einen Glob an', + glob_0_did_not_match_any_files: "Der Glob {0} passte auf keine Dateien", + entry_point_0_did_not_match_any_files_after_exclude: + "Der Glob {0} passte auf keine Dateien mehr, nachdem die Exclude-Patterns angewandt wurden", + entry_point_0_did_not_exist: "Angegebener Einstiegspunkt {0} existiert nicht", + entry_point_0_did_not_match_any_packages: + "Der Einstiegspunkt-Glob {0} passte auf keine Verzeichnisse mit einer package.json-Datei", + file_0_not_an_object: "Die Datei {0} ist kein Objekt", + + // deserialization + serialized_project_referenced_0_not_part_of_project: + "Serialisiertes Projekt referenziert Reflection {0}, welche kein Teil des Projekts ist", + saved_relative_path_0_resolved_from_1_is_not_a_file: + "Serialisiertes Projekt referenziert {0}, was relativ zu {1} nicht existiert", + + // options + circular_reference_extends_0: `Zyklische Referenz im "extends"-Feld von {0} gefunden`, + failed_resolve_0_to_file_in_1: "Konnte {0} in {1} nicht zu einer Datei auflösen", + + glob_0_should_use_posix_slash: + `Der Glob "{0}" maskiert nichtspezielle Zeichen. Glob-Eingaben für TypeDoc dürfen keine Windows-Pfadtrennzeichen (\\) verwenden, nutzen Sie stattdessen Posix-Pfadtrennzeichen (/)`, + option_0_can_only_be_specified_by_config_file: + `Die Option '{0}' darf nur in einer Konfigurationsdatei angegeben werden`, + option_0_expected_a_value_but_none_provided: "--{0} erwartet einen Wert, aber keiner wurde als Argument übergeben", + unknown_option_0_may_have_meant_1: "Unbekannte Option: {0}, meinten Sie vielleicht:\n\t{1}", + + typedoc_key_in_0_ignored: + `Das Feld 'typedoc' in {0} wurde von der entryPointStrategy "legacy-packages" verwendet und wird ignoriert`, + typedoc_options_must_be_object_in_0: + `Konnte das Feld "typedocOptions" in {0} nicht parsen, stellen Sie sicher, dass es existiert und ein Objekt enthält`, + tsconfig_file_0_does_not_exist: "Die tsconfig-Datei {0} existiert nicht", + tsconfig_file_specifies_options_file: + `"typedocOptions" in der tsconfig-Datei gibt eine einzulesende Datei mit Optionen an, aber die Optionsdatei wurde schon eingelesen. Das ist wahrscheinlich ein Konfigurationsfehler`, + tsconfig_file_specifies_tsconfig_file: + `"typedocOptions" in der tsconfig-Datei darf keine tsconfig-Datei zum Einlesen angeben`, + tags_0_defined_in_typedoc_json_overwritten_by_tsdoc_json: + "Die {0} aus der typedoc.json werden durch die Konfiguration in der tsdoc.json überschrieben", + failed_read_tsdoc_json_0: "Konnte tsdoc.json-Datei unter {0} nicht lesen", + invalid_tsdoc_json_0: "Die Datei {0} ist keine gültige tsdoc.json-Datei", + + options_file_0_does_not_exist: "Die Optionsdatei {0} existiert nicht", + failed_read_options_file_0: + "Konnte {0} nicht parsen, stellen Sie sicher, dass die Datei existiert und ein Objekt exportiert", + + // plugins + invalid_plugin_0_missing_load_function: "Ungültige Struktur im Plugin {0}, keine load-Funktion gefunden", + plugin_0_could_not_be_loaded: "Das Plugin {0} konnte nicht geladen werden", + + // option declarations help + help_options: + "JSON-Datei mit Optionen, die geladen werden soll. Ist keine angegeben, schaut TypeDoc nach einer 'typedoc.json' im aktuellen Verzeichnis", + help_tsconfig: + "TypeScript-Konfigurationsdatei, die geladen werden soll. Ist keine angegeben, schaut TypeDoc nach einer 'tsconfig.json' im aktuellen Verzeichnis", + help_compilerOptions: "Ausgewählte TypeScript-Compiler-Optionen überschreiben, die von TypeDoc genutzt werden", + help_lang: "Setzt die Sprache für die generierte Dokumentation und für die von TypeDoc ausgegebenen Meldungen", + help_locales: + "Fügt Übersetzungen für eine bestimmte Sprache hinzu. Die Option ist hauptsächlich als Überbrückung gedacht, bis TypeDoc die Sprache offiziell unterstützt", + help_packageOptions: + "Setzt Optionen, die innerhalb jedes Packages verwendet werden, falls die entryPointStrategy auf packages gesetzt ist", + + help_entryPoints: "Die Einstiegspunkte der Dokumentation", + help_entryPointStrategy: "Die zu nutzende Strategie, um die Einstiegspunkte in Dokumentationsmodule umzuwandeln", + help_alwaysCreateEntryPointModule: + "Falls gesetzt, erstellt TypeDoc immer ein `Modul` für Einstiegspunkte, selbst wenn nur eins angegeben wurde", + help_projectDocuments: + "Dokumente, die als Kinder zur Root-Ebene der generierten Dokumentation hinzugefügt werden sollen. Unterstützt Globs, um mehrere Dateien zu selektieren", + help_exclude: + "Patterns zum Ausschließen von Dateien, wenn nach Dateien in einem Verzeichnis gesucht wird, das als Einstiegspunkt angegeben wurde", + help_externalPattern: "Patterns für Dateien, die als extern betrachtet werden sollen", + help_excludeExternals: "Verhindert die Dokumentation von als extern aufgelösten Symbolen", + help_excludeNotDocumented: + "Verhindert, dass Symbole in der Dokumentation erscheinen, die nicht explizit dokumentiert wurden", + help_excludeNotDocumentedKinds: "Arten von Reflections, die von excludeNotDocumented entfernt werden können", + help_excludeInternal: "Verhindert, dass Symbole in der Dokumentation erscheinen, die mit @internal markiert sind", + help_excludeCategories: "Schließt Symbole aus dieser Kategorie von der Dokumentation aus", + help_excludePrivate: "Ignoriert private Variablen und Methoden, Standardwert ist true.", + help_excludeProtected: "Ignoriert geschützte Variablen und Methoden", + help_excludeReferences: "Wird ein Symbol mehrfach exportiert, ignoriere alle außer dem ersten Export", + help_externalSymbolLinkMappings: + "Definiert eigene Links für Symbole, die nicht in der Dokumentation enthalten sind", + help_out: + "Gibt den Pfad an, wohin die Dokumentation für die Default-Ausgabe geschrieben werden soll. Der Standard-Ausgabetyp kann von Plugins geändert werden.", + help_html: "Gibt den Pfad an, wohin die HTML-Dokumentation geschrieben werden soll.", + help_json: + "Gibt den Pfad und den Dateinamen an, wohin eine JSON-Datei mit einer Beschreibung des Projekts geschrieben werden soll", + help_pretty: "Gibt an, ob die JSON-Datei mit Tabs formatiert werden soll", + help_emit: "Gibt an, was TypeDoc ausgeben soll, 'docs', 'both', oder 'none'", + help_theme: "Gibt den Namen des Themes an, mit dem die Dokumentation erstellt werden soll", + help_router: "Gibt den Namen des Routers an, der zum Ermitteln der Dateinamen in der Dokumentation verwendet wird", + help_lightHighlightTheme: "Gibt das Theme für die Syntaxhervorhebung im Light-Modus an", + help_darkHighlightTheme: "Gibt das Theme für die Syntaxhervorhebung im Dark-Modus an", + help_highlightLanguages: "Gibt die Sprachen an, die geladen werden sollen, um Code bei der Ausgabe hervorzuheben", + help_ignoredHighlightLanguages: + "Gibt Sprachen an, welche als gültige Sprache für die Syntaxhervorhebung erkannt werden, aber zur Laufzeit nicht hervorgehoben werden", + help_typePrintWidth: "Breite beim Rendern eines Typs, ab der Code in eine neue Zeile umgebrochen wird", + help_customCss: "Pfad auf eine eigene CSS-Datei, die zusätzlich zum Theme importiert wird", + help_customJs: "Pfade auf eine eigene einzubindende JavaScript-Datei", + help_markdownItOptions: + "Gibt Optionen an, die zu markdown-it weitergereicht werden, dem von TypeDoc verwendeten Markdown-Parser", + help_markdownItLoader: + "Gibt ein Callback an, das beim Laden der markdown-it-Instanz gerufen wird. Dem Callback wird die Instanz des Parsers übergeben, den TypeDoc verwenden wird", + help_maxTypeConversionDepth: "Setzt die maximale Tiefe von Typen, bis zu der diese konvertiert werden", + help_name: "Setzt den Namen des Projekts, der im Header des Templates verwendet wird", + help_includeVersion: "Fügt die Package-Version zum Projektnamen hinzu", + help_disableSources: "Deaktiviert das Setzen der Quelle, wenn eine Reflection dokumentiert wird", + help_sourceLinkTemplate: + "Gibt ein Link-Template an, das beim Generieren von Quelldatei-URLs verwendet wird. Wenn nicht gesetzt, wird automatisch ein Template vom Git-Remote erstellt. Unterstützt die Platzhalter {path}, {line} und {gitRevision}", + help_gitRevision: + "Nutzt die angegebene Revision statt der neuesten Revision zum Verlinken der Quellcode-Dateien auf GitHub/Bitbucket. Hat keinen Effekt, wenn disableSources gesetzt ist", + help_gitRemote: + "Nutzt das angegebene Remote zum Verlinken von Quellcode-Dateien auf GitHub/Bitbucket. Hat keinen Effekt, wenn disableGit oder disableSources gesetzt ist", + help_disableGit: + "Gehe davon aus, dass auf alles mit dem sourceLinkTemplate verlinkt werden kann, sourceLinkTemplate muss gesetzt sein, falls die Option aktiviert ist. Der Platzhalter {path} ist dann relativ zum basePath", + help_basePath: "Gibt den Basispfad an, der beim Anzeigen von Dateipfaden verwendet wird", + help_excludeTags: "Entfernt die angegebenen Block- und Modifier-Tags von den Doc-Kommentaren", + help_notRenderedTags: + "Tags, die in den Doc-Kommentaren bewahrt werden, aber in der Dokumentation nicht angezeigt werden sollen", + help_cascadedModifierTags: "Modifier-Tags, die in alle Kinder einer Eltern-Reflection kopiert werden sollen", + help_readme: + "Pfad auf die Readme-Datei, die auf der Indexseite angezeigt werden soll. `none`, um die Indexseite zu deaktivieren und die Dokumentation auf der Seite mit den globalen Variablen beginnen zu lassen", + help_cname: "Setzt den CNAME-Dateitext, nützlich für eigene Domains bei GitHub-Pages", + help_favicon: "Pfad auf ein Favicon, welches als Icon für die Seite eingebunden werden soll", + help_sourceLinkExternal: + "Gibt an, dass Quelldatei-Links als externe Links behandelt und in einem neuen Tab geöffnet werden sollen", + help_markdownLinkExternal: + "Gibt an, dass http[s]://-Links in Kommentaren und Markdown-Dateien als externe Links behandelt und in einem neuen Tab geöffnet werden sollen", + help_githubPages: + "Erzeugt eine .nojekyll-Datei, um 404-Fehler bei GitHub-Pages zu vermeiden. Standardwert ist `true`", + help_hostedBaseUrl: + "Gibt die Basis-URL an, die beim Erzeugen einer sitemap.xml im Ausgabeverzeichnis und für kanonische Links verwendet wird. Wenn nicht angegeben, wird keine Sitemap erzeugt", + help_useHostedBaseUrlForAbsoluteLinks: + "Wenn gesetzt, erzeugt TypeDoc unter Verwendung der Option hostedBaseUrl absolute Links auf Unterseiten der Seite", + help_hideGenerator: "Gibt den TypeDoc-Link am Ende der Seite nicht aus", + help_customFooterHtml: "Eigener Footer nach dem TypeDoc-Link", + help_customFooterHtmlDisableWrapper: "Wenn gesetzt, wird das Wrapper-Element um customFooterHtml nicht ausgegeben", + help_cacheBust: "Zeitpunkt der Erstellung der Dokumentation in Links auf statische Assets inkludieren", + help_searchInComments: + "Wenn gesetzt, wird der Suchindex auch Kommentare enthalten. Dies wird die Größe des Suchindex stark erhöhen", + help_searchInDocuments: + "Wenn gesetzt, wird der Suchindex auch Dokumente enthalten. Dies wird die Größe des Suchindex stark erhöhen", + help_cleanOutputDir: "Wenn gesetzt, löscht TypeDoc das Ausgabeverzeichnis vor dem Schreiben der Dokumentation", + help_titleLink: + "Setzt den Link des Titels im Header. Standardmäßig wird auf die Startseite der Dokumentation verlinkt", + help_navigationLinks: "Gibt Links an, die mit in den Header geschrieben werden", + help_sidebarLinks: "Gibt Links an, die mit in die Seitenleiste geschrieben werden", + help_navigationLeaves: "Zweige des Navigationsbaums, die nicht ausgeklappt sein sollen", + help_headings: "Legt fest, welche optionalen Überschriften ausgegeben werden sollen", + help_sluggerConfiguration: "Legt fest, wie Anker im generierten HTML festgelegt werden.", + help_navigation: "Legt fest, wie die Navigationsseitenleiste organisiert wird", + help_includeHierarchySummary: + "Wenn gesetzt, wird eine Übersicht der Reflection-Hierarchie auf der Zusammenfassungsseite ausgegeben. Standardwert ist `true`", + help_visibilityFilters: + "Gibt die standardmäßige Sichtbarkeit für eingebaute Filter sowie zusätzliche Filter anhand eines Modifier-Tags an.", + help_searchCategoryBoosts: "Konfiguriert die Suche so, dass ausgewählte Kategorien als relevanter bewertet werden", + help_searchGroupBoosts: + 'Konfiguriert die Suche so, dass ausgewählte Symbolarten (z.B. "Klasse") als relevanter bewertet werden', + help_useFirstParagraphOfCommentAsSummary: + "Wenn gesetzt und kein @summary-Tag vorhanden ist, verwendet TypeDoc den ersten Absatz eines Kommentars als die Kurzzusammenfassung in der Modul- oder Namensraum-Ansicht", + help_jsDocCompatibility: + "Setzt Kompatibilitätsoptionen beim Parsen von Kommentaren, welche die Ähnlichkeit zu JSDoc-Kommentaren erhöhen", + help_suppressCommentWarningsInDeclarationFiles: + "Verhindert, dass Warnungen gemeldet werden, die durch unspezifizierte Tags innerhalb von Kommentaren in .d.ts-Dateien verursacht wurden.", + help_commentStyle: "Legt fest, wie TypeDoc nach Kommentaren sucht", + help_useTsLinkResolution: + "Verwendet TypeScripts Mechanismus zur Auflösung von Links beim Ermitteln des Ziels eines @link-Tags. Betrifft nur Kommentare im JSDoc-Stil", + help_preserveLinkText: + "Wenn gesetzt, wird bei @link-Tags ohne expliziten Link-Text der Textinhalt als Link verwendet. Wenn nicht gesetzt, wird der Name der Ziel-Reflection verwendet", + help_blockTags: "Block-Tags, die TypeDoc beim Parsen von Kommentaren erkennen soll", + help_inlineTags: "Inline-Tags, die TypeDoc beim Parsen von Kommentaren erkennen soll", + help_modifierTags: "Modifier-Tags, die TypeDoc beim Parsen von Kommentaren erkennen soll", + help_categorizeByGroup: "Gibt an, ob die Kategorisierung auf der Gruppen-Ebene vorgenommen werden soll", + help_groupReferencesByType: + "Wenn gesetzt, werden Referenzen zusammen mit dem Typ, auf den sie verweisen, gruppiert und nicht innerhalb einer 'Referenzen'-Gruppe", + help_defaultCategory: "Gibt die Standard-Kategorie für Reflections ohne eine Kategorie an", + help_categoryOrder: + "Gibt die Reihenfolge an, in der Kategorien erscheinen. * legt die relative Reihenfolge für Kategorien fest, die nicht in der Liste sind", + help_groupOrder: + "Gibt die Reihenfolge an, in der Gruppen erscheinen. * legt die relative Reihenfolge für Gruppen fest, die nicht in der Liste sind", + help_sort: "Gibt die Sortierstrategie für dokumentierte Werte an", + help_sortEntryPoints: + "Wenn gesetzt, werden auf Einstiegspunkte die gleichen Sortierregeln angewandt, die auch für andere Reflections gelten", + help_kindSortOrder: "Gibt die Sortierreihenfolge für Reflections an, wenn ein 'kind' festgelegt ist", + help_watch: "Überwache Dateien auf Änderungen und baue die Dokumentation bei Änderungen neu", + help_preserveWatchOutput: "Wenn gesetzt, leert TypeDoc den Bildschirm nicht zwischen Kompilierungsschritten", + help_skipErrorChecking: "Führt die Typenprüfung von TypeScript nicht vor Erzeugung der Dokumentation aus", + help_help: "Gibt diese Nachricht aus", + help_version: "Gibt die Version von TypeDoc aus", + help_showConfig: "Gibt die aufgelöste Konfiguration aus und stoppt", + help_plugin: + "Gibt die NPM-Plugins an, die geladen werden sollen. Nicht angeben, um alle installierten Plugins zu laden", + help_logLevel: "Gibt an, welches Level für das Logging verwendet werden soll", + help_treatWarningsAsErrors: "Wenn gesetzt, werden alle Warnungen als Fehler behandelt", + help_treatValidationWarningsAsErrors: + "Wenn gesetzt, werden alle Warnungen, die während der Validierung erzeugt wurden, als Fehler behandelt. Diese Option kann nicht zum Deaktivieren von treatWarningsAsErrors für Validierungswarnungen verwendet werden", + help_intentionallyNotExported: + "Eine Liste von Typen, welche keine Warnungen der Art 'referenziert, aber nicht dokumentiert' erzeugen sollen", + help_requiredToBeDocumented: "Eine Liste von Reflection-Arten, die dokumentiert werden müssen", + help_packagesRequiringDocumentation: "Eine Liste von Packages, die dokumentiert werden müssen", + help_intentionallyNotDocumented: + "Eine Liste von vollständigen Reflection-Namen, welche keine Warnungen erzeugen sollen, wenn sie nicht dokumentiert sind", + help_validation: "Gibt an, welche Validierungsschritte TypeDoc auf die erzeugte Dokumentation anwenden soll", + + // ================================================================== + // Option validation + // ================================================================== + unknown_option_0_you_may_have_meant_1: `Unbekannte Option '{0}'. Meinten Sie vielleicht:\n\t{1}`, + option_0_must_be_between_1_and_2: "{0} muss zwischen {1} und {2} liegen", + option_0_must_be_equal_to_or_greater_than_1: "{0} muss größer oder gleich {1} sein", + option_0_must_be_less_than_or_equal_to_1: "{0} muss kleiner oder gleich {1} sein", + option_0_must_be_one_of_1: "{0} muss enthalten sein in {1}", + flag_0_is_not_valid_for_1_expected_2: "Das Flag '{0}' ist nicht gültig für {1}, erwartet wird {2}", + expected_object_with_flag_values_for_0: "Erwartet für {0} wird entweder true/false oder ein Objekt mit Flag-Werten", + flag_values_for_0_must_be_booleans: "Flag-Werte für {0} müssen Wahrheitswerte sein", + locales_must_be_an_object: + "Die Option 'locales' muss auf ein Objekt der folgenden Form gesetzt werden: { en: { theme_implements: \"Implements\" }}", + exclude_not_documented_specified_0_valid_values_are_1: + "excludeNotDocumentedKinds erlaubt nur bekannte Werte, und ungültige Werte wurden angegeben ({0}). Die gültigen Arten sind:\n{1}", + external_symbol_link_mappings_must_be_object: + "externalSymbolLinkMappings muss vom Typ Record> sein", + highlight_theme_0_must_be_one_of_1: "{0} muss einer der folgenden Werte sein: {1}", + highlightLanguages_contains_invalid_languages_0: + "highlightLanguages enthält ungültige Sprachen: {0}, führen Sie typedoc --help aus, um eine Liste unterstützter Sprachen zu erhalten", + hostedBaseUrl_must_start_with_http: "hostedBaseUrl muss mit http:// oder https:// anfangen", + useHostedBaseUrlForAbsoluteLinks_requires_hostedBaseUrl: + "Die Option useHostedBaseUrlForAbsoluteLinks erfordert, dass auch hostedBaseUrl gesetzt wird", + favicon_must_have_one_of_the_following_extensions_0: "Favicon muss eine der folgenden Dateiendungen haben: {0}", + option_0_must_be_an_object: "Die Option '{0}' muss ein Objekt (kein Array) sein", + option_0_must_be_a_function: "Die Option '{0}' muss eine Funktion sein", + option_0_must_be_object_with_urls: "{0} muss ein Objekt sein, mit String-Labels als Schlüssel und URLs als Werte", + visibility_filters_only_include_0: "visibilityFilters darf nur die folgenden nicht-@-Schlüssel enthalten: {0}", + visibility_filters_must_be_booleans: "Alle Werte von visibilityFilters müssen Wahrheitswerte sein", + option_0_values_must_be_numbers: "Alle Werte von {0} müssen Zahlen sein", + option_0_values_must_be_array_of_tags: "{0} muss ein Array mit gültigen Tag-Namen sein", + option_0_specified_1_but_only_2_is_valid: + "{0} erlaubt nur bekannte Werte, und ungültige Werte wurden angegeben ({1}). Die gültigen Sortierungsstrategien sind:\n{2}", + option_outputs_must_be_array: + `Option "outputs" muss ein Array aus Elementen vom Typ { name: string, path: string, options?: TypeDocOptions } sein.`, + specified_output_0_has_not_been_defined: `Angegebene Ausgabe "{0}" wurde nicht definiert.`, + + // https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts + alert_note: "Hinweis", + alert_tip: "Tipp", + alert_important: "Wichtig", + alert_warning: "Warnung", + alert_caution: "Achtung", + + // ReflectionKind singular translations + kind_project: "Projekt", + kind_module: "Modul", + kind_namespace: "Namensraum", + kind_enum: "Aufzählung", + kind_enum_member: "Aufzählungselement", + kind_variable: "Variable", + kind_function: "Funktion", + kind_class: "Klasse", + kind_interface: "Schnittstelle", + kind_constructor: "Konstruktor", + kind_property: "Eigenschaft", + kind_method: "Methode", + kind_call_signature: "Aufrufsignatur", + kind_index_signature: "Indexsignatur", + kind_constructor_signature: "Konstruktorsignatur", + kind_parameter: "Parameter", + kind_type_literal: "Typenliteral", + kind_type_parameter: "Typenparameter", + kind_accessor: "Zugriffsfunktion", + kind_get_signature: "Abfragesignatur", + kind_set_signature: "Änderungssignatur", + kind_type_alias: "Typenalias", + kind_reference: "Referenz", + kind_document: "Dokument", + + // ReflectionKind plural translations + kind_plural_project: "Projekte", + kind_plural_module: "Module", + kind_plural_namespace: "Namensräume", + kind_plural_enum: "Aufzählungen", + kind_plural_enum_member: "Aufzählungselemente", + kind_plural_variable: "Variablen", + kind_plural_function: "Funktionen", + kind_plural_class: "Klassen", + kind_plural_interface: "Schnittstellen", + kind_plural_constructor: "Konstruktoren", + kind_plural_property: "Eigenschaften", + kind_plural_method: "Methoden", + kind_plural_call_signature: "Aufrufsignaturen", + kind_plural_index_signature: "Indexsignaturen", + kind_plural_constructor_signature: "Konstruktorsignaturen", + kind_plural_parameter: "Parameter", + kind_plural_type_literal: "Typenliterale", + kind_plural_type_parameter: "Typenparameter", + kind_plural_accessor: "Zugriffsfunktionen", + kind_plural_get_signature: "Abfragesignaturen", + kind_plural_set_signature: "Änderungssignaturen", + kind_plural_type_alias: "Typenaliasse", + kind_plural_reference: "Referenzen", + kind_plural_document: "Dokumente", + + // ReflectionFlag translations + flag_private: "Privat", + flag_protected: "Geschützt", + flag_public: "Öffentlich", + flag_static: "Statisch", + flag_external: "Extern", + flag_optional: "Optional", + flag_rest: "Rest", + flag_abstract: "Abstrakt", + flag_const: "Konstant", + flag_readonly: "Schreibgeschützt", + flag_inherited: "Geerbt", + + // ================================================================== + // Strings that show up in the default theme + // ================================================================== + // Page headings/labels + theme_implements: "Implementiert", + theme_indexable: "Indexierbar", + theme_type_declaration: "Typendeklaration", + theme_index: "Index", + theme_hierarchy: "Hierarchie", + theme_hierarchy_summary: "Hierarchieübersicht", + theme_hierarchy_view_summary: "Zusammenfassung anzeigen", + theme_implemented_by: "Implementiert von", + theme_defined_in: "Definiert in", + theme_implementation_of: "Implementierung von", + theme_inherited_from: "Geerbt von", + theme_overrides: "Überschreibt", + theme_returns: "Rückgabewert", + theme_generated_using_typedoc: "Generiert mit TypeDoc", // If this includes "TypeDoc", theme will insert a link at that location. + // Search + theme_preparing_search_index: "Bereite Suchindex vor...", + // Left nav bar + theme_loading: "Lade...", + // Right nav bar + theme_settings: "Einstellungen", + theme_member_visibility: "Member-Sichtbarkeit", + theme_theme: "Theme", + theme_os: "OS", + theme_light: "Light", + theme_dark: "Dark", + theme_on_this_page: "Auf dieser Seite", + + // aria-label + theme_search: "Suchen", + theme_menu: "Menu", + theme_permalink: "Permalink", + theme_folder: "Ordner", + + // Used by the frontend JS + // For the English translations only, these should also be added to + // src/lib/output/themes/default/assets/typedoc/Application.ts + // Also uses theme_folder and singular kinds + theme_copy: "Kopieren", + theme_copied: "Kopiert!", + theme_normally_hidden: "Dieser Member ist normalerweise aufgrund der Filtereinstellungen versteckt.", + theme_hierarchy_expand: "Ausklappen", + theme_hierarchy_collapse: "Einklappen", + theme_search_index_not_available: "Der Suchindex ist nicht verfügbar", + theme_search_no_results_found_for_0: "Keine Resultate gefunden für {0}", + theme_search_placeholder: "Dokumentation durchsuchen", +}); diff --git a/src/test/internationalization.test.ts b/src/test/internationalization.test.ts index 685bf4b1a..68e1b528f 100644 --- a/src/test/internationalization.test.ts +++ b/src/test/internationalization.test.ts @@ -26,6 +26,7 @@ describe("Internationalization", () => { ok(langs.includes("en")); ok(langs.includes("ko")); ok(langs.includes("ja")); + ok(langs.includes("de")); }); it("Supports translating without placeholders", () => { From 22b97f96afbdf2697a3562deda522065c39c1562 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 26 Apr 2025 12:03:23 -0600 Subject: [PATCH 010/119] Add note about highlightLanguages to site Resolves #2943 --- site/doc-comments/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/doc-comments/index.md b/site/doc-comments/index.md index 8aeb790c2..cb7de493d 100644 --- a/site/doc-comments/index.md +++ b/site/doc-comments/index.md @@ -27,6 +27,9 @@ TypeDoc supports code blocks in markdown and uses the syntax highlighting theme with the [`--lightHighlightTheme`](../options/output.md#lighthighlighttheme) and [`--darkHighlightTheme`](../options/output.md#darkhighlighttheme) options. +TypeDoc only loads some of the languages supported by Shiki by default. If you +want to load additional languages, use the +[`highlightLanguages`](../options/output.md#highlightlanguages) option. ````ts /** From 7460b9bf572c87e4544fc2ac8c2d8248c739e262 Mon Sep 17 00:00:00 2001 From: Brandon Keepers Date: Sat, 26 Apr 2025 14:24:17 -0400 Subject: [PATCH 011/119] Use same chevron on index (#2924) --- src/lib/output/themes/default/partials/icon.tsx | 1 + src/lib/output/themes/default/partials/index.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/output/themes/default/partials/icon.tsx b/src/lib/output/themes/default/partials/icon.tsx index 27a415cbe..91ce4875d 100644 --- a/src/lib/output/themes/default/partials/icon.tsx +++ b/src/lib/output/themes/default/partials/icon.tsx @@ -61,6 +61,7 @@ export interface Icons extends Record JSX.Element> { checkbox(): JSX.Element; menu(): JSX.Element; search(): JSX.Element; + /** @deprecated */ chevronSmall(): JSX.Element; anchor(): JSX.Element; folder(): JSX.Element; diff --git a/src/lib/output/themes/default/partials/index.tsx b/src/lib/output/themes/default/partials/index.tsx index 6f878365a..8c1f03236 100644 --- a/src/lib/output/themes/default/partials/index.tsx +++ b/src/lib/output/themes/default/partials/index.tsx @@ -45,7 +45,7 @@ export function index(context: DefaultThemeRenderContext, props: ContainerReflec
- {context.icons.chevronSmall()} + {context.icons.chevronDown()}
{i18n.theme_index()}
From f31aa35210151c10fc507ecaefbe60fa07bc1c6b Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 26 Apr 2025 12:31:14 -0600 Subject: [PATCH 012/119] Update changelog --- CHANGELOG.md | 14 ++++++++++++++ scripts/visual_regression.js | 1 + 2 files changed, 15 insertions(+) mode change 100644 => 100755 scripts/visual_regression.js diff --git a/CHANGELOG.md b/CHANGELOG.md index d00c8081b..15264282f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ title: Changelog ## Unreleased +### Features + +- Added German (de) localization, #2941. + +### Bug Fixes + +- TypeDoc's default theme now uses the same chevron for all collapsible elements, #2924 + The `chevronSmall` helper is now deprecated and will be removed with v0.29.0. + +### Thanks! + +- @blutorange +- @bkeepers + ## v0.28.3 (2025-04-20) ### Bug Fixes diff --git a/scripts/visual_regression.js b/scripts/visual_regression.js old mode 100644 new mode 100755 index f2c84d3d5..8f3be17aa --- a/scripts/visual_regression.js +++ b/scripts/visual_regression.js @@ -1,3 +1,4 @@ +#!/usr/bin/env node // @ts-check import util from "util"; import { cpSync, existsSync, mkdirSync, rmSync } from "fs"; From ae5420142637b0f656d8f0d0162fcd18a1edb818 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 26 Apr 2025 12:59:57 -0600 Subject: [PATCH 013/119] Break navigation lines Resolves #2940 --- CHANGELOG.md | 1 + .../default/assets/typedoc/Navigation.ts | 22 +++++++++++++-- src/lib/output/themes/lib.tsx | 20 ++++++------- src/test/output/lib.test.tsx | 28 +++++++++++++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 src/test/output/lib.test.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 15264282f..fbe7778d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ title: Changelog ### Features +- The navigation in the default theme will now attempt to break long names onto multiple lines, #2940. - Added German (de) localization, #2941. ### Bug Fixes diff --git a/src/lib/output/themes/default/assets/typedoc/Navigation.ts b/src/lib/output/themes/default/assets/typedoc/Navigation.ts index cf1d3b05a..253469879 100644 --- a/src/lib/output/themes/default/assets/typedoc/Navigation.ts +++ b/src/lib/output/themes/default/assets/typedoc/Navigation.ts @@ -110,12 +110,30 @@ function addNavText( el.icon || el.kind }">`; } - a.appendChild(document.createElement("span")).textContent = el.text; + a.appendChild(wbr(el.text, document.createElement("span"))); } else { const span = parent.appendChild(document.createElement("span")); const label = window.translations.folder.replaceAll('"', """); span.innerHTML = ``; - span.appendChild(document.createElement("span")).textContent = el.text; + span.appendChild(wbr(el.text, document.createElement("span"))); } } + +function wbr(str: string, element: HTMLElement) { + // Keep this in sync with the same helper in lib.tsx + // We use lookahead/lookbehind to indicate where the string should + // be split without consuming a character. + // (?<=[^A-Z])(?=[A-Z]) -- regular camel cased text + // (?<=[A-Z])(?=[A-Z][a-z]) -- acronym + // (?<=[_-])(?=[^_-]) -- snake + const parts = str.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/); + for (let i = 0; i < parts.length; ++i) { + if (i !== 0) { + element.appendChild(document.createElement("wbr")); + } + element.appendChild(document.createTextNode(parts[i])); + } + + return element; +} diff --git a/src/lib/output/themes/lib.tsx b/src/lib/output/themes/lib.tsx index 7a930a9bf..f9aa0938d 100644 --- a/src/lib/output/themes/lib.tsx +++ b/src/lib/output/themes/lib.tsx @@ -49,18 +49,14 @@ export function getKindClass(refl: Reflection): string { * @return The original string containing ```` tags where possible. */ export function wbr(str: string): (string | JSX.Element)[] { - // TODO surely there is a better way to do this, but I'm tired. - const ret: (string | JSX.Element)[] = []; - const re = /[\s\S]*?(?:[^_-][_-](?=[^_-])|[^A-Z](?=[A-Z][^A-Z]))/g; - let match: RegExpExecArray | null; - let i = 0; - while ((match = re.exec(str))) { - ret.push(match[0], ); - i += match[0].length; - } - ret.push(str.slice(i)); - - return ret; + // Keep this in sync with the same helper in Navigation.ts + // We use lookahead/lookbehind to indicate where the string should + // be split without consuming a character. + // (?<=[^A-Z])(?=[A-Z]) -- regular camel cased text + // (?<=[A-Z])(?=[A-Z][a-z]) -- acronym + // (?<=[_-])(?=[^_-]) -- snake + const parts = str.split(/(?<=[^A-Z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[_-])(?=[^_-])/); + return parts.flatMap(p => [p, ]).slice(0, -1); } export function join(joiner: JSX.Children, list: readonly T[], cb: (x: T) => JSX.Children) { diff --git a/src/test/output/lib.test.tsx b/src/test/output/lib.test.tsx new file mode 100644 index 000000000..9acf34fab --- /dev/null +++ b/src/test/output/lib.test.tsx @@ -0,0 +1,28 @@ +import { deepStrictEqual as equal } from "node:assert"; +import { wbr } from "../../lib/output/themes/lib.js"; +import { JSX } from "#utils"; + +describe("wbr", () => { + it("No breaks", () => { + equal(wbr("hello"), ["hello"]); + }); + + it("Adds to camelCased text", () => { + equal(wbr("helloWorld"), ["hello", , "World"]); + equal(wbr("helloWorldMulti"), ["hello", , "World", , "Multi"]); + }); + + it("Adds to snake_cased text", () => { + equal(wbr("snake_case_text"), ["snake_", , "case_", , "text"]); + equal(wbr("snake__case__text"), ["snake__", , "case__", , "text"]); + }); + + it("Adds to dashed-text", () => { + equal(wbr("dashed-text"), ["dashed-", , "text"]); + }); + + it("Adds appropriately with acronyms", () => { + equal(wbr("HTMLImageElement"), ["HTML", , "Image", , "Element"]); + equal(wbr("theHTMLImageElement"), ["the", , "HTML", , "Image", , "Element"]); + }); +}); From 18620a2e505f0ee2b06a0dd3f240276884e8de9d Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 26 Apr 2025 13:18:25 -0600 Subject: [PATCH 014/119] Fix tests --- .../modules/ExpandType.NestedBehavior1.json | 8 +++++++- src/test/renderer/specs/modules/ExpandType.json | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json b/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json index b6c548382..6f1e2a05b 100644 --- a/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json +++ b/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json @@ -145,7 +145,13 @@ "href": "#aexpanded" }, "children": { - "span": "AExpanded" + "span": [ + "A", + { + "wbr": [] + }, + "Expanded" + ] } }, { diff --git a/src/test/renderer/specs/modules/ExpandType.json b/src/test/renderer/specs/modules/ExpandType.json index 5e9c63ab3..dc9662dd9 100644 --- a/src/test/renderer/specs/modules/ExpandType.json +++ b/src/test/renderer/specs/modules/ExpandType.json @@ -332,7 +332,13 @@ "href": "#aexpanded" }, "children": { - "span": "AExpanded" + "span": [ + "A", + { + "wbr": [] + }, + "Expanded" + ] } }, { @@ -341,7 +347,13 @@ "href": "#bexpanded" }, "children": { - "span": "BExpanded" + "span": [ + "B", + { + "wbr": [] + }, + "Expanded" + ] } }, { From f5fd9ae6702994994f5d32851b9cc021074fd0be Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 26 Apr 2025 13:25:50 -0600 Subject: [PATCH 015/119] Remove hidden reflections from class hierarchy --- CHANGELOG.md | 2 ++ src/lib/converter/plugins/ImplementsPlugin.ts | 14 +++++++++++++- src/test/behavior.c2.test.ts | 17 ++++++++++++++++- .../converter2/behavior/removeReflection.ts | 7 +++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbe7778d2..e3389191d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ title: Changelog - TypeDoc's default theme now uses the same chevron for all collapsible elements, #2924 The `chevronSmall` helper is now deprecated and will be removed with v0.29.0. +- Classes/interfaces marked with `@hidden` will no longer appear in the + "Hierarchy" section of the docs. ### Thanks! diff --git a/src/lib/converter/plugins/ImplementsPlugin.ts b/src/lib/converter/plugins/ImplementsPlugin.ts index 8d53dedb5..c79215ed9 100644 --- a/src/lib/converter/plugins/ImplementsPlugin.ts +++ b/src/lib/converter/plugins/ImplementsPlugin.ts @@ -9,7 +9,7 @@ import { ReflectionKind, SignatureReflection, } from "../../models/index.js"; -import { ReferenceType, ReflectionType, type Type } from "../../models/types.js"; +import { ReferenceType, ReflectionType, type SomeType, type Type } from "../../models/types.js"; import { filterMap, type TranslatedString, zip } from "#utils"; import { ConverterComponent } from "../components.js"; import type { Context } from "../context.js"; @@ -234,6 +234,18 @@ export class ImplementsPlugin extends ConverterComponent { }); } + // Remove hidden classes/interfaces which we inherit from + if (reflection.kindOf(ReflectionKind.ClassOrInterface)) { + const notHiddenType = (t: SomeType) => + !(t instanceof ReferenceType) || + !t.symbolId || + !project.symbolIdHasBeenRemoved(t.symbolId); + reflection.implementedTypes = reflection.implementedTypes?.filter(notHiddenType); + if (!reflection.implementedTypes?.length) delete reflection.implementedTypes; + reflection.extendedTypes = reflection.extendedTypes?.filter(notHiddenType); + if (!reflection.extendedTypes?.length) delete reflection.extendedTypes; + } + if ( reflection.kindOf(ReflectionKind.ClassOrInterface) && reflection.extendedTypes diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index d410bcd29..e240c3332 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -988,10 +988,25 @@ describe("Behavior Tests", () => { project.removeReflection(query(project, "nested")); equal( Object.values(project.reflections).map((r) => r.name), - ["typedoc"], + ["typedoc", "Base", "NotHidden", "NotHiddenImpl", "constructor", "NotHiddenImpl"], ); }); + it("Removes heritage clause references to hidden classes.", () => { + const project = convert("removeReflection"); + const Base = query(project, "Base"); + equal(Base.extendedBy, undefined); + equal(Base.implementedBy, undefined); + + const NotHidden = query(project, "NotHidden"); + equal(NotHidden.extendedTypes, undefined); + equal(NotHidden.implementedTypes, undefined); + + const NotHiddenImpl = query(project, "NotHiddenImpl"); + equal(NotHiddenImpl.extendedTypes, undefined); + equal(NotHiddenImpl.implementedTypes, undefined); + }); + it("Handles @see tags", () => { const project = convert("seeTags"); const foo = query(project, "foo"); diff --git a/src/test/converter2/behavior/removeReflection.ts b/src/test/converter2/behavior/removeReflection.ts index 159f0781c..33d99802f 100644 --- a/src/test/converter2/behavior/removeReflection.ts +++ b/src/test/converter2/behavior/removeReflection.ts @@ -5,3 +5,10 @@ export function foo(first: string, second: string, third: string) { } export function nested(a: 1 | { a; 1 }) {} + +export interface Base {} +/** @hidden */ +export interface Hidden extends Base {} +export interface NotHidden extends Hidden {} + +export class NotHiddenImpl implements Hidden {} From 8921f13c9260f022b6e2388fdd180393e016b15d Mon Sep 17 00:00:00 2001 From: "142vip.cn" Date: Sun, 27 Apr 2025 13:09:55 +0800 Subject: [PATCH 016/119] docs: Update overview.md (#2946) --- site/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/overview.md b/site/overview.md index b5c7b4145..43db24a8a 100644 --- a/site/overview.md +++ b/site/overview.md @@ -62,7 +62,7 @@ const project = await app.convert(); if (project) { // Generate configured outputs - await generateOutputs(project); + await app.generateOutputs(project); // Alternatively... const outputDir = "docs"; From 964b128e74eb5e5896e4b722d48884e38e53ec90 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Wed, 30 Apr 2025 20:09:55 -0600 Subject: [PATCH 017/119] Fix options schema generation Resolves #2950 --- scripts/generate_options_schema.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/generate_options_schema.js b/scripts/generate_options_schema.js index 74f880ba0..333444196 100644 --- a/scripts/generate_options_schema.js +++ b/scripts/generate_options_schema.js @@ -1,9 +1,13 @@ // @ts-check import { writeFileSync } from "fs"; -import { Internationalization, ParameterType } from "../dist/index.js"; +import { ParameterType } from "../dist/index.js"; import { addTypeDocOptions } from "../dist/lib/utils/options/sources/typedoc.js"; import { SORT_STRATEGIES } from "../dist/lib/utils/sort.js"; +import { setTranslations } from "#utils"; +import { loadTranslations } from "../dist/lib/internationalization/internationalization.js"; + +setTranslations(loadTranslations("en")); const IGNORED_OPTIONS = new Set(["help", "version"]); From 683e9fe9e48d3f522322817dd173a5203f0de828 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Wed, 30 Apr 2025 20:25:39 -0600 Subject: [PATCH 018/119] Handle JSDoc wildcard types Resolves #2949 --- CHANGELOG.md | 1 + src/lib/converter/types.ts | 10 ++++++++++ src/test/converter2/issues/gh2949.js | 5 +++++ src/test/issues.c2.test.ts | 5 +++++ 4 files changed, 21 insertions(+) create mode 100644 src/test/converter2/issues/gh2949.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e3389191d..69340b376 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ title: Changelog The `chevronSmall` helper is now deprecated and will be removed with v0.29.0. - Classes/interfaces marked with `@hidden` will no longer appear in the "Hierarchy" section of the docs. +- TypeDoc now handles wildcard JSDoc types, #2949. ### Thanks! diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 5b1820a22..e37350266 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -93,6 +93,7 @@ export function loadConverters() { // Only used if skipLibCheck: true jsDocNullableTypeConverter, jsDocNonNullableTypeConverter, + jsDocAllTypeConverter, ] ) { for (const key of actor.kind) { @@ -1154,6 +1155,15 @@ const jsDocNonNullableTypeConverter: TypeConverter = { convertType: requestBugReport, }; +const jsDocAllTypeConverter: TypeConverter = { + kind: [ts.SyntaxKind.JSDocAllType], + convert() { + return new IntrinsicType("any"); + }, + // Should be a UnionType + convertType: requestBugReport, +}; + function requestBugReport(context: Context, nodeOrType: ts.Node | ts.Type) { if ("kind" in nodeOrType) { const kindName = ts.SyntaxKind[nodeOrType.kind]; diff --git a/src/test/converter2/issues/gh2949.js b/src/test/converter2/issues/gh2949.js new file mode 100644 index 000000000..724ffc586 --- /dev/null +++ b/src/test/converter2/issues/gh2949.js @@ -0,0 +1,5 @@ +/** + * @callback Test + * @returns {Promise<*>} + */ +export {}; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 912f4d66a..5137f2638 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2128,4 +2128,9 @@ describe("Issue Tests", () => { const project = convert(); equal(project.children?.map(c => c.name), ["notExcluded"]); }); + + it("#2949 handles JSDoc wildcard types", () => { + const project = convert(); + equal(query(project, "Test").type?.toString(), "() => Promise"); + }); }); From aed8474a830b923611163c2803249886bf5a0e0e Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 3 May 2025 18:24:59 -0600 Subject: [PATCH 019/119] Bump version to 0.28.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d245a6844..5b71fe305 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.3", + "version": "0.28.4", "homepage": "https://typedoc.org", "type": "module", "exports": { From ec610e4e7e922e5ebbe9c2228b8e3bdbc9864891 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Sun, 4 May 2025 00:25:51 +0000 Subject: [PATCH 020/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69340b376..7adbb94e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.4 (2025-05-04) + ### Features - The navigation in the default theme will now attempt to break long names onto multiple lines, #2940. From 56c1e1740fcc631d53278c97128ed12538bd6f57 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 9 May 2025 10:00:19 -0600 Subject: [PATCH 021/119] Fix benign circularity, add CI check --- .github/workflows/ci.yml | 5 ++++- src/lib/models/ProjectReflection.ts | 7 +++++-- src/lib/models/index.ts | 2 +- src/lib/serialization/schema.ts | 1 + src/lib/utils-common/index.ts | 1 - src/test/project.test.ts | 7 ++++++- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccfbb7068..699ba4ff8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,10 @@ jobs: - name: Circular dependency check uses: gerrit0/circular-dependency-check@v2.0.2 with: - entry: dist/index.js + entry: > + dist/index.js + dist/lib/models/index.js + dist/browser-utils.js build-release: runs-on: ubuntu-latest name: Node 22 Release diff --git a/src/lib/models/ProjectReflection.ts b/src/lib/models/ProjectReflection.ts index 747bf812b..3e1be097c 100644 --- a/src/lib/models/ProjectReflection.ts +++ b/src/lib/models/ProjectReflection.ts @@ -9,11 +9,14 @@ import type { TypeParameterReflection } from "./TypeParameterReflection.js"; import { ReflectionKind } from "./kind.js"; import { Comment, type CommentDisplayPart } from "./Comment.js"; import { ReflectionSymbolId } from "./ReflectionSymbolId.js"; -import { type Deserializer, JSONOutput, type Serializer } from "#serialization"; +import type { Deserializer, JSONOutput, Serializer } from "#serialization"; import { assertNever, DefaultMap, i18n, type NormalizedPath, removeIfPresent, StableKeyMap } from "#utils"; import type { DocumentReflection } from "./DocumentReflection.js"; import type { FileRegistry } from "./FileRegistry.js"; +// Keep this in sync with JSONOutput.SCHEMA_VERSION +export const JSON_SCHEMA_VERSION = "2.0"; + /** * A reflection that represents the root of the project. * @@ -391,7 +394,7 @@ export class ProjectReflection extends ContainerReflection { }); return { - schemaVersion: JSONOutput.SCHEMA_VERSION, + schemaVersion: JSON_SCHEMA_VERSION, ...super.toObject(serializer), variant: this.variant, packageName: this.packageName, diff --git a/src/lib/models/index.ts b/src/lib/models/index.ts index 5788dae60..f54c3662f 100644 --- a/src/lib/models/index.ts +++ b/src/lib/models/index.ts @@ -5,7 +5,7 @@ export * from "./DocumentReflection.js"; export * from "./FileRegistry.js"; export * from "./kind.js"; export * from "./ParameterReflection.js"; -export * from "./ProjectReflection.js"; +export { ProjectReflection } from "./ProjectReflection.js"; export * from "./ReferenceReflection.js"; export * from "./Reflection.js"; export * from "./ReflectionCategory.js"; diff --git a/src/lib/serialization/schema.ts b/src/lib/serialization/schema.ts index 8c1861770..0c9dd6b26 100644 --- a/src/lib/serialization/schema.ts +++ b/src/lib/serialization/schema.ts @@ -32,6 +32,7 @@ import type * as M from "#models"; import type { IfInternal, NormalizedPath } from "#utils"; +// Keep this in sync with JSON_SCHEMA_VERSION in ProjectReflection.ts export const SCHEMA_VERSION = "2.0"; /** diff --git a/src/lib/utils-common/index.ts b/src/lib/utils-common/index.ts index ceb743edf..135c9068a 100644 --- a/src/lib/utils-common/index.ts +++ b/src/lib/utils-common/index.ts @@ -8,7 +8,6 @@ export * from "./events.js"; export * from "./general.js"; export * from "./hooks.js"; export * from "./i18n.js"; -export * from "./index.js"; export * as JSX from "./jsx.js"; export * from "./logger.js"; export * from "./map.js"; diff --git a/src/test/project.test.ts b/src/test/project.test.ts index 9fc79ba33..aaafab3f7 100644 --- a/src/test/project.test.ts +++ b/src/test/project.test.ts @@ -1,5 +1,6 @@ -import { splitUnquotedString } from "../index.js"; +import { JSONOutput, splitUnquotedString } from "../index.js"; import Assert from "assert"; +import { JSON_SCHEMA_VERSION } from "../lib/models/ProjectReflection.js"; describe("Project", function () { describe("splitUnquotedString", () => { @@ -32,4 +33,8 @@ describe("Project", function () { Assert.strictEqual(result[1], "d", "Wrong split"); }); }); + + describe("JSON schema version", () => { + Assert.strictEqual(JSON_SCHEMA_VERSION, JSONOutput.SCHEMA_VERSION); + }); }); From ea16fb7cc48e3cbdaca006e866c5dbaf45a19dfa Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 9 May 2025 10:47:40 -0600 Subject: [PATCH 022/119] Fix CI config --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 699ba4ff8..63caaa7d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: - name: Circular dependency check uses: gerrit0/circular-dependency-check@v2.0.2 with: - entry: > + entry: | dist/index.js dist/lib/models/index.js dist/browser-utils.js From 766fe9e47c43f451109753c8ff93a059ee096b2f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Wed, 14 May 2025 18:39:48 -0600 Subject: [PATCH 023/119] Fix references to mapped type aliases Resolves #2954 --- CHANGELOG.md | 4 ++++ src/lib/converter/types.ts | 11 +++++++++++ src/test/converter2/issues/gh2954.ts | 11 +++++++++++ src/test/issues.c2.test.ts | 7 +++++++ 4 files changed, 33 insertions(+) create mode 100644 src/test/converter2/issues/gh2954.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7adbb94e9..3eab27d10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Bug Fixes + +- References to type aliases defined as mapped types will now correctly create a reference to the type alias, #2954. + ## v0.28.4 (2025-05-04) ### Features diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index e37350266..f8b8764ab 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -792,6 +792,17 @@ const referenceConverter: TypeConverter< return ref; } + // #2954 mapped type aliases are special! The type that we have here will + // not point at the type alias which names it like we want, but instead at + // the mapped type instantiation. Fall back to converting via the original + // type node to avoid creating a reference which points to the mapped type. + if ( + originalNode && ts.isTypeReferenceNode(originalNode) && isObjectType(type) && + type.objectFlags & ts.ObjectFlags.Mapped + ) { + return referenceConverter.convert(context, originalNode); + } + let name: string; if (ts.isIdentifier(node.typeName)) { name = node.typeName.text; diff --git a/src/test/converter2/issues/gh2954.ts b/src/test/converter2/issues/gh2954.ts new file mode 100644 index 000000000..b45a00eb2 --- /dev/null +++ b/src/test/converter2/issues/gh2954.ts @@ -0,0 +1,11 @@ +export type AliasA = Readonly>; + +export type AliasB = Readonly>; + +export type AliasC = Readonly<{}>; + +export interface InterfaceA { + propertyA: AliasA; + propertyB: AliasB; + propertyC: AliasC; +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 5137f2638..229ac06e0 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2133,4 +2133,11 @@ describe("Issue Tests", () => { const project = convert(); equal(query(project, "Test").type?.toString(), "() => Promise"); }); + + it("#2954 handles Readonly with Record type", () => { + const project = convert(); + equal(query(project, "InterfaceA.propertyA").type?.toString(), "AliasA"); + equal(query(project, "InterfaceA.propertyB").type?.toString(), "AliasB"); + equal(query(project, "InterfaceA.propertyC").type?.toString(), "AliasC"); + }); }); From c38985fc2ceaa212c7878b8c1440020381645756 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 23 May 2025 11:45:57 -0600 Subject: [PATCH 024/119] Fix ignoredHighlightLanguages docs See #2956 --- site/options/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/options/output.md b/site/options/output.md index ab859eb87..d01692514 100644 --- a/site/options/output.md +++ b/site/options/output.md @@ -235,7 +235,7 @@ loads the following languages. ## ignoredHighlightLanguages Specifies languages used in code blocks which should be silently ignored by TypeDoc. -By default, TypeDoc will produce an error if a code block specifies a language which +By default, TypeDoc will produce a warning if a code block specifies a language which is not present in the highlightLanguages array. ```json From c0d3cb36bf83d65477e7bccb60a26c04ade6701e Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 25 May 2025 11:07:20 -0600 Subject: [PATCH 025/119] ignoredHighlightLanguages now works for supported langs Resolves #2956 --- CHANGELOG.md | 2 ++ src/lib/output/themes/MarkedPlugin.tsx | 30 +++++++++++++------------- src/lib/utils/highlighter.tsx | 10 +++++---- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3eab27d10..2ad5d5e59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ title: Changelog ### Bug Fixes - References to type aliases defined as mapped types will now correctly create a reference to the type alias, #2954. +- `ignoredHighlightLanguages` can now be used to prevent warnings for codeblocks containing languages + which are supported by Shiki but are not loaded, #2956. ## v0.28.4 (2025-05-04) diff --git a/src/lib/output/themes/MarkedPlugin.tsx b/src/lib/output/themes/MarkedPlugin.tsx index ba398a23a..b9eee11e9 100644 --- a/src/lib/output/themes/MarkedPlugin.tsx +++ b/src/lib/output/themes/MarkedPlugin.tsx @@ -88,21 +88,21 @@ export class MarkedPlugin extends ContextAwareRendererComponent { lang = lang || "typescript"; lang = lang.toLowerCase(); if (!isSupportedLanguage(lang)) { - this.application.logger.warn( - i18n.unsupported_highlight_language_0_not_highlighted_in_comment_for_1( - lang, - getFriendlyFullName(this.page?.model || { name: "(unknown)" }), - ), - ); - return text; - } - if (!isLoadedLanguage(lang)) { - this.application.logger.warn( - i18n.unloaded_language_0_not_highlighted_in_comment_for_1( - lang, - getFriendlyFullName(this.page?.model || { name: "(unknown)" }), - ), - ); + if (isLoadedLanguage(lang)) { + this.application.logger.warn( + i18n.unloaded_language_0_not_highlighted_in_comment_for_1( + lang, + getFriendlyFullName(this.page?.model || { name: "(unknown)" }), + ), + ); + } else { + this.application.logger.warn( + i18n.unsupported_highlight_language_0_not_highlighted_in_comment_for_1( + lang, + getFriendlyFullName(this.page?.model || { name: "(unknown)" }), + ), + ); + } return text; } diff --git a/src/lib/utils/highlighter.tsx b/src/lib/utils/highlighter.tsx index 4ccc0d5b8..e539dafb4 100644 --- a/src/lib/utils/highlighter.tsx +++ b/src/lib/utils/highlighter.tsx @@ -162,8 +162,12 @@ export async function loadHighlighter( highlighter = new ShikiHighlighter(hl, lightTheme, darkTheme); } +function isPlainLanguage(lang: string) { + return ignoredLanguages?.includes(lang) || plaintextLanguages.includes(lang); +} + export function isSupportedLanguage(lang: string) { - return ignoredLanguages?.includes(lang) || getSupportedLanguages().includes(lang); + return isPlainLanguage(lang) || supportedLanguages.includes(lang); } export function getSupportedLanguages(): string[] { @@ -175,9 +179,7 @@ export function getSupportedThemes(): string[] { } export function isLoadedLanguage(lang: string): boolean { - return ( - isSupportedLanguage(lang) || highlighter?.supports(lang) || false - ); + return isPlainLanguage(lang) || highlighter?.supports(lang) || false; } export function highlight(code: string, lang: string): string { From b7dc8189347c14420348833644a70cfc9c67ea98 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Mon, 26 May 2025 09:00:41 -0600 Subject: [PATCH 026/119] Bump version to 0.28.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b71fe305..eacdfccce 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.4", + "version": "0.28.5", "homepage": "https://typedoc.org", "type": "module", "exports": { From 0e3443497364eb0f43d15ee62f4305b0d62b58e5 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Mon, 26 May 2025 15:01:30 +0000 Subject: [PATCH 027/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ad5d5e59..4252939da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.5 (2025-05-26) + ### Bug Fixes - References to type aliases defined as mapped types will now correctly create a reference to the type alias, #2954. From 17774dd3b1f3c7dc26a51b158ea3e3db34705c9f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Mon, 26 May 2025 15:14:36 -0600 Subject: [PATCH 028/119] Fix inverted language check, #2956 --- CHANGELOG.md | 4 ++++ src/lib/output/themes/MarkedPlugin.tsx | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4252939da..071bd7f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ title: Changelog ### Bug Fixes +- Attempting to highlight a supported language which is not enabled is now a warning, not an error, #2956. + +### Bug Fixes + - References to type aliases defined as mapped types will now correctly create a reference to the type alias, #2954. - `ignoredHighlightLanguages` can now be used to prevent warnings for codeblocks containing languages which are supported by Shiki but are not loaded, #2956. diff --git a/src/lib/output/themes/MarkedPlugin.tsx b/src/lib/output/themes/MarkedPlugin.tsx index b9eee11e9..6c422dc1f 100644 --- a/src/lib/output/themes/MarkedPlugin.tsx +++ b/src/lib/output/themes/MarkedPlugin.tsx @@ -87,8 +87,8 @@ export class MarkedPlugin extends ContextAwareRendererComponent { public getHighlighted(text: string, lang?: string): string { lang = lang || "typescript"; lang = lang.toLowerCase(); - if (!isSupportedLanguage(lang)) { - if (isLoadedLanguage(lang)) { + if (!isLoadedLanguage(lang)) { + if (isSupportedLanguage(lang)) { this.application.logger.warn( i18n.unloaded_language_0_not_highlighted_in_comment_for_1( lang, From 6f6f3cff51f03b9239cc851aa739baaa8bbb0924 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Mon, 26 May 2025 15:15:18 -0600 Subject: [PATCH 029/119] Fix changelog order --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 071bd7f7c..596f9309b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,12 @@ title: Changelog ## Unreleased -## v0.28.5 (2025-05-26) - ### Bug Fixes - Attempting to highlight a supported language which is not enabled is now a warning, not an error, #2956. +## v0.28.5 (2025-05-26) + ### Bug Fixes - References to type aliases defined as mapped types will now correctly create a reference to the type alias, #2954. From 48cff6ca1eb3ea55f20f4f4ceb32239f7583a4d9 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 19 May 2025 23:47:25 -0400 Subject: [PATCH 030/119] Test escapes in Markdown link title --- src/test/comments.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index cc1d9c6f1..7acbe3f89 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1500,6 +1500,26 @@ describe("Comment Parser", () => { ); }); + it("Recognizes markdown links which contain escapes in the label", () => { + const comment = getComment(String.raw`/** + * [\[brackets\]](./relative.md) + */`); + + equal( + comment.summary, + [ + { kind: "text", text: String.raw`[\[brackets\]](` }, + { + kind: "relative-link", + text: "./relative.md", + target: 1, + targetAnchor: undefined, + }, + { kind: "text", text: ")" }, + ] satisfies CommentDisplayPart[], + ); + }); + it("Recognizes markdown reference definition blocks", () => { const comment = getComment(`/** * [1]: ./example.md From bfeeae3f40edfaada9368d9db19db820222fcfa2 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 19 May 2025 23:48:38 -0400 Subject: [PATCH 031/119] Fix escapes in Markdown link title --- src/lib/converter/comments/textParser.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 1c020a201..5719d6a30 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -328,6 +328,10 @@ function isRelativePath(link: string) { function findLabelEnd(text: string, pos: number) { while (pos < text.length) { switch (text[pos]) { + case "\\": + ++pos; + if (pos < text.length && text[pos] === "\n") return pos; + break; case "\n": case "]": case "[": From 12d2456150d40e622218294ae077b335b7b7fd6b Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 20 May 2025 04:14:33 -0400 Subject: [PATCH 032/119] Exhaustively test Markdown link title contents --- src/test/comments.test.ts | 166 +++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 7acbe3f89..ac49b6f6f 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -5,7 +5,7 @@ import type { CommentParserConfig } from "../lib/converter/comments/index.js"; import { lexBlockComment } from "../lib/converter/comments/blockLexer.js"; import { lexLineComments } from "../lib/converter/comments/lineLexer.js"; import { type Token, TokenSyntaxKind } from "../lib/converter/comments/lexer.js"; -import { parseComment } from "../lib/converter/comments/parser.js"; +import { parseComment, parseCommentString } from "../lib/converter/comments/parser.js"; import { lexCommentString } from "../lib/converter/comments/rawLexer.js"; import { Comment, type CommentDisplayPart, CommentTag } from "../lib/models/index.js"; import { TestLogger } from "./TestLogger.js"; @@ -13,6 +13,68 @@ import { extractTagName } from "../lib/converter/comments/tagName.js"; import { FileRegistry } from "../lib/models/FileRegistry.js"; import { dedent, MinimalSourceFile, type NormalizedPath } from "#utils"; +const CONTENT_PARTS = ["text", "`code`"]; +const SEPARATORS = [" ", "\n", "\n ", "\n\n"]; +const MAX_CONTENT_PARTS = 3; +function* generateLinkTitleCases() { + const makeCase = (linkTitle: string) => { + const ok = !linkTitle.includes("\n\n"); + const input = `[${linkTitle}](./relative.md)`; + const expect: CommentDisplayPart[] = input + .replace("(./relative.md)", "(") + .split(/(`code`)/g) + .map((text) => { + const kind = text[0] === "`" ? "code" : "text"; + return { kind, text }; + }); + if (ok) { + expect.push( + { + kind: "relative-link", + text: "./relative.md", + target: 1, + targetAnchor: undefined, + }, + { kind: "text", text: ")" }, + ); + } else { + expect[expect.length - 1].text += "./relative.md)"; + } + expect[expect.length - 1].text += "\n["; + expect.push( + { kind: "code", text: "`code`" }, + { kind: "text", text: "](" }, + { + kind: "relative-link", + text: "./relative.md", + target: 1, + targetAnchor: undefined, + }, + { kind: "text", text: ")" }, + ); + return { input: input + "\n[`code`](./relative.md)", expect }; + }; + for (let n = 1; n <= MAX_CONTENT_PARTS; n++) { + // 3 bits for each part (except the first): + // selects a part from CONTENT_PARTS + // selects a preceding separator from SEPARATORS + for (let bits = 0; bits < 2 ** (3 * n); bits += 4) { + const inner = Array.from({ length: n }, (_, i) => { + const partSelections = bits >> (3 * i); + const part = CONTENT_PARTS[partSelections & 4 ? 1 : 0]; + const sepText = SEPARATORS[partSelections & 3]; + return i === 0 ? part : `${sepText}${part}`; + }).join(""); + // We also wrap the parts with arbitrary leading and trailing whitespace + for (const prefix of ["", ...SEPARATORS]) { + for (const suffix of ["", ...SEPARATORS]) { + yield makeCase(`${prefix}${inner}${suffix}`); + } + } + } + } +} + describe("Block Comment Lexer", () => { function lex(text: string): Token[] { return Array.from(lexBlockComment(text)); @@ -1520,6 +1582,18 @@ describe("Comment Parser", () => { ); }); + it("Parses markdown link titles with arbitrarily-separated arbitrary combinations of text and code", () => { + const embedInComment = (input: string) => { + const lines = input.split("\n"); + const embedded = `/**\n${lines.map(line => " * " + line).join("\n")}\n */`; + return getComment(embedded); + }; + for (const { input, expect } of generateLinkTitleCases()) { + const comment = embedInComment(input); + equal(comment.summary, expect, `input: ${JSON.stringify(input)}`); + } + }); + it("Recognizes markdown reference definition blocks", () => { const comment = getComment(`/** * [1]: ./example.md @@ -1683,6 +1757,96 @@ describe("Comment Parser", () => { }); }); +describe("Raw Comment Parser", () => { + const config: CommentParserConfig = { + blockTags: new Set([ + "@param", + "@remarks", + "@module", + "@inheritDoc", + "@defaultValue", + ]), + inlineTags: new Set(["@link"]), + modifierTags: new Set([ + "@public", + "@private", + "@protected", + "@readonly", + "@enum", + "@event", + "@packageDocumentation", + ]), + jsDocCompatibility: { + defaultTag: true, + exampleTag: true, + ignoreUnescapedBraces: false, + inheritDocTag: false, + }, + suppressCommentWarningsInDeclarationFiles: false, + useTsLinkResolution: false, + commentStyle: "jsdoc", + }; + + let files: FileRegistry; + function getComment(text: string) { + files = new FileRegistry(); + const logger = new TestLogger(); + const content = lexCommentString(text); + const comment = parseCommentString( + content, + config, + new MinimalSourceFile(text, "/dev/zero" as NormalizedPath), + logger, + files, + ); + logger.expectNoOtherMessages(); + return comment; + } + + afterEach(() => { + files = undefined!; + }); + + it("Recognizes markdown links which contain escapes in the label", () => { + const comment = getComment(String.raw`[\[brackets\]](./relative.md)`); + + equal( + comment.content, + [ + { kind: "text", text: String.raw`[\[brackets\]](` }, + { + kind: "relative-link", + text: "./relative.md", + target: 1, + targetAnchor: undefined, + }, + { kind: "text", text: ")" }, + ] satisfies CommentDisplayPart[], + ); + }); + + it("Parses markdown link titles with arbitrarily-separated arbitrary combinations of text and code", () => { + for (const { input, expect } of generateLinkTitleCases()) { + const comment = getComment(input); + equal(comment.content, expect, `input: ${JSON.stringify(input)}`); + } + }); +}); + +describe("Markdown Link Title Generation", () => { + const inputs = [] as string[]; + for (const { input } of generateLinkTitleCases()) { + inputs.push(input); + } + const inputsSet = new Set(inputs); + equal(inputsSet.size, inputs.length, "each generated input must be unique"); + + const expectCount = Array.from({ length: MAX_CONTENT_PARTS }, (_, i) => i + 1) + .map(n => (SEPARATORS.length * CONTENT_PARTS.length) ** n / SEPARATORS.length * (SEPARATORS.length + 1) ** 2) + .reduce((a, b) => a + b); + equal(inputsSet.size, expectCount, "generated input count"); +}); + describe("extractTagName", () => { it("Handles simple name", () => { equal(extractTagName("T - abc"), { name: "T", newText: "abc" }); From fb342af7d5ef4863b52c2875b6bff46581a7c8c6 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 20 May 2025 04:16:55 -0400 Subject: [PATCH 033/119] Fix multi-line Markdown link titles --- src/lib/converter/comments/textParser.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 5719d6a30..ea6b2cba7 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -162,7 +162,6 @@ function checkMarkdownLink( let searchStart: number; if (reentry.withinLinkLabel) { searchStart = data.pos; - reentry.withinLinkLabel = false; } else if (token.text[data.pos] === "[") { searchStart = data.pos + 1; } else { @@ -170,15 +169,15 @@ function checkMarkdownLink( } const labelEnd = findLabelEnd(token.text, searchStart); - if (labelEnd === -1) { - // This markdown link might be split across multiple display parts - // [ `text` ](link) - // ^^ text - // ^^^^^^ code - // ^^^^^^^^ text - reentry.withinLinkLabel = true; + if (labelEnd === -1 || token.text[labelEnd] === "\n") { + // This markdown link might be split across multiple lines or input tokens + // [prefix `code` suffix](target) + // ........^^^^^^................ + // Unless we encounter two consecutive line feeds, expect it to keep going. + reentry.withinLinkLabel = labelEnd !== data.pos || !data.atNewLine; return; } + reentry.withinLinkLabel = false; if (token.text[labelEnd] === "]" && token.text[labelEnd + 1] === "(") { const link = MdHelpers.parseLinkDestination( From c3fd884004af173a5e7918ddfa60e5476c6607f7 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 20 May 2025 11:27:23 -0400 Subject: [PATCH 034/119] Test more multi-line Markdown link titles --- src/test/comments.test.ts | 46 ++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index ac49b6f6f..e8fcb602c 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1565,18 +1565,27 @@ describe("Comment Parser", () => { it("Recognizes markdown links which contain escapes in the label", () => { const comment = getComment(String.raw`/** * [\[brackets\]](./relative.md) + * + * [ + * multi-line + * \[brackets\] + * ](./relative.md) */`); + const link = { + kind: "relative-link", + text: "./relative.md", + target: 1, + targetAnchor: undefined, + } as const; + equal( comment.summary, [ { kind: "text", text: String.raw`[\[brackets\]](` }, - { - kind: "relative-link", - text: "./relative.md", - target: 1, - targetAnchor: undefined, - }, + link, + { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](` }, + link, { kind: "text", text: ")" }, ] satisfies CommentDisplayPart[], ); @@ -1808,18 +1817,29 @@ describe("Raw Comment Parser", () => { }); it("Recognizes markdown links which contain escapes in the label", () => { - const comment = getComment(String.raw`[\[brackets\]](./relative.md)`); + const comment = getComment(dedent(String.raw` + [\[brackets\]](./relative.md) + + [ + multi-line + \[brackets\] + ](./relative.md) + `)); + + const link = { + kind: "relative-link", + text: "./relative.md", + target: 1, + targetAnchor: undefined, + } as const; equal( comment.content, [ { kind: "text", text: String.raw`[\[brackets\]](` }, - { - kind: "relative-link", - text: "./relative.md", - target: 1, - targetAnchor: undefined, - }, + link, + { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](` }, + link, { kind: "text", text: ")" }, ] satisfies CommentDisplayPart[], ); From d00556800ce2706baea16575e45f3337b1841e1c Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Tue, 20 May 2025 11:28:31 -0400 Subject: [PATCH 035/119] Avoid rescanning known Markdown link title content --- src/lib/converter/comments/textParser.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index ea6b2cba7..8c9b7c2d8 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -76,6 +76,7 @@ export function textContent( reentry: TextParserReentryState, ) { let lastPartEnd = 0; + let canEndMarkdownLink = true; const data: TextParserData = { sourcePath, token, @@ -87,6 +88,7 @@ export function textContent( }; function addRef(ref: RelativeLink) { + canEndMarkdownLink = true; outContent.push({ kind: "text", text: token.text.slice(lastPartEnd, ref.pos), @@ -116,10 +118,15 @@ export function textContent( } while (data.pos < token.text.length) { - const link = checkMarkdownLink(data, reentry); - if (link) { - addRef(link); - continue; + if (canEndMarkdownLink) { + const link = checkMarkdownLink(data, reentry); + if (link) { + addRef(link); + continue; + } + // If we're within Markdown link text, then `checkMarkdownLink` + // already scanned `token` up to a line feed (if any). + canEndMarkdownLink = !reentry.withinLinkLabel; } const reference = checkReference(data); @@ -134,7 +141,9 @@ export function textContent( continue; } - data.atNewLine = token.text[data.pos] === "\n"; + const atNewLine = token.text[data.pos] === "\n"; + data.atNewLine = atNewLine; + if (atNewLine) canEndMarkdownLink = true; ++data.pos; } From d596da961073b226b3f00bd3b2dfab905746bb6b Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Wed, 21 May 2025 11:13:30 -0400 Subject: [PATCH 036/119] Test whitespace around Markdown link destinations --- src/test/comments.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index e8fcb602c..2b1a7aab8 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1569,7 +1569,9 @@ describe("Comment Parser", () => { * [ * multi-line * \[brackets\] - * ](./relative.md) + * ]( + * ./relative.md + * ) */`); const link = { @@ -1584,9 +1586,9 @@ describe("Comment Parser", () => { [ { kind: "text", text: String.raw`[\[brackets\]](` }, link, - { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](` }, + { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](\n ` }, link, - { kind: "text", text: ")" }, + { kind: "text", text: "\n )" }, ] satisfies CommentDisplayPart[], ); }); @@ -1823,7 +1825,9 @@ describe("Raw Comment Parser", () => { [ multi-line \[brackets\] - ](./relative.md) + ]( + ./relative.md + ) `)); const link = { @@ -1838,9 +1842,9 @@ describe("Raw Comment Parser", () => { [ { kind: "text", text: String.raw`[\[brackets\]](` }, link, - { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](` }, + { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](\n ` }, link, - { kind: "text", text: ")" }, + { kind: "text", text: "\n )" }, ] satisfies CommentDisplayPart[], ); }); From 82c2a96f295c80f3a353f4d599ceba05009a013d Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Wed, 21 May 2025 11:15:47 -0400 Subject: [PATCH 037/119] Fix parsing of Markdown link destinations preceded by whitespace --- src/lib/converter/comments/textParser.ts | 102 ++++++++++++++--------- 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 8c9b7c2d8..1934167d4 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -41,6 +41,7 @@ interface RelativeLink { */ export class TextParserReentryState { withinLinkLabel = false; + withinLinkDest = false; private lastPartWasNewline = false; checkState(token: Token) { @@ -48,11 +49,13 @@ export class TextParserReentryState { case TokenSyntaxKind.Code: if (/\n\s*\n/.test(token.text)) { this.withinLinkLabel = false; + this.withinLinkDest = false; } break; case TokenSyntaxKind.NewLine: if (this.lastPartWasNewline) { this.withinLinkLabel = false; + this.withinLinkDest = false; } break; } @@ -124,9 +127,9 @@ export function textContent( addRef(link); continue; } - // If we're within Markdown link text, then `checkMarkdownLink` + // If we're within a Markdown link, then `checkMarkdownLink` // already scanned `token` up to a line feed (if any). - canEndMarkdownLink = !reentry.withinLinkLabel; + canEndMarkdownLink = !reentry.withinLinkLabel && !reentry.withinLinkDest; } const reference = checkReference(data); @@ -143,7 +146,7 @@ export function textContent( const atNewLine = token.text[data.pos] === "\n"; data.atNewLine = atNewLine; - if (atNewLine) canEndMarkdownLink = true; + if (atNewLine && !reentry.withinLinkDest) canEndMarkdownLink = true; ++data.pos; } @@ -169,7 +172,7 @@ function checkMarkdownLink( const { token, sourcePath, files } = data; let searchStart: number; - if (reentry.withinLinkLabel) { + if (reentry.withinLinkLabel || reentry.withinLinkDest) { searchStart = data.pos; } else if (token.text[data.pos] === "[") { searchStart = data.pos + 1; @@ -177,44 +180,65 @@ function checkMarkdownLink( return; } - const labelEnd = findLabelEnd(token.text, searchStart); - if (labelEnd === -1 || token.text[labelEnd] === "\n") { - // This markdown link might be split across multiple lines or input tokens - // [prefix `code` suffix](target) - // ........^^^^^^................ - // Unless we encounter two consecutive line feeds, expect it to keep going. - reentry.withinLinkLabel = labelEnd !== data.pos || !data.atNewLine; - return; + if (!reentry.withinLinkDest) { + const labelEnd = findLabelEnd(token.text, searchStart); + if (labelEnd === -1 || token.text[labelEnd] === "\n") { + // This markdown link might be split across multiple lines or input tokens + // [prefix `code` suffix](target) + // ........^^^^^^................ + // Unless we encounter two consecutive line feeds, expect it to keep going. + reentry.withinLinkLabel = labelEnd !== data.pos || !data.atNewLine; + return; + } + reentry.withinLinkLabel = false; + if (!token.text.startsWith("](", labelEnd)) return; + searchStart = labelEnd + 2; } - reentry.withinLinkLabel = false; - - if (token.text[labelEnd] === "]" && token.text[labelEnd + 1] === "(") { - const link = MdHelpers.parseLinkDestination( - token.text, - labelEnd + 2, - token.text.length, - ); - - if (link.ok) { - // Only make a relative-link display part if it's actually a relative link. - // Discard protocol:// links, unix style absolute paths, and windows style absolute paths. - if (isRelativePath(link.str)) { - const { target, anchor } = files.register( - sourcePath, - link.str as NormalizedPath, - ) || { target: undefined, anchor: undefined }; - return { - pos: labelEnd + 2, - end: link.pos, - target, - targetAnchor: anchor, - }; - } - // This was a link, skip ahead to ensure we don't happen to parse - // something else as a link within the link. - data.pos = link.pos - 1; + // Skip whitespace (including line breaks) between "](" and the link destination. + // https://spec.commonmark.org/0.31.2/#links + const end = token.text.length; + let lookahead = searchStart; + for (let newlines = 0;; ++lookahead) { + if (lookahead === end) { + reentry.withinLinkDest = true; + return; } + switch (token.text[lookahead]) { + case "\n": + if (++newlines === 2) { + reentry.withinLinkDest = false; + return; + } + continue; + case " ": + case "\t": + continue; + } + break; + } + reentry.withinLinkDest = false; + + const link = MdHelpers.parseLinkDestination(token.text, lookahead, end); + if (link.ok) { + // Only make a relative-link display part if it's actually a relative link. + // Discard protocol:// links, unix style absolute paths, and windows style absolute paths. + if (isRelativePath(link.str)) { + const { target, anchor } = files.register( + sourcePath, + link.str as NormalizedPath, + ) || { target: undefined, anchor: undefined }; + return { + pos: lookahead, + end: link.pos, + target, + targetAnchor: anchor, + }; + } + + // This was a link, skip ahead to ensure we don't happen to parse + // something else as a link within the link. + data.pos = link.pos - 1; } } From d107fcef2883d32c04e409375a5620c8a44bf709 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Mon, 26 May 2025 21:54:08 -0400 Subject: [PATCH 038/119] Test parentheses in Markdown link titles --- src/test/comments.test.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 2b1a7aab8..70b7966ac 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1562,13 +1562,14 @@ describe("Comment Parser", () => { ); }); - it("Recognizes markdown links which contain escapes in the label", () => { + it("Recognizes markdown links which contain parentheses and escapes in the label", () => { const comment = getComment(String.raw`/** - * [\[brackets\]](./relative.md) + * [(parens) \[brackets\]](./relative.md) * * [ * multi-line * \[brackets\] + * (parens) * ]( * ./relative.md * ) @@ -1584,9 +1585,9 @@ describe("Comment Parser", () => { equal( comment.summary, [ - { kind: "text", text: String.raw`[\[brackets\]](` }, + { kind: "text", text: String.raw`[(parens) \[brackets\]](` }, link, - { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](\n ` }, + { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n (parens)\n](\n ` }, link, { kind: "text", text: "\n )" }, ] satisfies CommentDisplayPart[], @@ -1818,13 +1819,14 @@ describe("Raw Comment Parser", () => { files = undefined!; }); - it("Recognizes markdown links which contain escapes in the label", () => { + it("Recognizes markdown links which contain parentheses and escapes in the label", () => { const comment = getComment(dedent(String.raw` - [\[brackets\]](./relative.md) + [(parens) \[brackets\]](./relative.md) [ multi-line \[brackets\] + (parens) ]( ./relative.md ) @@ -1840,9 +1842,9 @@ describe("Raw Comment Parser", () => { equal( comment.content, [ - { kind: "text", text: String.raw`[\[brackets\]](` }, + { kind: "text", text: String.raw`[(parens) \[brackets\]](` }, link, - { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n](\n ` }, + { kind: "text", text: `)\n\n[\n multi-line\n ${String.raw`\[brackets\]`}\n (parens)\n](\n ` }, link, { kind: "text", text: "\n )" }, ] satisfies CommentDisplayPart[], From e8eafdce79c0674b99c5030368c61ca25fd4eed3 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 1 Jun 2025 11:17:29 -0600 Subject: [PATCH 039/119] Simplify test code slightly --- src/lib/converter/comments/textParser.ts | 2 -- src/test/comments.test.ts | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 1934167d4..c2f705894 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -19,7 +19,6 @@ interface TextParserData { sourcePath: NormalizedPath; token: Token; pos: number; - i18n: TranslationProxy; warning: (msg: TranslatedString, token: Token) => void; files: FileRegistry; atNewLine: boolean; @@ -84,7 +83,6 @@ export function textContent( sourcePath, token, pos: 0, // relative to the token - i18n, warning, files: files, atNewLine, diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 70b7966ac..27d50c9d2 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1600,6 +1600,7 @@ describe("Comment Parser", () => { const embedded = `/**\n${lines.map(line => " * " + line).join("\n")}\n */`; return getComment(embedded); }; + for (const { input, expect } of generateLinkTitleCases()) { const comment = embedInComment(input); equal(comment.summary, expect, `input: ${JSON.stringify(input)}`); @@ -1799,9 +1800,8 @@ describe("Raw Comment Parser", () => { commentStyle: "jsdoc", }; - let files: FileRegistry; function getComment(text: string) { - files = new FileRegistry(); + const files = new FileRegistry(); const logger = new TestLogger(); const content = lexCommentString(text); const comment = parseCommentString( @@ -1815,10 +1815,6 @@ describe("Raw Comment Parser", () => { return comment; } - afterEach(() => { - files = undefined!; - }); - it("Recognizes markdown links which contain parentheses and escapes in the label", () => { const comment = getComment(dedent(String.raw` [(parens) \[brackets\]](./relative.md) @@ -1860,7 +1856,7 @@ describe("Raw Comment Parser", () => { }); describe("Markdown Link Title Generation", () => { - const inputs = [] as string[]; + const inputs: string[] = []; for (const { input } of generateLinkTitleCases()) { inputs.push(input); } From bb45c312e5ea943d9298106b90fec93bab0c03a5 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 1 Jun 2025 11:22:06 -0600 Subject: [PATCH 040/119] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 596f9309b..17f1ff18b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ title: Changelog ### Bug Fixes - Attempting to highlight a supported language which is not enabled is now a warning, not an error, #2956. +- Improved compatibility with CommonMark's link parsing, #2959. ## v0.28.5 (2025-05-26) From bc7e8c22051fa13b4e0a48cf42b9bd23e58d062e Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 1 Jun 2025 16:31:32 -0600 Subject: [PATCH 041/119] Support relative links to package dir Resolves #2961 --- CHANGELOG.md | 4 ++ src/lib/converter/comments/textParser.ts | 4 +- src/lib/converter/converter.ts | 4 ++ src/lib/converter/plugins/PackagePlugin.ts | 6 ++- src/lib/internationalization/locales/de.cts | 2 +- src/lib/internationalization/locales/en.cts | 2 +- src/lib/internationalization/locales/ja.cts | 2 +- src/lib/internationalization/locales/zh.cts | 2 +- src/lib/models/Comment.ts | 9 ++-- src/lib/models/DeclarationReflection.ts | 4 +- src/lib/models/FileRegistry.ts | 52 ++++++++++++++------- src/lib/models/ProjectReflection.ts | 6 +-- src/lib/models/ReferenceReflection.ts | 4 +- src/lib/models/Reflection.ts | 4 ++ src/lib/models/types.ts | 48 ++++++++++--------- src/lib/output/plugins/AssetsPlugin.ts | 10 +++- src/lib/serialization/deserializer.ts | 8 ++-- src/lib/serialization/schema.ts | 19 ++++---- src/lib/utils/ValidatingFileRegistry.ts | 19 ++++---- src/test/comments.test.ts | 42 ++++++++--------- src/test/converter.test.ts | 3 +- src/test/models/types.test.ts | 4 +- 22 files changed, 154 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17f1ff18b..2b30ec1ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Features + +- TypeDoc now supports resolving relative paths in links to the package directory as belonging to the project, #2961. + ### Bug Fixes - Attempting to highlight a supported language which is not enabled is now a warning, not an error, #2956. diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index c2f705894..6277e9aa2 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -7,7 +7,7 @@ */ import type { TranslationProxy } from "../../internationalization/index.js"; import type { CommentDisplayPart, RelativeLinkDisplayPart } from "../../models/index.js"; -import type { FileRegistry } from "../../models/FileRegistry.js"; +import type { FileId, FileRegistry } from "../../models/FileRegistry.js"; import { HtmlAttributeParser, ParserState } from "#node-utils"; import { type Token, TokenSyntaxKind } from "./lexer.js"; @@ -28,7 +28,7 @@ interface RelativeLink { pos: number; end: number; /** May be undefined if the registry can't find this file */ - target: number | undefined; + target: FileId | undefined; targetAnchor: string | undefined; } diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 7da84bbcf..23e16a478 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -322,6 +322,10 @@ export class Converter extends AbstractComponent { this.application.options.getValue("name"), this.application.files, ); + if (this.owner.options.packageDir) { + project.files.registerReflectionPath(normalizePath(this.owner.options.packageDir), project); + } + const context = new Context(this, programs, project); this.trigger(Converter.EVENT_BEGIN, context); diff --git a/src/lib/converter/plugins/PackagePlugin.ts b/src/lib/converter/plugins/PackagePlugin.ts index d8f4414d6..8dbe44b41 100644 --- a/src/lib/converter/plugins/PackagePlugin.ts +++ b/src/lib/converter/plugins/PackagePlugin.ts @@ -6,7 +6,7 @@ import type { ProjectReflection } from "../../models/index.js"; import { ApplicationEvents } from "../../application-events.js"; import { ConverterEvents } from "../converter-events.js"; import type { Converter } from "../converter.js"; -import { type GlobString, i18n, MinimalSourceFile, type NormalizedPath } from "#utils"; +import { type GlobString, i18n, MinimalSourceFile, type NormalizedPath, NormalizedPathUtils } from "#utils"; import { discoverPackageJson, type EntryPointStrategy, @@ -139,6 +139,10 @@ export class PackagePlugin extends ConverterComponent { ); project.readme = content; + project.files.registerReflectionPath(this.readmeFile, project); + // In packages mode, this probably won't do anything unless someone uses the readme + // option to select a different file. + project.files.registerReflectionPath(NormalizedPathUtils.dirname(this.readmeFile), project); // This isn't ideal, but seems better than figuring out the readme // path over in the include plugin... diff --git a/src/lib/internationalization/locales/de.cts b/src/lib/internationalization/locales/de.cts index 1bd6e7413..3da33fda3 100644 --- a/src/lib/internationalization/locales/de.cts +++ b/src/lib/internationalization/locales/de.cts @@ -221,7 +221,7 @@ export = localeUtils.buildIncompleteTranslation({ // deserialization serialized_project_referenced_0_not_part_of_project: "Serialisiertes Projekt referenziert Reflection {0}, welche kein Teil des Projekts ist", - saved_relative_path_0_resolved_from_1_is_not_a_file: + saved_relative_path_0_resolved_from_1_does_not_exist: "Serialisiertes Projekt referenziert {0}, was relativ zu {1} nicht existiert", // options diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index 6a8e0792a..4979afda6 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -210,7 +210,7 @@ export = { // deserialization serialized_project_referenced_0_not_part_of_project: `Serialized project referenced reflection {0}, which was not a part of the project`, - saved_relative_path_0_resolved_from_1_is_not_a_file: + saved_relative_path_0_resolved_from_1_does_not_exist: `Serialized project referenced {0}, which does not exist relative to {1}`, // options diff --git a/src/lib/internationalization/locales/ja.cts b/src/lib/internationalization/locales/ja.cts index 9f5ab3070..40c7edf08 100644 --- a/src/lib/internationalization/locales/ja.cts +++ b/src/lib/internationalization/locales/ja.cts @@ -146,7 +146,7 @@ export = localeUtils.buildIncompleteTranslation({ file_0_not_an_object: "ファイル {0} はオブジェクトではありません", serialized_project_referenced_0_not_part_of_project: "シリアル化されたプロジェクトは、プロジェクトの一部ではないリフレクション {0} を参照しました", - // saved_relative_path_0_resolved_from_1_is_not_a_file + // saved_relative_path_0_resolved_from_1_does_not_exist circular_reference_extends_0: '{0} の "extends" フィールドで循環参照が検出されました', failed_resolve_0_to_file_in_1: "{0} を {1} 内のファイルに解決できませんでした", option_0_can_only_be_specified_by_config_file: "'{0}' オプションは設定ファイル経由でのみ指定できます", diff --git a/src/lib/internationalization/locales/zh.cts b/src/lib/internationalization/locales/zh.cts index 8ac1d1e55..9250ad7e9 100644 --- a/src/lib/internationalization/locales/zh.cts +++ b/src/lib/internationalization/locales/zh.cts @@ -181,7 +181,7 @@ export = localeUtils.buildIncompleteTranslation({ // deserialization serialized_project_referenced_0_not_part_of_project: "序列化项目引用了反射 {0},但它不是项目的一部分", - saved_relative_path_0_resolved_from_1_is_not_a_file: "序列化项目引用的 {0} 不存在或无法在 {1} 下找到", + saved_relative_path_0_resolved_from_1_does_not_exist: "序列化项目引用的 {0} 不存在或无法在 {1} 下找到", // options circular_reference_extends_0: "{0} 的“extends”字段出现循环引用", diff --git a/src/lib/models/Comment.ts b/src/lib/models/Comment.ts index 0ec479eba..136b98d90 100644 --- a/src/lib/models/Comment.ts +++ b/src/lib/models/Comment.ts @@ -1,8 +1,9 @@ import { assertNever, i18n, NonEnumerable, type NormalizedPath, removeIf } from "#utils"; -import type { Reflection } from "./Reflection.js"; +import type { Reflection, ReflectionId } from "./Reflection.js"; import { ReflectionSymbolId } from "./ReflectionSymbolId.js"; import type { Deserializer, JSONOutput, Serializer } from "#serialization"; +import type { FileId } from "./FileRegistry.js"; /** * Represents a parsed piece of a comment. @@ -62,7 +63,7 @@ export interface RelativeLinkDisplayPart { * A link to either some document outside of the project or a reflection. * This may be `undefined` if the relative path does not exist. */ - target: number | undefined; + target: FileId | undefined; /** * Anchor within the target page, validated after rendering if possible */ @@ -238,10 +239,10 @@ export class Comment { parts: JSONOutput.CommentDisplayPart[], ): CommentDisplayPart[] { const links: [ - number, + ReflectionId, InlineTagDisplayPart | RelativeLinkDisplayPart, ][] = []; - const files: [number, RelativeLinkDisplayPart][] = []; + const files: [FileId, RelativeLinkDisplayPart][] = []; const result = parts.map((part): CommentDisplayPart => { switch (part.kind) { diff --git a/src/lib/models/DeclarationReflection.ts b/src/lib/models/DeclarationReflection.ts index 2a2976433..6f225773b 100644 --- a/src/lib/models/DeclarationReflection.ts +++ b/src/lib/models/DeclarationReflection.ts @@ -1,5 +1,5 @@ import { type ReferenceType, ReflectionType, type SomeType } from "./types.js"; -import { type TraverseCallback, TraverseProperty } from "./Reflection.js"; +import { type ReflectionId, type TraverseCallback, TraverseProperty } from "./Reflection.js"; import { ContainerReflection } from "./ContainerReflection.js"; import type { SignatureReflection } from "./SignatureReflection.js"; import type { TypeParameterReflection } from "./TypeParameterReflection.js"; @@ -346,7 +346,7 @@ export class DeclarationReflection extends ContainerReflection { de.defer(() => { for (const [id, sid] of Object.entries(obj.symbolIdMap || {})) { const refl = this.project.getReflectionById( - de.oldIdToNewId[+id] ?? -1, + de.oldIdToNewId[+id as ReflectionId] ?? -1, ); if (refl) { this.project.registerSymbolId( diff --git a/src/lib/models/FileRegistry.ts b/src/lib/models/FileRegistry.ts index 5b11c6737..0081c87cc 100644 --- a/src/lib/models/FileRegistry.ts +++ b/src/lib/models/FileRegistry.ts @@ -3,22 +3,24 @@ import type { ProjectReflection, Reflection } from "./index.js"; import type { ReflectionId } from "./Reflection.js"; import { type NormalizedPath, NormalizedPathUtils } from "#utils"; +export type FileId = number & { __mediaIdBrand: never }; + export class FileRegistry { protected nextId = 1; // The combination of these two make up the registry - protected mediaToReflection = new Map(); - protected mediaToPath = new Map(); + protected mediaToReflection = new Map(); + protected mediaToPath = new Map(); protected reflectionToPath = new Map(); - protected pathToMedia = new Map(); + protected pathToMedia = new Map(); // Lazily created as we get names for rendering - protected names = new Map(); + protected names = new Map(); protected nameUsage = new Map(); registerAbsolute(absolute: NormalizedPath): { - target: number; + target: FileId; anchor: string | undefined; } { const anchorIndex = absolute.indexOf("#"); @@ -27,24 +29,36 @@ export class FileRegistry { anchor = absolute.substring(anchorIndex + 1); absolute = absolute.substring(0, anchorIndex) as NormalizedPath; } - absolute = absolute.replace(/#.*/, "") as NormalizedPath; + const existing = this.pathToMedia.get(absolute); if (existing) { return { target: existing, anchor }; } - this.mediaToPath.set(this.nextId, absolute); - this.pathToMedia.set(absolute, this.nextId); + this.mediaToPath.set(this.nextId as FileId, absolute); + this.pathToMedia.set(absolute, this.nextId as FileId); - return { target: this.nextId++, anchor }; + return { target: this.nextId++ as FileId, anchor }; } + /** + * Registers the specified path as the canonical file for this reflection + */ registerReflection(absolute: NormalizedPath, reflection: Reflection) { const { target } = this.registerAbsolute(absolute); this.reflectionToPath.set(reflection.id, absolute); this.mediaToReflection.set(target, reflection.id); } + /** + * Registers the specified path as a path which should be resolved to the specified + * reflection. A reflection *may* be associated with multiple paths. + */ + registerReflectionPath(absolute: NormalizedPath, reflection: Reflection) { + const { target } = this.registerAbsolute(absolute); + this.mediaToReflection.set(target, reflection.id); + } + getReflectionPath(reflection: Reflection): string | undefined { return this.reflectionToPath.get(reflection.id); } @@ -52,7 +66,7 @@ export class FileRegistry { register( sourcePath: NormalizedPath, relativePath: NormalizedPath, - ): { target: number; anchor: string | undefined } | undefined { + ): { target: FileId; anchor: string | undefined } | undefined { return this.registerAbsolute( NormalizedPathUtils.resolve(NormalizedPathUtils.dirname(sourcePath), relativePath), ); @@ -67,7 +81,7 @@ export class FileRegistry { } resolve( - id: number, + id: FileId, project: ProjectReflection, ): string | Reflection | undefined { const reflId = this.mediaToReflection.get(id); @@ -77,7 +91,11 @@ export class FileRegistry { return this.mediaToPath.get(id); } - getName(id: number): string | undefined { + resolvePath(id: FileId): string | undefined { + return this.mediaToPath.get(id); + } + + getName(id: FileId): string | undefined { const absolute = this.mediaToPath.get(id); if (!absolute) return; @@ -137,19 +155,19 @@ export class FileRegistry { * a single object, and should merge in files from the other registries. */ fromObject(de: Deserializer, obj: JSONOutput.FileRegistry): void { - for (const [key, val] of Object.entries(obj.entries)) { - const absolute = NormalizedPathUtils.resolve(de.projectRoot, val); - de.oldFileIdToNewFileId[+key] = this.registerAbsolute(absolute).target; + for (const [fileId, path] of Object.entries(obj.entries)) { + const absolute = NormalizedPathUtils.resolve(de.projectRoot, path); + de.oldFileIdToNewFileId[+fileId as FileId] = this.registerAbsolute(absolute).target; } de.defer((project) => { - for (const [media, reflId] of Object.entries(obj.reflections)) { + for (const [fileId, reflId] of Object.entries(obj.reflections)) { const refl = project.getReflectionById( de.oldIdToNewId[reflId]!, ); if (refl) { this.mediaToReflection.set( - de.oldFileIdToNewFileId[+media]!, + de.oldFileIdToNewFileId[+fileId as FileId]!, refl.id, ); } diff --git a/src/lib/models/ProjectReflection.ts b/src/lib/models/ProjectReflection.ts index 3e1be097c..4a6ebaa0a 100644 --- a/src/lib/models/ProjectReflection.ts +++ b/src/lib/models/ProjectReflection.ts @@ -1,4 +1,4 @@ -import { type Reflection, TraverseProperty } from "./Reflection.js"; +import { type Reflection, type ReflectionId, TraverseProperty } from "./Reflection.js"; import { ContainerReflection } from "./ContainerReflection.js"; import { ReferenceReflection } from "./ReferenceReflection.js"; import type { DeclarationReflection } from "./DeclarationReflection.js"; @@ -49,7 +49,7 @@ export class ProjectReflection extends ContainerReflection { * * This may be replaced with a `Map` someday. */ - reflections: { [id: number]: Reflection } = { [this.id]: this }; + reflections: { [id: number]: Reflection } = {}; /** * The name of the package that this reflection documents according to package.json. @@ -421,7 +421,7 @@ export class ProjectReflection extends ContainerReflection { de.defer(() => { // Unnecessary conditional in release for (const [id, sid] of Object.entries(obj.symbolIdMap || {})) { - const refl = this.getReflectionById(de.oldIdToNewId[+id] ?? -1); + const refl = this.getReflectionById(de.oldIdToNewId[+id as ReflectionId] ?? -1); if (refl) { this.registerSymbolId(refl, new ReflectionSymbolId(sid)); } else { diff --git a/src/lib/models/ReferenceReflection.ts b/src/lib/models/ReferenceReflection.ts index 8eb7098fa..8637970f0 100644 --- a/src/lib/models/ReferenceReflection.ts +++ b/src/lib/models/ReferenceReflection.ts @@ -1,7 +1,7 @@ import { DeclarationReflection } from "./DeclarationReflection.js"; import { ReflectionKind } from "./kind.js"; import type { Deserializer, JSONOutput, Serializer } from "#serialization"; -import type { Reflection } from "./Reflection.js"; +import type { Reflection, ReflectionId } from "./Reflection.js"; /** * Describes a reflection which does not exist at this location, but is referenced. Used for imported reflections. @@ -83,7 +83,7 @@ export class ReferenceReflection extends DeclarationReflection { return { ...super.toObject(serializer), variant: this.variant, - target: this.tryGetTargetReflection()?.id ?? -1, + target: this.tryGetTargetReflection()?.id ?? -1 as ReflectionId, }; } diff --git a/src/lib/models/Reflection.ts b/src/lib/models/Reflection.ts index 62d7438c5..2354687f6 100644 --- a/src/lib/models/Reflection.ts +++ b/src/lib/models/Reflection.ts @@ -265,6 +265,10 @@ export type ReflectionVisitor = { [K in keyof ReflectionVariant]?: (refl: ReflectionVariant[K]) => void; }; +/** + * Alias for a `number` which references a reflection. + * `-1` may be used for an invalid reflection ID. + */ export type ReflectionId = number & { __reflectionIdBrand: never }; /** diff --git a/src/lib/models/types.ts b/src/lib/models/types.ts index 14e54166e..a1e99c80e 100644 --- a/src/lib/models/types.ts +++ b/src/lib/models/types.ts @@ -1,4 +1,4 @@ -import type { Reflection } from "./Reflection.js"; +import type { Reflection, ReflectionId } from "./Reflection.js"; import type { DeclarationReflection } from "./DeclarationReflection.js"; import type { ProjectReflection } from "./ProjectReflection.js"; import type { Deserializer, JSONOutput, Serializer } from "#serialization"; @@ -916,13 +916,13 @@ export class ReferenceType extends Type { */ preferValues = false; - private _target: ReflectionSymbolId | number; + private _target: ReflectionSymbolId | ReflectionId; @NonEnumerable private _project: ProjectReflection | null; private constructor( name: string, - target: ReflectionSymbolId | Reflection | number, + target: ReflectionSymbolId | Reflection | ReflectionId, project: ProjectReflection | null, qualifiedName: string, ) { @@ -948,7 +948,7 @@ export class ReferenceType extends Type { static createResolvedReference( name: string, - target: Reflection | number, + target: Reflection | ReflectionId, project: ProjectReflection | null, ) { return new ReferenceType(name, target, project, name); @@ -961,7 +961,7 @@ export class ReferenceType extends Type { * @internal */ static createBrokenReference(name: string, project: ProjectReflection) { - return new ReferenceType(name, -1, project, name); + return new ReferenceType(name, -1 as ReflectionId, project, name); } protected override getTypeString() { @@ -988,7 +988,7 @@ export class ReferenceType extends Type { if (typeof this._target === "number") { target = this._target; } else if (this._project?.symbolIdHasBeenRemoved(this._target)) { - target = -1; + target = -1 as ReflectionId; } else if (this.reflection) { target = this.reflection.id; } else { @@ -1035,24 +1035,26 @@ export class ReferenceType extends Type { override fromObject(de: Deserializer, obj: JSONOutput.ReferenceType): void { this.typeArguments = de.reviveMany(obj.typeArguments, (t) => de.constructType(t)); - if (typeof obj.target === "number" && obj.target !== -1) { - de.defer((project) => { - const target = project.getReflectionById( - de.oldIdToNewId[obj.target as number] ?? -1, - ); - if (target) { - this._project = project; - this._target = target.id; - } else { - de.logger.warn( - i18n.serialized_project_referenced_0_not_part_of_project( - JSON.stringify(obj.target), - ), + if (typeof obj.target === "number") { + if (obj.target === -1) { + this._target = -1 as ReflectionId; + } else { + de.defer((project) => { + const target = project.getReflectionById( + de.oldIdToNewId[obj.target as ReflectionId] ?? -1, ); - } - }); - } else if (obj.target === -1) { - this._target = -1; + if (target) { + this._project = project; + this._target = target.id; + } else { + de.logger.warn( + i18n.serialized_project_referenced_0_not_part_of_project( + JSON.stringify(obj.target), + ), + ); + } + }); + } } else { this._project = de.project!; this._target = new ReflectionSymbolId(obj.target); diff --git a/src/lib/output/plugins/AssetsPlugin.ts b/src/lib/output/plugins/AssetsPlugin.ts index f477677a7..71b53d35b 100644 --- a/src/lib/output/plugins/AssetsPlugin.ts +++ b/src/lib/output/plugins/AssetsPlugin.ts @@ -1,6 +1,6 @@ import { RendererComponent } from "../components.js"; import { RendererEvent } from "../events.js"; -import { copySync, readFile, writeFileSync } from "../../utils/fs.js"; +import { copySync, isFile, readFile, writeFileSync } from "../../utils/fs.js"; import { DefaultTheme } from "../themes/default/DefaultTheme.js"; import { getStyles } from "../../utils/highlighter.js"; import { type EnumKeys, getEnumKeys, i18n, type NormalizedPath } from "#utils"; @@ -123,7 +123,13 @@ export class AssetsPlugin extends RendererComponent { const media = join(event.outputDirectory, "media"); const toCopy = event.project.files.getNameToAbsoluteMap(); for (const [fileName, absolute] of toCopy.entries()) { - copySync(absolute, join(media, fileName)); + if (isFile(absolute)) { + copySync(absolute, join(media, fileName)); + } else { + this.application.logger.warn( + i18n.relative_path_0_is_not_a_file_and_will_not_be_copied_to_output(absolute), + ); + } } } } diff --git a/src/lib/serialization/deserializer.ts b/src/lib/serialization/deserializer.ts index 4bb458f06..d2cb57bde 100644 --- a/src/lib/serialization/deserializer.ts +++ b/src/lib/serialization/deserializer.ts @@ -3,6 +3,7 @@ import { ConditionalType, DeclarationReflection, DocumentReflection, + type FileId, type FileRegistry, IndexedAccessType, InferredType, @@ -19,6 +20,7 @@ import { ReferenceReflection, ReferenceType, Reflection, + type ReflectionId, ReflectionKind, ReflectionType, type ReflectionVariant, @@ -166,7 +168,7 @@ export class Deserializer { }, reference(obj) { // Correct reference will be restored in fromObject - return ReferenceType.createResolvedReference(obj.name, -2, null); + return ReferenceType.createResolvedReference(obj.name, -2 as ReflectionId, null); }, reflection(obj, de) { return new ReflectionType( @@ -213,8 +215,8 @@ export class Deserializer { */ projectRoot!: NormalizedPath; - oldIdToNewId: Record = {}; - oldFileIdToNewFileId: Record = {}; + oldIdToNewId: Record = {}; + oldFileIdToNewFileId: Record = {}; project: ProjectReflection | undefined; constructor(public logger: Logger) {} diff --git a/src/lib/serialization/schema.ts b/src/lib/serialization/schema.ts index 0c9dd6b26..1db8459f5 100644 --- a/src/lib/serialization/schema.ts +++ b/src/lib/serialization/schema.ts @@ -77,6 +77,9 @@ type S = { -readonly [K2 in K]: ToSerialized; }; +export type ReflectionId = M.ReflectionId; +export type FileId = M.FileId; + export interface ReflectionSymbolId { packageName: string; packagePath: NormalizedPath; @@ -126,7 +129,7 @@ export interface ReferenceReflection * -1 if the reference refers to a symbol that does not exist in the documentation. * Otherwise, the reflection ID. */ - target: number; + target: ReflectionId; } /** @category Reflections */ @@ -203,7 +206,7 @@ export interface ProjectReflection extends */ schemaVersion: string; symbolIdMap: - | Record + | Record | IfInternal; files: FileRegistry; } @@ -216,7 +219,7 @@ export interface ContainerReflection extends "children" | "documents" | "groups" | "categories" > { - childrenIncludingDocuments?: number[]; + childrenIncludingDocuments?: ReflectionId[]; } /** @category Reflections */ @@ -294,7 +297,7 @@ export interface ReferenceType extends "type" | "name" | "typeArguments" | "package" | "externalUrl" > { - target: number | ReflectionSymbolId; + target: ReflectionId | ReflectionSymbolId; qualifiedName?: string; refersToTypeParameter?: boolean; preferValues?: boolean; @@ -388,7 +391,7 @@ export interface InlineTagDisplayPart { kind: "inline-tag"; tag: `@${string}`; text: string; - target?: string | number | ReflectionSymbolId; + target?: string | ReflectionId | ReflectionSymbolId; tsLinkText?: string; } @@ -406,7 +409,7 @@ export interface RelativeLinkDisplayPart { /** * File ID, if present */ - target?: number; + target?: FileId; /** * Anchor within the target file, if present */ @@ -417,7 +420,7 @@ export interface SourceReference extends S; + entries: Record; /** File ID to reflection ID */ - reflections: Record; + reflections: Record; } diff --git a/src/lib/utils/ValidatingFileRegistry.ts b/src/lib/utils/ValidatingFileRegistry.ts index f16afad64..0a52b52ca 100644 --- a/src/lib/utils/ValidatingFileRegistry.ts +++ b/src/lib/utils/ValidatingFileRegistry.ts @@ -1,16 +1,19 @@ -import { FileRegistry } from "../models/FileRegistry.js"; -import { isFile } from "./fs.js"; +import { type FileId, FileRegistry } from "../models/FileRegistry.js"; import type { Deserializer, JSONOutput } from "#serialization"; import { i18n, type NormalizedPath, NormalizedPathUtils } from "#utils"; +import { existsSync } from "fs"; export class ValidatingFileRegistry extends FileRegistry { override register( sourcePath: NormalizedPath, relativePath: NormalizedPath, - ): { target: number; anchor: string | undefined } | undefined { + ): { target: FileId; anchor: string | undefined } | undefined { const absolute = NormalizedPathUtils.resolve(NormalizedPathUtils.dirname(sourcePath), relativePath); const absoluteWithoutAnchor = absolute.replace(/#.*/, ""); - if (!isFile(absoluteWithoutAnchor)) { + // Note: We allow paths to directories to be registered here, but the AssetsPlugin will not + // copy them to the output path. This is so that we can link to directories and associate them + // with reflections in packages mode. + if (!existsSync(absoluteWithoutAnchor)) { return; } return this.registerAbsolute(absolute); @@ -19,9 +22,9 @@ export class ValidatingFileRegistry extends FileRegistry { override fromObject(de: Deserializer, obj: JSONOutput.FileRegistry) { for (const [key, val] of Object.entries(obj.entries)) { const absolute = NormalizedPathUtils.resolve(de.projectRoot, val); - if (!isFile(absolute)) { + if (!existsSync(absolute)) { de.logger.warn( - i18n.saved_relative_path_0_resolved_from_1_is_not_a_file( + i18n.saved_relative_path_0_resolved_from_1_does_not_exist( val, de.projectRoot, ), @@ -29,7 +32,7 @@ export class ValidatingFileRegistry extends FileRegistry { continue; } - de.oldFileIdToNewFileId[+key] = this.registerAbsolute(absolute).target; + de.oldFileIdToNewFileId[+key as FileId] = this.registerAbsolute(absolute).target; } de.defer((project) => { @@ -39,7 +42,7 @@ export class ValidatingFileRegistry extends FileRegistry { ); if (refl) { this.mediaToReflection.set( - de.oldFileIdToNewFileId[+media]!, + de.oldFileIdToNewFileId[+media as FileId]!, refl.id, ); } else { diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 27d50c9d2..f474337d6 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -10,7 +10,7 @@ import { lexCommentString } from "../lib/converter/comments/rawLexer.js"; import { Comment, type CommentDisplayPart, CommentTag } from "../lib/models/index.js"; import { TestLogger } from "./TestLogger.js"; import { extractTagName } from "../lib/converter/comments/tagName.js"; -import { FileRegistry } from "../lib/models/FileRegistry.js"; +import { type FileId, FileRegistry } from "../lib/models/FileRegistry.js"; import { dedent, MinimalSourceFile, type NormalizedPath } from "#utils"; const CONTENT_PARTS = ["text", "`code`"]; @@ -32,7 +32,7 @@ function* generateLinkTitleCases() { { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: ")" }, @@ -47,7 +47,7 @@ function* generateLinkTitleCases() { { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: ")" }, @@ -1480,14 +1480,14 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: ") ![](" }, { kind: "relative-link", text: "image.png", - target: 2, + target: 2 as FileId, targetAnchor: undefined, }, { @@ -1519,7 +1519,7 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, // Labels can also include single newlines @@ -1529,7 +1529,7 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, // But not double! @@ -1554,7 +1554,7 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: ")" }, @@ -1578,7 +1578,7 @@ describe("Comment Parser", () => { const link = { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, } as const; @@ -1622,14 +1622,14 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./example.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: "\n[2]:" }, { kind: "relative-link", text: "<./example with space>", - target: 2, + target: 2 as FileId, targetAnchor: undefined, }, { @@ -1667,14 +1667,14 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./test.png", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: '" >\n { { kind: "relative-link", text: "./test.png", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: '" >\n { { kind: "relative-link", text: "./path.md#foo", - target: 1, + target: 1 as FileId, targetAnchor: "foo", }, { kind: "text", text: '" >\n[test](' }, { kind: "relative-link", text: "./test.txt#bar", - target: 2, + target: 2 as FileId, targetAnchor: "bar", }, { kind: "text", text: ")" }, @@ -1758,15 +1758,15 @@ describe("Comment Parser", () => { { kind: "relative-link", text: "./&a.png", - target: 1, + target: 1 as FileId, targetAnchor: undefined, }, { kind: "text", text: '" >' }, ] satisfies CommentDisplayPart[], ); - equal(files.getName(1), "&a.png"); - equal(files.getName(1), "&a.png"); + equal(files.getName(1 as FileId), "&a.png"); + equal(files.getName(1 as FileId), "&a.png"); }); }); @@ -1831,7 +1831,7 @@ describe("Raw Comment Parser", () => { const link = { kind: "relative-link", text: "./relative.md", - target: 1, + target: 1 as FileId, targetAnchor: undefined, } as const; diff --git a/src/test/converter.test.ts b/src/test/converter.test.ts index bad427dc4..ce1072d2a 100644 --- a/src/test/converter.test.ts +++ b/src/test/converter.test.ts @@ -21,7 +21,6 @@ import type { ModelToObject } from "../lib/serialization/schema.js"; import { getExpandedEntryPointsForPaths, normalizePath } from "../lib/utils/index.js"; import { getConverterApp, getConverterBase, getConverterProgram } from "./programs.js"; import { FileRegistry } from "../lib/models/FileRegistry.js"; -import { ValidatingFileRegistry } from "../lib/utils/ValidatingFileRegistry.js"; const comparisonSerializer = new Serializer(); comparisonSerializer.addSerializer({ @@ -201,7 +200,7 @@ describe("Converter", function () { it(`[${file}] converts fixtures`, function () { before(); resetReflectionID(); - app.files = new ValidatingFileRegistry(); + app.files = new FileRegistry(); const entryPoints = getExpandedEntryPointsForPaths( app.logger, [path], diff --git a/src/test/models/types.test.ts b/src/test/models/types.test.ts index 516e4990d..966a68c97 100644 --- a/src/test/models/types.test.ts +++ b/src/test/models/types.test.ts @@ -266,7 +266,7 @@ describe("Type.toString", () => { const project = new ProjectReflection("test", new FileRegistry()); const type = new T.OptionalType( new T.QueryType( - T.ReferenceType.createResolvedReference("X", -1, project), + T.ReferenceType.createBrokenReference("X", project), ), ); equal(type.toString(), "typeof X?"); @@ -287,7 +287,7 @@ describe("Type.toString", () => { const project = new ProjectReflection("test", new FileRegistry()); const type = new T.TypeOperatorType( new T.QueryType( - T.ReferenceType.createResolvedReference("X", -1, project), + T.ReferenceType.createBrokenReference("X", project), ), "keyof", ); From a2f0d16e903f8fe9f11c645794ee27d885c34c2d Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Tue, 3 Jun 2025 21:20:45 -0600 Subject: [PATCH 042/119] Handpick smaller suite of link tests --- src/test/comments.test.ts | 124 +++++++++++--------------------------- 1 file changed, 36 insertions(+), 88 deletions(-) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index f474337d6..0d2d77405 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -13,68 +13,6 @@ import { extractTagName } from "../lib/converter/comments/tagName.js"; import { type FileId, FileRegistry } from "../lib/models/FileRegistry.js"; import { dedent, MinimalSourceFile, type NormalizedPath } from "#utils"; -const CONTENT_PARTS = ["text", "`code`"]; -const SEPARATORS = [" ", "\n", "\n ", "\n\n"]; -const MAX_CONTENT_PARTS = 3; -function* generateLinkTitleCases() { - const makeCase = (linkTitle: string) => { - const ok = !linkTitle.includes("\n\n"); - const input = `[${linkTitle}](./relative.md)`; - const expect: CommentDisplayPart[] = input - .replace("(./relative.md)", "(") - .split(/(`code`)/g) - .map((text) => { - const kind = text[0] === "`" ? "code" : "text"; - return { kind, text }; - }); - if (ok) { - expect.push( - { - kind: "relative-link", - text: "./relative.md", - target: 1 as FileId, - targetAnchor: undefined, - }, - { kind: "text", text: ")" }, - ); - } else { - expect[expect.length - 1].text += "./relative.md)"; - } - expect[expect.length - 1].text += "\n["; - expect.push( - { kind: "code", text: "`code`" }, - { kind: "text", text: "](" }, - { - kind: "relative-link", - text: "./relative.md", - target: 1 as FileId, - targetAnchor: undefined, - }, - { kind: "text", text: ")" }, - ); - return { input: input + "\n[`code`](./relative.md)", expect }; - }; - for (let n = 1; n <= MAX_CONTENT_PARTS; n++) { - // 3 bits for each part (except the first): - // selects a part from CONTENT_PARTS - // selects a preceding separator from SEPARATORS - for (let bits = 0; bits < 2 ** (3 * n); bits += 4) { - const inner = Array.from({ length: n }, (_, i) => { - const partSelections = bits >> (3 * i); - const part = CONTENT_PARTS[partSelections & 4 ? 1 : 0]; - const sepText = SEPARATORS[partSelections & 3]; - return i === 0 ? part : `${sepText}${part}`; - }).join(""); - // We also wrap the parts with arbitrary leading and trailing whitespace - for (const prefix of ["", ...SEPARATORS]) { - for (const suffix of ["", ...SEPARATORS]) { - yield makeCase(`${prefix}${inner}${suffix}`); - } - } - } - } -} - describe("Block Comment Lexer", () => { function lex(text: string): Token[] { return Array.from(lexBlockComment(text)); @@ -1595,16 +1533,25 @@ describe("Comment Parser", () => { }); it("Parses markdown link titles with arbitrarily-separated arbitrary combinations of text and code", () => { - const embedInComment = (input: string) => { - const lines = input.split("\n"); - const embedded = `/**\n${lines.map(line => " * " + line).join("\n")}\n */`; - return getComment(embedded); - }; - - for (const { input, expect } of generateLinkTitleCases()) { - const comment = embedInComment(input); - equal(comment.summary, expect, `input: ${JSON.stringify(input)}`); + const comment = `/** + [text](./1) + [text \`code\`]( \t ./2 ) + [\ntext \`code\`\n](./3) + [\ntext\n\`code\`\n]( ./4 ) + [ \t\`code\` text]( \t./5\t ) + + [\n\n\`code\`](./no1) + [text\n\n](./no2) + [ text\n](\n \n ./no3 \n) + [ text\n\ntext](./no4) + */`; + + const relativeParts = getComment(comment).summary.filter(p => p.kind === "relative-link"); + + for (let i = 0; i < relativeParts.length; i++) { + equal(relativeParts[i].text, `./${i + 1}`); } + equal(relativeParts.length, 5); }); it("Recognizes markdown reference definition blocks", () => { @@ -1848,27 +1795,28 @@ describe("Raw Comment Parser", () => { }); it("Parses markdown link titles with arbitrarily-separated arbitrary combinations of text and code", () => { - for (const { input, expect } of generateLinkTitleCases()) { - const comment = getComment(input); - equal(comment.content, expect, `input: ${JSON.stringify(input)}`); + const comment = `/** + [text](./1) + [text \`code\`]( \t ./2 ) + [\ntext \`code\`\n](./3) + [\ntext\n\`code\`\n]( ./4 ) + [ \t\`code\` text]( \t./5\t ) + + [\n\n\`code\`](./no1) + [text\n\n](./no2) + [ text\n](\n \n ./no3 \n) + [ text\n\ntext](./no4) + */`; + + const relativeParts = getComment(comment).content.filter(p => p.kind === "relative-link"); + + for (let i = 0; i < relativeParts.length; i++) { + equal(relativeParts[i].text, `./${i + 1}`); } + equal(relativeParts.length, 5); }); }); -describe("Markdown Link Title Generation", () => { - const inputs: string[] = []; - for (const { input } of generateLinkTitleCases()) { - inputs.push(input); - } - const inputsSet = new Set(inputs); - equal(inputsSet.size, inputs.length, "each generated input must be unique"); - - const expectCount = Array.from({ length: MAX_CONTENT_PARTS }, (_, i) => i + 1) - .map(n => (SEPARATORS.length * CONTENT_PARTS.length) ** n / SEPARATORS.length * (SEPARATORS.length + 1) ** 2) - .reduce((a, b) => a + b); - equal(inputsSet.size, expectCount, "generated input count"); -}); - describe("extractTagName", () => { it("Handles simple name", () => { equal(extractTagName("T - abc"), { name: "T", newText: "abc" }); From dbefdbd6bd40a62fea31a81c433405123792ea25 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 26 Jun 2025 19:44:29 -0600 Subject: [PATCH 043/119] Recognize and respect type-only exports Resolves #2962 --- CHANGELOG.md | 1 + src/lib/converter/context.ts | 1 + src/lib/converter/symbols.ts | 58 +++++++++++++++++++++++++++- src/test/converter2/issues/gh2962.ts | 18 +++++++++ src/test/issues.c2.test.ts | 16 ++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/test/converter2/issues/gh2962.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b30ec1ce..6ae3912b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ title: Changelog - Attempting to highlight a supported language which is not enabled is now a warning, not an error, #2956. - Improved compatibility with CommonMark's link parsing, #2959. +- Classes, variables, and functions exported with `export { type X }` are now detected and converted as interfaces/type aliases, #2962. ## v0.28.5 (2025-05-26) diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index f745025a5..843802ac4 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -190,6 +190,7 @@ export class Context { exportSymbol: ts.Symbol | undefined, ) { if ( + !reflection.comment && exportSymbol && reflection.kind & (ReflectionKind.SomeModule | ReflectionKind.Reference) diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index 8ad104e3c..6ae22c7b9 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -409,6 +409,42 @@ function convertTypeAlias( } } +function convertTypeAliasFromValueDeclaration( + context: Context, + symbol: ts.Symbol, + exportSymbol: ts.Symbol | undefined, + valueKind: ReflectionKind, +): undefined { + const comment = context.getComment(symbol, valueKind); + + const reflection = new DeclarationReflection( + exportSymbol?.name || symbol.name, + ReflectionKind.TypeAlias, + context.scope, + ); + reflection.comment = comment; + context.postReflectionCreation(reflection, symbol, exportSymbol); + context.finalizeDeclarationReflection(reflection); + + reflection.type = context.converter.convertType( + context.withScope(reflection), + context.checker.getTypeOfSymbol(symbol), + ); + + if (reflection.type.type === "reflection" && reflection.type.declaration.children) { + // #2817 lift properties of object literal types up to the reflection level. + const typeDecl = reflection.type.declaration; + reflection.project.mergeReflections(typeDecl, reflection); + delete reflection.type; + + // When created any signatures will be created with __type as their + // name, rename them so that they have the alias's name as their name + for (const sig of reflection.signatures || []) { + sig.name = reflection.name; + } + } +} + function attachUnionComments( context: Context, declaration: ts.TypeAliasDeclaration, @@ -495,6 +531,10 @@ function convertFunctionOrMethod( symbol: ts.Symbol, exportSymbol?: ts.Symbol, ): undefined | ts.SymbolFlags { + if (isTypeOnlyExport(exportSymbol)) { + return convertTypeAliasFromValueDeclaration(context, symbol, exportSymbol, ReflectionKind.Function); + } + // Can't just check method flag because this might be called for properties as well // This will *NOT* be called for variables that look like functions, they need a special case. const isMethod = !!( @@ -573,7 +613,7 @@ function convertClassOrInterface( exportSymbol?: ts.Symbol, ) { const reflection = context.createDeclarationReflection( - ts.SymbolFlags.Class & symbol.flags + (ts.SymbolFlags.Class & symbol.flags) && !isTypeOnlyExport(exportSymbol) ? ReflectionKind.Class : ReflectionKind.Interface, symbol, @@ -618,7 +658,7 @@ function convertClassOrInterface( context.finalizeDeclarationReflection(reflection); - if (classDeclaration) { + if (classDeclaration && reflection.kind === ReflectionKind.Class) { // Classes can have static props const staticType = context.checker.getTypeOfSymbolAtLocation( symbol, @@ -984,6 +1024,10 @@ function convertVariable( symbol: ts.Symbol, exportSymbol?: ts.Symbol, ): undefined | ts.SymbolFlags { + if (isTypeOnlyExport(exportSymbol)) { + return convertTypeAliasFromValueDeclaration(context, symbol, exportSymbol, ReflectionKind.Variable); + } + const declaration = symbol.getDeclarations()?.[0]; const comment = context.getComment(symbol, ReflectionKind.Variable); @@ -1485,3 +1529,13 @@ function isFunctionLikeInitializer(node: ts.Expression): boolean { return false; } + +function isTypeOnlyExport(symbol: ts.Symbol | undefined): boolean { + if (!symbol) return false; + + const declaration = symbol.declarations?.[0]; + if (!declaration) return false; + if (!ts.isExportSpecifier(declaration)) return false; + + return declaration.isTypeOnly || declaration.parent.parent.isTypeOnly; +} diff --git a/src/test/converter2/issues/gh2962.ts b/src/test/converter2/issues/gh2962.ts new file mode 100644 index 000000000..9a41ee059 --- /dev/null +++ b/src/test/converter2/issues/gh2962.ts @@ -0,0 +1,18 @@ +class Class { + msg: string; + + constructor(msg: string) { + this.msg = msg; + } +} + +const Var = 123; + +function Func(a: T) {} + +export { type Class, type Func, type Var }; + +class Class2 {} +const Var2 = 123; +function Func2() {} +export type { Class2, Func2, Var2 }; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 229ac06e0..c9a37079f 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2140,4 +2140,20 @@ describe("Issue Tests", () => { equal(query(project, "InterfaceA.propertyB").type?.toString(), "AliasB"); equal(query(project, "InterfaceA.propertyC").type?.toString(), "AliasC"); }); + + it("#2962 handles type-only exports", () => { + const project = convert(); + equal(project.children?.map(c => [c.name, ReflectionKind[c.kind]]), [ + ["Class", "Interface"], + ["Class2", "Interface"], + ["Func", "TypeAlias"], + ["Func2", "TypeAlias"], + ["Var", "TypeAlias"], + ["Var2", "TypeAlias"], + ]); + + equal(query(project, "Class").children?.map(c => c.name), ["msg"]); + equal(query(project, "Func").type?.toString(), "(a: T) => void"); + equal(query(project, "Var").type?.toString(), "123"); + }); }); From 1ef4ff3443df82397669338d1c65c5026f501a72 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 26 Jun 2025 20:29:22 -0600 Subject: [PATCH 044/119] Improve link validation warning messages Resolves #2967 --- CHANGELOG.md | 1 + src/lib/internationalization/locales/en.cts | 2 + src/lib/validation/links.ts | 91 +++++++++---------- src/test/behavior.c2.test.ts | 20 ++++ .../behavior/linkResolutionErrors.ts | 6 ++ src/test/converter2/issues/gh2681.ts | 4 - src/test/issues.c2.test.ts | 37 +------- 7 files changed, 73 insertions(+), 88 deletions(-) create mode 100644 src/test/converter2/behavior/linkResolutionErrors.ts delete mode 100644 src/test/converter2/issues/gh2681.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ae3912b2..8f69d24bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ title: Changelog - Attempting to highlight a supported language which is not enabled is now a warning, not an error, #2956. - Improved compatibility with CommonMark's link parsing, #2959. - Classes, variables, and functions exported with `export { type X }` are now detected and converted as interfaces/type aliases, #2962. +- Improved warning messaging for links to symbols which were resolved, but the symbols were not included in the documentation, #2967. ## v0.28.5 (2025-05-26) diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index 4979afda6..f95e82520 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -91,6 +91,8 @@ export = { inline_tag_not_closed: `Inline tag is not closed`, // validation + comment_for_0_links_to_1_not_included_in_docs_use_external_link_2: + `The comment for {0} links to "{1}" which was resolved but is not included in the documentation. To fix this warning export it or add {2} to the externalSymbolLinkMappings option`, failed_to_resolve_link_to_0_in_comment_for_1: `Failed to resolve link to "{0}" in comment for {1}`, failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2: `Failed to resolve link to "{0}" in comment for {1}. You may have wanted "{2}"`, diff --git a/src/lib/validation/links.ts b/src/lib/validation/links.ts index ac1b80dc5..e79e56a99 100644 --- a/src/lib/validation/links.ts +++ b/src/lib/validation/links.ts @@ -2,6 +2,7 @@ import { i18n, type Logger } from "#utils"; import { type Comment, type CommentDisplayPart, + type InlineTagDisplayPart, type ProjectReflection, type Reflection, ReflectionKind, @@ -11,7 +12,7 @@ import { const linkTags = ["@link", "@linkcode", "@linkplain"]; function getBrokenPartLinks(parts: readonly CommentDisplayPart[]) { - const links: string[] = []; + const links: InlineTagDisplayPart[] = []; for (const part of parts) { if ( @@ -19,7 +20,7 @@ function getBrokenPartLinks(parts: readonly CommentDisplayPart[]) { linkTags.includes(part.tag) && (!part.target || part.target instanceof ReflectionSymbolId) ) { - links.push(part.text.trim()); + links.push(part); } } @@ -53,21 +54,22 @@ export function validateLinks( function checkReflection(reflection: Reflection, logger: Logger) { if (reflection.isProject() || reflection.isDeclaration()) { for (const broken of getBrokenPartLinks(reflection.readme || [])) { + const linkText = broken.text.trim(); // #2360, "@" is a future reserved character in TSDoc component paths // If a link starts with it, and doesn't include a module source indicator "!" // then the user probably is trying to link to a package containing "@" with an absolute link. - if (broken.startsWith("@") && !broken.includes("!")) { + if (linkText.startsWith("@") && !linkText.includes("!")) { logger.warn( i18n.failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2( - broken, + linkText, reflection.getFriendlyFullName(), - broken.replace(/[.#~]/, "!"), + linkText.replace(/[.#~]/, "!"), ), ); } else { logger.warn( i18n.failed_to_resolve_link_to_0_in_readme_for_1( - broken, + linkText, reflection.getFriendlyFullName(), ), ); @@ -77,21 +79,19 @@ function checkReflection(reflection: Reflection, logger: Logger) { if (reflection.isDocument()) { for (const broken of getBrokenPartLinks(reflection.content)) { - // #2360, "@" is a future reserved character in TSDoc component paths - // If a link starts with it, and doesn't include a module source indicator "!" - // then the user probably is trying to link to a package containing "@" with an absolute link. - if (broken.startsWith("@") && !broken.includes("!")) { + const linkText = broken.text.trim(); + if (linkText.startsWith("@") && !linkText.includes("!")) { logger.warn( i18n.failed_to_resolve_link_to_0_in_document_1_may_have_meant_2( - broken, + linkText, reflection.getFriendlyFullName(), - broken.replace(/[.#~]/, "!"), + linkText.replace(/[.#~]/, "!"), ), ); } else { logger.warn( i18n.failed_to_resolve_link_to_0_in_document_1( - broken, + linkText, reflection.getFriendlyFullName(), ), ); @@ -100,25 +100,7 @@ function checkReflection(reflection: Reflection, logger: Logger) { } for (const broken of getBrokenLinks(reflection.comment)) { - // #2360, "@" is a future reserved character in TSDoc component paths - // If a link starts with it, and doesn't include a module source indicator "!" - // then the user probably is trying to link to a package containing "@" with an absolute link. - if (broken.startsWith("@") && !broken.includes("!")) { - logger.warn( - i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2( - broken, - reflection.getFriendlyFullName(), - broken.replace(/[.#~]/, "!"), - ), - ); - } else { - logger.warn( - i18n.failed_to_resolve_link_to_0_in_comment_for_1( - broken, - reflection.getFriendlyFullName(), - ), - ); - } + reportBrokenCommentLink(broken, reflection, logger); } if ( @@ -132,22 +114,35 @@ function checkReflection(reflection: Reflection, logger: Logger) { getBrokenPartLinks, ) ) { - if (broken.startsWith("@") && !broken.includes("!")) { - logger.warn( - i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2( - broken, - reflection.getFriendlyFullName(), - broken.replace(/[.#~]/, "!"), - ), - ); - } else { - logger.warn( - i18n.failed_to_resolve_link_to_0_in_comment_for_1( - broken, - reflection.getFriendlyFullName(), - ), - ); - } + reportBrokenCommentLink(broken, reflection, logger); } } } + +function reportBrokenCommentLink(broken: InlineTagDisplayPart, reflection: Reflection, logger: Logger) { + const linkText = broken.text.trim(); + if (broken.target instanceof ReflectionSymbolId) { + logger.warn( + i18n.comment_for_0_links_to_1_not_included_in_docs_use_external_link_2( + reflection.getFriendlyFullName(), + linkText, + `{ "${broken.target.packageName}": { "${broken.target.qualifiedName}": "#" }}`, + ), + ); + } else if (linkText.startsWith("@") && !linkText.includes("!")) { + logger.warn( + i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2( + linkText, + reflection.getFriendlyFullName(), + linkText.replace(/[.#~]/, "!"), + ), + ); + } else { + logger.warn( + i18n.failed_to_resolve_link_to_0_in_comment_for_1( + linkText, + reflection.getFriendlyFullName(), + ), + ); + } +} diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index e240c3332..cf073fbcb 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -887,6 +887,26 @@ describe("Behavior Tests", () => { ]); }); + it("Handles links which do not resolve correctly", () => { + const project = convert("linkResolutionErrors"); + app.options.setValue("validation", { + invalidLink: true, + notDocumented: false, + notExported: false, + }); + + app.validate(project); + logger.expectMessage( + 'warn: The comment for abc links to "Map.size" which was resolved but is not included in the documentation. To fix this warning export it or add { "typescript": { "Map.size": "#" }} to the externalSymbolLinkMappings option', + ); + logger.expectMessage( + 'warn: Failed to resolve link to "DoesNotExist" in comment for abc', + ); + logger.expectMessage( + 'warn: Failed to resolve link to "@typedoc/foo.DoesNotExist" in comment for abc. You may have wanted "@typedoc/foo!DoesNotExist"', + ); + }); + it("Handles merged declarations", () => { const project = convert("mergedDeclarations"); const a = query(project, "SingleCommentMultiDeclaration"); diff --git a/src/test/converter2/behavior/linkResolutionErrors.ts b/src/test/converter2/behavior/linkResolutionErrors.ts new file mode 100644 index 000000000..02e8d9f22 --- /dev/null +++ b/src/test/converter2/behavior/linkResolutionErrors.ts @@ -0,0 +1,6 @@ +/** + * {@link Map.size} TS resolves link, not included in docs #2700 #2967 + * {@link DoesNotExist} Symbol does not exist #2681 + * {@link @typedoc/foo.DoesNotExist} Symbol does not exist, looks like an attempt to link to a package directly #2360 + */ +export const abc = new Map(); diff --git a/src/test/converter2/issues/gh2681.ts b/src/test/converter2/issues/gh2681.ts deleted file mode 100644 index 1bf23a79a..000000000 --- a/src/test/converter2/issues/gh2681.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * {@link Generator} - */ -export const bug = 123; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index c9a37079f..c5d1410a3 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -9,7 +9,6 @@ import { QueryType, ReferenceReflection, ReflectionKind, - ReflectionSymbolId, ReflectionType, SignatureReflection, UnionType, @@ -1671,16 +1670,6 @@ describe("Issue Tests", () => { logger.expectNoOtherMessages(); }); - it("#2681 reports warnings on @link tags which resolve to a type not included in the documentation", () => { - const project = convert(); - app.options.setValue("validation", false); - app.options.setValue("validation", { invalidLink: true }); - app.validate(project); - logger.expectMessage( - 'warn: Failed to resolve link to "Generator" in comment for bug', - ); - }); - it("#2683 supports @param on parameters with functions", () => { const project = convert(); const action = querySig(project, "action"); @@ -1733,31 +1722,7 @@ describe("Issue Tests", () => { ); }); - it("#2700a correctly parses links to global properties", () => { - const project = convert(); - app.options.setValue("validation", { - invalidLink: true, - notDocumented: false, - notExported: false, - }); - - app.validate(project); - logger.expectMessage( - 'warn: Failed to resolve link to "Map.size | size user specified" in comment for abc', - ); - logger.expectMessage( - 'warn: Failed to resolve link to "Map.size user specified" in comment for abc', - ); - logger.expectMessage( - 'warn: Failed to resolve link to "Map.size" in comment for abc', - ); - - const abc = query(project, "abc"); - const link = abc.comment?.summary.find((c) => c.kind === "inline-tag"); - ok(link?.target instanceof ReflectionSymbolId); - }); - - it("#2700b respects user specified link text when resolving external links", () => { + it("#2700 respects user specified link text when resolving external links", () => { const project = convert(); const abc = query(project, "abc"); From e2298227dc9f6e02d64b07668c7d49352509deb0 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 26 Jun 2025 20:43:31 -0600 Subject: [PATCH 045/119] Fix nested deserialization of documents Co-Authored-By: Sebastian Mueller --- CHANGELOG.md | 5 +++++ src/lib/models/DocumentReflection.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f69d24bb..06ac2c5d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ title: Changelog - Improved compatibility with CommonMark's link parsing, #2959. - Classes, variables, and functions exported with `export { type X }` are now detected and converted as interfaces/type aliases, #2962. - Improved warning messaging for links to symbols which were resolved, but the symbols were not included in the documentation, #2967. +- Fixed an issue preventing nested documents from being deserialized from TypeDoc's JSON output or used in packages mode, #2969. + +### Thanks! + +- @yGuy ## v0.28.5 (2025-05-26) diff --git a/src/lib/models/DocumentReflection.ts b/src/lib/models/DocumentReflection.ts index 2cb21f2e5..d19d62b4f 100644 --- a/src/lib/models/DocumentReflection.ts +++ b/src/lib/models/DocumentReflection.ts @@ -79,6 +79,6 @@ export class DocumentReflection extends Reflection { this.content = Comment.deserializeDisplayParts(de, obj.content); this.frontmatter = obj.frontmatter; this.relevanceBoost = obj.relevanceBoost; - this.children = de.reviveMany(obj.children, (obj) => de.reflectionBuilders.document(this, obj)); + this.children = de.reviveMany(obj.children, (obj) => de.constructReflection(obj)); } } From a68c3bbd8cb646bb817417217feaf0e14cdff8cc Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 26 Jun 2025 21:39:25 -0600 Subject: [PATCH 046/119] Support comments on source declarations in more cases This seems like a reasonable tradeoff between allowing doc comments to be placed on members which are otherwise undocumentable and avoiding issues with functions where comments on the export declaration would only partially override the comments due to `@param` comments being present on the parameters very early. Resolves #2964 --- CHANGELOG.md | 1 + site/doc-comments/index.md | 48 ++++++++++++++++++++++++++ src/lib/converter/context.ts | 16 +++++++-- src/test/converter2/issues/gh2964.d.ts | 17 +++++++++ src/test/issues.c2.test.ts | 5 +++ 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/test/converter2/issues/gh2964.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 06ac2c5d1..0d353be73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ title: Changelog ### Features - TypeDoc now supports resolving relative paths in links to the package directory as belonging to the project, #2961. +- Declarations without comments will now check for comments on their export specifier, #2964. ### Bug Fixes diff --git a/site/doc-comments/index.md b/site/doc-comments/index.md index cb7de493d..f0e2c7441 100644 --- a/site/doc-comments/index.md +++ b/site/doc-comments/index.md @@ -79,6 +79,54 @@ Will be rendered as: > function example(): void; > ``` +## Comment Discovery + +In most cases, TypeDoc's comment discovery closely mirrors TypeScript's discovery. If a comment is placed +directly before a declaration or typically belongs to a declaration but lives on a parent node, TypeDoc +will include it in the documentation. + +```ts +/** + * This works + * @param x this works + */ +function example(x: string, /** This too */ y: number) {} +/** This also works */ +class Example2 {} +``` + +TypeDoc also supports discovering comments in some locations which TypeScript does not. + +1. Comments on type aliases directly containing unions may have comments before each union branch + to document the union. + + ```ts + type Choices = + /** Comment for option 1 */ + | "option_1" + /** Comment for option 2 */ + | { option_1: number }; + ``` + +2. Comments on export specifiers which export (or re-export) a member. + + ```ts + /** A comment here will take precedence over a module comment in Lib */ + export * as Lib from "lib"; + ``` + + Comments on export specifiers only have higher priority than the module comment for modules + and references where the symbol appears in the documentation multiple times. + + ```ts + export * as Lib from "lib"; // Use the @module comment + /** Preserved for backwards compatibility, prefer {@link Lib} */ + export * as Library from "lib"; + + /** This comment will be used for someFunction only if someFunction does not have a comment directly on it */ + export { someFunction } from "lib"; + ``` + ## See Also - The [Tags overview](../tags.md) diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index 843802ac4..f2df66df1 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -189,18 +189,30 @@ export class Context { symbol: ts.Symbol | undefined, exportSymbol: ts.Symbol | undefined, ) { + // Allow comments on export declarations to take priority over comments directly + // on the symbol to enable overriding comments on modules/references, #1504 if ( !reflection.comment && exportSymbol && - reflection.kind & - (ReflectionKind.SomeModule | ReflectionKind.Reference) + (reflection.kind & + (ReflectionKind.SomeModule | ReflectionKind.Reference)) ) { reflection.comment = this.getComment(exportSymbol, reflection.kind); } + + // If that didn't get us a comment (the normal case), then get the comment from + // the source declarations, this is the common case. if (symbol && !reflection.comment) { reflection.comment = this.getComment(symbol, reflection.kind); } + // If we still don't have a comment, check for any comments on the export declaration, + // we don't have to worry about functions being weird in this case as the regular declaration + // doesn't have any comment. + if (exportSymbol && !reflection.comment) { + reflection.comment = this.getComment(exportSymbol, ReflectionKind.Reference); + } + if (this.shouldBeStatic) { reflection.setFlag(ReflectionFlag.Static); } diff --git a/src/test/converter2/issues/gh2964.d.ts b/src/test/converter2/issues/gh2964.d.ts new file mode 100644 index 000000000..4ec53bb25 --- /dev/null +++ b/src/test/converter2/issues/gh2964.d.ts @@ -0,0 +1,17 @@ +declare module "*.gh2964" { + export const data: "string"; +} + +/** @ignore */ +export { data as first } from "test.gh2964"; +/** @ignore */ +export { data as second } from "test.gh2964"; + +/** Comment on variable */ +const third: "not ignored"; +/** @ignore -- does not work as third has a comment */ +export { third }; + +const fourth: "ignored"; +/** @ignore -- works as fourth does not have a comment */ +export { fourth }; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index c5d1410a3..5b8e86637 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2121,4 +2121,9 @@ describe("Issue Tests", () => { equal(query(project, "Func").type?.toString(), "(a: T) => void"); equal(query(project, "Var").type?.toString(), "123"); }); + + it("#2964 handles ignored instances of wildcard declared modules", () => { + const project = convert(); + equal(project.children?.map(c => c.name), ["third"]); + }); }); From d89f9d25512a34f8e23195828e3471304cdea6dc Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 26 Jun 2025 21:48:22 -0600 Subject: [PATCH 047/119] Bump version to v0.28.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eacdfccce..59d98ffe0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.5", + "version": "0.28.6", "homepage": "https://typedoc.org", "type": "module", "exports": { From d47f04caecf13418d59d6bf934ad7d4b061e3411 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Fri, 27 Jun 2025 03:49:12 +0000 Subject: [PATCH 048/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d353be73..4d8c1462a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.6 (2025-06-27) + ### Features - TypeDoc now supports resolving relative paths in links to the package directory as belonging to the project, #2961. From 4dac6cdd926185e6cd75cd8448235a8436d9ac05 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 27 Jun 2025 10:54:46 -0600 Subject: [PATCH 049/119] Fix missing comments on type-only classes/functions Resolves #2970 --- CHANGELOG.md | 4 ++++ src/lib/converter/comments/discovery.ts | 8 +++++++- src/test/converter2/issues/gh2970.ts | 10 ++++++++++ src/test/issues.c2.test.ts | 9 +++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/test/converter2/issues/gh2970.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8c1462a..8263eab2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Bug Fixes + +- Classes and functions exported with `export { type X }` are no longer missing comments, #2970. + ## v0.28.6 (2025-06-27) ### Features diff --git a/src/lib/converter/comments/discovery.ts b/src/lib/converter/comments/discovery.ts index 052d3a06f..9ef7fd5ea 100644 --- a/src/lib/converter/comments/discovery.ts +++ b/src/lib/converter/comments/discovery.ts @@ -80,6 +80,7 @@ const wantedKinds: Record = { [ReflectionKind.Interface]: [ ts.SyntaxKind.InterfaceDeclaration, ts.SyntaxKind.TypeAliasDeclaration, + ts.SyntaxKind.ClassDeclaration, // type only exports ], [ReflectionKind.Constructor]: [ts.SyntaxKind.Constructor], [ReflectionKind.Property]: variablePropertyKinds, @@ -104,7 +105,12 @@ const wantedKinds: Record = { [ReflectionKind.Accessor]: [ts.SyntaxKind.PropertyDeclaration], [ReflectionKind.GetSignature]: [ts.SyntaxKind.GetAccessor], [ReflectionKind.SetSignature]: [ts.SyntaxKind.SetAccessor], - [ReflectionKind.TypeAlias]: [ts.SyntaxKind.TypeAliasDeclaration], + [ReflectionKind.TypeAlias]: [ + ts.SyntaxKind.TypeAliasDeclaration, + ts.SyntaxKind.FunctionDeclaration, // type only exports + // Intentionally not included to avoid comments being copied for variable/alias combos + // ts.SyntaxKind.VariableDeclaration, + ], [ReflectionKind.Reference]: [ ts.SyntaxKind.NamespaceExport, ts.SyntaxKind.ExportSpecifier, diff --git a/src/test/converter2/issues/gh2970.ts b/src/test/converter2/issues/gh2970.ts new file mode 100644 index 000000000..0a357dcd2 --- /dev/null +++ b/src/test/converter2/issues/gh2970.ts @@ -0,0 +1,10 @@ +/** Comment */ +class Class {} + +/** Comment */ +const Var = 123; + +/** Comment */ +function Func() {} + +export type { Class, Func, Var }; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 5b8e86637..2cc8f0fb8 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2126,4 +2126,13 @@ describe("Issue Tests", () => { const project = convert(); equal(project.children?.map(c => c.name), ["third"]); }); + + it("#2970 includes comments on type only exports", () => { + const project = convert(); + equal(project.children?.map(c => [c.name, Comment.combineDisplayParts(c.comment?.summary)]), [ + ["Class", "Comment"], + ["Func", "Comment"], + ["Var", "Comment"], + ]); + }); }); From dc58c064a60d3e8756a608310e29fececd8d5481 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 28 Jun 2025 12:05:33 -0600 Subject: [PATCH 050/119] Update deps --- dprint.json | 6 +- package.json | 20 +- pnpm-lock.yaml | 853 ++++++++++++++++++++++++----------------------- static/style.css | 11 +- 4 files changed, 451 insertions(+), 439 deletions(-) diff --git a/dprint.json b/dprint.json index 25bbe47f3..2237a8ba6 100644 --- a/dprint.json +++ b/dprint.json @@ -19,9 +19,9 @@ "src/test/converter2/issues/gh2631/crlf.md" ], "plugins": [ - "https://plugins.dprint.dev/typescript-0.94.0.wasm", + "https://plugins.dprint.dev/typescript-0.95.8.wasm", "https://plugins.dprint.dev/json-0.20.0.wasm", - "https://plugins.dprint.dev/markdown-0.18.0.wasm", - "https://plugins.dprint.dev/g-plane/malva-v0.11.2.wasm" + "https://plugins.dprint.dev/markdown-0.19.0.wasm", + "https://plugins.dprint.dev/g-plane/malva-v0.12.1.wasm" ] } diff --git a/package.json b/package.json index 59d98ffe0..cb71b828d 100644 --- a/package.json +++ b/package.json @@ -31,32 +31,32 @@ "pnpm": ">= 10" }, "dependencies": { - "@gerrit0/mini-shiki": "^3.2.2", + "@gerrit0/mini-shiki": "^3.7.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", - "yaml": "^2.7.1" + "yaml": "^2.8.0" }, "peerDependencies": { "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" }, "devDependencies": { - "@eslint/js": "^9.24.0", + "@eslint/js": "^9.30.0", "@types/lunr": "^2.3.7", "@types/markdown-it": "^14.1.2", "@types/mocha": "^10.0.10", "@types/node": "18", "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe", "c8": "^10.1.3", - "dprint": "^0.49.1", - "esbuild": "^0.25.2", - "eslint": "^9.24.0", - "mocha": "^11.1.0", - "puppeteer": "^24.6.0", + "dprint": "^0.50.0", + "esbuild": "^0.25.5", + "eslint": "^9.30.0", + "mocha": "^11.7.1", + "puppeteer": "^24.11.1", "semver": "^7.7.1", - "tsx": "^4.19.3", + "tsx": "^4.20.3", "typescript": "5.8.3", - "typescript-eslint": "^8.29.0" + "typescript-eslint": "^8.35.0" }, "files": [ "/bin", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ffae878e..54ac886f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@gerrit0/mini-shiki': - specifier: ^3.2.2 - version: 3.2.2 + specifier: ^3.7.0 + version: 3.7.0 lunr: specifier: ^2.3.9 version: 2.3.9 @@ -21,12 +21,12 @@ importers: specifier: ^9.0.5 version: 9.0.5 yaml: - specifier: ^2.7.1 - version: 2.7.1 + specifier: ^2.8.0 + version: 2.8.0 devDependencies: '@eslint/js': - specifier: ^9.24.0 - version: 9.24.0 + specifier: ^9.30.0 + version: 9.30.0 '@types/lunr': specifier: ^2.3.7 version: 2.3.7 @@ -46,32 +46,32 @@ importers: specifier: ^10.1.3 version: 10.1.3 dprint: - specifier: ^0.49.1 - version: 0.49.1 + specifier: ^0.50.0 + version: 0.50.0 esbuild: - specifier: ^0.25.2 - version: 0.25.2 + specifier: ^0.25.5 + version: 0.25.5 eslint: - specifier: ^9.24.0 - version: 9.24.0 + specifier: ^9.30.0 + version: 9.30.0 mocha: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^11.7.1 + version: 11.7.1 puppeteer: - specifier: ^24.6.0 - version: 24.6.0(typescript@5.8.3) + specifier: ^24.11.1 + version: 24.11.1(typescript@5.8.3) semver: specifier: ^7.7.1 version: 7.7.1 tsx: - specifier: ^4.19.3 - version: 4.19.3 + specifier: ^4.20.3 + version: 4.20.3 typescript: specifier: 5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.29.0 - version: 8.29.0(eslint@9.24.0)(typescript@5.8.3) + specifier: ^8.35.0 + version: 8.35.0(eslint@9.30.0)(typescript@5.8.3) packages: @@ -87,197 +87,197 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@dprint/darwin-arm64@0.49.1': - resolution: {integrity: sha512-ib6KcJWo/M5RJWXOQKhP664FG1hAvG7nrbkh+j8n+oXdzmbyDdXTP+zW+aM3/sIQUkGaZky1xy1j2VeScMEEHQ==} + '@dprint/darwin-arm64@0.50.0': + resolution: {integrity: sha512-KqWpsvm4JveYdKDLSLlQINGNW4pEAGHcTFPEHR5qXMYV4pPomLgHHPyBrxe3XdGtlUp4I8HfvBMBw3b/LKd06A==} cpu: [arm64] os: [darwin] - '@dprint/darwin-x64@0.49.1': - resolution: {integrity: sha512-vIVgnYxV7YYa1d6Uyz707RbgB9rwefGPam+rzaueFNPQjdOxPOTQDuMEJDS+Z3BlI00MfeoupIfIUGsXoM4dpQ==} + '@dprint/darwin-x64@0.50.0': + resolution: {integrity: sha512-kFeeLYhCIVAe1SMtFYk1q0qWxrkmW8FhOBTUh2oblr4AnAjpjb03m8BVUrHHKFeBTsppwck+1b8hzU6LRZO7fA==} cpu: [x64] os: [darwin] - '@dprint/linux-arm64-glibc@0.49.1': - resolution: {integrity: sha512-ZeIh6qMPWLBBifDtU0XadpK36b4WoaTqCOt0rWKfoTjq1RAt78EgqETWp43Dbr6et/HvTgYdoWF0ZNEu2FJFFA==} + '@dprint/linux-arm64-glibc@0.50.0': + resolution: {integrity: sha512-EL0+uMSdj/n+cZOP9ZO8ndvjmtOSWXNsMHKdAAaTG0+EjH9M9YKXD6kopP6PKOR5pJuiyHCRpVKJ4xoD4adfpQ==} cpu: [arm64] os: [linux] - '@dprint/linux-arm64-musl@0.49.1': - resolution: {integrity: sha512-/nuRyx+TykN6MqhlSCRs/t3o1XXlikiwTc9emWdzMeLGllYvJrcht9gRJ1/q1SqwCFhzgnD9H7roxxfji1tc+Q==} + '@dprint/linux-arm64-musl@0.50.0': + resolution: {integrity: sha512-bzyYxKtFw/hYAA+7lWQGQGo2YFPnH7Ql9uWxxWqiGaWVPU66K9WQt0RUEqu1hQBrCk9mMz3jx5l4oKWQ/Dc0fw==} cpu: [arm64] os: [linux] - '@dprint/linux-riscv64-glibc@0.49.1': - resolution: {integrity: sha512-RHBqrnvGO+xW4Oh0QuToBqWtkXMcfjqa1TqbBFF03yopFzZA2oRKX83PhjTWgd/IglaOns0BgmaLJy/JBSxOfQ==} + '@dprint/linux-riscv64-glibc@0.50.0': + resolution: {integrity: sha512-ElFqmKs96NyVXWqd2SJGJGtyVmUWNiLUyaImEzL7XZRmpoJG+Ky7SryhccMQU0ENtQmY0CVgZipLZ1SqhIoluA==} cpu: [riscv64] os: [linux] - '@dprint/linux-x64-glibc@0.49.1': - resolution: {integrity: sha512-MjFE894mIQXOKBencuakKyzAI4KcDe/p0Y9lRp9YSw/FneR4QWH9VBH90h8fRxcIlWMArjFFJJAtsBnn5qgxeg==} + '@dprint/linux-x64-glibc@0.50.0': + resolution: {integrity: sha512-Kim8TtCdpCQUNqF2D96vunuonYy6tPfp/AQblSVA4ADChVyFLGfPaQIECpGAAKxXnIG2SX5JRQP7nB/4JgPNbA==} cpu: [x64] os: [linux] - '@dprint/linux-x64-musl@0.49.1': - resolution: {integrity: sha512-CvGBWOksHgrL1uzYqtPFvZz0+E82BzgoCIEHJeuYaveEn37qWZS5jqoCm/vz6BfoivE1dVuyyOT78Begj9KxkQ==} + '@dprint/linux-x64-musl@0.50.0': + resolution: {integrity: sha512-ChZf0BnS3S6BIfqAPgQKqEh/7vgD1xc0MpcFcTrvkVQHuSdCQu1XiqUN12agzxB+Y5Ml9exgzP8lYgNza7iXvw==} cpu: [x64] os: [linux] - '@dprint/win32-arm64@0.49.1': - resolution: {integrity: sha512-gQa4s82lMcXjfdxjWBQun6IJlXdPZZaIj2/2cqXWVEOYPKxAZ/JvGzt2pPG+i73h9KHjNLIV8M9ckqEH3oHufg==} + '@dprint/win32-arm64@0.50.0': + resolution: {integrity: sha512-xSY607bRyIPG7UO3uRa5c5wTGHKJqLUkQst85Hcz89EL/It6wswwUSNcywDydssN99HmSHop4fIf6FJTEpEp2g==} cpu: [arm64] os: [win32] - '@dprint/win32-x64@0.49.1': - resolution: {integrity: sha512-nPU6+hoVze5JJlgET7woYWElBw0IUaB/9XKTaglknQuUUfsmD75D9pkgJTxdIxl9Bg/i5O7c9wb3Nj4XNiTIfw==} + '@dprint/win32-x64@0.50.0': + resolution: {integrity: sha512-uGDjrK88LOet9a8pPRM9nKins93mK2NLozqL/hCNV88Nu5Nk0bBeVwRMAnPapjV3Jo+hsJOeq3Z1ibrq2c3v8w==} cpu: [x64] os: [win32] - '@esbuild/aix-ppc64@0.25.2': - resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.2': - resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.2': - resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.2': - resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.2': - resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.2': - resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.2': - resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.2': - resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.2': - resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.2': - resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.2': - resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.2': - resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.2': - resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.2': - resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.2': - resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.2': - resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.2': - resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.2': - resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.2': - resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.2': - resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.2': - resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.2': - resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.2': - resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.2': - resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.2': - resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -288,40 +288,50 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.20.0': - resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.2.1': - resolution: {integrity: sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==} + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.12.0': - resolution: {integrity: sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==} + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.24.0': - resolution: {integrity: sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==} + '@eslint/js@9.30.0': + resolution: {integrity: sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.7': - resolution: {integrity: sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==} + '@eslint/plugin-kit@0.3.3': + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@gerrit0/mini-shiki@3.2.2': - resolution: {integrity: sha512-vaZNGhGLKMY14HbF53xxHNgFO9Wz+t5lTlGNpl2N9xFiKQ0I5oIe0vKjU9dh7Nb3Dw6lZ7wqUE0ri+zcdpnK+Q==} + '@gerrit0/mini-shiki@3.7.0': + resolution: {integrity: sha512-7iY9wg4FWXmeoFJpUL2u+tsmh0d0jcEJHAIzVxl3TG4KL493JNnisdLAILZ77zcD+z3J0keEXZ+lFzUgzQzPDg==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -377,22 +387,22 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@puppeteer/browsers@2.9.0': - resolution: {integrity: sha512-8+xM+cFydYET4X/5/3yZMHs7sjS6c9I6H5I3xJdb6cinzxWUT/I2QVw4avxCQ8QDndwdHkG/FiSZIrCjAbaKvQ==} + '@puppeteer/browsers@2.10.5': + resolution: {integrity: sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==} engines: {node: '>=18'} hasBin: true - '@shikijs/engine-oniguruma@3.2.1': - resolution: {integrity: sha512-wZZAkayEn6qu2+YjenEoFqj0OyQI64EWsNR6/71d1EkG4sxEOFooowKivsWPpaWNBu3sxAG+zPz5kzBL/SsreQ==} + '@shikijs/engine-oniguruma@3.7.0': + resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} - '@shikijs/langs@3.2.1': - resolution: {integrity: sha512-If0iDHYRSGbihiA8+7uRsgb1er1Yj11pwpX1c6HLYnizDsKAw5iaT3JXj5ZpaimXSWky/IhxTm7C6nkiYVym+A==} + '@shikijs/langs@3.7.0': + resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} - '@shikijs/themes@3.2.1': - resolution: {integrity: sha512-k5DKJUT8IldBvAm8WcrDT5+7GA7se6lLksR+2E3SvyqGTyFMzU2F9Gb7rmD+t+Pga1MKrYFxDIeyWjMZWM6uBQ==} + '@shikijs/themes@3.7.0': + resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} - '@shikijs/types@3.2.1': - resolution: {integrity: sha512-/NTWAk4KE2M8uac0RhOsIhYQf4pdU0OywQuYDGIGAJ6Mjunxl2cGiuLkvu4HLCMn+OTTLRWkjZITp+aYJv60yA==} + '@shikijs/types@3.7.0': + resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -436,51 +446,63 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.29.0': - resolution: {integrity: sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ==} + '@typescript-eslint/eslint-plugin@8.35.0': + resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + '@typescript-eslint/parser': ^8.35.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.29.0': - resolution: {integrity: sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==} + '@typescript-eslint/parser@8.35.0': + resolution: {integrity: sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.29.0': - resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==} + '@typescript-eslint/project-service@8.35.0': + resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.35.0': + resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.29.0': - resolution: {integrity: sha512-ahaWQ42JAOx+NKEf5++WC/ua17q5l+j1GFrbbpVKzFL/tKVc0aYY8rVSYUpUvt2hUP1YBr7mwXzx+E/DfUWI9Q==} + '@typescript-eslint/tsconfig-utils@8.35.0': + resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.35.0': + resolution: {integrity: sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.29.0': - resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==} + '@typescript-eslint/types@8.35.0': + resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.29.0': - resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==} + '@typescript-eslint/typescript-estree@8.35.0': + resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.29.0': - resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==} + '@typescript-eslint/utils@8.35.0': + resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.29.0': - resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==} + '@typescript-eslint/visitor-keys@8.35.0': + resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typestrong/fs-fixture-builder@https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe': @@ -493,8 +515,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true @@ -505,10 +527,6 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -525,10 +543,6 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -571,10 +585,6 @@ packages: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -613,12 +623,12 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} - chromium-bidi@3.0.0: - resolution: {integrity: sha512-ZOGRDAhBMX1uxL2Cm2TDuhImbrsEz5A/tTcVU6RpXEWaTNUNwsHW6njUXizh51Ir6iqHbKAfhA2XK33uBcLo5A==} + chromium-bidi@5.1.0: + resolution: {integrity: sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==} peerDependencies: devtools-protocol: '*' @@ -665,6 +675,15 @@ packages: supports-color: optional: true + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize@4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} @@ -676,15 +695,15 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - devtools-protocol@0.0.1425554: - resolution: {integrity: sha512-uRfxR6Nlzdzt0ihVIkV+sLztKgs7rgquY/Mhcv1YNCWDh5IZgl5mnn2aeEnW5stYTE0wwiF4RYVz8eMEpV1SEw==} + devtools-protocol@0.0.1464554: + resolution: {integrity: sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw==} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} engines: {node: '>=0.3.1'} - dprint@0.49.1: - resolution: {integrity: sha512-pO9XH79SyXybj2Vhc9ITZMEI8cJkdlQQRoD8oEfPH6Jjpp/7WX5kIgECVd3DBOjjAdCSiW6R47v3gJBx/qZVkw==} + dprint@0.50.0: + resolution: {integrity: sha512-aNJhOQsUS5D9k/YkMUaLLniIpxEBUR0ZwT0RXGQV5YpaGwE2nx6FcKuVkC6wRaZXTr8X0NpV/2HFbcvNuI2jtA==} hasBin: true eastasianwidth@0.2.0: @@ -710,8 +729,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - esbuild@0.25.2: - resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} hasBin: true @@ -728,20 +747,20 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-scope@8.3.0: - resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.24.0: - resolution: {integrity: sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==} + eslint@9.30.0: + resolution: {integrity: sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -750,8 +769,8 @@ packages: jiti: optional: true - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: @@ -891,6 +910,10 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -906,10 +929,6 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1030,10 +1049,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -1045,8 +1060,8 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mocha@11.1.0: - resolution: {integrity: sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==} + mocha@11.7.1: + resolution: {integrity: sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true @@ -1060,10 +1075,6 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -1146,12 +1157,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - puppeteer-core@24.6.0: - resolution: {integrity: sha512-Cukxysy12m0v350bhl/Gzof0XQYmtON9l2VvGp3D4BOQZVgyf+y5wIpcjDZQ/896Okoi95dKRGRV8E6a7SYAQQ==} + puppeteer-core@24.11.1: + resolution: {integrity: sha512-I0Gv3jWBRY9E3NTBElp7br7Gaid5RbFTxCRRMHym1kCf0ompO0Pel4REGsGDwMWkg3uwFzIH7t7qXs3T4DKRWA==} engines: {node: '>=18'} - puppeteer@24.6.0: - resolution: {integrity: sha512-wYTB8WkzAr7acrlsp+0at1PZjOJPOxe6dDWKOG/kaX4Zjck9RXCFx3CtsxsAGzPn/Yv6AzgJC/CW1P5l+qxsqw==} + puppeteer@24.11.1: + resolution: {integrity: sha512-QbccB/LgxX4tSZRzr9KQ1Jajdvu3n35Dlf/Otjz0QfR+6mDoZdMWLcWF94uQoC3OJerCyYm5hlU2Ru4nBoId2A==} engines: {node: '>=18'} hasBin: true @@ -1161,9 +1172,9 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -1191,6 +1202,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -1273,8 +1289,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@2.0.1: - resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -1282,8 +1298,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.19.3: - resolution: {integrity: sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==} + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} hasBin: true @@ -1294,8 +1310,8 @@ packages: typed-query-selector@2.12.0: resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} - typescript-eslint@8.29.0: - resolution: {integrity: sha512-ep9rVd9B4kQsZ7ZnWCVxUE/xDLUUUsRzE0poAeNu+4CkFErLfuvPt/qtm2EpnSyfvsR0S6QzDFSrPCFBwf64fg==} + typescript-eslint@8.35.0: + resolution: {integrity: sha512-uEnz70b7kBz6eg/j0Czy6K5NivaYopgxRjsnAJ2Fx5oTLo3wefTHIbL7AkQr1+7tJCRVpTs/wiM8JR/11Loq9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1328,8 +1344,8 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerpool@6.5.1: - resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + workerpool@9.3.3: + resolution: {integrity: sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==} wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -1342,8 +1358,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.1: - resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -1358,9 +1374,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yaml@2.7.1: - resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} - engines: {node: '>= 14'} + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@21.1.1: @@ -1397,116 +1413,121 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@dprint/darwin-arm64@0.49.1': + '@dprint/darwin-arm64@0.50.0': optional: true - '@dprint/darwin-x64@0.49.1': + '@dprint/darwin-x64@0.50.0': optional: true - '@dprint/linux-arm64-glibc@0.49.1': + '@dprint/linux-arm64-glibc@0.50.0': optional: true - '@dprint/linux-arm64-musl@0.49.1': + '@dprint/linux-arm64-musl@0.50.0': optional: true - '@dprint/linux-riscv64-glibc@0.49.1': + '@dprint/linux-riscv64-glibc@0.50.0': optional: true - '@dprint/linux-x64-glibc@0.49.1': + '@dprint/linux-x64-glibc@0.50.0': optional: true - '@dprint/linux-x64-musl@0.49.1': + '@dprint/linux-x64-musl@0.50.0': optional: true - '@dprint/win32-arm64@0.49.1': + '@dprint/win32-arm64@0.50.0': optional: true - '@dprint/win32-x64@0.49.1': + '@dprint/win32-x64@0.50.0': optional: true - '@esbuild/aix-ppc64@0.25.2': + '@esbuild/aix-ppc64@0.25.5': optional: true - '@esbuild/android-arm64@0.25.2': + '@esbuild/android-arm64@0.25.5': optional: true - '@esbuild/android-arm@0.25.2': + '@esbuild/android-arm@0.25.5': optional: true - '@esbuild/android-x64@0.25.2': + '@esbuild/android-x64@0.25.5': optional: true - '@esbuild/darwin-arm64@0.25.2': + '@esbuild/darwin-arm64@0.25.5': optional: true - '@esbuild/darwin-x64@0.25.2': + '@esbuild/darwin-x64@0.25.5': optional: true - '@esbuild/freebsd-arm64@0.25.2': + '@esbuild/freebsd-arm64@0.25.5': optional: true - '@esbuild/freebsd-x64@0.25.2': + '@esbuild/freebsd-x64@0.25.5': optional: true - '@esbuild/linux-arm64@0.25.2': + '@esbuild/linux-arm64@0.25.5': optional: true - '@esbuild/linux-arm@0.25.2': + '@esbuild/linux-arm@0.25.5': optional: true - '@esbuild/linux-ia32@0.25.2': + '@esbuild/linux-ia32@0.25.5': optional: true - '@esbuild/linux-loong64@0.25.2': + '@esbuild/linux-loong64@0.25.5': optional: true - '@esbuild/linux-mips64el@0.25.2': + '@esbuild/linux-mips64el@0.25.5': optional: true - '@esbuild/linux-ppc64@0.25.2': + '@esbuild/linux-ppc64@0.25.5': optional: true - '@esbuild/linux-riscv64@0.25.2': + '@esbuild/linux-riscv64@0.25.5': optional: true - '@esbuild/linux-s390x@0.25.2': + '@esbuild/linux-s390x@0.25.5': optional: true - '@esbuild/linux-x64@0.25.2': + '@esbuild/linux-x64@0.25.5': optional: true - '@esbuild/netbsd-arm64@0.25.2': + '@esbuild/netbsd-arm64@0.25.5': optional: true - '@esbuild/netbsd-x64@0.25.2': + '@esbuild/netbsd-x64@0.25.5': optional: true - '@esbuild/openbsd-arm64@0.25.2': + '@esbuild/openbsd-arm64@0.25.5': optional: true - '@esbuild/openbsd-x64@0.25.2': + '@esbuild/openbsd-x64@0.25.5': optional: true - '@esbuild/sunos-x64@0.25.2': + '@esbuild/sunos-x64@0.25.5': optional: true - '@esbuild/win32-arm64@0.25.2': + '@esbuild/win32-arm64@0.25.5': optional: true - '@esbuild/win32-ia32@0.25.2': + '@esbuild/win32-ia32@0.25.5': optional: true - '@esbuild/win32-x64@0.25.2': + '@esbuild/win32-x64@0.25.5': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.24.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@9.30.0)': dependencies: - eslint: 9.24.0 + eslint: 9.30.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.7.0(eslint@9.30.0)': + dependencies: + eslint: 9.30.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.20.0': + '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 debug: 4.4.0(supports-color@8.1.1) @@ -1514,9 +1535,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.2.1': {} + '@eslint/config-helpers@0.3.0': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 - '@eslint/core@0.12.0': + '@eslint/core@0.15.1': dependencies: '@types/json-schema': 7.0.15 @@ -1524,7 +1549,7 @@ snapshots: dependencies: ajv: 6.12.6 debug: 4.4.0(supports-color@8.1.1) - espree: 10.3.0 + espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 @@ -1534,21 +1559,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.24.0': {} + '@eslint/js@9.30.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.7': + '@eslint/plugin-kit@0.3.3': dependencies: - '@eslint/core': 0.12.0 + '@eslint/core': 0.15.1 levn: 0.4.1 - '@gerrit0/mini-shiki@3.2.2': + '@gerrit0/mini-shiki@3.7.0': dependencies: - '@shikijs/engine-oniguruma': 3.2.1 - '@shikijs/langs': 3.2.1 - '@shikijs/themes': 3.2.1 - '@shikijs/types': 3.2.1 + '@shikijs/engine-oniguruma': 3.7.0 + '@shikijs/langs': 3.7.0 + '@shikijs/themes': 3.7.0 + '@shikijs/types': 3.7.0 '@shikijs/vscode-textmate': 10.0.2 '@humanfs/core@0.19.1': {} @@ -1599,33 +1624,33 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@puppeteer/browsers@2.9.0': + '@puppeteer/browsers@2.10.5': dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 - semver: 7.7.1 + semver: 7.7.2 tar-fs: 3.0.8 yargs: 17.7.2 transitivePeerDependencies: - bare-buffer - supports-color - '@shikijs/engine-oniguruma@3.2.1': + '@shikijs/engine-oniguruma@3.7.0': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.7.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.2.1': + '@shikijs/langs@3.7.0': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.7.0 - '@shikijs/themes@3.2.1': + '@shikijs/themes@3.7.0': dependencies: - '@shikijs/types': 3.2.1 + '@shikijs/types': 3.7.0 - '@shikijs/types@3.2.1': + '@shikijs/types@3.7.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -1668,90 +1693,105 @@ snapshots: '@types/node': 18.19.76 optional: true - '@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3))(eslint@9.30.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.29.0(eslint@9.24.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.29.0 - '@typescript-eslint/type-utils': 8.29.0(eslint@9.24.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.29.0(eslint@9.24.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.29.0 - eslint: 9.24.0 + '@typescript-eslint/parser': 8.35.0(eslint@9.30.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/type-utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + eslint: 9.30.0 graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.0.1(typescript@5.8.3) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.0(supports-color@8.1.1) + eslint: 9.30.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.29.0(eslint@9.24.0)(typescript@5.8.3)': + '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.29.0 - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 debug: 4.4.0(supports-color@8.1.1) - eslint: 9.24.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.29.0': + '@typescript-eslint/scope-manager@8.35.0': dependencies: - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 - '@typescript-eslint/type-utils@8.29.0(eslint@9.24.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.35.0(eslint@9.30.0)(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.29.0(eslint@9.24.0)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) debug: 4.4.0(supports-color@8.1.1) - eslint: 9.24.0 - ts-api-utils: 2.0.1(typescript@5.8.3) + eslint: 9.30.0 + ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.29.0': {} + '@typescript-eslint/types@8.35.0': {} - '@typescript-eslint/typescript-estree@8.29.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 debug: 4.4.0(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.1 - ts-api-utils: 2.0.1(typescript@5.8.3) + ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.29.0(eslint@9.24.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.35.0(eslint@9.30.0)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.24.0) - '@typescript-eslint/scope-manager': 8.29.0 - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.3) - eslint: 9.24.0 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.0) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + eslint: 9.30.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.29.0': + '@typescript-eslint/visitor-keys@8.35.0': dependencies: - '@typescript-eslint/types': 8.29.0 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/types': 8.35.0 + eslint-visitor-keys: 4.2.1 '@typestrong/fs-fixture-builder@https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe': {} - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.14.0 + acorn: 8.15.0 - acorn@8.14.0: {} + acorn@8.15.0: {} agent-base@7.1.3: {} @@ -1762,8 +1802,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-colors@4.1.3: {} - ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -1774,11 +1812,6 @@ snapshots: ansi-styles@6.2.1: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - argparse@2.0.1: {} ast-types@0.13.4: @@ -1818,8 +1851,6 @@ snapshots: basic-ftp@5.0.5: {} - binary-extensions@2.3.0: {} - brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -1860,21 +1891,13 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chokidar@3.6.0: + chokidar@4.0.3: dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + readdirp: 4.1.2 - chromium-bidi@3.0.0(devtools-protocol@0.0.1425554): + chromium-bidi@5.1.0(devtools-protocol@0.0.1464554): dependencies: - devtools-protocol: 0.0.1425554 + devtools-protocol: 0.0.1464554 mitt: 3.0.1 zod: 3.24.2 @@ -1917,6 +1940,10 @@ snapshots: optionalDependencies: supports-color: 8.1.1 + debug@4.4.1: + dependencies: + ms: 2.1.3 + decamelize@4.0.0: {} deep-is@0.1.4: {} @@ -1927,21 +1954,21 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - devtools-protocol@0.0.1425554: {} + devtools-protocol@0.0.1464554: {} - diff@5.2.0: {} + diff@7.0.0: {} - dprint@0.49.1: + dprint@0.50.0: optionalDependencies: - '@dprint/darwin-arm64': 0.49.1 - '@dprint/darwin-x64': 0.49.1 - '@dprint/linux-arm64-glibc': 0.49.1 - '@dprint/linux-arm64-musl': 0.49.1 - '@dprint/linux-riscv64-glibc': 0.49.1 - '@dprint/linux-x64-glibc': 0.49.1 - '@dprint/linux-x64-musl': 0.49.1 - '@dprint/win32-arm64': 0.49.1 - '@dprint/win32-x64': 0.49.1 + '@dprint/darwin-arm64': 0.50.0 + '@dprint/darwin-x64': 0.50.0 + '@dprint/linux-arm64-glibc': 0.50.0 + '@dprint/linux-arm64-musl': 0.50.0 + '@dprint/linux-riscv64-glibc': 0.50.0 + '@dprint/linux-x64-glibc': 0.50.0 + '@dprint/linux-x64-musl': 0.50.0 + '@dprint/win32-arm64': 0.50.0 + '@dprint/win32-x64': 0.50.0 eastasianwidth@0.2.0: {} @@ -1961,33 +1988,33 @@ snapshots: dependencies: is-arrayish: 0.2.1 - esbuild@0.25.2: + esbuild@0.25.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.2 - '@esbuild/android-arm': 0.25.2 - '@esbuild/android-arm64': 0.25.2 - '@esbuild/android-x64': 0.25.2 - '@esbuild/darwin-arm64': 0.25.2 - '@esbuild/darwin-x64': 0.25.2 - '@esbuild/freebsd-arm64': 0.25.2 - '@esbuild/freebsd-x64': 0.25.2 - '@esbuild/linux-arm': 0.25.2 - '@esbuild/linux-arm64': 0.25.2 - '@esbuild/linux-ia32': 0.25.2 - '@esbuild/linux-loong64': 0.25.2 - '@esbuild/linux-mips64el': 0.25.2 - '@esbuild/linux-ppc64': 0.25.2 - '@esbuild/linux-riscv64': 0.25.2 - '@esbuild/linux-s390x': 0.25.2 - '@esbuild/linux-x64': 0.25.2 - '@esbuild/netbsd-arm64': 0.25.2 - '@esbuild/netbsd-x64': 0.25.2 - '@esbuild/openbsd-arm64': 0.25.2 - '@esbuild/openbsd-x64': 0.25.2 - '@esbuild/sunos-x64': 0.25.2 - '@esbuild/win32-arm64': 0.25.2 - '@esbuild/win32-ia32': 0.25.2 - '@esbuild/win32-x64': 0.25.2 + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 escalade@3.2.0: {} @@ -2001,25 +2028,25 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-scope@8.3.0: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} - eslint@9.24.0: + eslint@9.30.0: dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.24.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.30.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.20.0 - '@eslint/config-helpers': 0.2.1 - '@eslint/core': 0.12.0 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 + '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.24.0 - '@eslint/plugin-kit': 0.2.7 + '@eslint/js': 9.30.0 + '@eslint/plugin-kit': 0.3.3 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 @@ -2030,9 +2057,9 @@ snapshots: cross-spawn: 7.0.6 debug: 4.4.0(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint-scope: 8.3.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -2050,11 +2077,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.3.0: + espree@10.4.0: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 esprima@4.0.1: {} @@ -2072,7 +2099,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -2148,7 +2175,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -2182,19 +2209,21 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 transitivePeerDependencies: - supports-color ignore@5.3.2: {} + ignore@7.0.5: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -2209,10 +2238,6 @@ snapshots: is-arrayish@0.2.1: {} - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -2322,10 +2347,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -2334,25 +2355,25 @@ snapshots: mitt@3.0.1: {} - mocha@11.1.0: + mocha@11.7.1: dependencies: - ansi-colors: 4.1.3 browser-stdout: 1.3.1 - chokidar: 3.6.0 + chokidar: 4.0.3 debug: 4.4.0(supports-color@8.1.1) - diff: 5.2.0 + diff: 7.0.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 glob: 10.4.5 he: 1.2.0 js-yaml: 4.1.0 log-symbols: 4.1.0 - minimatch: 5.1.6 + minimatch: 9.0.5 ms: 2.1.3 + picocolors: 1.1.1 serialize-javascript: 6.0.2 strip-json-comments: 3.1.1 supports-color: 8.1.1 - workerpool: 6.5.1 + workerpool: 9.3.3 yargs: 17.7.2 yargs-parser: 21.1.1 yargs-unparser: 2.0.0 @@ -2363,8 +2384,6 @@ snapshots: netmask@2.0.2: {} - normalize-path@3.0.0: {} - once@1.4.0: dependencies: wrappy: 1.0.2 @@ -2390,7 +2409,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 get-uri: 6.0.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -2439,7 +2458,7 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -2460,27 +2479,27 @@ snapshots: punycode@2.3.1: {} - puppeteer-core@24.6.0: + puppeteer-core@24.11.1: dependencies: - '@puppeteer/browsers': 2.9.0 - chromium-bidi: 3.0.0(devtools-protocol@0.0.1425554) - debug: 4.4.0(supports-color@8.1.1) - devtools-protocol: 0.0.1425554 + '@puppeteer/browsers': 2.10.5 + chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) + debug: 4.4.1 + devtools-protocol: 0.0.1464554 typed-query-selector: 2.12.0 - ws: 8.18.1 + ws: 8.18.3 transitivePeerDependencies: - bare-buffer - bufferutil - supports-color - utf-8-validate - puppeteer@24.6.0(typescript@5.8.3): + puppeteer@24.11.1(typescript@5.8.3): dependencies: - '@puppeteer/browsers': 2.9.0 - chromium-bidi: 3.0.0(devtools-protocol@0.0.1425554) + '@puppeteer/browsers': 2.10.5 + chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) cosmiconfig: 9.0.0(typescript@5.8.3) - devtools-protocol: 0.0.1425554 - puppeteer-core: 24.6.0 + devtools-protocol: 0.0.1464554 + puppeteer-core: 24.11.1 typed-query-selector: 2.12.0 transitivePeerDependencies: - bare-buffer @@ -2495,9 +2514,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 + readdirp@4.1.2: {} require-directory@2.1.1: {} @@ -2515,6 +2532,8 @@ snapshots: semver@7.7.1: {} + semver@7.7.2: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -2532,7 +2551,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1 socks: 2.8.4 transitivePeerDependencies: - supports-color @@ -2614,15 +2633,15 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@2.0.1(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 tslib@2.8.1: {} - tsx@4.19.3: + tsx@4.20.3: dependencies: - esbuild: 0.25.2 + esbuild: 0.25.5 get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 @@ -2633,12 +2652,12 @@ snapshots: typed-query-selector@2.12.0: {} - typescript-eslint@8.29.0(eslint@9.24.0)(typescript@5.8.3): + typescript-eslint@8.35.0(eslint@9.30.0)(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.24.0)(typescript@5.8.3))(eslint@9.24.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.29.0(eslint@9.24.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.29.0(eslint@9.24.0)(typescript@5.8.3) - eslint: 9.24.0 + '@typescript-eslint/eslint-plugin': 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3))(eslint@9.30.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.35.0(eslint@9.30.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) + eslint: 9.30.0 typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -2665,7 +2684,7 @@ snapshots: word-wrap@1.2.5: {} - workerpool@6.5.1: {} + workerpool@9.3.3: {} wrap-ansi@7.0.0: dependencies: @@ -2681,11 +2700,11 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.1: {} + ws@8.18.3: {} y18n@5.0.8: {} - yaml@2.7.1: {} + yaml@2.8.0: {} yargs-parser@21.1.1: {} diff --git a/static/style.css b/static/style.css index 7c3fbdb90..5ba5a2a90 100644 --- a/static/style.css +++ b/static/style.css @@ -504,15 +504,8 @@ body { background: var(--color-background); font-family: - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - "Noto Sans", - Helvetica, - Arial, - sans-serif, - "Apple Color Emoji", - "Segoe UI Emoji"; + -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", + Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; color: var(--color-text); margin: 0; From 1bf3217dbcf2e4a9ba3eb496296dd6d62db36d5f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 28 Jun 2025 13:34:35 -0600 Subject: [PATCH 051/119] Improve test coverage --- CHANGELOG.md | 2 + src/lib/converter/utils/repository.ts | 2 +- .../internationalization.ts | 2 +- src/lib/internationalization/locales/en.cts | 1 + src/lib/models/Comment.ts | 2 +- src/lib/utils/options/declaration.ts | 34 ++-- src/lib/validation/links.ts | 4 - src/test/Repository.test.ts | 48 ++++++ src/test/converter/comment/comment.ts | 2 +- src/test/converter/comment/specs.json | 17 +- .../validation/unusedMergeModuleWith.ts | 5 + src/test/internationalization.test.ts | 30 +++- src/test/models/comment.test.ts | 32 ++++ src/test/utils/options/declaration.test.ts | 22 ++- .../utils/options/default-options.test.ts | 162 ++++++++++++++++++ src/test/validation.test.ts | 121 +++++++------ 16 files changed, 394 insertions(+), 92 deletions(-) create mode 100644 src/test/converter2/validation/unusedMergeModuleWith.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8263eab2a..f0b3e828a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ title: Changelog ### Bug Fixes - Classes and functions exported with `export { type X }` are no longer missing comments, #2970. +- Setting `locale` to an unknown value will now cause TypeDoc to operate in English instead of a debug locale. +- Array options will now report an error if set to a non-array/non-string value. ## v0.28.6 (2025-06-27) diff --git a/src/lib/converter/utils/repository.ts b/src/lib/converter/utils/repository.ts index 831a54c13..66cd170ca 100644 --- a/src/lib/converter/utils/repository.ts +++ b/src/lib/converter/utils/repository.ts @@ -126,7 +126,7 @@ export class GitRepository implements Repository { logger: Logger, ): GitRepository | undefined { gitRevision ||= git("-C", path, "rev-parse", "HEAD").stdout.trim(); - if (!gitRevision) return; // Will only happen in a repo with no commits. + if (gitRevision == "HEAD") return; // Will only happen in a repo with no commits. let urlTemplate: string | undefined; if (sourceLinkTemplate) { diff --git a/src/lib/internationalization/internationalization.ts b/src/lib/internationalization/internationalization.ts index c802ece18..820de94f4 100644 --- a/src/lib/internationalization/internationalization.ts +++ b/src/lib/internationalization/internationalization.ts @@ -66,7 +66,7 @@ export function loadTranslations(lang: string): Record { try { return req(`./locales/${lang}.cjs`); } catch { - return {}; + return loadTranslations("en"); } } diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index f95e82520..b095e79cc 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -411,6 +411,7 @@ export = { "The useHostedBaseUrlForAbsoluteLinks option requires that hostedBaseUrl be set", favicon_must_have_one_of_the_following_extensions_0: "Favicon must have one of the following extensions: {0}", option_0_must_be_an_object: "The '{0}' option must be a non-array object", + option_0_must_be_an_array_of_string: "The '{0}' option must be set to an array of strings", option_0_must_be_a_function: "The '{0}' option must be a function", option_0_must_be_object_with_urls: `{0} must be an object with string labels as keys and URL values`, visibility_filters_only_include_0: `visibilityFilters can only include the following non-@ keys: {0}`, diff --git a/src/lib/models/Comment.ts b/src/lib/models/Comment.ts index 136b98d90..08fb76395 100644 --- a/src/lib/models/Comment.ts +++ b/src/lib/models/Comment.ts @@ -116,7 +116,7 @@ export class CommentTag { similarTo(other: CommentTag) { return ( this.tag === other.tag && - this.name === other.tag && + this.name === other.name && Comment.combineDisplayParts(this.content) === Comment.combineDisplayParts(other.content) ); diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 454bcae84..a18c094c2 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -685,6 +685,16 @@ export type DeclarationOptionToOptionType = T exten T extends FlagsDeclarationOption ? U : ParameterTypeToOptionTypeMap[Exclude]; +function toStringArray(value: unknown, option: DeclarationOption) { + if (Array.isArray(value) && value.every(v => typeof v === "string")) { + return value; + } else if (typeof value === "string") { + return [value]; + } + + throw new Error(i18n.option_0_must_be_an_array_of_string(option.name)); +} + const converters: { [K in ParameterType]: ( value: unknown, @@ -737,33 +747,18 @@ const converters: { return !!value; }, [ParameterType.Array](value, option) { - let strArrValue: string[] = []; - if (Array.isArray(value)) { - strArrValue = value.map(String); - } else if (typeof value === "string") { - strArrValue = [value]; - } + const strArrValue = toStringArray(value, option); option.validate?.(strArrValue); return strArrValue; }, [ParameterType.PathArray](value, option, configPath) { - let strArrValue: string[] = []; - if (Array.isArray(value)) { - strArrValue = value.map(String); - } else if (typeof value === "string") { - strArrValue = [value]; - } + const strArrValue = toStringArray(value, option); const normalized = strArrValue.map((path) => normalizePath(resolve(configPath, path))); option.validate?.(normalized); return normalized; }, [ParameterType.ModuleArray](value, option, configPath) { - let strArrValue: string[] = []; - if (Array.isArray(value)) { - strArrValue = value.map(String); - } else if (typeof value === "string") { - strArrValue = [value]; - } + const strArrValue = toStringArray(value, option); const resolved = resolveModulePaths(strArrValue, configPath); option.validate?.(resolved); return resolved; @@ -782,7 +777,8 @@ const converters: { return createGlobString(configPath, s); }; - const globs = Array.isArray(value) ? value.map(toGlobString) : [toGlobString(value)]; + const strArrValue = toStringArray(value, option); + const globs = strArrValue.map(toGlobString); option.validate?.(globs); return globs; }, diff --git a/src/lib/validation/links.ts b/src/lib/validation/links.ts index e79e56a99..e5067d42d 100644 --- a/src/lib/validation/links.ts +++ b/src/lib/validation/links.ts @@ -45,10 +45,6 @@ export function validateLinks( for (const id in project.reflections) { checkReflection(project.reflections[id], logger); } - - if (!(project.id in project.reflections)) { - checkReflection(project, logger); - } } function checkReflection(reflection: Reflection, logger: Logger) { diff --git a/src/test/Repository.test.ts b/src/test/Repository.test.ts index a3995d5b0..f87891295 100644 --- a/src/test/Repository.test.ts +++ b/src/test/Repository.test.ts @@ -314,3 +314,51 @@ describe("RepositoryManager - git enabled", () => { equal(repo.getURL(ign, 1), undefined); }); }); + +describe("RepositoryManager - edge cases", () => { + let fix: Project; + const logger = new TestLogger(); + const manager = new RepositoryManager( + "", + "", + "remote", + "", + false, // disable git + logger, + ); + + beforeEach(() => { + fix = tempdirProject(); + }); + + afterEach(() => { + fix.rm(); + logger.expectNoOtherMessages(); + }); + + it("Handles repositories without any commit", () => { + fix.write(); + git(fix.cwd, "init", "-b", "test"); + equal(manager.getRepository(fix.cwd + "/test.txt"), undefined); + }); + + it("Handles a remote which does not exist", () => { + fix.addFile("test.txt"); + fix.write(); + git(fix.cwd, "init", "-b", "test"); + git(fix.cwd, "add", "."); + git(fix.cwd, "commit", "-m", "test", "--no-gpg-sign"); + equal(manager.getRepository(fix.cwd + "/test.txt"), undefined); + logger.expectMessage('warn: The provided git remote "remote" was not valid. Source links will be broken'); + }); + + it("Handles a remote which does not match a known domain", () => { + fix.addFile("test.txt"); + fix.write(); + git(fix.cwd, "init", "-b", "test"); + git(fix.cwd, "add", "."); + git(fix.cwd, "commit", "-m", "test", "--no-gpg-sign"); + git(fix.cwd, "remote", "add", "remote", "https://example.com/fake.git"); + equal(manager.getRepository(fix.cwd + "/test.txt"), undefined); + }); +}); diff --git a/src/test/converter/comment/comment.ts b/src/test/converter/comment/comment.ts index c8c079e7a..919d7ac50 100644 --- a/src/test/converter/comment/comment.ts +++ b/src/test/converter/comment/comment.ts @@ -1,5 +1,5 @@ /** - * Module doc comment with document. + * Module doc comment with document and link to [self](./comment.ts) and {@link https://example.com} * * @document document.md * @packageDocumentation diff --git a/src/test/converter/comment/specs.json b/src/test/converter/comment/specs.json index 50d02f6c3..711f06806 100644 --- a/src/test/converter/comment/specs.json +++ b/src/test/converter/comment/specs.json @@ -16,7 +16,22 @@ "summary": [ { "kind": "text", - "text": "Module doc comment with document." + "text": "Module doc comment with document and link to [self](" + }, + { + "kind": "relative-link", + "text": "./comment.ts", + "target": 1 + }, + { + "kind": "text", + "text": ") and " + }, + { + "kind": "inline-tag", + "tag": "@link", + "text": "https://example.com", + "target": "https://example.com" } ] }, diff --git a/src/test/converter2/validation/unusedMergeModuleWith.ts b/src/test/converter2/validation/unusedMergeModuleWith.ts new file mode 100644 index 000000000..dce95101e --- /dev/null +++ b/src/test/converter2/validation/unusedMergeModuleWith.ts @@ -0,0 +1,5 @@ +/** + * @module + * @mergeModuleWith notUsed + */ +export const test = 1; diff --git a/src/test/internationalization.test.ts b/src/test/internationalization.test.ts index 68e1b528f..1e076520d 100644 --- a/src/test/internationalization.test.ts +++ b/src/test/internationalization.test.ts @@ -18,7 +18,10 @@ allValidTranslationKeys.push( allValidTranslationKeys.push(...inlineTags.map((s) => "tag_" + s.substring(1))); describe("Internationalization", () => { - const inter = new Internationalization(); + let inter: Internationalization; + beforeEach(() => { + inter = new Internationalization(); + }); afterEach(() => inter.setLocale("en")); it("Supports getting the list of supported languages", () => { @@ -43,6 +46,31 @@ describe("Internationalization", () => { inter.setLocale("zh"); equal(i18n.loaded_plugin_0("X"), "已加载插件 X"); }); + + it("Handles locales which do not exist", () => { + equal(i18n.loaded_plugin_0("X"), "Loaded plugin X"); + inter.setLocale("fakeLocale"); + equal(i18n.loaded_plugin_0("X"), "Loaded plugin X"); + }); + + it("Supports adding translations for loaded locale", () => { + inter.addTranslations("en", { testTranslation: "Test translation" }); + // @ts-expect-error testTranslation isn't a defined translation + equal(i18n.testTranslation(), "Test translation"); + }); + + it("Supports adding translations for not-loaded locale", () => { + inter.addTranslations("fake", { testTranslation: "Fake translation" }); + inter.setLocale("fake"); + // @ts-expect-error testTranslation isn't a defined translation + equal(i18n.testTranslation(), "Fake translation"); + }); + + it("Considers translations which have only been added by plugins to be real", () => { + inter.addTranslations("fake", { testTranslation: "Fake translation" }); + const supported = inter.getSupportedLanguages(); + ok(supported.includes("fake")); + }); }); describe("Locales", () => { diff --git a/src/test/models/comment.test.ts b/src/test/models/comment.test.ts index e302b7c0f..b4e4b91f4 100644 --- a/src/test/models/comment.test.ts +++ b/src/test/models/comment.test.ts @@ -1,6 +1,38 @@ import { deepStrictEqual as equal } from "assert"; import { Comment, type CommentDisplayPart, CommentTag } from "../../index.js"; +describe("Comment.similarTo", () => { + it("Checks for similar summaries", () => { + const a = new Comment([{ kind: "text", text: "a" }]); + const b = new Comment([{ kind: "text", text: "a" }]); + const c = new Comment([{ kind: "text", text: "c" }]); + + equal(a.similarTo(b), true); + equal(a.similarTo(c), false); + }); + + it("Ignores modifier tags", () => { + const a = new Comment([{ kind: "text", text: "a" }]); + a.modifierTags.add("@test"); + const b = new Comment([{ kind: "text", text: "a" }]); + + equal(a.similarTo(b), true); + }); + + it("Checks block tags", () => { + const a = new Comment([], [new CommentTag("@test", [{ kind: "text", text: "a" }])]); + const b = new Comment([], [new CommentTag("@test", [{ kind: "text", text: "a" }])]); + const c = new Comment([], [new CommentTag("@test", [{ kind: "text", text: "c" }])]); + const d = new Comment([], [new CommentTag("@test2", [{ kind: "text", text: "c" }])]); + const e = new Comment(); + + equal(a.similarTo(b), true); + equal(a.similarTo(c), false); + equal(a.similarTo(d), false); + equal(a.similarTo(e), false); + }); +}); + describe("Comment.combineDisplayParts", () => { it("Handles text and code", () => { const parts: CommentDisplayPart[] = [ diff --git a/src/test/utils/options/declaration.test.ts b/src/test/utils/options/declaration.test.ts index c61ae72ce..cd943ab53 100644 --- a/src/test/utils/options/declaration.test.ts +++ b/src/test/utils/options/declaration.test.ts @@ -200,7 +200,10 @@ describe("Options - conversions", () => { convert(["12,3"], optionWithType(ParameterType.Array), ""), ["12,3"], ); - equal(convert(true, optionWithType(ParameterType.Array), ""), []); + throws( + () => convert(true, optionWithType(ParameterType.Array), ""), + new Error("The 'test' option must be set to an array of strings"), + ); equal( convert("/,a", optionWithType(ParameterType.PathArray), ""), @@ -214,9 +217,9 @@ describe("Options - conversions", () => { ), [normalizePath(resolve("/foo"))], ); - equal( - convert(true, optionWithType(ParameterType.PathArray), ""), - [], + throws( + () => convert(true, optionWithType(ParameterType.PathArray), ""), + new Error("The 'test' option must be set to an array of strings"), ); equal( @@ -231,9 +234,14 @@ describe("Options - conversions", () => { ), ["a,b"], ); - equal( - convert(true, optionWithType(ParameterType.ModuleArray), ""), - [], + throws( + () => convert(true, optionWithType(ParameterType.ModuleArray), ""), + new Error("The 'test' option must be set to an array of strings"), + ); + + throws( + () => convert(true, optionWithType(ParameterType.GlobArray), ""), + new Error("The 'test' option must be set to an array of strings"), ); }); diff --git a/src/test/utils/options/default-options.test.ts b/src/test/utils/options/default-options.test.ts index 88bf20f51..5975bd99c 100644 --- a/src/test/utils/options/default-options.test.ts +++ b/src/test/utils/options/default-options.test.ts @@ -131,6 +131,168 @@ describe("Default Options", () => { }); }); + describe("locales", () => { + it("Should disallow non-objects", () => { + throws(() => opts.setValue("locales", null as never)); + }); + + it("Should disallow objects containing non-objects", () => { + throws(() => opts.setValue("locales", { test: false } as never)); + }); + + it("Should disallow locales containing non-strings", () => { + throws(() => opts.setValue("locales", { test: { key: false } } as never)); + }); + + it("Should allow valid locale shapes", () => { + doesNotThrow(() => opts.setValue("locales", { test: { key: "hi" } })); + }); + }); + + describe("packageOptions", () => { + it("Should disallow non-objects", () => { + throws(() => opts.setValue("packageOptions", null as never)); + }); + + it("Should allow objects", () => { + doesNotThrow(() => opts.setValue("packageOptions", { test: false } as never)); + }); + }); + + describe("blockTags", () => { + it("Should disallow non-tags", () => { + throws(() => opts.setValue("blockTags", ["@bad-non-tag"])); + }); + + it("Should allow tags", () => { + doesNotThrow(() => opts.setValue("blockTags", ["@good"])); + }); + }); + + describe("excludeNotDocumentedKinds", () => { + it("Should disallow invalid kind strings", () => { + throws(() => opts.setValue("excludeNotDocumentedKinds", ["InvalidKind"] as never)); + }); + + it("Should disallow disallowed kind strings", () => { + throws(() => opts.setValue("excludeNotDocumentedKinds", ["Project"] as never)); + }); + + it("Should kinds", () => { + doesNotThrow(() => opts.setValue("excludeNotDocumentedKinds", ["Variable"])); + }); + }); + + describe("externalSymbolLinkMappings", () => { + it("Should disallow non-objects", () => { + throws(() => opts.setValue("externalSymbolLinkMappings", null as never)); + }); + + it("Should disallow objects containing non-objects", () => { + throws(() => opts.setValue("externalSymbolLinkMappings", { test: false } as never)); + }); + + it("Should disallow mappings containing non-strings", () => { + throws(() => opts.setValue("externalSymbolLinkMappings", { test: { key: false } } as never)); + }); + + it("Should allow valid mapping shapes", () => { + doesNotThrow(() => opts.setValue("externalSymbolLinkMappings", { test: { key: "hi" } })); + }); + }); + + describe("outputs", () => { + it("Should disallow non-arrays", () => { + throws(() => opts.setValue("outputs", null as never)); + }); + + it("Should arrays of invalid shape", () => { + throws(() => opts.setValue("outputs", [{}] as never)); + }); + + it("Should allow valid shapes", () => { + doesNotThrow(() => opts.setValue("outputs", [{ name: "html", path: "out" }])); + }); + }); + + describe("highlightLanguages", () => { + it("Should disallow non-arrays", () => { + throws(() => opts.setValue("highlightLanguages", null as never)); + }); + + it("Should disallow arrays containing unknown languages", () => { + throws(() => opts.setValue("highlightLanguages", ["notASupportedLanguage"] as never)); + }); + + it("Should allow valid languages", () => { + doesNotThrow(() => opts.setValue("highlightLanguages", ["typescript"])); + }); + }); + + describe("markdownItLoader", () => { + it("Should disallow non-functions", () => { + throws(() => opts.setValue("markdownItLoader", null as never)); + }); + + it("Should allow functions", () => { + doesNotThrow(() => opts.setValue("markdownItLoader", () => {})); + }); + }); + + describe("favicon", () => { + it("Should disallow paths with an unknown extension", () => { + throws(() => opts.setValue("favicon", "test.txt")); + }); + + it("Should allow remote paths with an unknown extension", () => { + doesNotThrow(() => opts.setValue("favicon", "https://example.com/test.txt")); + }); + + it("Should allow paths with a known extension", () => { + doesNotThrow(() => opts.setValue("favicon", "test.png")); + }); + }); + + describe("hostedBaseurl", () => { + it("Should disallow non-http urls", () => { + throws(() => opts.setValue("hostedBaseUrl", "test")); + throws(() => opts.setValue("hostedBaseUrl", "ftp://test")); + }); + + it("Should allow http and https urls", () => { + doesNotThrow(() => opts.setValue("hostedBaseUrl", "http://example.com/")); + doesNotThrow(() => opts.setValue("hostedBaseUrl", "https://example.com/")); + }); + }); + + describe("visibilityFilters", () => { + it("Should disallow non-objects", () => { + throws(() => opts.setValue("visibilityFilters", "test" as never)); + }); + + it("Should disallow objects with an invalid key", () => { + throws(() => opts.setValue("visibilityFilters", { bad: true } as never)); + }); + + it("Should disallow objects with a non-boolean values", () => { + throws(() => opts.setValue("visibilityFilters", { private: "bad" } as never)); + }); + + it("Should allow objects with a valid shape", () => { + doesNotThrow(() => opts.setValue("visibilityFilters", { private: true })); + doesNotThrow(() => opts.setValue("visibilityFilters", { "@test": true })); + }); + }); + + describe("kindSortOrder", () => { + it("Should disallow non-kind strings", () => { + throws(() => opts.setValue("kindSortOrder", ["Bad"] as never)); + }); + it("Should allow valid sort orders", () => { + doesNotThrow(() => opts.setValue("kindSortOrder", ["Accessor"])); + }); + }); + it("Package Options Documentation", () => { const allOptions = opts .getDeclarations() diff --git a/src/test/validation.test.ts b/src/test/validation.test.ts index cba32af1d..905672c25 100644 --- a/src/test/validation.test.ts +++ b/src/test/validation.test.ts @@ -2,73 +2,58 @@ import { ok } from "assert"; import { join } from "path"; import { validateDocumentation } from "../lib/validation/documentation.js"; import { validateExports } from "../lib/validation/exports.js"; -import { getConverter2App, getConverter2Program } from "./programs.js"; +import { getConverter2App, getConverter2Program, getConverter2Project } from "./programs.js"; import { TestLogger } from "./TestLogger.js"; import { fileURLToPath } from "url"; +import { validateMergeModuleWith } from "../lib/validation/unusedMergeModuleWith.js"; -function convertValidationFile(file: string) { - const app = getConverter2App(); - const program = getConverter2Program(); - const sourceFile = program.getSourceFile( - join(fileURLToPath(import.meta.url), "../converter2/validation", file), - ); +function convertValidationFile(...files: [string, ...string[]]) { + return getConverter2Project(files, "validation"); +} - ok(sourceFile, "Specified source file does not exist."); +describe("validateExports", () => { + function expectWarning( + typeName: string, + file: string, + referencingName: string, + intentionallyNotExported: readonly string[] = [], + ) { + const project = convertValidationFile(file); - const project = app.converter.convert([ - { - displayName: "validation", - program, - sourceFile, - }, - ]); + const logger = new TestLogger(); + validateExports(project, logger, intentionallyNotExported); - return project; -} + logger.expectMessage( + `warn: ${typeName}, defined in */${file}, is referenced by ${referencingName} but not included in the documentation`, + ); + } -function expectWarning( - typeName: string, - file: string, - referencingName: string, - intentionallyNotExported: readonly string[] = [], -) { - const project = convertValidationFile(file); + function expectNoWarning( + file: string, + intentionallyNotExported: readonly string[] = [], + ) { + const app = getConverter2App(); + const program = getConverter2Program(); + const sourceFile = program.getSourceFile( + join(fileURLToPath(import.meta.url), "../converter2/validation", file), + ); - const logger = new TestLogger(); - validateExports(project, logger, intentionallyNotExported); + ok(sourceFile, "Specified source file does not exist."); - logger.expectMessage( - `warn: ${typeName}, defined in */${file}, is referenced by ${referencingName} but not included in the documentation`, - ); -} + const project = app.converter.convert([ + { + displayName: "validation", + program, + sourceFile, + }, + ]); -function expectNoWarning( - file: string, - intentionallyNotExported: readonly string[] = [], -) { - const app = getConverter2App(); - const program = getConverter2Program(); - const sourceFile = program.getSourceFile( - join(fileURLToPath(import.meta.url), "../converter2/validation", file), - ); - - ok(sourceFile, "Specified source file does not exist."); - - const project = app.converter.convert([ - { - displayName: "validation", - program, - sourceFile, - }, - ]); - - const logger = new TestLogger(); - - validateExports(project, logger, intentionallyNotExported); - logger.expectNoOtherMessages(); -} + const logger = new TestLogger(); + + validateExports(project, logger, intentionallyNotExported); + logger.expectNoOtherMessages(); + } -describe("validateExports", () => { it("Should warn if a variable type is missing", () => { expectWarning("Foo", "variable.ts", "foo"); }); @@ -224,3 +209,27 @@ describe("validateDocumentation", () => { logger.expectNoOtherMessages(); }); }); + +describe("validateMergeModuleWith", () => { + it("Should warn if the project has a @mergeModuleWith tag", () => { + const project = convertValidationFile("unusedMergeModuleWith.ts"); + + const logger = new TestLogger(); + validateMergeModuleWith(project, logger); + + logger.expectMessage( + `warn: has a @mergeModuleWith tag which could not be resolved`, + ); + }); + + it("Should warn if the project's module has a @mergeModuleWith tag", () => { + const project = convertValidationFile("unusedMergeModuleWith.ts", "return.ts"); + + const logger = new TestLogger(); + validateMergeModuleWith(project, logger); + + logger.expectMessage( + `warn: unusedMergeModuleWith has a @mergeModuleWith tag which could not be resolved`, + ); + }); +}); From 5409c0dc0d92e197320c55be97d30b2aded81816 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 28 Jun 2025 21:32:06 -0600 Subject: [PATCH 052/119] More test coverage improvements --- src/lib/converter/comments/linkResolver.ts | 97 ++++++++++--- src/lib/converter/converter.ts | 19 ++- .../converter/plugins/LinkResolverPlugin.ts | 99 +------------ src/lib/models/ContainerReflection.ts | 4 + src/lib/models/Reflection.ts | 4 + .../themes/default/templates/hierarchy.tsx | 2 +- src/lib/utils-common/array.ts | 17 +-- src/lib/utils-common/jsx.elements.ts | 2 + src/lib/utils-common/jsx.ts | 13 +- src/lib/utils-common/map.ts | 2 +- src/test/behavior.c2.test.ts | 19 ++- src/test/converter/types/specs.json | 132 +++++++++++------- src/test/converter/types/tuple.ts | 2 + src/test/converter2/behavior/document.md | 1 - src/test/converter2/behavior/documentTag.ts | 5 - src/test/converter2/behavior/documents/doc.md | 1 + .../converter2/behavior/documents/docs.ts | 8 ++ .../behavior/documents/package.json | 3 + .../converter2/behavior/documents/readme.md | 1 + src/test/declarationReference.test.ts | 10 ++ src/test/programs.ts | 3 +- src/test/renderer/testRendererUtils.ts | 8 +- src/test/utils-common/enum.test.ts | 42 +++++- src/test/utils-common/jsx.test.tsx | 53 ++++++- src/test/utils-common/map.test.ts | 114 +++++++++++++++ src/test/utils/general.test.ts | 34 +++-- 26 files changed, 464 insertions(+), 231 deletions(-) delete mode 100644 src/test/converter2/behavior/document.md delete mode 100644 src/test/converter2/behavior/documentTag.ts create mode 100644 src/test/converter2/behavior/documents/doc.md create mode 100644 src/test/converter2/behavior/documents/docs.ts create mode 100644 src/test/converter2/behavior/documents/package.json create mode 100644 src/test/converter2/behavior/documents/readme.md create mode 100644 src/test/utils-common/map.test.ts diff --git a/src/lib/converter/comments/linkResolver.ts b/src/lib/converter/comments/linkResolver.ts index 4bcfc1014..45a0f9482 100644 --- a/src/lib/converter/comments/linkResolver.ts +++ b/src/lib/converter/comments/linkResolver.ts @@ -1,9 +1,8 @@ import ts from "typescript"; import { - type Comment, type CommentDisplayPart, - DeclarationReflection, type InlineTagDisplayPart, + makeRecursiveVisitor, Reflection, ReflectionKind, ReflectionSymbolId, @@ -35,27 +34,28 @@ export type LinkResolverOptions = { }; export function resolveLinks( - comment: Comment, reflection: Reflection, externalResolver: ExternalSymbolResolver, options: LinkResolverOptions, ) { - comment.summary = resolvePartLinks( - reflection, - comment.summary, - externalResolver, - options, - ); - for (const tag of comment.blockTags) { - tag.content = resolvePartLinks( + if (reflection.comment) { + reflection.comment.summary = resolvePartLinks( reflection, - tag.content, + reflection.comment.summary, externalResolver, options, ); + for (const tag of reflection.comment.blockTags) { + tag.content = resolvePartLinks( + reflection, + tag.content, + externalResolver, + options, + ); + } } - if (reflection instanceof DeclarationReflection && reflection.readme) { + if ((reflection.isDeclaration() || reflection.isProject()) && reflection.readme) { reflection.readme = resolvePartLinks( reflection, reflection.readme, @@ -64,6 +64,18 @@ export function resolveLinks( ); } + if (reflection.isDeclaration()) { + reflection.type?.visit( + makeRecursiveVisitor({ + union: (type) => { + type.elementSummaries = type.elementSummaries?.map( + (parts) => resolvePartLinks(reflection, parts, externalResolver, options), + ); + }, + }), + ); + } + if (reflection.isDocument()) { reflection.content = resolvePartLinks( reflection, @@ -72,6 +84,57 @@ export function resolveLinks( options, ); } + + if ( + reflection.isParameter() && + reflection.type?.type === "reference" && + reflection.type.highlightedProperties + ) { + const resolved = new Map( + Array.from( + reflection.type.highlightedProperties, + ([name, parts]) => { + return [ + name, + resolvePartLinks(reflection, parts, externalResolver, options), + ]; + }, + ), + ); + + reflection.type.highlightedProperties = resolved; + } + + if (reflection.isContainer()) { + if (reflection.groups) { + for (const group of reflection.groups) { + if (group.description) { + group.description = resolvePartLinks( + reflection, + group.description, + externalResolver, + options, + ); + } + + if (group.categories) { + for (const cat of group.categories) { + if (cat.description) { + cat.description = resolvePartLinks(reflection, cat.description, externalResolver, options); + } + } + } + } + } + + if (reflection.categories) { + for (const cat of reflection.categories) { + if (cat.description) { + cat.description = resolvePartLinks(reflection, cat.description, externalResolver, options); + } + } + } + } } export function resolvePartLinks( @@ -133,10 +196,10 @@ function resolveLinkTag( if (tsTargets.length) { // Find the target most likely to have a real url in the generated documentation - // 1. A direct export (class, interface, variable) - // 2. A property of a direct export (class/interface property) - // 3. A property of a type of an export (property on type alias) - // 4. Whatever the first symbol found was + // 4. A direct export (class, interface, variable) + // 3. A property of a direct export (class/interface property) + // 2. A property of a type of an export (property on type alias) + // 1. Whatever the first symbol found was target = maxElementByScore(tsTargets, (r) => { if (r.kindOf(ReflectionKind.SomeExport)) { return 4; diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 23e16a478..5e6b43615 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -10,7 +10,7 @@ import { DocumentReflection, type ParameterReflection, ProjectReflection, - type Reflection, + Reflection, ReflectionKind, type ReflectionSymbolId, type SignatureReflection, @@ -436,25 +436,32 @@ export class Converter extends AbstractComponent { } } + resolveLinks(reflection: Reflection): void; + /** @deprecated just pass the reflection */ resolveLinks(comment: Comment, owner: Reflection): void; resolveLinks( parts: readonly CommentDisplayPart[], owner: Reflection, ): CommentDisplayPart[]; resolveLinks( - comment: Comment | readonly CommentDisplayPart[], - owner: Reflection, + comment: Reflection | Comment | readonly CommentDisplayPart[], + owner?: Reflection, ): CommentDisplayPart[] | undefined { - if (comment instanceof Comment) { + if (comment instanceof Reflection) { resolveLinks( comment, - owner, + (ref, part, refl, id) => this.resolveExternalLink(ref, part, refl, id), + { preserveLinkText: this.preserveLinkText }, + ); + } else if (comment instanceof Comment) { + resolveLinks( + owner!, (ref, part, refl, id) => this.resolveExternalLink(ref, part, refl, id), { preserveLinkText: this.preserveLinkText }, ); } else { return resolvePartLinks( - owner, + owner!, comment, (ref, part, refl, id) => this.resolveExternalLink(ref, part, refl, id), { preserveLinkText: this.preserveLinkText }, diff --git a/src/lib/converter/plugins/LinkResolverPlugin.ts b/src/lib/converter/plugins/LinkResolverPlugin.ts index fb2c876ce..d2086c8f8 100644 --- a/src/lib/converter/plugins/LinkResolverPlugin.ts +++ b/src/lib/converter/plugins/LinkResolverPlugin.ts @@ -2,13 +2,7 @@ import { ConverterComponent } from "../components.js"; import type { Context, Converter } from "../../converter/index.js"; import { ConverterEvents } from "../converter-events.js"; import { Option, type ValidationOptions } from "../../utils/index.js"; -import { - ContainerReflection, - makeRecursiveVisitor, - type ProjectReflection, - type Reflection, - type ReflectionCategory, -} from "../../models/index.js"; +import type { ProjectReflection } from "../../models/index.js"; import { discoverAllReferenceTypes } from "../../utils/reflections.js"; import { ApplicationEvents } from "../../application-events.js"; @@ -40,84 +34,7 @@ export class LinkResolverPlugin extends ConverterComponent { resolveLinks(project: ProjectReflection) { for (const id in project.reflections) { const reflection = project.reflections[id]; - if (reflection.comment) { - this.owner.resolveLinks(reflection.comment, reflection); - } - - if (reflection.isDeclaration()) { - reflection.type?.visit( - makeRecursiveVisitor({ - union: (type) => { - type.elementSummaries = type.elementSummaries?.map( - (parts) => this.owner.resolveLinks(parts, reflection), - ); - }, - }), - ); - - if (reflection.readme) { - reflection.readme = this.owner.resolveLinks( - reflection.readme, - reflection, - ); - } - } - - if (reflection.isDocument()) { - reflection.content = this.owner.resolveLinks( - reflection.content, - reflection, - ); - } - - if ( - reflection.isParameter() && - reflection.type?.type === "reference" && - reflection.type.highlightedProperties - ) { - const resolved = new Map( - Array.from( - reflection.type.highlightedProperties, - ([name, parts]) => { - return [ - name, - this.owner.resolveLinks(parts, reflection), - ]; - }, - ), - ); - - reflection.type.highlightedProperties = resolved; - } - - if (reflection instanceof ContainerReflection) { - if (reflection.groups) { - for (const group of reflection.groups) { - if (group.description) { - group.description = this.owner.resolveLinks( - group.description, - reflection, - ); - } - - if (group.categories) { - for (const cat of group.categories) { - this.resolveCategoryLinks(cat, reflection); - } - } - } - } - - if (reflection.categories) { - for (const cat of reflection.categories) { - this.resolveCategoryLinks(cat, reflection); - } - } - } - } - - if (project.readme) { - project.readme = this.owner.resolveLinks(project.readme, project); + this.owner.resolveLinks(reflection); } for ( @@ -144,16 +61,4 @@ export class LinkResolverPlugin extends ConverterComponent { } } } - - private resolveCategoryLinks( - category: ReflectionCategory, - owner: Reflection, - ) { - if (category.description) { - category.description = this.owner.resolveLinks( - category.description, - owner, - ); - } - } } diff --git a/src/lib/models/ContainerReflection.ts b/src/lib/models/ContainerReflection.ts index cf9835c2e..f29492f5e 100644 --- a/src/lib/models/ContainerReflection.ts +++ b/src/lib/models/ContainerReflection.ts @@ -113,6 +113,10 @@ export abstract class ContainerReflection extends Reflection { } } + override isContainer(): this is ContainerReflection { + return true; + } + override traverse(callback: TraverseCallback) { for (const child of this.children?.slice() || []) { if (callback(child, TraverseProperty.Children) === false) { diff --git a/src/lib/models/Reflection.ts b/src/lib/models/Reflection.ts index 2354687f6..9468671d1 100644 --- a/src/lib/models/Reflection.ts +++ b/src/lib/models/Reflection.ts @@ -11,6 +11,7 @@ import type { ParameterReflection } from "./ParameterReflection.js"; import type { ReferenceReflection } from "./ReferenceReflection.js"; import type { SignatureReflection } from "./SignatureReflection.js"; import type { TypeParameterReflection } from "./TypeParameterReflection.js"; +import type { ContainerReflection } from "./ContainerReflection.js"; /** * Current reflection id. @@ -455,6 +456,9 @@ export abstract class Reflection { isReference(): this is ReferenceReflection { return this.variant === "reference"; } + isContainer(): this is ContainerReflection { + return false; + } /** * Check if this reflection or any of its parents have been marked with the `@deprecated` tag. diff --git a/src/lib/output/themes/default/templates/hierarchy.tsx b/src/lib/output/themes/default/templates/hierarchy.tsx index fcc9091f2..f16d57ef8 100644 --- a/src/lib/output/themes/default/templates/hierarchy.tsx +++ b/src/lib/output/themes/default/templates/hierarchy.tsx @@ -37,7 +37,7 @@ function fullHierarchy( {context.reflectionIcon(root)} {root.name} - {children.length &&
    {children}
} + {children.length ?
    {children}
: null} ); } diff --git a/src/lib/utils-common/array.ts b/src/lib/utils-common/array.ts index 143ddd4bf..7f30b8cbc 100644 --- a/src/lib/utils-common/array.ts +++ b/src/lib/utils-common/array.ts @@ -149,13 +149,9 @@ export function filterMap( } export function firstDefined( - array: readonly T[] | undefined, + array: readonly T[], callback: (element: T, index: number) => U | undefined, ): U | undefined { - if (array === undefined) { - return undefined; - } - for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); if (result !== undefined) { @@ -176,17 +172,6 @@ export function aggregate(arr: T[], fn: (item: T) => number) { return arr.reduce((sum, it) => sum + fn(it), 0); } -export function aggregateWithJoiner( - arr: T[], - fn: (item: T) => number, - joiner: string, -) { - return ( - arr.reduce((sum, it) => sum + fn(it), 0) + - (arr.length - 1) * joiner.length - ); -} - export function joinArray( arr: readonly T[] | undefined, joiner: string, diff --git a/src/lib/utils-common/jsx.elements.ts b/src/lib/utils-common/jsx.elements.ts index d7c2fe489..c6d23dfaa 100644 --- a/src/lib/utils-common/jsx.elements.ts +++ b/src/lib/utils-common/jsx.elements.ts @@ -144,6 +144,8 @@ export type JsxChildren = | JsxElement | string | number + | boolean + | bigint | null | undefined | JsxChildren[]; diff --git a/src/lib/utils-common/jsx.ts b/src/lib/utils-common/jsx.ts index ef93e6c82..e6fd9a5a8 100644 --- a/src/lib/utils-common/jsx.ts +++ b/src/lib/utils-common/jsx.ts @@ -98,7 +98,7 @@ export function setRenderSettings(options: { pretty: boolean }) { } export function renderElement(element: JsxElement | null | undefined): string { - if (!element || typeof element === "boolean") { + if (!element) { return ""; } @@ -161,11 +161,11 @@ export function renderElement(element: JsxElement | null | undefined): string { function renderChildren(children: JsxChildren[]) { for (const child of children) { - if (!child) continue; + if (typeof child === "boolean") continue; if (Array.isArray(child)) { renderChildren(child); - } else if (typeof child === "string" || typeof child === "number") { + } else if (typeof child === "string" || typeof child === "number" || typeof child === "bigint") { html += escapeHtml(child.toString()); } else { html += renderElement(child); @@ -180,7 +180,7 @@ export function renderElement(element: JsxElement | null | undefined): string { * @internal */ export function renderElementToText(element: JsxElement | null | undefined) { - if (!element || typeof element === "boolean") { + if (!element) { return ""; } @@ -205,11 +205,12 @@ export function renderElementToText(element: JsxElement | null | undefined) { function renderChildren(children: JsxChildren[]) { for (const child of children) { - if (!child) continue; + if (typeof child === "boolean") continue; if (Array.isArray(child)) { renderChildren(child); - } else if (typeof child === "string" || typeof child === "number") { + } else if (typeof child === "string" || typeof child === "number" || typeof child === "bigint") { + // Turn non-breaking spaces into regular spaces html += child.toString().replaceAll("\u00A0", " "); } else { html += renderElementToText(child); diff --git a/src/lib/utils-common/map.ts b/src/lib/utils-common/map.ts index a893cdceb..034e1de18 100644 --- a/src/lib/utils-common/map.ts +++ b/src/lib/utils-common/map.ts @@ -49,7 +49,7 @@ export class StableKeyMap { } forEach( - callbackfn: (value: V, key: K, map: Map) => void, + callbackfn: (value: V, key: K, map: StableKeyMap) => void, thisArg?: any, ): void { for (const [k, v] of this.entries()) { diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index cf073fbcb..593c3646d 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -1466,17 +1466,24 @@ describe("Behavior Tests", () => { }); it("Supports @document tags", () => { - const project = convert("documentTag"); + const project = convert("documents/docs"); equal(reflToTree(project), { - HasDescriptor: "Interface", + hasDocs: "Variable", }); - const refl = query(project, "HasDescriptor"); - equal(refl.documents?.length, 1); + equal(project.documents?.length, 1); - equal(refl.documents[0].content, [ - { kind: "text", text: "External doc!" }, + equal(project.documents[0].content, [ + { kind: "text", text: "Link to [project](" }, + { kind: "relative-link", target: 1, targetAnchor: undefined, text: "./docs.ts" }, + { kind: "text", text: ")" }, + ]); + + equal(project.readme, [ + { kind: "text", text: "Doc projects readme: [docs](" }, + { kind: "relative-link", target: 2, targetAnchor: undefined, text: "./doc.md" }, + { kind: "text", text: ")" }, ]); }); }); diff --git a/src/test/converter/types/specs.json b/src/test/converter/types/specs.json index 1ee7e6aad..4431c5bab 100644 --- a/src/test/converter/types/specs.json +++ b/src/test/converter/types/specs.json @@ -1339,6 +1339,24 @@ "kind": 2, "flags": {}, "children": [ + { + "id": 68, + "name": "Empty", + "variant": "declaration", + "kind": 2097152, + "flags": {}, + "sources": [ + { + "fileName": "tuple.ts", + "line": 18, + "character": 12, + "url": "typedoc://tuple.ts#L18" + } + ], + "type": { + "type": "tuple" + } + }, { "id": 66, "name": "LeadingRest", @@ -1732,6 +1750,7 @@ { "title": "Type Aliases", "children": [ + 68, 66, 58, 64, @@ -1760,14 +1779,14 @@ ] }, { - "id": 68, + "id": 69, "name": "type-operator", "variant": "declaration", "kind": 2, "flags": {}, "children": [ { - "id": 70, + "id": 71, "name": "B", "variant": "declaration", "kind": 2097152, @@ -1793,14 +1812,14 @@ } }, { - "id": 71, + "id": 72, "name": "C", "variant": "declaration", "kind": 2097152, "flags": {}, "children": [ { - "id": 73, + "id": 74, "name": "prop1", "variant": "declaration", "kind": 1024, @@ -1819,7 +1838,7 @@ } }, { - "id": 74, + "id": 75, "name": "prop2", "variant": "declaration", "kind": 1024, @@ -1842,8 +1861,8 @@ { "title": "Properties", "children": [ - 73, - 74 + 74, + 75 ] } ], @@ -1857,7 +1876,7 @@ ] }, { - "id": 75, + "id": 76, "name": "D", "variant": "declaration", "kind": 2097152, @@ -1875,14 +1894,14 @@ "operator": "keyof", "target": { "type": "reference", - "target": 71, + "target": 72, "name": "C", "package": "typedoc" } } }, { - "id": 69, + "id": 70, "name": "a", "variant": "declaration", "kind": 32, @@ -1912,15 +1931,15 @@ { "title": "Type Aliases", "children": [ - 70, 71, - 75 + 72, + 76 ] }, { "title": "Variables", "children": [ - 69 + 70 ] } ], @@ -1934,14 +1953,14 @@ ] }, { - "id": 76, + "id": 77, "name": "union-or-intersection", "variant": "declaration", "kind": 2, "flags": {}, "children": [ { - "id": 77, + "id": 78, "name": "FirstType", "variant": "declaration", "kind": 256, @@ -1956,7 +1975,7 @@ }, "children": [ { - "id": 78, + "id": 79, "name": "firstProperty", "variant": "declaration", "kind": 1024, @@ -1987,7 +2006,7 @@ { "title": "Properties", "children": [ - 78 + 79 ] } ], @@ -2001,7 +2020,7 @@ ] }, { - "id": 79, + "id": 80, "name": "SecondType", "variant": "declaration", "kind": 256, @@ -2016,7 +2035,7 @@ }, "children": [ { - "id": 80, + "id": 81, "name": "secondProperty", "variant": "declaration", "kind": 1024, @@ -2047,7 +2066,7 @@ { "title": "Properties", "children": [ - 80 + 81 ] } ], @@ -2061,7 +2080,7 @@ ] }, { - "id": 81, + "id": 82, "name": "ThirdType", "variant": "declaration", "kind": 256, @@ -2076,7 +2095,7 @@ }, "children": [ { - "id": 84, + "id": 85, "name": "thirdComplexProperty", "variant": "declaration", "kind": 1024, @@ -2113,13 +2132,13 @@ "types": [ { "type": "reference", - "target": 77, + "target": 78, "name": "FirstType", "package": "typedoc" }, { "type": "reference", - "target": 79, + "target": 80, "name": "SecondType", "package": "typedoc" } @@ -2131,7 +2150,7 @@ } }, { - "id": 83, + "id": 84, "name": "thirdIntersectionProperty", "variant": "declaration", "kind": 1024, @@ -2157,13 +2176,13 @@ "types": [ { "type": "reference", - "target": 77, + "target": 78, "name": "FirstType", "package": "typedoc" }, { "type": "reference", - "target": 81, + "target": 82, "name": "ThirdType", "package": "typedoc" } @@ -2171,7 +2190,7 @@ } }, { - "id": 82, + "id": 83, "name": "thirdUnionProperty", "variant": "declaration", "kind": 1024, @@ -2197,13 +2216,13 @@ "types": [ { "type": "reference", - "target": 77, + "target": 78, "name": "FirstType", "package": "typedoc" }, { "type": "reference", - "target": 79, + "target": 80, "name": "SecondType", "package": "typedoc" } @@ -2215,9 +2234,9 @@ { "title": "Properties", "children": [ + 85, 84, - 83, - 82 + 83 ] } ], @@ -2235,9 +2254,9 @@ { "title": "Interfaces", "children": [ - 77, - 79, - 81 + 78, + 80, + 82 ] } ], @@ -2261,8 +2280,8 @@ 50, 54, 57, - 68, - 76 + 69, + 77 ] } ], @@ -2559,81 +2578,86 @@ "qualifiedName": "leadingRest" }, "68": { + "packageName": "typedoc", + "packagePath": "src/test/converter/types/tuple.ts", + "qualifiedName": "Empty" + }, + "69": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "" }, - "69": { + "70": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "a" }, - "70": { + "71": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "B" }, - "71": { + "72": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "C" }, - "73": { + "74": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "__type.prop1" }, - "74": { + "75": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "__type.prop2" }, - "75": { + "76": { "packageName": "typedoc", "packagePath": "src/test/converter/types/type-operator.ts", "qualifiedName": "D" }, - "76": { + "77": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "" }, - "77": { + "78": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "FirstType" }, - "78": { + "79": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "FirstType.firstProperty" }, - "79": { + "80": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "SecondType" }, - "80": { + "81": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "SecondType.secondProperty" }, - "81": { + "82": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "ThirdType" }, - "82": { + "83": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "ThirdType.thirdUnionProperty" }, - "83": { + "84": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "ThirdType.thirdIntersectionProperty" }, - "84": { + "85": { "packageName": "typedoc", "packagePath": "src/test/converter/types/union-or-intersection.ts", "qualifiedName": "ThirdType.thirdComplexProperty" @@ -2657,8 +2681,8 @@ "4": 50, "5": 54, "6": 57, - "7": 68, - "8": 76 + "7": 69, + "8": 77 } } } diff --git a/src/test/converter/types/tuple.ts b/src/test/converter/types/tuple.ts index fe473906b..7bc3116e1 100644 --- a/src/test/converter/types/tuple.ts +++ b/src/test/converter/types/tuple.ts @@ -15,6 +15,8 @@ export type LeadingRest = [...string[], number]; // returnMapped isn't good enough here. export const leadingRest = {} as any as [...string[], number]; +export type Empty = []; + // Helper to force TS to give us types, rather than type nodes, for a given declaration. function returnMapped() { return {} as any as { [K in keyof T]: T[K] }; diff --git a/src/test/converter2/behavior/document.md b/src/test/converter2/behavior/document.md deleted file mode 100644 index 794870100..000000000 --- a/src/test/converter2/behavior/document.md +++ /dev/null @@ -1 +0,0 @@ -External doc! diff --git a/src/test/converter2/behavior/documentTag.ts b/src/test/converter2/behavior/documentTag.ts deleted file mode 100644 index 71e00d05a..000000000 --- a/src/test/converter2/behavior/documentTag.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * @document ./document.md - */ -export interface HasDescriptor { -} diff --git a/src/test/converter2/behavior/documents/doc.md b/src/test/converter2/behavior/documents/doc.md new file mode 100644 index 000000000..69daae76d --- /dev/null +++ b/src/test/converter2/behavior/documents/doc.md @@ -0,0 +1 @@ +Link to [project](./docs.ts) diff --git a/src/test/converter2/behavior/documents/docs.ts b/src/test/converter2/behavior/documents/docs.ts new file mode 100644 index 000000000..c73719e9d --- /dev/null +++ b/src/test/converter2/behavior/documents/docs.ts @@ -0,0 +1,8 @@ +/** + * Link to [doc](./doc.md) + * + * @module + * @document doc.md + */ + +export const hasDocs = true; diff --git a/src/test/converter2/behavior/documents/package.json b/src/test/converter2/behavior/documents/package.json new file mode 100644 index 000000000..b724be201 --- /dev/null +++ b/src/test/converter2/behavior/documents/package.json @@ -0,0 +1,3 @@ +{ + "name": "docs" +} diff --git a/src/test/converter2/behavior/documents/readme.md b/src/test/converter2/behavior/documents/readme.md new file mode 100644 index 000000000..811c08d76 --- /dev/null +++ b/src/test/converter2/behavior/documents/readme.md @@ -0,0 +1 @@ +Doc projects readme: [docs](./doc.md) diff --git a/src/test/declarationReference.test.ts b/src/test/declarationReference.test.ts index 2f525dddb..a01e8890e 100644 --- a/src/test/declarationReference.test.ts +++ b/src/test/declarationReference.test.ts @@ -1,5 +1,6 @@ import { deepStrictEqual as equal } from "assert"; import { + meaningToString, parseComponent, parseComponentPath, parseDeclarationReference, @@ -226,4 +227,13 @@ describe("Declaration References", () => { }); }); }); + + describe("meaningToString", () => { + it("Converts to string", () => { + equal(meaningToString({ keyword: "class" }), "class"); + equal(meaningToString({ keyword: "class", index: 1 }), "class(1)"); + equal(meaningToString({ label: "X" }), "X"); + equal(meaningToString({}), ""); + }); + }); }); diff --git a/src/test/programs.ts b/src/test/programs.ts index 8c6a51110..c63b42f26 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -15,7 +15,7 @@ import { createAppForTesting } from "../lib/application.js"; import { existsSync } from "fs"; import { clearCommentCache } from "../lib/converter/comments/index.js"; import { diagnostics } from "../lib/utils/loggers.js"; -import { normalizePath, readFile } from "#node-utils"; +import { normalizePath, readFile, ValidatingFileRegistry } from "#node-utils"; let converterApp: Application | undefined; let converterProgram: ts.Program | undefined; @@ -174,6 +174,7 @@ export function getConverter2Project(entries: string[], folder: string) { ok(entryPoints.length > 0, "Expected at least one entry point"); app.options.setValue("entryPoints", entryPoints); + app.files = new ValidatingFileRegistry(); clearCommentCache(); return app.converter.convert( files.map((file, index) => { diff --git a/src/test/renderer/testRendererUtils.ts b/src/test/renderer/testRendererUtils.ts index bb501f703..1ecdd812f 100644 --- a/src/test/renderer/testRendererUtils.ts +++ b/src/test/renderer/testRendererUtils.ts @@ -47,12 +47,12 @@ function collapseStrings(data: any[]): unknown { } function renderElementToSnapshot(element: JsxChildren): unknown { - if (!element || typeof element === "boolean") { - return ""; + if (typeof element === "string" || typeof element === "number" || typeof element === "bigint") { + return element.toString(); } - if (typeof element === "string" || typeof element === "number") { - return element.toString(); + if (!element || typeof element === "boolean") { + return ""; } if (Array.isArray(element)) { diff --git a/src/test/utils-common/enum.test.ts b/src/test/utils-common/enum.test.ts index 1c58bc503..60fab2397 100644 --- a/src/test/utils-common/enum.test.ts +++ b/src/test/utils-common/enum.test.ts @@ -1,9 +1,45 @@ -import { ok } from "assert"; +import { deepStrictEqual as equal, ok } from "assert"; import { ReflectionKind } from "../../lib/models/index.js"; -import { getEnumKeys } from "#utils"; +import { debugFlags, getEnumFlags, getEnumKeys, hasAllFlags, hasAnyFlag, removeFlag } from "#utils"; describe("Enum utils", () => { - it("Should be able to get enum keys", () => { + it("getEnumFlags", () => { + const keys = getEnumFlags(123); + equal(keys, [1, 2, 8, 16, 32, 64]); + }); + + it("removeFlag", () => { + equal(removeFlag(3, 2), 1); + equal(removeFlag(3, 1), 2); + equal(removeFlag(3, 4), 3); + }); + + it("hasAllFlags", () => { + equal(hasAllFlags(3, 1), true); + equal(hasAllFlags(3, 1 | 2), true); + equal(hasAllFlags(3, 1 | 4), false); + }); + + it("hasAnyFlag", () => { + equal(hasAnyFlag(3, 1), true); + equal(hasAnyFlag(3, 1 | 2), true); + equal(hasAnyFlag(3, 1 | 4), true); + equal(hasAnyFlag(3, 4), false); + }); + + it("debugFlags", () => { + enum Flags { + A = 1, + B = 2, + C = 4, + } + equal(debugFlags(Flags, Flags.A), ["A"]); + equal(debugFlags(Flags, Flags.A | Flags.B), ["A", "B"]); + equal(debugFlags(Flags, Flags.A | Flags.C), ["A", "C"]); + equal(debugFlags(Flags, 0), []); + }); + + it("getEnumKeys", () => { const keys = getEnumKeys(ReflectionKind); ok(keys.includes("Project")); ok(!keys.includes("SignatureContainer")); diff --git a/src/test/utils-common/jsx.test.tsx b/src/test/utils-common/jsx.test.tsx index eef2abe8a..a642c0a31 100644 --- a/src/test/utils-common/jsx.test.tsx +++ b/src/test/utils-common/jsx.test.tsx @@ -1,9 +1,13 @@ import { deepStrictEqual as equal } from "assert"; import { JSX } from "#utils"; - -const renderElement = JSX.renderElement; +import { renderElement, renderElementToText } from "../../lib/utils-common/jsx.js"; describe("JSX", () => { + it("Handles null/undefined", () => { + equal(renderElement(null), ``); + equal(renderElement(undefined), ``); + }); + it("Works with basic case", () => { const element = (
@@ -70,6 +74,9 @@ describe("JSX", () => { it("Supports for injecting HTML", () => { equal(renderElement(), "foo"); + + // This is should never be used in common usage, but it shouldn't break + equal(JSX.Raw({ html: "" }), null); }); it("Supports SVG elements", () => { @@ -89,4 +96,46 @@ describe("JSX", () => { const quot = `test"quote`; equal(renderElement(
), `
`); }); + + it("Handles non-string attributes", () => { + equal(renderElement(
), `
`); + }); + + it("Handles void elements", () => { + equal(renderElement(
), `
`); + }); + + it("Handles boolean children", () => { + equal(renderElement({true}{false}), ``); + }); + + it("Handles zero", () => { + equal(renderElement({0}), `0`); + }); +}); + +describe("JSX.renderElementToText", () => { + it("Handles null/undefined", () => { + equal(renderElementToText(null), ``); + equal(renderElementToText(undefined), ``); + }); + + it("Handles zero", () => { + equal(renderElementToText({0}), `0`); + }); + + it("Handles boolean children", () => { + equal(renderElementToText({true}{false}), ``); + }); + + it("Handles Raw elements", () => { + equal(renderElementToText(), ``); + }); + + it("Handles components", () => { + function Component() { + return hi; + } + equal(renderElementToText(), `hi`); + }); }); diff --git a/src/test/utils-common/map.test.ts b/src/test/utils-common/map.test.ts new file mode 100644 index 000000000..4eabb5d01 --- /dev/null +++ b/src/test/utils-common/map.test.ts @@ -0,0 +1,114 @@ +import { deepStrictEqual as equal, ok } from "assert"; +import { DefaultMap, StableKeyMap } from "#utils"; + +describe("DefaultMap", () => { + it("Creates entries if they do not exist", () => { + const map = new DefaultMap(() => 123); + + equal(map.get("a"), 123); + map.set("b", 5); + equal(map.get("b"), 5); + equal(map.getNoInsert("c"), undefined); + }); +}); + +describe("StableKeyMap", () => { + interface StableKeyed { + getStableKey(): string; + } + const a = { + id: 1, + getStableKey() { + return "a"; + }, + }; + const a2 = { + id: 2, + getStableKey() { + return "a"; + }, + }; + + it("Inserts objects via a stable key", () => { + const map = new StableKeyMap(); + + equal(map.size, 0); + map.set(a, 1); + equal(map.size, 1); + equal(map.get(a), 1); + equal(map.get(a2), 1); + }); + + it("Supports clear()", () => { + const map = new StableKeyMap(); + map.set(a, 1); + map.clear(); + equal(map.size, 0); + }); + + it("Supports delete()", () => { + const map = new StableKeyMap(); + map.set(a, 1); + equal(map.delete(a2), true); + equal(map.size, 0); + }); + + it("Supports forEach()", () => { + const map = new StableKeyMap(); + map.set(a, 1); + let called = false; + map.forEach((value, key, map2) => { + called = true; + equal(value, 1); + ok(key === a); + ok(map === map2); + }); + ok(called); + }); + + it("Supports entries()", () => { + const map = new StableKeyMap(); + map.set(a, 1); + let called = false; + for (const [key, value] of map.entries()) { + called = true; + equal(value, 1); + ok(key === a); + } + ok(called); + }); + + it("Supports keys()", () => { + const map = new StableKeyMap(); + map.set(a, 1); + let called = false; + for (const key of map.keys()) { + called = true; + ok(key === a); + } + ok(called); + }); + + it("Supports values()", () => { + const map = new StableKeyMap(); + map.set(a, 1); + let called = false; + for (const value of map.values()) { + called = true; + equal(value, 1); + } + ok(called); + }); + + it("Supports [Symbol.iterator]", () => { + const map = new StableKeyMap(); + map.set(a, 1); + let called = false; + for (const [key, value] of map) { + called = true; + equal(value, 1); + ok(key === a); + } + ok(called); + }); +}); diff --git a/src/test/utils/general.test.ts b/src/test/utils/general.test.ts index 108ea73af..b9cbeaa06 100644 --- a/src/test/utils/general.test.ts +++ b/src/test/utils/general.test.ts @@ -1,27 +1,39 @@ -import { deepStrictEqual as equal } from "assert/strict"; -import { dedent } from "#utils"; +import { deepStrictEqual as equal, doesNotThrow, throws } from "assert/strict"; +import { assert, assertNever, dedent } from "#utils"; -describe("Dedent test helper", () => { - it("Works on empty string", () => { +describe("general.ts", () => { + it("dedent works on empty string", () => { equal(dedent(""), ""); }); - it("Works with indented text", () => { + it("dedent works with indented text", () => { equal( dedent(` - Text here - `), + Text here + `), "Text here", ); }); - it("Works with multiple lines", () => { + it("dedent works with multiple lines", () => { equal( dedent(` - Text here - More indented - `), + Text here + More indented + `), "Text here\n More indented", ); }); + + it("assertNever", () => { + throws( + () => assertNever("x" as never), + new Error(`Expected handling to cover all possible cases, but it didn't cover: "x"`), + ); + }); + + it("assert", () => { + doesNotThrow(() => assert(true)); + throws(() => assert(false), new Error("Assertion failed")); + }); }); From 784e48dd7d52d31ea4b1f448bcdbfb63ae52ebd6 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 16:19:00 -0600 Subject: [PATCH 053/119] Fix lint --- src/test/issues.c2.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 2cc8f0fb8..94a1b6e76 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -1735,7 +1735,7 @@ describe("Issue Tests", () => { caption: "resolver caption", }; }); - app.converter.resolveLinks(abc.comment, abc); + app.converter.resolveLinks(abc); app.converter["_externalSymbolResolvers"] = resolvers; equal(getLinks(abc), [ From a5c09b7f23f0ee6a507c9a8c7ecb8ea7e33740fb Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 16:21:47 -0600 Subject: [PATCH 054/119] Update semver --- package.json | 2 +- pnpm-lock.yaml | 266 ++++++++++++++++++++++--------------------------- 2 files changed, 120 insertions(+), 148 deletions(-) diff --git a/package.json b/package.json index cb71b828d..3041339a3 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "eslint": "^9.30.0", "mocha": "^11.7.1", "puppeteer": "^24.11.1", - "semver": "^7.7.1", + "semver": "^7.7.2", "tsx": "^4.20.3", "typescript": "5.8.3", "typescript-eslint": "^8.35.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54ac886f2..15045d4e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,8 +37,8 @@ importers: specifier: ^10.0.10 version: 10.0.10 '@types/node': - specifier: '18' - version: 18.19.76 + specifier: ^18.19.113 + version: 18.19.113 '@typestrong/fs-fixture-builder': specifier: github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe version: https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe @@ -61,8 +61,8 @@ importers: specifier: ^24.11.1 version: 24.11.1(typescript@5.8.3) semver: - specifier: ^7.7.1 - version: 7.7.1 + specifier: ^7.7.2 + version: 7.7.2 tsx: specifier: ^4.20.3 version: 4.20.3 @@ -75,12 +75,12 @@ importers: packages: - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': @@ -282,12 +282,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.1': - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.7.0': resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -349,8 +343,8 @@ packages: resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.2': - resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} '@isaacs/cliui@8.0.2': @@ -365,11 +359,11 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.1': + resolution: {integrity: sha512-mBLKRHc7Ffw/hObYb9+cunuGNjshQk+vZdwZBJoqiysK/mW3Jq0UXosq8aIhMnLevANhR9yoYfdUEOHg6M9y0g==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.26': + resolution: {integrity: sha512-Z9rjt4BUVEbLFpw0qjCklVxxf421wrmcbP4w+LmBUxYCyJTYYSclgJD0YsCgGqQCtCIPiz7kjbYYJiAKhjJ3kA==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -410,8 +404,8 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -437,8 +431,8 @@ packages: '@types/mocha@10.0.10': resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} - '@types/node@18.19.76': - resolution: {integrity: sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==} + '@types/node@18.19.113': + resolution: {integrity: sha512-TmSTE9vyebJ9vSEiU+P+0Sp4F5tMgjiEOZaQUW6wA3ODvi6uBgkHQ+EsIu0pbiKvf9QHEvyRCiaz03rV0b+IaA==} '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -559,13 +553,18 @@ packages: bare-events@2.5.4: resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} - bare-fs@4.0.1: - resolution: {integrity: sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==} - engines: {bare: '>=1.7.0'} + bare-fs@4.1.5: + resolution: {integrity: sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true - bare-os@3.4.0: - resolution: {integrity: sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==} - engines: {bare: '>=1.6.0'} + bare-os@3.6.1: + resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} + engines: {bare: '>=1.14.0'} bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} @@ -585,11 +584,11 @@ packages: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -666,15 +665,6 @@ packages: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -715,8 +705,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} @@ -815,8 +805,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.19.0: - resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} @@ -844,8 +834,8 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} fsevents@2.3.3: @@ -861,8 +851,8 @@ packages: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} - get-tsconfig@4.10.0: - resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} get-uri@6.0.4: resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} @@ -1146,8 +1136,8 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} @@ -1187,8 +1177,8 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} run-parallel@1.2.0: @@ -1197,11 +1187,6 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -1230,8 +1215,8 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.4: - resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==} + socks@2.8.5: + resolution: {integrity: sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map@0.6.1: @@ -1241,8 +1226,8 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - streamx@2.22.0: - resolution: {integrity: sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==} + streamx@2.22.1: + resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -1272,8 +1257,8 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - tar-fs@3.0.8: - resolution: {integrity: sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==} + tar-fs@3.0.10: + resolution: {integrity: sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==} tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} @@ -1398,18 +1383,18 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod@3.24.2: - resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} snapshots: - '@babel/code-frame@7.26.2': + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} '@bcoe/v8-coverage@1.0.2': {} @@ -1515,11 +1500,6 @@ snapshots: '@esbuild/win32-x64@0.25.5': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.30.0)': - dependencies: - eslint: 9.30.0 - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.7.0(eslint@9.30.0)': dependencies: eslint: 9.30.0 @@ -1530,7 +1510,7 @@ snapshots: '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -1548,7 +1528,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -1587,7 +1567,7 @@ snapshots: '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.2': {} + '@humanwhocodes/retry@0.4.3': {} '@isaacs/cliui@8.0.2': dependencies: @@ -1602,12 +1582,12 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.1': {} - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.26': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.1 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -1619,19 +1599,19 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.0 + fastq: 1.19.1 '@pkgjs/parseargs@0.11.0': optional: true '@puppeteer/browsers@2.10.5': dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 semver: 7.7.2 - tar-fs: 3.0.8 + tar-fs: 3.0.10 yargs: 17.7.2 transitivePeerDependencies: - bare-buffer @@ -1659,7 +1639,7 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} '@types/hast@3.0.4': dependencies: @@ -1682,7 +1662,7 @@ snapshots: '@types/mocha@10.0.10': {} - '@types/node@18.19.76': + '@types/node@18.19.113': dependencies: undici-types: 5.26.5 @@ -1690,7 +1670,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 18.19.76 + '@types/node': 18.19.113 optional: true '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3))(eslint@9.30.0)(typescript@5.8.3)': @@ -1716,7 +1696,7 @@ snapshots: '@typescript-eslint/types': 8.35.0 '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.35.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) eslint: 9.30.0 typescript: 5.8.3 transitivePeerDependencies: @@ -1726,7 +1706,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) '@typescript-eslint/types': 8.35.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -1744,7 +1724,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) eslint: 9.30.0 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 @@ -1759,11 +1739,11 @@ snapshots: '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) '@typescript-eslint/types': 8.35.0 '@typescript-eslint/visitor-keys': 8.35.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.1 + semver: 7.7.2 ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -1825,38 +1805,36 @@ snapshots: bare-events@2.5.4: optional: true - bare-fs@4.0.1: + bare-fs@4.1.5: dependencies: bare-events: 2.5.4 bare-path: 3.0.0 bare-stream: 2.6.5(bare-events@2.5.4) - transitivePeerDependencies: - - bare-buffer optional: true - bare-os@3.4.0: + bare-os@3.6.1: optional: true bare-path@3.0.0: dependencies: - bare-os: 3.4.0 + bare-os: 3.6.1 optional: true bare-stream@2.6.5(bare-events@2.5.4): dependencies: - streamx: 2.22.0 + streamx: 2.22.1 optionalDependencies: bare-events: 2.5.4 optional: true basic-ftp@5.0.5: {} - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -1873,7 +1851,7 @@ snapshots: '@bcoe/v8-coverage': 1.0.2 '@istanbuljs/schema': 0.1.3 find-up: 5.0.0 - foreground-child: 3.3.0 + foreground-child: 3.3.1 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.1.7 @@ -1899,7 +1877,7 @@ snapshots: dependencies: devtools-protocol: 0.0.1464554 mitt: 3.0.1 - zod: 3.24.2 + zod: 3.25.67 cliui@8.0.1: dependencies: @@ -1934,16 +1912,12 @@ snapshots: data-uri-to-buffer@6.0.2: {} - debug@4.4.0(supports-color@8.1.1): + debug@4.4.1(supports-color@8.1.1): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 8.1.1 - debug@4.4.1: - dependencies: - ms: 2.1.3 - decamelize@4.0.0: {} deep-is@0.1.4: {} @@ -1976,7 +1950,7 @@ snapshots: emoji-regex@9.2.2: {} - end-of-stream@1.4.4: + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -2039,7 +2013,7 @@ snapshots: eslint@9.30.0: dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.30.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.0 @@ -2049,13 +2023,13 @@ snapshots: '@eslint/plugin-kit': 0.3.3 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.2 - '@types/estree': 1.0.6 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -2099,7 +2073,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -2123,9 +2097,9 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.19.0: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 fd-slicer@1.1.0: dependencies: @@ -2153,7 +2127,7 @@ snapshots: flatted@3.3.3: {} - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 @@ -2165,9 +2139,9 @@ snapshots: get-stream@5.2.0: dependencies: - pump: 3.0.2 + pump: 3.0.3 - get-tsconfig@4.10.0: + get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -2175,7 +2149,7 @@ snapshots: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -2189,7 +2163,7 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -2209,14 +2183,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -2323,7 +2297,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.1 + semver: 7.7.2 markdown-it@14.1.0: dependencies: @@ -2345,11 +2319,11 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minipass@7.1.2: {} @@ -2359,7 +2333,7 @@ snapshots: dependencies: browser-stdout: 1.3.1 chokidar: 4.0.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.1(supports-color@8.1.1) diff: 7.0.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 @@ -2409,7 +2383,7 @@ snapshots: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.3 - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) get-uri: 6.0.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -2431,7 +2405,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -2458,7 +2432,7 @@ snapshots: proxy-agent@6.5.0: dependencies: agent-base: 7.1.3 - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -2470,9 +2444,9 @@ snapshots: proxy-from-env@1.1.0: {} - pump@3.0.2: + pump@3.0.3: dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 once: 1.4.0 punycode.js@2.3.1: {} @@ -2483,7 +2457,7 @@ snapshots: dependencies: '@puppeteer/browsers': 2.10.5 chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) - debug: 4.4.1 + debug: 4.4.1(supports-color@8.1.1) devtools-protocol: 0.0.1464554 typed-query-selector: 2.12.0 ws: 8.18.3 @@ -2522,7 +2496,7 @@ snapshots: resolve-pkg-maps@1.0.0: {} - reusify@1.0.4: {} + reusify@1.1.0: {} run-parallel@1.2.0: dependencies: @@ -2530,8 +2504,6 @@ snapshots: safe-buffer@5.2.1: {} - semver@7.7.1: {} - semver@7.7.2: {} serialize-javascript@6.0.2: @@ -2551,12 +2523,12 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.3 - debug: 4.4.1 - socks: 2.8.4 + debug: 4.4.1(supports-color@8.1.1) + socks: 2.8.5 transitivePeerDependencies: - supports-color - socks@2.8.4: + socks@2.8.5: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 @@ -2566,7 +2538,7 @@ snapshots: sprintf-js@1.1.3: {} - streamx@2.22.0: + streamx@2.22.1: dependencies: fast-fifo: 1.3.2 text-decoder: 1.2.3 @@ -2603,12 +2575,12 @@ snapshots: dependencies: has-flag: 4.0.0 - tar-fs@3.0.8: + tar-fs@3.0.10: dependencies: - pump: 3.0.2 + pump: 3.0.3 tar-stream: 3.1.7 optionalDependencies: - bare-fs: 4.0.1 + bare-fs: 4.1.5 bare-path: 3.0.0 transitivePeerDependencies: - bare-buffer @@ -2617,7 +2589,7 @@ snapshots: dependencies: b4a: 1.6.7 fast-fifo: 1.3.2 - streamx: 2.22.0 + streamx: 2.22.1 test-exclude@7.0.1: dependencies: @@ -2642,7 +2614,7 @@ snapshots: tsx@4.20.3: dependencies: esbuild: 0.25.5 - get-tsconfig: 4.10.0 + get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 @@ -2674,7 +2646,7 @@ snapshots: v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.26 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -2732,4 +2704,4 @@ snapshots: yocto-queue@0.1.0: {} - zod@3.24.2: {} + zod@3.25.67: {} From e48c6bea8c39d195980ffd4fe848f1d70afe6a17 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 16:23:09 -0600 Subject: [PATCH 055/119] Correct lockfile --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15045d4e0..a59df8b90 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,7 @@ importers: specifier: ^10.0.10 version: 10.0.10 '@types/node': - specifier: ^18.19.113 + specifier: '18' version: 18.19.113 '@typestrong/fs-fixture-builder': specifier: github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe From 82ad8dbc2e3b3388cebba66349194b7e27bc0821 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 20:10:21 -0600 Subject: [PATCH 056/119] Add support for `@sortStrategy` Resolves #2965 --- CHANGELOG.md | 4 ++ site/options/organization.md | 15 +++++++ site/tags.md | 4 +- site/tags/sortStrategy.md | 34 +++++++++++++++ src/lib/converter/plugins/CategoryPlugin.ts | 20 +++++++-- src/lib/converter/plugins/GroupPlugin.ts | 41 +++++++++++++----- src/lib/internationalization/locales/en.cts | 4 +- src/lib/utils/options/sources/typedoc.ts | 5 +-- src/lib/utils/options/tsdoc-defaults.ts | 1 + src/lib/utils/sort.ts | 7 +++- src/test/behavior.c2.test.ts | 42 +++++++++++++++++++ .../converter2/behavior/sortStrategyTag.ts | 33 +++++++++++++++ tsdoc.json | 4 ++ 13 files changed, 193 insertions(+), 21 deletions(-) create mode 100644 site/tags/sortStrategy.md create mode 100644 src/test/converter2/behavior/sortStrategyTag.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f0b3e828a..8c83b0e84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Features + +- Introduced the `@sortStrategy` tag to override the `sort` option on a specific reflection, #2965. + ### Bug Fixes - Classes and functions exported with `export { type X }` are no longer missing comments, #2970. diff --git a/site/options/organization.md b/site/options/organization.md index 155b9cd67..68e673b94 100644 --- a/site/options/organization.md +++ b/site/options/organization.md @@ -81,6 +81,9 @@ Specifies the sort order for members. Sorting strategies will be applied in orde If an earlier sorting strategy determines the relative ordering of two reflections, later ordering strategies will not be applied. +This option defines the default sort strategy, the [`@sortStrategy`](../tags/sortStrategy.md) +tag may be used to override it for individual reflections. + For example, with the setting `["static-first", "visibility"]`, TypeDoc will first compare two reflections by if they are static or not, and if that comparison returns equal, will check the visibility of each reflection. On the other hand, if `["visibility", "static-first"]` is specified, @@ -104,6 +107,18 @@ The available sorting strategies are: - `documents-last` - `alphabetical-ignoring-documents` +The default sort order is: + +```json +{ + "sort": [ + "kind", + "instance-first", + "alphabetical-ignoring-documents" + ] +} +``` + ## sortEntryPoints ```bash diff --git a/site/tags.md b/site/tags.md index 1ff05824e..66787ce67 100644 --- a/site/tags.md +++ b/site/tags.md @@ -47,8 +47,9 @@ children: - tags/remarks.md - tags/returns.md - tags/sealed.md - - tags/since.md - tags/see.md + - tags/since.md + - tags/sortStrategy.md - tags/summary.md - tags/template.md - tags/throws.md @@ -130,6 +131,7 @@ examples for how to use the export ([`@example`](./tags/example.md)). - [`@returns`, `@return`](./tags/returns.md) - [`@see`](./tags/see.md) - [`@since`](./tags/since.md) +- [`@sortStrategy`](./tags/sortStrategy.md) - [`@summary`](./tags/summary.md) - [`@template`](./tags/template.md) - [`@throws`](./tags/throws.md) diff --git a/site/tags/sortStrategy.md b/site/tags/sortStrategy.md new file mode 100644 index 000000000..d68e1afcc --- /dev/null +++ b/site/tags/sortStrategy.md @@ -0,0 +1,34 @@ +--- +title: "@sortStrategy" +--- + +# @sortStrategy + +**Tag Kind:** [Block](../tags.md#Block-tags)
+ +This tag can be used to override the [sort](../options/organization.md#sort) locally +for a module, namespace, class, or interface. The override will be applied to direct +children of the declaration it appears on. If the declaration has a child which contains +children (e.g. a nested namespace) the grandchildren will _not_ be sorted according +to the `@sortStrategy` tag. + +## Example + +This class makes the most sense if the documentation is reviewed in the source order +rather than being sorted alphabetically. + +```ts +/** + * @sortStrategy source-order + */ +export class Class { + commonMethod(): void; + commonMethod2(): void; + lessCommonMethod(): void; + uncommonMethod(): void; +} +``` + +## See Also + +- The [`--sort`](../options/organization.md#sort) option diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index c68a5aadd..e0279caeb 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -15,6 +15,7 @@ import type { Context } from "../context.js"; import { ConverterEvents } from "../converter-events.js"; import type { Converter } from "../converter.js"; import { i18n } from "#utils"; +import { isValidSortStrategy } from "../../utils/sort.js"; /** * A handler that sorts and categorizes the found reflections in the resolving phase. @@ -22,7 +23,7 @@ import { i18n } from "#utils"; * The handler sets the ´category´ property of all reflections. */ export class CategoryPlugin extends ConverterComponent { - sortFunction!: ( + defaultSortFunction!: ( reflections: Array, ) => void; @@ -71,7 +72,7 @@ export class CategoryPlugin extends ConverterComponent { * Triggered when the converter begins converting a project. */ private setup() { - this.sortFunction = getSortFunction(this.application.options); + this.defaultSortFunction = getSortFunction(this.application.options); // Set up static properties if (this.defaultCategory) { @@ -203,12 +204,25 @@ export class CategoryPlugin extends ConverterComponent { } for (const cat of categories.values()) { - this.sortFunction(cat.children); + this.getSortFunction(parent)(cat.children); } return Array.from(categories.values()); } + getSortFunction(reflection: ContainerReflection) { + const tag = reflection.comment?.getTag("@sortStrategy"); + if (tag) { + const text = Comment.combineDisplayParts(tag.content); + // We don't need to warn about invalid strategies here because the group plugin + // runs first and will have already warned. + const strategies = text.split(/[,\s]+/).filter(isValidSortStrategy); + return getSortFunction(this.application.options, strategies); + } + + return this.defaultSortFunction; + } + /** * Callback used to sort categories by name. * diff --git a/src/lib/converter/plugins/GroupPlugin.ts b/src/lib/converter/plugins/GroupPlugin.ts index 441c97074..49cc8d087 100644 --- a/src/lib/converter/plugins/GroupPlugin.ts +++ b/src/lib/converter/plugins/GroupPlugin.ts @@ -9,14 +9,14 @@ import { import { ReflectionGroup } from "../../models/ReflectionGroup.js"; import { ConverterComponent } from "../components.js"; import type { Context } from "../context.js"; -import { getSortFunction } from "../../utils/sort.js"; -import { Option } from "../../utils/index.js"; +import { getSortFunction, isValidSortStrategy, SORT_STRATEGIES } from "../../utils/sort.js"; +import { Option, type SortStrategy } from "../../utils/index.js"; import { Comment } from "../../models/index.js"; import { ConverterEvents } from "../converter-events.js"; import type { Converter } from "../converter.js"; import { ApplicationEvents } from "../../application-events.js"; import assert from "assert"; -import { i18n } from "#utils"; +import { i18n, partition } from "#utils"; // Same as the defaultKindSortOrder in sort.ts const defaultGroupOrder = [ @@ -47,7 +47,7 @@ const defaultGroupOrder = [ * The handler sets the `groups` property of all container reflections. */ export class GroupPlugin extends ConverterComponent { - sortFunction!: ( + defaultSortFunction!: ( reflections: Array, ) => void; @@ -107,7 +107,7 @@ export class GroupPlugin extends ConverterComponent { } private setup() { - this.sortFunction = getSortFunction(this.application.options); + this.defaultSortFunction = getSortFunction(this.application.options); GroupPlugin.WEIGHTS = this.groupOrder; if (GroupPlugin.WEIGHTS.length === 0) { GroupPlugin.WEIGHTS = defaultGroupOrder.map((kind) => ReflectionKind.pluralString(kind)); @@ -115,19 +115,21 @@ export class GroupPlugin extends ConverterComponent { } private group(reflection: ContainerReflection) { + const sortFunction = this.getSortFunction(reflection); + if (reflection.childrenIncludingDocuments && !reflection.groups) { if (reflection.children) { if ( this.sortEntryPoints || !reflection.children.some((c) => c.kindOf(ReflectionKind.Module)) ) { - this.sortFunction(reflection.children); - this.sortFunction(reflection.documents || []); - this.sortFunction(reflection.childrenIncludingDocuments); + sortFunction(reflection.children); + sortFunction(reflection.documents || []); + sortFunction(reflection.childrenIncludingDocuments); } } else if (reflection.documents) { - this.sortFunction(reflection.documents); - this.sortFunction(reflection.childrenIncludingDocuments); + sortFunction(reflection.documents); + sortFunction(reflection.childrenIncludingDocuments); } if (reflection.comment?.hasModifier("@disableGroups")) { @@ -256,6 +258,25 @@ export class GroupPlugin extends ConverterComponent { return Array.from(groups.values()).sort(GroupPlugin.sortGroupCallback); } + getSortFunction(reflection: ContainerReflection) { + const tag = reflection.comment?.getTag("@sortStrategy"); + if (tag) { + const text = Comment.combineDisplayParts(tag.content); + const strategies = text.split(/[,\s]+/); + const [valid, invalid] = partition(strategies, isValidSortStrategy); + for (const inv of invalid) { + this.application.logger.warn(i18n.comment_for_0_specifies_1_as_sort_strategy_but_only_2_is_valid( + reflection.getFriendlyFullName(), + inv, + SORT_STRATEGIES.join("\n\t"), + )); + } + return getSortFunction(this.application.options, valid as SortStrategy[]); + } + + return this.defaultSortFunction; + } + /** * Callback used to sort groups by name. */ diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index b095e79cc..3c0a45ade 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -123,6 +123,8 @@ export = { `Comment for {0} includes @categoryDescription for "{1}", but no child is placed in that category`, comment_for_0_includes_groupDescription_for_1_but_no_child_in_group: `Comment for {0} includes @groupDescription for "{1}", but no child is placed in that group`, + comment_for_0_specifies_1_as_sort_strategy_but_only_2_is_valid: + `Comment for {0} specifies @sortStrategy with "{1}", which is an invalid sort strategy, the following are valid:\n\t{2}`, label_0_for_1_cannot_be_referenced: `The label "{0}" for {1} cannot be referenced with a declaration reference. Labels may only contain A-Z, 0-9, and _, and may not start with a number`, modifier_tag_0_is_mutually_exclusive_with_1_in_comment_for_2: @@ -419,7 +421,7 @@ export = { option_0_values_must_be_numbers: "All values of {0} must be numbers", option_0_values_must_be_array_of_tags: "{0} must be an array of valid tag names", option_0_specified_1_but_only_2_is_valid: - `{0} may only specify known values, and invalid values were provided ({1}). The valid sort strategies are:\n{2}`, + `{0} may only specify known values, and invalid values were provided ({1}). The valid options are:\n{2}`, option_outputs_must_be_array: `"outputs" option must be an array of { name: string, path: string, options?: TypeDocOptions } values.`, specified_output_0_has_not_been_defined: `Specified output "{0}" has not been defined.`, diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index c87d7c4e5..c04289f8c 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -845,10 +845,7 @@ export function addTypeDocOptions(options: Pick) { type: ParameterType.Array, defaultValue: OptionDefaults.sort, validate(value) { - const invalid = new Set(value); - for (const v of SORT_STRATEGIES) { - invalid.delete(v); - } + const invalid = setDifference(value, SORT_STRATEGIES); if (invalid.size !== 0) { throw new Error( diff --git a/src/lib/utils/options/tsdoc-defaults.ts b/src/lib/utils/options/tsdoc-defaults.ts index 39da35ef6..92e19f7c9 100644 --- a/src/lib/utils/options/tsdoc-defaults.ts +++ b/src/lib/utils/options/tsdoc-defaults.ts @@ -37,6 +37,7 @@ export const blockTags = [ "@return", "@satisfies", "@since", + "@sortStrategy", "@template", // Alias for @typeParam "@type", "@typedef", diff --git a/src/lib/utils/sort.ts b/src/lib/utils/sort.ts index 303f95113..b3d974ca8 100644 --- a/src/lib/utils/sort.ts +++ b/src/lib/utils/sort.ts @@ -163,7 +163,11 @@ const sorts: Record< }, }; -export function getSortFunction(opts: Options) { +export function isValidSortStrategy(strategy: string): strategy is SortStrategy { + return SORT_STRATEGIES.includes(strategy as never); +} + +export function getSortFunction(opts: Options, strategies: readonly SortStrategy[] = opts.getValue("sort")) { const kindSortOrder = opts .getValue("kindSortOrder") .map((k) => ReflectionKind[k]); @@ -174,7 +178,6 @@ export function getSortFunction(opts: Options) { } } - const strategies = opts.getValue("sort"); const data = { kindSortOrder }; return function sortReflections( diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index 593c3646d..d1b18e0a7 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -1486,4 +1486,46 @@ describe("Behavior Tests", () => { { kind: "text", text: ")" }, ]); }); + + it("Supports the @sortStrategy tag #2965", () => { + const project = convert("sortStrategyTag"); + + const getOrder = (name: string) => query(project, name).children?.map(c => c.name); + + equal(getOrder("A"), ["b", "c", "a"]); + equal(getOrder("B"), ["a", "b", "c"]); + equal(getOrder("C"), ["b", "c", "a"]); + + const getGroupOrders = (name: string) => + query(project, name).groups?.map(g => [g.title, g.children.map(c => c.name)]); + + equal(getGroupOrders("A"), [ + ["Variables", ["b", "a"]], + ["Functions", ["c"]], + ]); + + equal(getGroupOrders("B"), [ + ["Variables", ["a", "b"]], + ["Functions", ["c"]], + ]); + + equal(getGroupOrders("C"), [ + ["Variables", ["b", "c"]], + ["Functions", ["a"]], + ]); + + const getCategoryOrders = (name: string) => + query(project, name).categories?.map(g => [g.title, g.children.map(c => c.name)]); + + equal(getCategoryOrders("D"), [ + ["Cat", ["b", "a", "c"]], + ]); + + logger.expectMessage( + `warn: Comment for E specifies @sortStrategy with "invalid", which is an invalid sort strategy*`, + ); + logger.expectMessage( + `warn: Comment for E specifies @sortStrategy with "invalid2", which is an invalid sort strategy*`, + ); + }); }); diff --git a/src/test/converter2/behavior/sortStrategyTag.ts b/src/test/converter2/behavior/sortStrategyTag.ts new file mode 100644 index 000000000..f217b2e64 --- /dev/null +++ b/src/test/converter2/behavior/sortStrategyTag.ts @@ -0,0 +1,33 @@ +/** @sortStrategy source-order */ +export namespace A { + export const b = 1; + export function c() {} + export const a = 2; +} + +/** @sortStrategy alphabetical */ +export namespace B { + export function c() {} + export const b = 1; + export const a = 1; +} + +// Default is kind then alphabetical +export namespace C { + export const c = 1; + export function a() {} + export const b = 1; +} + +/** @sortStrategy source-order */ +export namespace D { + /** @category Cat */ + export const b = 1; + /** @category Cat */ + export const a = 1; + /** @category Cat */ + export const c = 1; +} + +/** @sortStrategy invalid, source-order, invalid2 */ +export namespace E {} diff --git a/tsdoc.json b/tsdoc.json index 4fd1c034d..44391fd71 100644 --- a/tsdoc.json +++ b/tsdoc.json @@ -237,6 +237,10 @@ { "tagName": "@useDeclaredType", "syntaxKind": "modifier" + }, + { + "tagName": "@sortStrategy", + "syntaxKind": "block" } ] } From af3b493f3678354988044965383083efb7b1edba Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 20:15:41 -0600 Subject: [PATCH 057/119] Fix spurious zeros in HTML output --- src/lib/output/themes/default/partials/moduleReflection.tsx | 2 +- src/lib/output/themes/default/partials/navigation.tsx | 2 +- src/lib/output/themes/default/templates/hierarchy.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/output/themes/default/partials/moduleReflection.tsx b/src/lib/output/themes/default/partials/moduleReflection.tsx index 9c0af4d77..fb358841b 100644 --- a/src/lib/output/themes/default/partials/moduleReflection.tsx +++ b/src/lib/output/themes/default/partials/moduleReflection.tsx @@ -23,7 +23,7 @@ export function moduleReflection(context: DefaultThemeRenderContext, mod: Declar
)} - {mod.isDeclaration() && mod.kind === ReflectionKind.Module && mod.readme?.length && ( + {mod.isDeclaration() && mod.kind === ReflectionKind.Module && !!mod.readme?.length && (
diff --git a/src/lib/output/themes/default/partials/navigation.tsx b/src/lib/output/themes/default/partials/navigation.tsx index d53bf60ad..480859b02 100644 --- a/src/lib/output/themes/default/partials/navigation.tsx +++ b/src/lib/output/themes/default/partials/navigation.tsx @@ -97,7 +97,7 @@ export function settings(context: DefaultThemeRenderContext) {
- {visibilityOptions.length && ( + {!!visibilityOptions.length && (
{i18n.theme_member_visibility()}
    {...visibilityOptions}
diff --git a/src/lib/output/themes/default/templates/hierarchy.tsx b/src/lib/output/themes/default/templates/hierarchy.tsx index f16d57ef8..2e9851ba7 100644 --- a/src/lib/output/themes/default/templates/hierarchy.tsx +++ b/src/lib/output/themes/default/templates/hierarchy.tsx @@ -37,7 +37,7 @@ function fullHierarchy( {context.reflectionIcon(root)} {root.name} - {children.length ?
    {children}
: null} + {!!children.length ?
    {children}
: null} ); } From 7e13754ac8c619d186d5294dd20c9eb254d45bca Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 20:18:58 -0600 Subject: [PATCH 058/119] Fix lint --- src/lib/output/themes/default/templates/hierarchy.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/output/themes/default/templates/hierarchy.tsx b/src/lib/output/themes/default/templates/hierarchy.tsx index 2e9851ba7..7f66e7472 100644 --- a/src/lib/output/themes/default/templates/hierarchy.tsx +++ b/src/lib/output/themes/default/templates/hierarchy.tsx @@ -37,7 +37,7 @@ function fullHierarchy( {context.reflectionIcon(root)} {root.name} - {!!children.length ?
    {children}
: null} + {!!children.length &&
    {children}
} ); } From 1ed0a621b5a1b267e25a1413c5b4ffa099a23627 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 29 Jun 2025 20:21:41 -0600 Subject: [PATCH 059/119] Bump version to 0.28.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3041339a3..d2bcf6d45 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.6", + "version": "0.28.7", "homepage": "https://typedoc.org", "type": "module", "exports": { From e8b6df3d83cbbe4010d5919ed72c6003c90036d1 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Mon, 30 Jun 2025 02:22:31 +0000 Subject: [PATCH 060/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c83b0e84..5e4432d04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.7 (2025-06-30) + ### Features - Introduced the `@sortStrategy` tag to override the `sort` option on a specific reflection, #2965. From 9121d780f1158158e2ed62fab8541852fc069184 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Mon, 7 Jul 2025 11:21:00 -0500 Subject: [PATCH 061/119] Allow hyphenated JSDoc tags JSDoc tags may contain hyphens. For example, `@TJS-type`, which is used by [`typescript-json-schema`](https://www.npmjs.com/package/typescript-json-schema). --- src/lib/converter/comments/blockLexer.ts | 2 +- src/lib/converter/comments/lineLexer.ts | 2 +- src/lib/converter/comments/rawLexer.ts | 2 +- src/lib/utils-common/validation.ts | 2 +- src/test/comments.test.ts | 12 ++++++++---- src/test/utils/options/default-options.test.ts | 6 +++++- 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/lib/converter/comments/blockLexer.ts b/src/lib/converter/comments/blockLexer.ts index 7bd0bb5af..52913989e 100644 --- a/src/lib/converter/comments/blockLexer.ts +++ b/src/lib/converter/comments/blockLexer.ts @@ -263,7 +263,7 @@ function* lexBlockComment2( if (lookahead !== pos + 1) { while ( lookahead < end && - /[a-z0-9]/i.test(file[lookahead]) + /[a-z0-9-]/i.test(file[lookahead]) ) { lookahead++; } diff --git a/src/lib/converter/comments/lineLexer.ts b/src/lib/converter/comments/lineLexer.ts index b7312f433..876494d77 100644 --- a/src/lib/converter/comments/lineLexer.ts +++ b/src/lib/converter/comments/lineLexer.ts @@ -179,7 +179,7 @@ function* lexLineComments2( if (lookahead !== pos + 1) { while ( lookahead < end && - /[a-z0-9]/i.test(file[lookahead]) + /[a-z0-9-]/i.test(file[lookahead]) ) { lookahead++; } diff --git a/src/lib/converter/comments/rawLexer.ts b/src/lib/converter/comments/rawLexer.ts index e8bc8d6bc..ff063dc11 100644 --- a/src/lib/converter/comments/rawLexer.ts +++ b/src/lib/converter/comments/rawLexer.ts @@ -176,7 +176,7 @@ function* lexCommentString2( if (lookahead !== pos + 1) { while ( lookahead < end && - /[a-z0-9]/i.test(file[lookahead]) + /[a-z0-9-]/i.test(file[lookahead]) ) { lookahead++; } diff --git a/src/lib/utils-common/validation.ts b/src/lib/utils-common/validation.ts index 8febb48d6..eca8886d9 100644 --- a/src/lib/utils-common/validation.ts +++ b/src/lib/utils-common/validation.ts @@ -120,5 +120,5 @@ export function optional(x: T): Optional { } export function isTagString(x: unknown): x is `@${string}` { - return typeof x === "string" && /^@[a-zA-Z][a-zA-Z0-9]*$/.test(x); + return typeof x === "string" && /^@[a-z][a-z0-9-]*$/i.test(x); } diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 0d2d77405..8647b6372 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -186,7 +186,7 @@ describe("Block Comment Lexer", () => { }); it("Should recognize tags", () => { - const tokens = lex("/* @tag @a @abc234 */"); + const tokens = lex("/* @tag @a @abc234 @abc-234 */"); equal(tokens, [ { kind: TokenSyntaxKind.Tag, text: "@tag", pos: 3 }, @@ -194,6 +194,8 @@ describe("Block Comment Lexer", () => { { kind: TokenSyntaxKind.Tag, text: "@a", pos: 8 }, { kind: TokenSyntaxKind.Text, text: " ", pos: 10 }, { kind: TokenSyntaxKind.Tag, text: "@abc234", pos: 11 }, + { kind: TokenSyntaxKind.Text, text: " ", pos: 18 }, + { kind: TokenSyntaxKind.Tag, text: "@abc-234", pos: 19 }, ]); }); @@ -641,7 +643,7 @@ describe("Line Comment Lexer", () => { }); it("Should recognize tags", () => { - const tokens = lex("// @tag @a @abc234"); + const tokens = lex("// @tag @a @abc234 @abc-234"); equal(tokens, [ { kind: TokenSyntaxKind.Tag, text: "@tag", pos: 3 }, @@ -649,6 +651,8 @@ describe("Line Comment Lexer", () => { { kind: TokenSyntaxKind.Tag, text: "@a", pos: 8 }, { kind: TokenSyntaxKind.Text, text: " ", pos: 10 }, { kind: TokenSyntaxKind.Tag, text: "@abc234", pos: 11 }, + { kind: TokenSyntaxKind.Text, text: " ", pos: 18 }, + { kind: TokenSyntaxKind.Tag, text: "@abc-234", pos: 19 }, ]); }); @@ -993,12 +997,12 @@ describe("Raw Lexer", () => { }); it("Should not recognize tags", () => { - const tokens = lex("@123 @@ @ @tag @a @abc234"); + const tokens = lex("@123 @@ @ @tag @a @abc234 @abc-234"); equal(tokens, [ { kind: TokenSyntaxKind.Text, - text: "@123 @@ @ @tag @a @abc234", + text: "@123 @@ @ @tag @a @abc234 @abc-234", pos: 0, }, ]); diff --git a/src/test/utils/options/default-options.test.ts b/src/test/utils/options/default-options.test.ts index 5975bd99c..8b9ab552c 100644 --- a/src/test/utils/options/default-options.test.ts +++ b/src/test/utils/options/default-options.test.ts @@ -161,11 +161,15 @@ describe("Default Options", () => { describe("blockTags", () => { it("Should disallow non-tags", () => { - throws(() => opts.setValue("blockTags", ["@bad-non-tag"])); + throws(() => opts.setValue("blockTags", ["@bad_tag"])); + throws(() => opts.setValue("blockTags", ["@2bad"])); }); it("Should allow tags", () => { doesNotThrow(() => opts.setValue("blockTags", ["@good"])); + doesNotThrow(() => opts.setValue("blockTags", ["@good2"])); + doesNotThrow(() => opts.setValue("blockTags", ["@Good"])); + doesNotThrow(() => opts.setValue("blockTags", ["@good-tag"])); }); }); From f60f0a4fa252fb07ad1a82a41cfba5f2698a3c2f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Wed, 9 Jul 2025 06:49:42 -0600 Subject: [PATCH 062/119] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4432d04..f68f25fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ title: Changelog ## Unreleased +### Features + +- Permit `-` within tag names to support `typescript-json-schema`'s `@TJS-type` tag, #2972. + +### Thanks! + +- @jonathanhefner + ## v0.28.7 (2025-06-30) ### Features From 8df8262350aa5f426799087e13113b1dd7c7368f Mon Sep 17 00:00:00 2001 From: Sage Abdullah Date: Thu, 10 Jul 2025 11:26:08 +0000 Subject: [PATCH 063/119] Handle relative links in --- src/lib/converter/comments/textParser.ts | 7 +++++- src/test/comments.test.ts | 32 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 6277e9aa2..fcaa10857 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -297,7 +297,7 @@ function checkReference(data: TextParserData): RelativeLink | undefined { } /** - * Looks for `` and `` + * Looks for ``, ``, and `` */ function checkTagLink(data: TextParserData): RelativeLink | undefined { const { pos, token } = data; @@ -311,6 +311,11 @@ function checkTagLink(data: TextParserData): RelativeLink | undefined { data.pos += 3; return checkAttribute(data, "href"); } + + if (token.text.startsWith(" { ); }); + it("Recognizes HTML picture source srcset links", () => { + const comment = getComment(`/** + * + * + * + */`); + + equal( + comment.summary, + [ + { kind: "text", text: '\n\n', + }, + ] satisfies CommentDisplayPart[], + ); + }); + it("Recognizes HTML anchor links", () => { const comment = getComment(`/** * From c0182ddb5e9f737180ff2b5e6ad1a194d55bbac9 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 10 Jul 2025 21:09:29 -0600 Subject: [PATCH 064/119] Permit functions as values in the plugin option --- CHANGELOG.md | 1 + scripts/clone_api_users.js | 1 + scripts/generate_options_schema.js | 1 + scripts/generate_site_plugins.js | 1 - site/options/configuration.md | 3 + site/tags/sortStrategy.md | 2 +- src/lib/internationalization/locales/en.cts | 1 + src/lib/utils-common/path.ts | 10 ++++ src/lib/utils/options/declaration.ts | 65 +++++++++++++++++---- src/lib/utils/options/readers/arguments.ts | 1 + src/lib/utils/options/sources/typedoc.ts | 2 +- src/lib/utils/plugins.ts | 51 +++++++++------- src/test/utils/options/declaration.test.ts | 52 +++++++++++++++++ src/test/utils/plugins.test.ts | 33 +++++++---- 14 files changed, 180 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f68f25fdd..f5d8edae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ title: Changelog ### Features +- If using JS config files, the `plugin` function can now be given plugin functions to load. - Permit `-` within tag names to support `typescript-json-schema`'s `@TJS-type` tag, #2972. ### Thanks! diff --git a/scripts/clone_api_users.js b/scripts/clone_api_users.js index 3a77f5e6e..d38c890db 100755 --- a/scripts/clone_api_users.js +++ b/scripts/clone_api_users.js @@ -93,6 +93,7 @@ if (import.meta.url.endsWith(process.argv[1])) { } console.log(`Cloning/updating took ${(Date.now() - start) / 1000} seconds`); + console.log(`Output is in ${args.values.output}`); // Check for repos listed in the wrong list const currentMinor = semver.parse(JSON.parse(readFileSync("package.json", "utf-8")).version)?.minor; diff --git a/scripts/generate_options_schema.js b/scripts/generate_options_schema.js index 333444196..8c8375f3f 100644 --- a/scripts/generate_options_schema.js +++ b/scripts/generate_options_schema.js @@ -36,6 +36,7 @@ addTypeDocOptions({ case ParameterType.GlobArray: case ParameterType.PathArray: case ParameterType.ModuleArray: + case ParameterType.PluginArray: data.type = "array"; data.items = { type: "string" }; data.default = /** @type {import("../dist/index.js").ArrayDeclarationOption} */ ( diff --git a/scripts/generate_site_plugins.js b/scripts/generate_site_plugins.js index e7889a8f3..7e0fde228 100644 --- a/scripts/generate_site_plugins.js +++ b/scripts/generate_site_plugins.js @@ -8,7 +8,6 @@ const NEXT_BREAKING_TYPEDOC_VERSION = semver.parse(TYPEDOC_VERSION)?.inc("minor" if (!NEXT_BREAKING_TYPEDOC_VERSION) { throw new Error("Failed to determine next TypeDoc version"); } -console.log(NEXT_BREAKING_TYPEDOC_VERSION); const CACHE_ROOT = "tmp/site-cache"; mkdirSync(CACHE_ROOT, { recursive: true }); diff --git a/site/options/configuration.md b/site/options/configuration.md index 74c5da8fa..a96aa0859 100644 --- a/site/options/configuration.md +++ b/site/options/configuration.md @@ -106,3 +106,6 @@ typedoc --plugin ./custom-plugin.js Specifies the plugins that should be loaded. By default, no plugins are loaded. See [Plugins](../plugins.md) for a list of available plugins. + +If using a JavaScript configuration file, the `plugin` option may be given +a function which will be called to load a plugin. diff --git a/site/tags/sortStrategy.md b/site/tags/sortStrategy.md index d68e1afcc..c795c08e0 100644 --- a/site/tags/sortStrategy.md +++ b/site/tags/sortStrategy.md @@ -4,7 +4,7 @@ title: "@sortStrategy" # @sortStrategy -**Tag Kind:** [Block](../tags.md#Block-tags)
+**Tag Kind:** [Block](../tags.md#block-tags)
This tag can be used to override the [sort](../options/organization.md#sort) locally for a module, namespace, class, or interface. The override will be applied to direct diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index 3c0a45ade..703f37664 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -414,6 +414,7 @@ export = { favicon_must_have_one_of_the_following_extensions_0: "Favicon must have one of the following extensions: {0}", option_0_must_be_an_object: "The '{0}' option must be a non-array object", option_0_must_be_an_array_of_string: "The '{0}' option must be set to an array of strings", + option_0_must_be_an_array_of_string_or_functions: "The '{0}' option must be set to an array of strings/functions", option_0_must_be_a_function: "The '{0}' option must be a function", option_0_must_be_object_with_urls: `{0} must be an object with string labels as keys and URL values`, visibility_filters_only_include_0: `visibilityFilters can only include the following non-@ keys: {0}`, diff --git a/src/lib/utils-common/path.ts b/src/lib/utils-common/path.ts index 507846a51..95b720f49 100644 --- a/src/lib/utils-common/path.ts +++ b/src/lib/utils-common/path.ts @@ -1,5 +1,9 @@ import { assert } from "./general.js"; +// Type only import is permitted +// eslint-disable-next-line no-restricted-importsgn +import type { Application } from "../application.js"; + /** * Represents a normalized path with path separators being `/` * On Windows, drives are represented like `C:/Users` for consistency @@ -15,6 +19,12 @@ export type NormalizedPath = "" | "/" | string & { readonly __normPath: unique s */ export type NormalizedPathOrModule = NormalizedPath | string & { readonly __normPathOrModule: unique symbol }; +/** + * Represents either a {@link NormalizedPath} or a Node module name + * (e.g. `typedoc-plugin-mdn-links` or `@gerrit0/typedoc-plugin`) + */ +export type NormalizedPathOrModuleOrFunction = NormalizedPathOrModule | ((app: Application) => Promise | void); + /** * Represents a glob path configured by a user. */ diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index a18c094c2..af707aa64 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -10,10 +10,12 @@ import { type NeverIfInternal, type NormalizedPath, type NormalizedPathOrModule, + type NormalizedPathOrModuleOrFunction, type TranslatedString, } from "#utils"; import type { TranslationProxy } from "../../internationalization/internationalization.js"; import { createGlobString, normalizePath } from "../paths.js"; +import type { Application } from "../../application.js"; /** @enum */ export const EmitStrategy = { @@ -122,7 +124,8 @@ export type TypeDocOptions = { TypeDocOptionMap[K] extends ManuallyValidatedOption< infer ManuallyValidated > ? ManuallyValidated : - TypeDocOptionMap[K] extends NormalizedPath[] | NormalizedPathOrModule[] | GlobString[] ? string[] : + TypeDocOptionMap[K] extends + NormalizedPath[] | NormalizedPathOrModule[] | NormalizedPathOrModuleOrFunction[] | GlobString[] ? string[] : TypeDocOptionMap[K] extends NormalizedPath ? string : TypeDocOptionMap[K] extends | string @@ -150,6 +153,8 @@ export type TypeDocOptionValues = { | string | string[] | GlobString[] + | NormalizedPathOrModule[] + | NormalizedPathOrModuleOrFunction[] | number | boolean | Record ? TypeDocOptionMap[K] : @@ -183,7 +188,7 @@ export interface TypeDocOptionMap { options: NormalizedPath; tsconfig: NormalizedPath; compilerOptions: unknown; - plugin: NormalizedPathOrModule[]; + plugin: NormalizedPathOrModuleOrFunction[]; lang: string; locales: ManuallyValidatedOption>>; packageOptions: ManuallyValidatedOption< @@ -404,7 +409,9 @@ export type KeyToDeclaration = TypeDocOptionMa TypeDocOptionMap[K] extends string | NormalizedPath ? StringDeclarationOption : TypeDocOptionMap[K] extends number ? NumberDeclarationOption : TypeDocOptionMap[K] extends GlobString[] ? GlobArrayDeclarationOption : - TypeDocOptionMap[K] extends string[] | NormalizedPath[] | NormalizedPathOrModule[] ? ArrayDeclarationOption : + TypeDocOptionMap[K] extends + string[] | NormalizedPath[] | NormalizedPathOrModule[] | NormalizedPathOrModuleOrFunction[] ? + ArrayDeclarationOption : unknown extends TypeDocOptionMap[K] ? MixedDeclarationOption | ObjectDeclarationOption : TypeDocOptionMap[K] extends ManuallyValidatedOption ? | (MixedDeclarationOption & { @@ -452,8 +459,14 @@ export enum ParameterType { PathArray, /** * Resolved according to the config directory if it starts with `.` + * @deprecated since 0.28.8, will be removed in 0.29 */ ModuleArray, + /** + * Resolved according to the config directory if it starts with `.` + * @internal - only intended for use with the plugin option + */ + PluginArray, /** * Relative to the config directory. */ @@ -567,7 +580,8 @@ export interface ArrayDeclarationOption extends DeclarationOptionBase { type: | ParameterType.Array | ParameterType.PathArray - | ParameterType.ModuleArray; + | ParameterType.ModuleArray + | ParameterType.PluginArray; /** * If not specified defaults to an empty array. @@ -674,6 +688,7 @@ export interface ParameterTypeToOptionTypeMap { [ParameterType.Array]: string[]; [ParameterType.PathArray]: NormalizedPath[]; [ParameterType.ModuleArray]: NormalizedPathOrModule[]; + [ParameterType.PluginArray]: Array void | Promise)>; [ParameterType.GlobArray]: GlobString[]; [ParameterType.Flags]: Record; @@ -685,7 +700,7 @@ export type DeclarationOptionToOptionType = T exten T extends FlagsDeclarationOption ? U : ParameterTypeToOptionTypeMap[Exclude]; -function toStringArray(value: unknown, option: DeclarationOption) { +function toStringArray(value: unknown, option: DeclarationOption): string[] { if (Array.isArray(value) && value.every(v => typeof v === "string")) { return value; } else if (typeof value === "string") { @@ -695,6 +710,19 @@ function toStringArray(value: unknown, option: DeclarationOption) { throw new Error(i18n.option_0_must_be_an_array_of_string(option.name)); } +function toStringOrFunctionArray( + value: unknown, + option: DeclarationOption, +): Array void | Promise)> { + if (Array.isArray(value) && value.every(v => typeof v === "string" || typeof v === "function")) { + return value; + } else if (typeof value === "string") { + return [value]; + } + + throw new Error(i18n.option_0_must_be_an_array_of_string_or_functions(option.name)); +} + const converters: { [K in ParameterType]: ( value: unknown, @@ -763,6 +791,13 @@ const converters: { option.validate?.(resolved); return resolved; }, + [ParameterType.PluginArray](value, option, configPath) { + const arrayValue = toStringOrFunctionArray(value, option); + const resolved = arrayValue.map(plugin => + typeof plugin === "function" ? plugin : resolveModulePath(plugin, configPath) + ); + return resolved; + }, [ParameterType.GlobArray](value, option, configPath) { const toGlobString = (v: unknown) => { const s = String(v); @@ -942,6 +977,12 @@ const defaultGetters: { } return []; }, + [ParameterType.PluginArray](option) { + if (option.defaultValue) { + return resolveModulePaths(option.defaultValue, process.cwd()); + } + return []; + }, [ParameterType.GlobArray](option) { return (option.defaultValue ?? []).map(g => createGlobString(normalizePath(process.cwd()), g)); }, @@ -959,12 +1000,14 @@ export function getDefaultValue(option: DeclarationOption) { } function resolveModulePaths(modules: readonly string[], configPath: string): NormalizedPathOrModule[] { - return modules.map((path) => { - if (path.startsWith(".")) { - return normalizePath(resolve(configPath, path)); - } - return normalizePath(path); - }); + return modules.map(path => resolveModulePath(path, configPath)); +} + +function resolveModulePath(path: string, configPath: string): NormalizedPathOrModule { + if (path.startsWith(".")) { + return normalizePath(resolve(configPath, path)); + } + return normalizePath(path); } function isTsNumericEnum(map: Record) { diff --git a/src/lib/utils/options/readers/arguments.ts b/src/lib/utils/options/readers/arguments.ts index e7a30c73f..d662f61dc 100644 --- a/src/lib/utils/options/readers/arguments.ts +++ b/src/lib/utils/options/readers/arguments.ts @@ -7,6 +7,7 @@ const ARRAY_OPTION_TYPES = new Set([ ParameterType.Array, ParameterType.PathArray, ParameterType.ModuleArray, + ParameterType.PluginArray, ParameterType.GlobArray, ]); diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index c04289f8c..c1658768c 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -923,7 +923,7 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "plugin", help: () => i18n.help_plugin(), - type: ParameterType.ModuleArray, + type: ParameterType.PluginArray, }); options.addDeclaration({ name: "logLevel", diff --git a/src/lib/utils/plugins.ts b/src/lib/utils/plugins.ts index 99f726639..591a9cceb 100644 --- a/src/lib/utils/plugins.ts +++ b/src/lib/utils/plugins.ts @@ -3,35 +3,42 @@ import { pathToFileURL } from "url"; import type { Application } from "../application.js"; import { nicePath } from "./paths.js"; -import { i18n, type TranslatedString } from "#utils"; +import { i18n, type NormalizedPathOrModuleOrFunction, type TranslatedString } from "#utils"; export async function loadPlugins( app: Application, - plugins: readonly string[], + plugins: readonly NormalizedPathOrModuleOrFunction[], ) { for (const plugin of plugins) { const pluginDisplay = getPluginDisplayName(plugin); try { - let instance: any; - // Try importing first to avoid warnings about requiring ESM being experimental. - // If that fails due to importing a directory, fall back to require. - try { - // On Windows, we need to ensure this path is a file path. - // Or we'll get ERR_UNSUPPORTED_ESM_URL_SCHEME - const esmPath = isAbsolute(plugin) - ? pathToFileURL(plugin).toString() - : plugin; - instance = await import(esmPath); - } catch (error: any) { - if (error.code === "ERR_UNSUPPORTED_DIR_IMPORT") { - // eslint-disable-next-line @typescript-eslint/no-require-imports - instance = require(plugin); - } else { - throw error; + let initFunction: any; + + if (typeof plugin === "function") { + initFunction = plugin; + } else { + let instance: any; + + // Try importing first to avoid warnings about requiring ESM being experimental. + // If that fails due to importing a directory, fall back to require. + try { + // On Windows, we need to ensure this path is a file path. + // Or we'll get ERR_UNSUPPORTED_ESM_URL_SCHEME + const esmPath = isAbsolute(plugin) + ? pathToFileURL(plugin).toString() + : plugin; + instance = await import(esmPath); + } catch (error: any) { + if (error.code === "ERR_UNSUPPORTED_DIR_IMPORT") { + // eslint-disable-next-line @typescript-eslint/no-require-imports + instance = require(plugin); + } else { + throw error; + } } + initFunction = instance.load; } - const initFunction = instance.load; if (typeof initFunction === "function") { await initFunction(app); @@ -54,7 +61,11 @@ export async function loadPlugins( } } -function getPluginDisplayName(plugin: string) { +function getPluginDisplayName(plugin: NormalizedPathOrModuleOrFunction) { + if (typeof plugin === "function") { + return plugin.name || "function"; + } + const path = nicePath(plugin); if (path.startsWith("./node_modules/")) { return path.substring("./node_modules/".length); diff --git a/src/test/utils/options/declaration.test.ts b/src/test/utils/options/declaration.test.ts index cd943ab53..ef0252fab 100644 --- a/src/test/utils/options/declaration.test.ts +++ b/src/test/utils/options/declaration.test.ts @@ -239,6 +239,32 @@ describe("Options - conversions", () => { new Error("The 'test' option must be set to an array of strings"), ); + equal( + convert("a,b", optionWithType(ParameterType.PluginArray), ""), + ["a,b"], + ); + equal( + convert( + ["a,b"], + optionWithType(ParameterType.PluginArray), + "", + ), + ["a,b"], + ); + const fn = () => {}; + equal( + convert( + ["a", fn], + optionWithType(ParameterType.PluginArray), + "", + ), + ["a", fn], + ); + throws( + () => convert(true, optionWithType(ParameterType.PluginArray), ""), + new Error("The 'test' option must be set to an array of strings/functions"), + ); + throws( () => convert(true, optionWithType(ParameterType.GlobArray), ""), new Error("The 'test' option must be set to an array of strings"), @@ -256,6 +282,17 @@ describe("Options - conversions", () => { ); }); + it("PluginArray is resolved if relative", () => { + equal( + convert( + ["./foo"], + optionWithType(ParameterType.PluginArray), + "", + ), + [normalizePath(join(process.cwd(), "foo"))], + ); + }); + it("Validates array options", () => { const declaration: ArrayDeclarationOption = { name: "test", @@ -519,6 +556,21 @@ describe("Options - default values", () => { ); }); + it("PluginArray", () => { + equal( + getDefaultValue(getDeclaration(ParameterType.PluginArray, void 0)), + [], + ); + equal( + getDefaultValue(getDeclaration(ParameterType.PluginArray, ["a"])), + ["a"], + ); + equal( + getDefaultValue(getDeclaration(ParameterType.PluginArray, ["./a"])), + [normalizePath(resolve("./a"))], + ); + }); + it("GlobArray", () => { equal( getDefaultValue(getDeclaration(ParameterType.GlobArray, void 0)), diff --git a/src/test/utils/plugins.test.ts b/src/test/utils/plugins.test.ts index 31f5962ba..0199d5ab6 100644 --- a/src/test/utils/plugins.test.ts +++ b/src/test/utils/plugins.test.ts @@ -1,8 +1,9 @@ import { tempdirProject } from "@typestrong/fs-fixture-builder"; -import type { Application } from "../../index.js"; +import { type Application, normalizePath } from "../../index.js"; import { loadPlugins } from "../../lib/utils/plugins.js"; import { TestLogger } from "../TestLogger.js"; import { join, resolve } from "path"; +import { deepStrictEqual as equal } from "assert/strict"; describe("loadPlugins", () => { let logger: TestLogger; @@ -20,7 +21,7 @@ describe("loadPlugins", () => { project.addFile("index.js", "exports.load = function load() {}"); project.write(); - const plugin = resolve(project.cwd, "index.js"); + const plugin = normalizePath(resolve(project.cwd, "index.js")); await loadPlugins(fakeApp, [plugin]); logger.expectMessage(`info: Loaded plugin ${plugin}`); }); @@ -31,10 +32,12 @@ describe("loadPlugins", () => { type: "commonjs", main: "index.js", }); - const plugin = project.addFile( - "index.js", - "exports.load = function load() {}", - ).path; + const plugin = normalizePath( + project.addFile( + "index.js", + "exports.load = function load() {}", + ).path, + ); project.write(); await loadPlugins(fakeApp, [plugin]); @@ -50,11 +53,21 @@ describe("loadPlugins", () => { project.addFile("index.js", "export function load() {}"); project.write(); - const plugin = join(resolve(project.cwd), "index.js"); + const plugin = normalizePath(join(resolve(project.cwd), "index.js")); await loadPlugins(fakeApp, [plugin]); logger.expectMessage(`info: Loaded plugin ${plugin}`); }); + it("Should support loading a function plugin", async () => { + let called = false as boolean; + function testFn() { + called = true; + } + await loadPlugins(fakeApp, [testFn]); + logger.expectMessage(`info: Loaded plugin testFn`); + equal(called, true); + }); + it("Should handle errors when requiring plugins", async () => { using project = tempdirProject(); project.addJsonFile("package.json", { @@ -64,7 +77,7 @@ describe("loadPlugins", () => { project.addFile("index.js", "throw Error('bad')"); project.write(); - const plugin = join(resolve(project.cwd), "index.js"); + const plugin = normalizePath(join(resolve(project.cwd), "index.js")); await loadPlugins(fakeApp, [plugin]); logger.expectMessage(`error: The plugin ${plugin} could not be loaded`); }); @@ -81,7 +94,7 @@ describe("loadPlugins", () => { ); project.write(); - const plugin = join(resolve(project.cwd), "index.js"); + const plugin = normalizePath(join(resolve(project.cwd), "index.js")); await loadPlugins(fakeApp, [plugin]); logger.expectMessage(`error: The plugin ${plugin} could not be loaded`); }); @@ -95,7 +108,7 @@ describe("loadPlugins", () => { project.addFile("index.js", ""); project.write(); - const plugin = join(resolve(project.cwd), "index.js"); + const plugin = normalizePath(join(resolve(project.cwd), "index.js")); await loadPlugins(fakeApp, [plugin]); logger.expectMessage( `error: Invalid structure in plugin ${plugin}, no load function found`, From c84da4f8b1f1740b71a1bfa2b1b13925b165c020 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Thu, 10 Jul 2025 21:33:50 -0600 Subject: [PATCH 065/119] Fix lint --- eslint.config.mjs | 6 ++++++ src/lib/utils-common/path.ts | 2 +- src/lib/utils/options/declaration.ts | 4 ++++ src/lib/utils/options/readers/arguments.ts | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 9544f7862..a72f396c1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -244,6 +244,12 @@ export default tslint.config( eslint.configs.recommended, ...tslint.configs.strictTypeChecked, config, + { + files: ["src/test/**/*"], + rules: { + "@typescript-eslint/no-deprecated": "off", + }, + }, { ignores: [ "eslint.config.mjs", diff --git a/src/lib/utils-common/path.ts b/src/lib/utils-common/path.ts index 95b720f49..feec89110 100644 --- a/src/lib/utils-common/path.ts +++ b/src/lib/utils-common/path.ts @@ -1,7 +1,7 @@ import { assert } from "./general.js"; // Type only import is permitted -// eslint-disable-next-line no-restricted-importsgn +// eslint-disable-next-line no-restricted-imports import type { Application } from "../application.js"; /** diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index af707aa64..61ca04387 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -580,6 +580,7 @@ export interface ArrayDeclarationOption extends DeclarationOptionBase { type: | ParameterType.Array | ParameterType.PathArray + // eslint-disable-next-line @typescript-eslint/no-deprecated | ParameterType.ModuleArray | ParameterType.PluginArray; @@ -687,6 +688,7 @@ export interface ParameterTypeToOptionTypeMap { [ParameterType.Object]: unknown; [ParameterType.Array]: string[]; [ParameterType.PathArray]: NormalizedPath[]; + // eslint-disable-next-line @typescript-eslint/no-deprecated [ParameterType.ModuleArray]: NormalizedPathOrModule[]; [ParameterType.PluginArray]: Array void | Promise)>; [ParameterType.GlobArray]: GlobString[]; @@ -785,6 +787,7 @@ const converters: { option.validate?.(normalized); return normalized; }, + // eslint-disable-next-line @typescript-eslint/no-deprecated [ParameterType.ModuleArray](value, option, configPath) { const strArrValue = toStringArray(value, option); const resolved = resolveModulePaths(strArrValue, configPath); @@ -971,6 +974,7 @@ const defaultGetters: { option.defaultValue?.map((value) => normalizePath(resolve(process.cwd(), value))) ?? [] ); }, + // eslint-disable-next-line @typescript-eslint/no-deprecated [ParameterType.ModuleArray](option) { if (option.defaultValue) { return resolveModulePaths(option.defaultValue, process.cwd()); diff --git a/src/lib/utils/options/readers/arguments.ts b/src/lib/utils/options/readers/arguments.ts index d662f61dc..9b277d526 100644 --- a/src/lib/utils/options/readers/arguments.ts +++ b/src/lib/utils/options/readers/arguments.ts @@ -6,6 +6,7 @@ import { i18n, type Logger, type TranslatedString } from "#utils"; const ARRAY_OPTION_TYPES = new Set([ ParameterType.Array, ParameterType.PathArray, + // eslint-disable-next-line @typescript-eslint/no-deprecated ParameterType.ModuleArray, ParameterType.PluginArray, ParameterType.GlobArray, From dcb0bd667deb2fae77d181113e35761e68e70c9f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 09:22:15 -0600 Subject: [PATCH 066/119] Introduce `Context.createSymbolId` Ref: https://github.com/Gerrit0/typedoc-plugin-missing-exports/pull/34 --- CHANGELOG.md | 1 + src/lib/converter/comments/blockLexer.ts | 7 +- src/lib/converter/comments/index.ts | 133 +++++++++-------------- src/lib/converter/comments/parser.ts | 18 ++- src/lib/converter/context.ts | 64 ++++++----- src/lib/converter/factories/signature.ts | 3 +- src/lib/converter/factories/symbol-id.ts | 3 +- src/lib/converter/jsdoc.ts | 3 +- src/lib/converter/types.ts | 5 +- src/test/comments.test.ts | 24 ++-- 10 files changed, 120 insertions(+), 141 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5d8edae1..a49f88842 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ title: Changelog - If using JS config files, the `plugin` function can now be given plugin functions to load. - Permit `-` within tag names to support `typescript-json-schema`'s `@TJS-type` tag, #2972. +- Exposed `Context.createSymbolId` for use by plugins. ### Thanks! diff --git a/src/lib/converter/comments/blockLexer.ts b/src/lib/converter/comments/blockLexer.ts index 52913989e..dae38840f 100644 --- a/src/lib/converter/comments/blockLexer.ts +++ b/src/lib/converter/comments/blockLexer.ts @@ -1,12 +1,15 @@ import ts from "typescript"; import { type Token, TokenSyntaxKind } from "./lexer.js"; import { resolveAliasedSymbol } from "../utils/symbols.js"; -import { createSymbolId } from "../factories/symbol-id.js"; +import type { Context } from "../context.js"; export function* lexBlockComment( file: string, pos = 0, end = file.length, + createSymbolId: Context["createSymbolId"] = () => { + throw new Error("unreachable"); + }, jsDoc: ts.JSDoc | undefined = undefined, checker: ts.TypeChecker | undefined = undefined, ): Generator { @@ -19,6 +22,7 @@ export function* lexBlockComment( end, getLinkTags(jsDoc), checker, + createSymbolId, ) ) { if (token.kind === TokenSyntaxKind.Text) { @@ -82,6 +86,7 @@ function* lexBlockComment2( ts.JSDocLink | ts.JSDocLinkCode | ts.JSDocLinkPlain >, checker: ts.TypeChecker | undefined, + createSymbolId: Context["createSymbolId"], ): Generator { pos += 2; // Leading '/*' end -= 2; // Trailing '*/' diff --git a/src/lib/converter/comments/index.ts b/src/lib/converter/comments/index.ts index 5828e711a..7b2b69db7 100644 --- a/src/lib/converter/comments/index.ts +++ b/src/lib/converter/comments/index.ts @@ -13,6 +13,7 @@ import { lexLineComments } from "./lineLexer.js"; import { parseComment } from "./parser.js"; import type { FileRegistry } from "../../models/FileRegistry.js"; import { assertNever, i18n, type Logger } from "#utils"; +import type { Context } from "../context.js"; export interface CommentParserConfig { blockTags: Set; @@ -24,6 +25,22 @@ export interface CommentParserConfig { commentStyle: CommentStyle; } +export interface CommentContext { + config: CommentParserConfig; + logger: Logger; + checker: ts.TypeChecker; + files: FileRegistry; + createSymbolId: Context["createSymbolId"]; +} + +export interface CommentContextOptionalChecker { + config: CommentParserConfig; + logger: Logger; + checker?: ts.TypeChecker | undefined; + files: FileRegistry; + createSymbolId: Context["createSymbolId"]; +} + const jsDocCommentKinds = [ ts.SyntaxKind.JSDocPropertyTag, ts.SyntaxKind.JSDocCallbackTag, @@ -45,10 +62,7 @@ export function clearCommentCache() { function getCommentWithCache( discovered: DiscoveredComment | undefined, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker | undefined, - files: FileRegistry, + context: CommentContextOptionalChecker, ) { if (!discovered) return; @@ -68,22 +82,19 @@ function getCommentWithCache( file.text, ranges[0].pos, ranges[0].end, + context.createSymbolId, jsDoc, - checker, + context.checker, ), - config, file, - logger, - files, + context, ); break; case ts.SyntaxKind.SingleLineCommentTrivia: comment = parseComment( lexLineComments(file.text, ranges), - config, file, - logger, - files, + context, ); break; default: @@ -100,18 +111,15 @@ function getCommentWithCache( function getCommentImpl( commentSource: DiscoveredComment | undefined, - config: CommentParserConfig, - logger: Logger, moduleComment: boolean, - checker: ts.TypeChecker | undefined, - files: FileRegistry, + context: CommentContext, ) { const comment = getCommentWithCache( commentSource, - config, - logger, - config.useTsLinkResolution ? checker : undefined, - files, + { + ...context, + checker: context.config.useTsLinkResolution ? context.checker : undefined, + }, ); if (comment?.getTag("@import") || comment?.getTag("@license")) { @@ -145,10 +153,7 @@ function getCommentImpl( export function getComment( symbol: ts.Symbol, kind: ReflectionKind, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker, - files: FileRegistry, + context: CommentContext, ): Comment | undefined { const declarations = symbol.declarations || []; @@ -158,16 +163,13 @@ export function getComment( ) { return getJsDocComment( declarations[0] as ts.JSDocPropertyLikeTag, - config, - logger, - checker, - files, + context, ); } const sf = declarations.find(ts.isSourceFile); if (sf) { - return getFileComment(sf, config, logger, checker, files); + return getFileComment(sf, context); } const isModule = declarations.some((decl) => { @@ -181,25 +183,19 @@ export function getComment( discoverComment( symbol, kind, - logger, - config.commentStyle, - checker, - !config.suppressCommentWarningsInDeclarationFiles, + context.logger, + context.config.commentStyle, + context.checker, + !context.config.suppressCommentWarningsInDeclarationFiles, ), - config, - logger, isModule, - checker, - files, + context, ); if (!comment && kind === ReflectionKind.Property) { return getConstructorParamPropertyComment( symbol, - config, - logger, - checker, - files, + context, ); } @@ -209,40 +205,28 @@ export function getComment( export function getNodeComment( node: ts.Node, moduleComment: boolean, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker | undefined, - files: FileRegistry, + context: CommentContext, ) { return getCommentImpl( - discoverNodeComment(node, config.commentStyle), - config, - logger, + discoverNodeComment(node, context.config.commentStyle), moduleComment, - checker, - files, + context, ); } export function getFileComment( file: ts.SourceFile, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker | undefined, - files: FileRegistry, + context: CommentContext, ): Comment | undefined { for ( const commentSource of discoverFileComments( file, - config.commentStyle, + context.config.commentStyle, ) ) { const comment = getCommentWithCache( commentSource, - config, - logger, - config.useTsLinkResolution ? checker : undefined, - files, + context, ); if (comment?.getTag("@license") || comment?.getTag("@import")) { @@ -261,16 +245,13 @@ export function getFileComment( function getConstructorParamPropertyComment( symbol: ts.Symbol, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker, - files: FileRegistry, + context: CommentContext, ): Comment | undefined { const decl = symbol.declarations?.find(ts.isParameter); if (!decl) return; const ctor = decl.parent; - const comment = getSignatureComment(ctor, config, logger, checker, files); + const comment = getSignatureComment(ctor, context); const paramTag = comment?.getIdentifiedTag(symbol.name, "@param"); if (paramTag) { @@ -282,18 +263,12 @@ function getConstructorParamPropertyComment( export function getSignatureComment( declaration: ts.SignatureDeclaration | ts.JSDocSignature, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker, - files: FileRegistry, + context: CommentContext, ): Comment | undefined { return getCommentImpl( - discoverSignatureComment(declaration, checker, config.commentStyle), - config, - logger, + discoverSignatureComment(declaration, context.checker, context.config.commentStyle), false, - checker, - files, + context, ); } @@ -304,10 +279,7 @@ export function getJsDocComment( | ts.JSDocTypedefTag | ts.JSDocTemplateTag | ts.JSDocEnumTag, - config: CommentParserConfig, - logger: Logger, - checker: ts.TypeChecker | undefined, - files: FileRegistry, + context: CommentContext, ): Comment | undefined { const file = declaration.getSourceFile(); @@ -331,10 +303,7 @@ export function getJsDocComment( jsDoc: parent, inheritedFromParentDeclaration: false, }, - config, - logger, - config.useTsLinkResolution ? checker : undefined, - files, + context, )!; // And pull out the tag we actually care about. @@ -352,7 +321,7 @@ export function getJsDocComment( // We could just put the same comment on everything, but due to how comment parsing works, // we'd have to search for any @template with a name starting with the first type parameter's name // which feels horribly hacky. - logger.warn( + context.logger.warn( i18n.multiple_type_parameters_on_template_tag_unsupported(), declaration, ); @@ -378,7 +347,7 @@ export function getJsDocComment( // was a comment attached. If there wasn't, then don't error about failing to find // a tag because this is unsupported. if (!ts.isJSDocTemplateTag(declaration)) { - logger.error( + context.logger.error( i18n.failed_to_find_jsdoc_tag_for_name_0(name), declaration, ); diff --git a/src/lib/converter/comments/parser.ts b/src/lib/converter/comments/parser.ts index b95210555..cc016ab2b 100644 --- a/src/lib/converter/comments/parser.ts +++ b/src/lib/converter/comments/parser.ts @@ -1,6 +1,6 @@ import assert, { ok } from "assert"; import { parseDocument as parseYamlDoc } from "yaml"; -import type { CommentParserConfig } from "./index.js"; +import type { CommentContextOptionalChecker, CommentParserConfig } from "./index.js"; import { Comment, type CommentDisplayPart, CommentTag, type InlineTagDisplayPart } from "../../models/index.js"; import type { MinimalSourceFile } from "#utils"; import { nicePath } from "../../utils/paths.js"; @@ -62,10 +62,8 @@ function makeLookaheadGenerator( export function parseComment( tokens: Generator, - config: CommentParserConfig, file: MinimalSourceFile, - logger: Logger, - files: FileRegistry, + context: CommentContextOptionalChecker, ): Comment { const lexer = makeLookaheadGenerator(tokens); const tok = lexer.done() || lexer.peek(); @@ -75,15 +73,15 @@ export function parseComment( comment.summary = blockContent( comment, lexer, - config, + context.config, i18n, warningImpl, - files, + context.files, ); while (!lexer.done()) { comment.blockTags.push( - blockTag(comment, lexer, config, i18n, warningImpl, files), + blockTag(comment, lexer, context.config, i18n, warningImpl, context.files), ); } @@ -93,19 +91,19 @@ export function parseComment( comment, i18n, () => `${nicePath(file.fileName)}:${file.getLineAndCharacterOfPosition(tok2.pos).line + 1}`, - (message) => logger.warn(message), + (message) => context.logger.warn(message), ); return comment; function warningImpl(message: TranslatedString, token: Token) { if ( - config.suppressCommentWarningsInDeclarationFiles && + context.config.suppressCommentWarningsInDeclarationFiles && hasDeclarationFileExtension(file.fileName) ) { return; } - logger.warn(message, token.pos, file); + context.logger.warn(message, token.pos, file); } } diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index f2df66df1..7f5697164 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -17,10 +17,17 @@ import type { Converter } from "./converter.js"; import { isNamedNode } from "./utils/nodes.js"; import { ConverterEvents } from "./converter-events.js"; import { resolveAliasedSymbol } from "./utils/symbols.js"; -import { getComment, getFileComment, getJsDocComment, getNodeComment, getSignatureComment } from "./comments/index.js"; +import { + type CommentContext, + getComment, + getFileComment, + getJsDocComment, + getNodeComment, + getSignatureComment, +} from "./comments/index.js"; import { getHumanName, getQualifiedName } from "../utils/tsutils.js"; import { findPackageForPath, normalizePath } from "#node-utils"; -import { createSymbolId } from "./factories/symbol-id.js"; +import { createSymbolIdImpl } from "./factories/symbol-id.js"; import { type NormalizedPath, removeIf } from "#utils"; /** @@ -267,7 +274,7 @@ export class Context { ): ReferenceType { const ref = ReferenceType.createUnresolvedReference( name ?? symbol.name, - createSymbolId(symbol), + this.createSymbolId(symbol), context.project, getQualifiedName(symbol, name ?? symbol.name), ); @@ -282,6 +289,18 @@ export class Context { return ref; } + /** + * Create a stable {@link ReflectionSymbolId} for the provided symbol, + * optionally targeting a specific declaration. + * + * @privateRemarks + * This is available on Context so that it can be monkey-patched by typedoc-plugin-missing-exports + * It might also turn out to be generally useful for other plugin users. + */ + createSymbolId(symbol: ts.Symbol, declaration?: ts.Declaration) { + return createSymbolIdImpl(symbol, declaration); + } + addChild(reflection: DeclarationReflection | DocumentReflection) { if (this.scope instanceof ContainerReflection) { this.scope.addChild(reflection); @@ -302,7 +321,7 @@ export class Context { registerReflection(reflection: Reflection, symbol: ts.Symbol | undefined, filePath?: NormalizedPath) { if (symbol) { this.reflectionIdToSymbolMap.set(reflection.id, symbol); - const id = createSymbolId(symbol); + const id = this.createSymbolId(symbol); // #2466 // If we just registered a member of a class or interface, then we need to check if @@ -339,7 +358,7 @@ export class Context { } getReflectionFromSymbol(symbol: ts.Symbol) { - return this.project.getReflectionFromSymbolId(createSymbolId(symbol)); + return this.project.getReflectionFromSymbolId(this.createSymbolId(symbol)); } getSymbolFromReflection(reflection: Reflection) { @@ -351,14 +370,21 @@ export class Context { this._program = program; } + private createCommentContext(): CommentContext { + return { + config: this.converter.config, + logger: this.logger, + checker: this.checker, + files: this.project.files, + createSymbolId: (s, d) => this.createSymbolId(s, d), + }; + } + getComment(symbol: ts.Symbol, kind: ReflectionKind) { return getComment( symbol, kind, - this.converter.config, - this.logger, - this.checker, - this.project.files, + this.createCommentContext(), ); } @@ -366,20 +392,14 @@ export class Context { return getNodeComment( node, moduleComment, - this.converter.config, - this.logger, - this.checker, - this.project.files, + this.createCommentContext(), ); } getFileComment(node: ts.SourceFile) { return getFileComment( node, - this.converter.config, - this.logger, - this.checker, - this.project.files, + this.createCommentContext(), ); } @@ -393,10 +413,7 @@ export class Context { ) { return getJsDocComment( declaration, - this.converter.config, - this.logger, - this.checker, - this.project.files, + this.createCommentContext(), ); } @@ -405,10 +422,7 @@ export class Context { ) { return getSignatureComment( declaration, - this.converter.config, - this.logger, - this.checker, - this.project.files, + this.createCommentContext(), ); } diff --git a/src/lib/converter/factories/signature.ts b/src/lib/converter/factories/signature.ts index 8bc9727bc..f3dc84e0a 100644 --- a/src/lib/converter/factories/signature.ts +++ b/src/lib/converter/factories/signature.ts @@ -17,7 +17,6 @@ import type { Context } from "../context.js"; import { ConverterEvents } from "../converter-events.js"; import { convertDefaultValue } from "../convert-expression.js"; import { removeUndefined } from "../utils/reflections.js"; -import { createSymbolId } from "./symbol-id.js"; export function createSignature( context: Context, @@ -51,7 +50,7 @@ export function createSignature( if (symbol && declaration) { context.project.registerSymbolId( sigRef, - createSymbolId(symbol, declaration), + context.createSymbolId(symbol, declaration), ); } diff --git a/src/lib/converter/factories/symbol-id.ts b/src/lib/converter/factories/symbol-id.ts index d74964250..ee7b4b74b 100644 --- a/src/lib/converter/factories/symbol-id.ts +++ b/src/lib/converter/factories/symbol-id.ts @@ -10,7 +10,8 @@ const declarationMapCache = new Map(); let transientCount = 0; const transientIds = new WeakMap(); -export function createSymbolId(symbol: ts.Symbol, declaration?: ts.Declaration) { +// Don't use this directly, use Context.createSymbolId instead. +export function createSymbolIdImpl(symbol: ts.Symbol, declaration?: ts.Declaration) { declaration ??= symbol.declarations?.[0]; const tsSource = declaration?.getSourceFile().fileName ?? ""; const sourceFileName = resolveDeclarationMaps(tsSource); diff --git a/src/lib/converter/jsdoc.ts b/src/lib/converter/jsdoc.ts index 53836f906..ca9be368e 100644 --- a/src/lib/converter/jsdoc.ts +++ b/src/lib/converter/jsdoc.ts @@ -14,7 +14,6 @@ import { import type { Context } from "./context.js"; import { ConverterEvents } from "./converter-events.js"; import { convertParameterNodes, convertTemplateParameterNodes } from "./factories/signature.js"; -import { createSymbolId } from "./factories/symbol-id.js"; export function convertJsDocAlias( context: Context, @@ -134,7 +133,7 @@ function convertJsDocSignature(context: Context, node: ts.JSDocSignature) { ); context.project.registerSymbolId( signature, - createSymbolId(symbol, node), + context.createSymbolId(symbol, node), ); context.registerReflection(signature, void 0); const signatureCtx = rc.withScope(signature); diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index f8b8764ab..56a08bafb 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -35,7 +35,6 @@ import { convertParameterNodes, convertTypeParameterNodes, createSignature } fro import { convertSymbol } from "./symbols.js"; import { isObjectType, isTypeReference } from "./utils/nodes.js"; import { removeUndefined } from "./utils/reflections.js"; -import { createSymbolId } from "./factories/symbol-id.js"; export interface TypeConverter< TNode extends ts.TypeNode = ts.TypeNode, @@ -277,7 +276,7 @@ const constructorConverter: TypeConverter = { } context.project.registerSymbolId( signature, - createSymbolId(symbol, node), + context.createSymbolId(symbol, node), ); context.registerReflection(signature, void 0); const signatureCtx = rc.withScope(signature); @@ -380,7 +379,7 @@ const functionTypeConverter: TypeConverter = { ); context.project.registerSymbolId( signature, - createSymbolId(symbol, node), + context.createSymbolId(symbol, node), ); context.registerReflection(signature, undefined); const signatureCtx = rc.withScope(signature); diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 8647b6372..690f8806a 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -13,6 +13,10 @@ import { extractTagName } from "../lib/converter/comments/tagName.js"; import { type FileId, FileRegistry } from "../lib/models/FileRegistry.js"; import { dedent, MinimalSourceFile, type NormalizedPath } from "#utils"; +const neverCalled = () => { + throw new Error("Should not be called"); +}; + describe("Block Comment Lexer", () => { function lex(text: string): Token[] { return Array.from(lexBlockComment(text)); @@ -1159,10 +1163,8 @@ describe("Comment Parser", () => { const content = lexBlockComment(file); const comment = parseComment( content, - config, new MinimalSourceFile(file, "/dev/zero" as NormalizedPath), - logger, - files, + { logger, files, config, createSymbolId: neverCalled }, ); equal( @@ -1186,10 +1188,8 @@ describe("Comment Parser", () => { const content = lexBlockComment(file); const comment = parseComment( content, - config, new MinimalSourceFile(file, "/dev/zero" as NormalizedPath), - logger, - files, + { logger, files, config, createSymbolId: neverCalled }, ); equal( @@ -1214,10 +1214,8 @@ describe("Comment Parser", () => { const content = lexBlockComment(file); const comment = parseComment( content, - config, new MinimalSourceFile(file, "/dev/zero" as NormalizedPath), - logger, - files, + { logger, files, config, createSymbolId: neverCalled }, ); equal( @@ -1242,10 +1240,8 @@ describe("Comment Parser", () => { const content = lexBlockComment(file); const comment = parseComment( content, - config, new MinimalSourceFile(file, "/dev/zero" as NormalizedPath), - logger, - files, + { logger, files, config, createSymbolId: neverCalled }, ); logger.expectMessage( @@ -1262,10 +1258,8 @@ describe("Comment Parser", () => { const content = lexBlockComment(text); const comment = parseComment( content, - config, new MinimalSourceFile(text, "/dev/zero" as NormalizedPath), - logger, - files, + { logger, files, config, createSymbolId: neverCalled }, ); logger.expectNoOtherMessages(); return comment; From d3899e0529e6f9baf1ec46dee0674e5440632204 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 09:38:26 -0600 Subject: [PATCH 067/119] Handle relative links within --- CHANGELOG.md | 5 ++++ src/lib/converter/comments/textParser.ts | 8 +++++- src/test/comments.test.ts | 32 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f68f25fdd..c7302f91d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,14 @@ title: Changelog - Permit `-` within tag names to support `typescript-json-schema`'s `@TJS-type` tag, #2972. +### Bug Fixes + +- Relative links in `` and `` elements will now be discovered by TypeDoc, #2975. + ### Thanks! - @jonathanhefner +- @laymonage ## v0.28.7 (2025-06-30) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index fcaa10857..15f138710 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -314,7 +314,13 @@ function checkTagLink(data: TextParserData): RelativeLink | undefined { if (token.text.startsWith(" { ); }); + it("Recognizes HTML picture source src links", () => { + const comment = getComment(`/** + * + * + * + */`); + + equal( + comment.summary, + [ + { kind: "text", text: '\n\n', + }, + ] satisfies CommentDisplayPart[], + ); + }); + it("Recognizes HTML anchor links", () => { const comment = getComment(`/** *
From 59db5b28cb7788fce05c8421888b170a97c7e3ff Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 13:05:02 -0600 Subject: [PATCH 068/119] Handle multiple URLs in srcset --- CHANGELOG.md | 3 +- index.html | 6 + src/lib/converter/comments/textParser.ts | 153 ++++++++++++++++++----- src/test/comments.test.ts | 90 ++++++++++++- 4 files changed, 214 insertions(+), 38 deletions(-) create mode 100644 index.html diff --git a/CHANGELOG.md b/CHANGELOG.md index c7302f91d..eabdb066f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ title: Changelog ### Bug Fixes -- Relative links in `` and `` elements will now be discovered by TypeDoc, #2975. +- Relative links in `` will now be discovered by TypeDoc, #2975. +- Relative links in `` and `` elements will now be discovered by TypeDoc, #2975. ### Thanks! diff --git a/index.html b/index.html new file mode 100644 index 000000000..cf7ca8870 --- /dev/null +++ b/index.html @@ -0,0 +1,6 @@ + + + + Wagtail + + diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 15f138710..4b4d8d3a5 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -136,9 +136,11 @@ export function textContent( continue; } - const tagLink = checkTagLink(data); - if (tagLink) { - addRef(tagLink); + const tagLinks = checkTagLink(data); + if (tagLinks.length) { + for (const tagLink of tagLinks) { + addRef(tagLink); + } continue; } @@ -299,61 +301,150 @@ function checkReference(data: TextParserData): RelativeLink | undefined { /** * Looks for ``, ``, and `` */ -function checkTagLink(data: TextParserData): RelativeLink | undefined { +function checkTagLink(data: TextParserData): RelativeLink[] { const { pos, token } = data; if (token.text.startsWith(" RelativeLink[] + >, +): RelativeLink[] { + const links: RelativeLink[] = []; const parser = new HtmlAttributeParser(data.token.text, data.pos); while (parser.state !== ParserState.END) { if ( parser.state === ParserState.BeforeAttributeValue && - parser.currentAttributeName === attr + attributes.hasOwnProperty(parser.currentAttributeName) ) { parser.step(); - if (isRelativePath(parser.currentAttributeValue)) { - data.pos = parser.pos; - const { target, anchor } = data.files.register( - data.sourcePath, - parser.currentAttributeValue as NormalizedPath, - ) || { target: undefined, anchor: undefined }; - return { - pos: parser.currentAttributeValueStart, - end: parser.currentAttributeValueEnd, - target, - targetAnchor: anchor, - }; - } - return; + links.push(...attributes[parser.currentAttributeName]( + data, + parser.currentAttributeValue, + parser.currentAttributeValueStart, + parser.currentAttributeValueEnd, + )); } parser.step(); } + + return links; +} + +function checkAttributeDirectPath( + data: TextParserData, + text: string, + pos: number, + end: number, +): RelativeLink[] { + if (isRelativePath(text.trim())) { + const { target, anchor } = data.files.register( + data.sourcePath, + text.trim() as NormalizedPath, + ) || { target: undefined, anchor: undefined }; + return [{ + pos, + end, + target, + targetAnchor: anchor, + }]; + } + + return []; +} + +// See https://html.spec.whatwg.org/multipage/images.html#srcset-attribute +function checkAttributeSrcSet(data: TextParserData, text: string, pos: number, _end: number): RelativeLink[] { + const result: RelativeLink[] = []; + + let textPos = 0; + parseImageCandidate(); + while (textPos < text.length && text[textPos] == ",") { + ++textPos; + parseImageCandidate(); + } + + return result; + + function parseImageCandidate() { + // 1. Zero or more ASCII whitespace + while (textPos < text.length && /[\t\r\f\n ]/.test(text[textPos])) ++textPos; + // 2. A valid non-empty URL that does not start or end with a comma + // TypeDoc: We don't exactly match this, PR welcome! For now, just permit anything + // that's not whitespace or a comma + const url = text.slice(textPos).match(/^[^\t\r\f\n ,]+/); + + if (url && isRelativePath(url[0])) { + const { target, anchor } = data.files.register( + data.sourcePath, + url[0] as NormalizedPath, + ) || { target: undefined, anchor: undefined }; + result.push({ + pos: pos + textPos, + end: pos + textPos + url[0].length, + target, + targetAnchor: anchor, + }); + } + textPos += url ? url[0].length : 0; + + // 3. Zero or more ASCII whitespace + while (textPos < text.length && /[\t\r\f\n ]/.test(text[textPos])) ++textPos; + + // 4. Zero or one of the following: + { + // A width descriptor, consisting of: ASCII whitespace, a valid non-negative integer giving + // a number greater than zero representing the width descriptor value, and a U+0077 LATIN + // SMALL LETTER W character. + const w = text.slice(textPos).match(/^\+?\d+\s*w/); + textPos += w ? w[0].length : 0; + + // A pixel density descriptor, consisting of: ASCII whitespace, a valid floating-point number + // giving a number greater than zero representing the pixel density descriptor value, and a + // U+0078 LATIN SMALL LETTER X character. + if (!w) { + const x = text.slice(textPos).match(/^\+?\d+(\.\d+)?([eE][+-]\d+)?\s*x/); + textPos += x ? x[0].length : 0; + } + } + + // 5. Zero or more ASCII whitespace + while (textPos < text.length && /[\t\r\f\n ]/.test(text[textPos])) ++textPos; + } } function isRelativePath(link: string) { diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 8924bab41..d39ef6e65 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1639,7 +1639,8 @@ describe("Comment Parser", () => { it("Recognizes HTML picture source srcset links", () => { const comment = getComment(`/** * - * + * + * * */`); @@ -1653,13 +1654,40 @@ describe("Comment Parser", () => { target: 1 as FileId, targetAnchor: undefined, }, - { kind: "text", text: '" >\n\n\n\n', @@ -1668,11 +1696,31 @@ describe("Comment Parser", () => { ); }); - it("Recognizes HTML picture source src links", () => { + it("Recognizes links", () => { + const comment = getComment(`/** + * + */`); + + equal( + comment.summary, + [ + { kind: "text", text: '' }, + ] satisfies CommentDisplayPart[], + ); + }); + + it("Recognizes HTML audio and video src links", () => { const comment = getComment(`/** * * - * + * */`); equal( @@ -1694,7 +1742,37 @@ describe("Comment Parser", () => { }, { kind: "text", - text: '"/>\n', + text: '"/>\n', + }, + ] satisfies CommentDisplayPart[], + ); + }); + + it("Recognizes img tag with both src and srcset", () => { + const comment = getComment(`/** + * + */`); + + equal( + comment.summary, + [ + { kind: "text", text: '', }, ] satisfies CommentDisplayPart[], ); From b418f6bfb3c2ef46b71c627088e0af75c0542b1b Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 13:31:25 -0600 Subject: [PATCH 069/119] Lint, wrong in this case, but appease it --- src/lib/converter/comments/textParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 4b4d8d3a5..2289aaa22 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -347,7 +347,7 @@ function checkAttributes( while (parser.state !== ParserState.END) { if ( parser.state === ParserState.BeforeAttributeValue && - attributes.hasOwnProperty(parser.currentAttributeName) + Object.prototype.hasOwnProperty.call(attributes, parser.currentAttributeName) ) { parser.step(); From bc54be832dd51f908f9f78084378ecc648a6d966 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 19:39:20 -0600 Subject: [PATCH 070/119] Discover parent types through mapped types Resolves #2978 --- .vscode/settings.json | 1 + CHANGELOG.md | 1 + scripts/testcase.js | 1 + src/lib/converter/plugins/ImplementsPlugin.ts | 84 +++++++++++++------ src/lib/serialization/schema.ts | 1 + src/test/converter/interface/specs.json | 4 +- src/test/converter2/issues/gh2978.ts | 15 ++++ src/test/issues.c2.test.ts | 9 ++ 8 files changed, 88 insertions(+), 28 deletions(-) mode change 100644 => 100755 scripts/testcase.js create mode 100644 src/test/converter2/issues/gh2978.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index f2c994728..b305aa521 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ }, "typescript.tsdk": "./node_modules/typescript/lib", + "dprint.path": "./node_modules/dprint/dprint", // Automatically run the formatter when certain files are saved. "[javascript]": { diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1068b49..c791bbdae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ title: Changelog - Relative links in `` will now be discovered by TypeDoc, #2975. - Relative links in `` and `` elements will now be discovered by TypeDoc, #2975. +- Improved inherited from/overwrites link discovery to point to parent properties in more cases, #2978 ### Thanks! diff --git a/scripts/testcase.js b/scripts/testcase.js old mode 100644 new mode 100755 index 365bbe84d..2a5c0d744 --- a/scripts/testcase.js +++ b/scripts/testcase.js @@ -1,3 +1,4 @@ +#!/usr/bin/env node // @ts-check import md from "markdown-it"; import cp from "child_process"; diff --git a/src/lib/converter/plugins/ImplementsPlugin.ts b/src/lib/converter/plugins/ImplementsPlugin.ts index c79215ed9..332d1640c 100644 --- a/src/lib/converter/plugins/ImplementsPlugin.ts +++ b/src/lib/converter/plugins/ImplementsPlugin.ts @@ -110,8 +110,10 @@ export class ImplementsPlugin extends ConverterComponent { project: ProjectReflection, reflection: DeclarationReflection, ) { + if (!reflection.extendedTypes) return; + const extendedTypes = filterMap( - reflection.extendedTypes ?? [], + reflection.extendedTypes, (type) => { return type instanceof ReferenceType && type.reflection instanceof DeclarationReflection @@ -139,23 +141,54 @@ export class ImplementsPlugin extends ConverterComponent { parentMember.signatures ?? [], ) ) { - childSig[key] = ReferenceType.createResolvedReference( + // If we're already pointing at something because TS said we should reference + // it, then don't overwrite the reference. + if (!childSig[key]?.reflection) { + childSig[key] = ReferenceType.createResolvedReference( + `${parent.name}.${parentMember.name}`, + parentSig, + project, + ); + } + } + + if (!child[key]?.reflection) { + child[key] = ReferenceType.createResolvedReference( `${parent.name}.${parentMember.name}`, - parentSig, + parentMember, project, ); } - child[key] = ReferenceType.createResolvedReference( - `${parent.name}.${parentMember.name}`, - parentMember, - project, - ); - this.handleInheritedComments(child, parentMember); } } } + + // #2978, this is very unfortunate. If a child's parent links are broken at this point, + // we replace them with an intentionally broken link so that they won't ever be resolved. + // This is done because if we don't do it then we run into issues where we have a link which + // points to some ReflectionSymbolId which might not exist now, but once we've gone through + // serialization/deserialization, might point to an unexpected location. (See the mixin + // converter tests, I suspect this might actually be an indication of something else slightly + // broken there, but don't want to spend more time with this right now.) + for (const child of reflection.children || []) { + if (child.inheritedFrom && !child.inheritedFrom.reflection) { + child.inheritedFrom = ReferenceType.createBrokenReference(child.inheritedFrom.name, project); + } + if (child.overwrites && !child.overwrites.reflection) { + child.overwrites = ReferenceType.createBrokenReference(child.overwrites.name, project); + } + + for (const childSig of child.getAllSignatures()) { + if (childSig.inheritedFrom && !childSig.inheritedFrom.reflection) { + childSig.inheritedFrom = ReferenceType.createBrokenReference(childSig.inheritedFrom.name, project); + } + if (childSig.overwrites && !childSig.overwrites.reflection) { + childSig.overwrites = ReferenceType.createBrokenReference(childSig.overwrites.name, project); + } + } + } } private onResolveEnd(context: Context) { @@ -522,9 +555,21 @@ function createLink( symbol: ts.Symbol, isInherit: boolean, ) { - const project = context.project; const name = `${expr.expression.getText()}.${getHumanName(symbol.name)}`; + // We should always have rootSymbols, but check just in case. We use the first + // symbol here as TypeDoc's models don't have multiple symbols for the parent + // reference. This is technically wrong because symbols might be declared in + // multiple locations (interface declaration merging), but that's an uncommon + // enough use case that it doesn't seem worthwhile to complicate the rest of the + // world to deal with it. + // Note that we also need to check that the root symbol isn't this symbol. + // This seems to happen sometimes when dealing with interface inheritance. + const rootSymbols = context.checker.getRootSymbols(symbol); + const ref = rootSymbols.length && rootSymbols[0] != symbol + ? context.createSymbolReference(rootSymbols[0], context, name) + : ReferenceType.createBrokenReference(name, context.project); + link(reflection); link(reflection.getSignature); link(reflection.setSignature); @@ -535,34 +580,21 @@ function createLink( link(sig); } - // Intentionally create broken links here. These will be replaced with real links during - // resolution if we can do so. We create broken links rather than real links because in the - // case of an inherited symbol, we'll end up referencing a single symbol ID rather than one - // for each class. function link( target: DeclarationReflection | SignatureReflection | undefined, ) { if (!target) return; if (clause.token === ts.SyntaxKind.ImplementsKeyword) { - target.implementationOf ??= ReferenceType.createBrokenReference( - name, - project, - ); + target.implementationOf ??= ref; return; } if (isInherit) { target.setFlag(ReflectionFlag.Inherited); - target.inheritedFrom ??= ReferenceType.createBrokenReference( - name, - project, - ); + target.inheritedFrom ??= ref; } else { - target.overwrites ??= ReferenceType.createBrokenReference( - name, - project, - ); + target.overwrites ??= ref; } } } diff --git a/src/lib/serialization/schema.ts b/src/lib/serialization/schema.ts index 1db8459f5..34debec86 100644 --- a/src/lib/serialization/schema.ts +++ b/src/lib/serialization/schema.ts @@ -58,6 +58,7 @@ type _ModelToObject = T extends M.CommentDisplayPart ? CommentDisplayPart : T extends M.SourceReference ? SourceReference : T extends M.FileRegistry ? FileRegistry : + T extends M.ReflectionSymbolId ? ReflectionSymbolId : never; type Primitive = string | number | undefined | null | boolean; diff --git a/src/test/converter/interface/specs.json b/src/test/converter/interface/specs.json index b629a2d17..5ca0c0914 100644 --- a/src/test/converter/interface/specs.json +++ b/src/test/converter/interface/specs.json @@ -2546,8 +2546,8 @@ }, "inheritedFrom": { "type": "reference", - "target": 106, - "name": "Base.base" + "target": 112, + "name": "Child.base" } }, { diff --git a/src/test/converter2/issues/gh2978.ts b/src/test/converter2/issues/gh2978.ts new file mode 100644 index 000000000..9dd8876aa --- /dev/null +++ b/src/test/converter2/issues/gh2978.ts @@ -0,0 +1,15 @@ +export interface Parent { + prop: string; +} + +export interface Child extends Partial {} + +export type Tricky = Omit & { x: number }; + +export interface HasX { + x: string; +} + +export interface InheritsX extends Tricky { + // InheritsX.x should *not* be linked to HasX.x +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 94a1b6e76..4aaf6b6f0 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2135,4 +2135,13 @@ describe("Issue Tests", () => { ["Var", "Comment"], ]); }); + + it("#2978 handles parent properties through mapped types", () => { + const project = convert(); + const prop = query(project, "Child.prop"); + equal(prop.inheritedFrom?.reflection?.getFullName(), "Parent.prop"); + const x = query(project, "InheritsX.x"); + equal(x.inheritedFrom?.reflection?.getFullName(), undefined); + equal(x.inheritedFrom?.name, "Tricky.x"); + }); }); From c02e49570ab74a054627674748f6b9f15cb1dead Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 19:46:49 -0600 Subject: [PATCH 071/119] Make site build stricter --- scripts/build_site.sh | 6 +++++- site/typedoc.config.jsonc | 1 + src/index.ts | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/build_site.sh b/scripts/build_site.sh index 6c8276895..11622b4d9 100755 --- a/scripts/build_site.sh +++ b/scripts/build_site.sh @@ -14,6 +14,10 @@ fi if [[ -n "$CI" || ! -d example/docs ]]; then cd example pnpm i + # Ignoring warnings here because we inherit from Array, which results in + # a few warnings because the docs in the .d.ts have bad @param comments + # We might want to change TypeDoc's validation logic to make this not a + # warning at some point if the relevant comments show up on both signatures. pnpm run typedoc --logLevel Error cd .. fi @@ -22,7 +26,7 @@ fi git show $(git describe --tags --abbrev=0):CHANGELOG.md | sed 's/#* Unreleased//' > site/generated/CHANGELOG.md # Build the actual site, references the API docs -node bin/typedoc --options site/typedoc.config.jsonc +node bin/typedoc --options site/typedoc.config.jsonc --treatWarningsAsErrors # Create/copy static files node scripts/generate_options_schema.js docs-site/schema.json diff --git a/site/typedoc.config.jsonc b/site/typedoc.config.jsonc index 48f0b91ae..ab9eca333 100644 --- a/site/typedoc.config.jsonc +++ b/site/typedoc.config.jsonc @@ -1,6 +1,7 @@ { "$schema": "https://typedoc.org/schema.json", "logLevel": "Verbose", + "treatWarningsAsErrors": true, "entryPointStrategy": "merge", "entryPoints": [], diff --git a/src/index.ts b/src/index.ts index 88fb52d87..166802605 100644 --- a/src/index.ts +++ b/src/index.ts @@ -139,6 +139,7 @@ export { MinimalSourceFile, type NormalizedPath, type NormalizedPathOrModule, + type NormalizedPathOrModuleOrFunction, type SymbolReference, type TranslatedString, translateTagName, From db5955371f1f471cd3da66f1f86216020cf69ee5 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 27 Jul 2025 19:49:22 -0600 Subject: [PATCH 072/119] Bump version to 0.28.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2bcf6d45..c345b7ab3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.7", + "version": "0.28.8", "homepage": "https://typedoc.org", "type": "module", "exports": { From da3786249784de30cc95346c869ba2c266fae618 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Mon, 28 Jul 2025 01:55:35 +0000 Subject: [PATCH 073/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c791bbdae..2f0cca41a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.8 (2025-07-28) + ### Features - If using JS config files, the `plugin` function can now be given plugin functions to load. From f625f15bc20defc4a128722c41dd7c479bd3f045 Mon Sep 17 00:00:00 2001 From: ProjectXero Date: Tue, 29 Jul 2025 13:03:27 +0800 Subject: [PATCH 074/119] Update Chinese translations --- src/lib/internationalization/locales/zh.cts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/lib/internationalization/locales/zh.cts b/src/lib/internationalization/locales/zh.cts index 9250ad7e9..5a0293423 100644 --- a/src/lib/internationalization/locales/zh.cts +++ b/src/lib/internationalization/locales/zh.cts @@ -79,6 +79,8 @@ export = localeUtils.buildIncompleteTranslation({ inline_tag_not_closed: "内联标签未关闭", // validation + comment_for_0_links_to_1_not_included_in_docs_use_external_link_2: + `{0} 注释中指向 “{1}” 的已解析的链接不会被包含在文档中。请将 {2} 导出或添加至 externalSymbolLinkMappings 选项以修复该警告`, failed_to_resolve_link_to_0_in_comment_for_1: "无法解析 {1} 注释中指向 “{0}” 的链接", failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2: "无法解析 {1} 的注释中指向 “{0}” 的链接。您可能想要 “{2}”", @@ -103,9 +105,11 @@ export = localeUtils.buildIncompleteTranslation({ "文档中并未使用 searchCategoryBoosts 中指定的所有类别。未使用的类别包括:\n{0}", not_all_search_group_boosts_used_0: "文档中并未使用 searchGroupBoosts 中指定的所有组。未使用的组为:\n{0}", comment_for_0_includes_categoryDescription_for_1_but_no_child_in_group: - "{0} 的评论包含“{1}”的 @categoryDescription,但该类别中没有子项", + "{0} 的注释中包含了 “{1}” 的 @categoryDescription,但该类别中没有子项", comment_for_0_includes_groupDescription_for_1_but_no_child_in_group: - "{0} 的注释包含“{1}”的 @groupDescription,但该组中没有子项", + "{0} 的注释中包含了 “{1}” 的 @groupDescription,但该分组中没有子项", + comment_for_0_specifies_1_as_sort_strategy_but_only_2_is_valid: + `{0} 的注释中指定的 “{1}” 的 @sortStrategy 无效,以下是有效的选项:\n\t{2}`, label_0_for_1_cannot_be_referenced: "无法使用声明引用来引用 {1} 的标签“{0}”。标签只能包含 A-Z、0-9 和 _,并且不能以数字开头", modifier_tag_0_is_mutually_exclusive_with_1_in_comment_for_2: "修饰符标签 {0} 与 {2} 注释中的 {1} 互斥", @@ -187,6 +191,8 @@ export = localeUtils.buildIncompleteTranslation({ circular_reference_extends_0: "{0} 的“extends”字段出现循环引用", failed_resolve_0_to_file_in_1: "无法将 {0} 解析为 {1} 中的文件", + glob_0_should_use_posix_slash: + `该 glob “{0}” 中转义了不是特殊字符的字符。输入 TypeDoc 的 glob 可能不会使用 Windows 路径分隔符(\\),请尝试将其替换为 POSIX 路径分隔符(/)`, option_0_can_only_be_specified_by_config_file: "“{0}”选项只能通过配置文件指定", option_0_expected_a_value_but_none_provided: "--{0} 需要一个值,但没有给出任何参数", unknown_option_0_may_have_meant_1: "未知选项:{0},你可能指的是:\n\t{1}", @@ -348,6 +354,8 @@ export = localeUtils.buildIncompleteTranslation({ "useHostedBaseUrlForAbsoluteLinks 选项要求设置 hostingBaseUrl", favicon_must_have_one_of_the_following_extensions_0: "favicon 的后缀名必须是下列之一:{0}", option_0_must_be_an_object: "“{0}”选项必须是非数组对象", + option_0_must_be_an_array_of_string: "“{0}”选项必须是字符串数组", + option_0_must_be_an_array_of_string_or_functions: "“{0}”选项必须是由字符串或函数构成的数组", option_0_must_be_a_function: "‘{0}’ 选项必须是一个函数", option_0_must_be_object_with_urls: "{0} 必须是具有字符串标签作为键和 URL 值的对象", visibility_filters_only_include_0: "visibilityFilters 只能包含以下非@键:{0}", @@ -514,6 +522,7 @@ export = localeUtils.buildIncompleteTranslation({ tag_return: "返回", tag_satisfies: "满足", tag_since: "添加于", + tag_sortStrategy: "排序策略", tag_template: "类型参数", tag_type: "类型", tag_typedef: "类型定义", @@ -545,6 +554,7 @@ export = localeUtils.buildIncompleteTranslation({ tag_virtual: "虚函数", tag_abstract: "抽象类", tag_class: "类", + tag_disableGroups: "禁用分组", tag_enum: "枚举", tag_event: "事件", tag_expand: "展开", From 73f3dc25f81fd7d548f108d2ae2fd5fd06a52e02 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 1 Aug 2025 08:38:47 -0600 Subject: [PATCH 075/119] Support TypeScript 5.9 Resolves #2989 --- dprint.json | 4 +- package.json | 16 +- pnpm-lock.yaml | 582 +++++++++++++++++++++++++------------------------ 3 files changed, 303 insertions(+), 299 deletions(-) diff --git a/dprint.json b/dprint.json index 2237a8ba6..4faadb249 100644 --- a/dprint.json +++ b/dprint.json @@ -19,9 +19,9 @@ "src/test/converter2/issues/gh2631/crlf.md" ], "plugins": [ - "https://plugins.dprint.dev/typescript-0.95.8.wasm", + "https://plugins.dprint.dev/typescript-0.95.9.wasm", "https://plugins.dprint.dev/json-0.20.0.wasm", "https://plugins.dprint.dev/markdown-0.19.0.wasm", - "https://plugins.dprint.dev/g-plane/malva-v0.12.1.wasm" + "https://plugins.dprint.dev/g-plane/malva-v0.14.1.wasm" ] } diff --git a/package.json b/package.json index c345b7ab3..7a49c7b42 100644 --- a/package.json +++ b/package.json @@ -31,32 +31,32 @@ "pnpm": ">= 10" }, "dependencies": { - "@gerrit0/mini-shiki": "^3.7.0", + "@gerrit0/mini-shiki": "^3.9.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", "yaml": "^2.8.0" }, "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x" }, "devDependencies": { - "@eslint/js": "^9.30.0", + "@eslint/js": "^9.32.0", "@types/lunr": "^2.3.7", "@types/markdown-it": "^14.1.2", "@types/mocha": "^10.0.10", "@types/node": "18", "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe", "c8": "^10.1.3", - "dprint": "^0.50.0", - "esbuild": "^0.25.5", - "eslint": "^9.30.0", + "dprint": "^0.50.1", + "esbuild": "^0.25.8", + "eslint": "^9.32.0", "mocha": "^11.7.1", "puppeteer": "^24.11.1", "semver": "^7.7.2", "tsx": "^4.20.3", - "typescript": "5.8.3", - "typescript-eslint": "^8.35.0" + "typescript": "5.9.2", + "typescript-eslint": "^8.38.0" }, "files": [ "/bin", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a59df8b90..7809dfed2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@gerrit0/mini-shiki': - specifier: ^3.7.0 - version: 3.7.0 + specifier: ^3.9.0 + version: 3.9.0 lunr: specifier: ^2.3.9 version: 2.3.9 @@ -25,8 +25,8 @@ importers: version: 2.8.0 devDependencies: '@eslint/js': - specifier: ^9.30.0 - version: 9.30.0 + specifier: ^9.32.0 + version: 9.32.0 '@types/lunr': specifier: ^2.3.7 version: 2.3.7 @@ -46,20 +46,20 @@ importers: specifier: ^10.1.3 version: 10.1.3 dprint: - specifier: ^0.50.0 - version: 0.50.0 + specifier: ^0.50.1 + version: 0.50.1 esbuild: - specifier: ^0.25.5 - version: 0.25.5 + specifier: ^0.25.8 + version: 0.25.8 eslint: - specifier: ^9.30.0 - version: 9.30.0 + specifier: ^9.32.0 + version: 9.32.0 mocha: specifier: ^11.7.1 version: 11.7.1 puppeteer: specifier: ^24.11.1 - version: 24.11.1(typescript@5.8.3) + version: 24.11.1(typescript@5.9.2) semver: specifier: ^7.7.2 version: 7.7.2 @@ -67,11 +67,11 @@ importers: specifier: ^4.20.3 version: 4.20.3 typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 5.9.2 + version: 5.9.2 typescript-eslint: - specifier: ^8.35.0 - version: 8.35.0(eslint@9.30.0)(typescript@5.8.3) + specifier: ^8.38.0 + version: 8.38.0(eslint@9.32.0)(typescript@5.9.2) packages: @@ -87,197 +87,203 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@dprint/darwin-arm64@0.50.0': - resolution: {integrity: sha512-KqWpsvm4JveYdKDLSLlQINGNW4pEAGHcTFPEHR5qXMYV4pPomLgHHPyBrxe3XdGtlUp4I8HfvBMBw3b/LKd06A==} + '@dprint/darwin-arm64@0.50.1': + resolution: {integrity: sha512-NNKf3dxXn567pd/hpCVLHLbC0dI7s3YvQnUEwjRTOAQVMp6O7/ME+Tg1RPGsDP1IB+Y2fIYSM4qmG02zQrqjAQ==} cpu: [arm64] os: [darwin] - '@dprint/darwin-x64@0.50.0': - resolution: {integrity: sha512-kFeeLYhCIVAe1SMtFYk1q0qWxrkmW8FhOBTUh2oblr4AnAjpjb03m8BVUrHHKFeBTsppwck+1b8hzU6LRZO7fA==} + '@dprint/darwin-x64@0.50.1': + resolution: {integrity: sha512-PcY75U3UC/0CLOxWzE0zZJZ2PxzUM5AX2baYL1ovgDGCfqO1H0hINiyxfx/8ncGgPojWBkLs+zrcFiGnXx7BQg==} cpu: [x64] os: [darwin] - '@dprint/linux-arm64-glibc@0.50.0': - resolution: {integrity: sha512-EL0+uMSdj/n+cZOP9ZO8ndvjmtOSWXNsMHKdAAaTG0+EjH9M9YKXD6kopP6PKOR5pJuiyHCRpVKJ4xoD4adfpQ==} + '@dprint/linux-arm64-glibc@0.50.1': + resolution: {integrity: sha512-q0TOGy9FsoSKsEQ4sIMKyFweF5M8rW1S5OfwJDNRR2TU2riWByU9TKYIZUzg53iuwYKRypr/kJ5kdbl516afRQ==} cpu: [arm64] os: [linux] - '@dprint/linux-arm64-musl@0.50.0': - resolution: {integrity: sha512-bzyYxKtFw/hYAA+7lWQGQGo2YFPnH7Ql9uWxxWqiGaWVPU66K9WQt0RUEqu1hQBrCk9mMz3jx5l4oKWQ/Dc0fw==} + '@dprint/linux-arm64-musl@0.50.1': + resolution: {integrity: sha512-XRtxN2cA9rc06WFzzVPDIZYGGLmUXqpVf3F0XhhHV77ikQLJZ5reF4xBOQ+0HjJ/zy8W/HzuGDAHedWyCrRf9g==} cpu: [arm64] os: [linux] - '@dprint/linux-riscv64-glibc@0.50.0': - resolution: {integrity: sha512-ElFqmKs96NyVXWqd2SJGJGtyVmUWNiLUyaImEzL7XZRmpoJG+Ky7SryhccMQU0ENtQmY0CVgZipLZ1SqhIoluA==} + '@dprint/linux-riscv64-glibc@0.50.1': + resolution: {integrity: sha512-vAk/eYhSjA3LJ/yuYgxkHamiK8+m6YdqVBO/Ka+i16VxyjQyOdcMKBkrLCIqSxgyXd6b8raf9wM59HJbaIpoOg==} cpu: [riscv64] os: [linux] - '@dprint/linux-x64-glibc@0.50.0': - resolution: {integrity: sha512-Kim8TtCdpCQUNqF2D96vunuonYy6tPfp/AQblSVA4ADChVyFLGfPaQIECpGAAKxXnIG2SX5JRQP7nB/4JgPNbA==} + '@dprint/linux-x64-glibc@0.50.1': + resolution: {integrity: sha512-EpW5KLekaq4hXmKBWWtfBgZ244S4C+vFmMOd1YaGi8+f0hmPTJzVWLdIgpO2ZwfPQ5iycaVI/JS514PQmXPOvg==} cpu: [x64] os: [linux] - '@dprint/linux-x64-musl@0.50.0': - resolution: {integrity: sha512-ChZf0BnS3S6BIfqAPgQKqEh/7vgD1xc0MpcFcTrvkVQHuSdCQu1XiqUN12agzxB+Y5Ml9exgzP8lYgNza7iXvw==} + '@dprint/linux-x64-musl@0.50.1': + resolution: {integrity: sha512-assISBbaKKL8LkjrIy/5tpE157MVW6HbyIKAjTtg3tPNM3lDn1oH3twuGtK9WBsN/VoEP3QMZVauolcUJT/VOg==} cpu: [x64] os: [linux] - '@dprint/win32-arm64@0.50.0': - resolution: {integrity: sha512-xSY607bRyIPG7UO3uRa5c5wTGHKJqLUkQst85Hcz89EL/It6wswwUSNcywDydssN99HmSHop4fIf6FJTEpEp2g==} + '@dprint/win32-arm64@0.50.1': + resolution: {integrity: sha512-ZeaRMQYoFjrsO3lvI1SqzDWDGH1GGXWmNSeXvcFuAf2OgYQJWMBlLotCKiHNJ3uyYneoyhTg2tv9QkApNkZV4Q==} cpu: [arm64] os: [win32] - '@dprint/win32-x64@0.50.0': - resolution: {integrity: sha512-uGDjrK88LOet9a8pPRM9nKins93mK2NLozqL/hCNV88Nu5Nk0bBeVwRMAnPapjV3Jo+hsJOeq3Z1ibrq2c3v8w==} + '@dprint/win32-x64@0.50.1': + resolution: {integrity: sha512-pMm8l/hRZ9zYylKw/yCaYkSV3btYB9UyMDbWqyxNthkQ1gckWrk17VTI6WjwwQuHD4Iaz5JgAYLS36hlUzWkxA==} cpu: [x64] os: [win32] - '@esbuild/aix-ppc64@0.25.5': - resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.5': - resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.5': - resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.5': - resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.5': - resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.5': - resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.5': - resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.5': - resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.5': - resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.5': - resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.5': - resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.5': - resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.5': - resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.5': - resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.5': - resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.5': - resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.5': - resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.5': - resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.5': - resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.5': - resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.5': - resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.5': - resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.5': - resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.5': - resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.5': - resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -300,10 +306,6 @@ packages: resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.14.0': - resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.1': resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -312,20 +314,20 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.30.0': - resolution: {integrity: sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==} + '@eslint/js@9.32.0': + resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.3': - resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + '@eslint/plugin-kit@0.3.4': + resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@gerrit0/mini-shiki@3.7.0': - resolution: {integrity: sha512-7iY9wg4FWXmeoFJpUL2u+tsmh0d0jcEJHAIzVxl3TG4KL493JNnisdLAILZ77zcD+z3J0keEXZ+lFzUgzQzPDg==} + '@gerrit0/mini-shiki@3.9.0': + resolution: {integrity: sha512-p58r5PE/hIKtE7aYzeDYZr4DPrOidwoUFPX3p6rPEZWtb7zWX3b7Bu9LZ17XODFiRO4x/VzTE15KYNEaZ3khuw==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -386,17 +388,17 @@ packages: engines: {node: '>=18'} hasBin: true - '@shikijs/engine-oniguruma@3.7.0': - resolution: {integrity: sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw==} + '@shikijs/engine-oniguruma@3.9.1': + resolution: {integrity: sha512-WPlL/xqviwS3te4unSGGGfflKsuHLMI6tPdNYvgz/IygcBT6UiwDFSzjBKyebwi5GGSlXsjjdoJLIBnAplmEZw==} - '@shikijs/langs@3.7.0': - resolution: {integrity: sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ==} + '@shikijs/langs@3.9.1': + resolution: {integrity: sha512-Vyy2Yv9PP3Veh3VSsIvNncOR+O93wFsNYgN2B6cCCJlS7H9SKFYc55edsqernsg8WT/zam1cfB6llJsQWLnVhA==} - '@shikijs/themes@3.7.0': - resolution: {integrity: sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ==} + '@shikijs/themes@3.9.1': + resolution: {integrity: sha512-zAykkGECNICCMXpKeVvq04yqwaSuAIvrf8MjsU5bzskfg4XreU+O0B5wdNCYRixoB9snd3YlZ373WV5E/g5T9A==} - '@shikijs/types@3.7.0': - resolution: {integrity: sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg==} + '@shikijs/types@3.9.1': + resolution: {integrity: sha512-rqM3T7a0iM1oPKz9iaH/cVgNX9Vz1HERcUcXJ94/fulgVdwqfnhXzGxO4bLrAnh/o5CPLy3IcYedogfV+Ns0Qg==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -440,63 +442,63 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.35.0': - resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} + '@typescript-eslint/eslint-plugin@8.38.0': + resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.35.0 + '@typescript-eslint/parser': ^8.38.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.35.0': - resolution: {integrity: sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==} + '@typescript-eslint/parser@8.38.0': + resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/project-service@8.35.0': - resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} + '@typescript-eslint/project-service@8.38.0': + resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.35.0': - resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} + '@typescript-eslint/scope-manager@8.38.0': + resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.35.0': - resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} + '@typescript-eslint/tsconfig-utils@8.38.0': + resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/type-utils@8.35.0': - resolution: {integrity: sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==} + '@typescript-eslint/type-utils@8.38.0': + resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.35.0': - resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} + '@typescript-eslint/types@8.38.0': + resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.35.0': - resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} + '@typescript-eslint/typescript-estree@8.38.0': + resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.35.0': - resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} + '@typescript-eslint/utils@8.38.0': + resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.35.0': - resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} + '@typescript-eslint/visitor-keys@8.38.0': + resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typestrong/fs-fixture-builder@https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe': @@ -692,8 +694,8 @@ packages: resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} engines: {node: '>=0.3.1'} - dprint@0.50.0: - resolution: {integrity: sha512-aNJhOQsUS5D9k/YkMUaLLniIpxEBUR0ZwT0RXGQV5YpaGwE2nx6FcKuVkC6wRaZXTr8X0NpV/2HFbcvNuI2jtA==} + dprint@0.50.1: + resolution: {integrity: sha512-s+kUyQp2rGpwsM3vVmXySOY3v1NjYyRpKfQZdP4rfNTz6zQuICSO6nqIXNm3YdK1MwNFR/EXSFMuE1YPuulhow==} hasBin: true eastasianwidth@0.2.0: @@ -719,8 +721,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - esbuild@0.25.5: - resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} engines: {node: '>=18'} hasBin: true @@ -749,8 +751,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.30.0: - resolution: {integrity: sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==} + eslint@9.32.0: + resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1295,15 +1297,15 @@ packages: typed-query-selector@2.12.0: resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} - typescript-eslint@8.35.0: - resolution: {integrity: sha512-uEnz70b7kBz6eg/j0Czy6K5NivaYopgxRjsnAJ2Fx5oTLo3wefTHIbL7AkQr1+7tJCRVpTs/wiM8JR/11Loq9A==} + typescript-eslint@8.38.0: + resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} hasBin: true @@ -1398,111 +1400,114 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@dprint/darwin-arm64@0.50.0': + '@dprint/darwin-arm64@0.50.1': optional: true - '@dprint/darwin-x64@0.50.0': + '@dprint/darwin-x64@0.50.1': optional: true - '@dprint/linux-arm64-glibc@0.50.0': + '@dprint/linux-arm64-glibc@0.50.1': optional: true - '@dprint/linux-arm64-musl@0.50.0': + '@dprint/linux-arm64-musl@0.50.1': optional: true - '@dprint/linux-riscv64-glibc@0.50.0': + '@dprint/linux-riscv64-glibc@0.50.1': optional: true - '@dprint/linux-x64-glibc@0.50.0': + '@dprint/linux-x64-glibc@0.50.1': optional: true - '@dprint/linux-x64-musl@0.50.0': + '@dprint/linux-x64-musl@0.50.1': optional: true - '@dprint/win32-arm64@0.50.0': + '@dprint/win32-arm64@0.50.1': optional: true - '@dprint/win32-x64@0.50.0': + '@dprint/win32-x64@0.50.1': optional: true - '@esbuild/aix-ppc64@0.25.5': + '@esbuild/aix-ppc64@0.25.8': optional: true - '@esbuild/android-arm64@0.25.5': + '@esbuild/android-arm64@0.25.8': optional: true - '@esbuild/android-arm@0.25.5': + '@esbuild/android-arm@0.25.8': optional: true - '@esbuild/android-x64@0.25.5': + '@esbuild/android-x64@0.25.8': optional: true - '@esbuild/darwin-arm64@0.25.5': + '@esbuild/darwin-arm64@0.25.8': optional: true - '@esbuild/darwin-x64@0.25.5': + '@esbuild/darwin-x64@0.25.8': optional: true - '@esbuild/freebsd-arm64@0.25.5': + '@esbuild/freebsd-arm64@0.25.8': optional: true - '@esbuild/freebsd-x64@0.25.5': + '@esbuild/freebsd-x64@0.25.8': optional: true - '@esbuild/linux-arm64@0.25.5': + '@esbuild/linux-arm64@0.25.8': optional: true - '@esbuild/linux-arm@0.25.5': + '@esbuild/linux-arm@0.25.8': optional: true - '@esbuild/linux-ia32@0.25.5': + '@esbuild/linux-ia32@0.25.8': optional: true - '@esbuild/linux-loong64@0.25.5': + '@esbuild/linux-loong64@0.25.8': optional: true - '@esbuild/linux-mips64el@0.25.5': + '@esbuild/linux-mips64el@0.25.8': optional: true - '@esbuild/linux-ppc64@0.25.5': + '@esbuild/linux-ppc64@0.25.8': optional: true - '@esbuild/linux-riscv64@0.25.5': + '@esbuild/linux-riscv64@0.25.8': optional: true - '@esbuild/linux-s390x@0.25.5': + '@esbuild/linux-s390x@0.25.8': optional: true - '@esbuild/linux-x64@0.25.5': + '@esbuild/linux-x64@0.25.8': optional: true - '@esbuild/netbsd-arm64@0.25.5': + '@esbuild/netbsd-arm64@0.25.8': optional: true - '@esbuild/netbsd-x64@0.25.5': + '@esbuild/netbsd-x64@0.25.8': optional: true - '@esbuild/openbsd-arm64@0.25.5': + '@esbuild/openbsd-arm64@0.25.8': optional: true - '@esbuild/openbsd-x64@0.25.5': + '@esbuild/openbsd-x64@0.25.8': optional: true - '@esbuild/sunos-x64@0.25.5': + '@esbuild/openharmony-arm64@0.25.8': optional: true - '@esbuild/win32-arm64@0.25.5': + '@esbuild/sunos-x64@0.25.8': optional: true - '@esbuild/win32-ia32@0.25.5': + '@esbuild/win32-arm64@0.25.8': optional: true - '@esbuild/win32-x64@0.25.5': + '@esbuild/win32-ia32@0.25.8': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.30.0)': + '@esbuild/win32-x64@0.25.8': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0)': dependencies: - eslint: 9.30.0 + eslint: 9.32.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -1517,10 +1522,6 @@ snapshots: '@eslint/config-helpers@0.3.0': {} - '@eslint/core@0.14.0': - dependencies: - '@types/json-schema': 7.0.15 - '@eslint/core@0.15.1': dependencies: '@types/json-schema': 7.0.15 @@ -1539,21 +1540,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.30.0': {} + '@eslint/js@9.32.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.3': + '@eslint/plugin-kit@0.3.4': dependencies: '@eslint/core': 0.15.1 levn: 0.4.1 - '@gerrit0/mini-shiki@3.7.0': + '@gerrit0/mini-shiki@3.9.0': dependencies: - '@shikijs/engine-oniguruma': 3.7.0 - '@shikijs/langs': 3.7.0 - '@shikijs/themes': 3.7.0 - '@shikijs/types': 3.7.0 + '@shikijs/engine-oniguruma': 3.9.1 + '@shikijs/langs': 3.9.1 + '@shikijs/themes': 3.9.1 + '@shikijs/types': 3.9.1 '@shikijs/vscode-textmate': 10.0.2 '@humanfs/core@0.19.1': {} @@ -1617,20 +1618,20 @@ snapshots: - bare-buffer - supports-color - '@shikijs/engine-oniguruma@3.7.0': + '@shikijs/engine-oniguruma@3.9.1': dependencies: - '@shikijs/types': 3.7.0 + '@shikijs/types': 3.9.1 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.7.0': + '@shikijs/langs@3.9.1': dependencies: - '@shikijs/types': 3.7.0 + '@shikijs/types': 3.9.1 - '@shikijs/themes@3.7.0': + '@shikijs/themes@3.9.1': dependencies: - '@shikijs/types': 3.7.0 + '@shikijs/types': 3.9.1 - '@shikijs/types@3.7.0': + '@shikijs/types@3.9.1': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -1673,96 +1674,97 @@ snapshots: '@types/node': 18.19.113 optional: true - '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3))(eslint@9.30.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.35.0(eslint@9.30.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.35.0 - '@typescript-eslint/type-utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.35.0 - eslint: 9.30.0 + '@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.38.0 + eslint: 9.32.0 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.35.0 - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.35.0 + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.38.0 debug: 4.4.1(supports-color@8.1.1) - eslint: 9.30.0 - typescript: 5.8.3 + eslint: 9.32.0 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.38.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) - '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2) + '@typescript-eslint/types': 8.38.0 debug: 4.4.1(supports-color@8.1.1) - typescript: 5.8.3 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.35.0': + '@typescript-eslint/scope-manager@8.38.0': dependencies: - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/visitor-keys': 8.35.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/visitor-keys': 8.38.0 - '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)': dependencies: - typescript: 5.8.3 + typescript: 5.9.2 - '@typescript-eslint/type-utils@8.35.0(eslint@9.30.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.38.0(eslint@9.32.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) debug: 4.4.1(supports-color@8.1.1) - eslint: 9.30.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + eslint: 9.32.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.35.0': {} + '@typescript-eslint/types@8.38.0': {} - '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/visitor-keys': 8.35.0 + '@typescript-eslint/project-service': 8.38.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2) + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/visitor-keys': 8.38.0 debug: 4.4.1(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.35.0(eslint@9.30.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.38.0(eslint@9.32.0)(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.0) - '@typescript-eslint/scope-manager': 8.35.0 - '@typescript-eslint/types': 8.35.0 - '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) - eslint: 9.30.0 - typescript: 5.8.3 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) + '@typescript-eslint/scope-manager': 8.38.0 + '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) + eslint: 9.32.0 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.35.0': + '@typescript-eslint/visitor-keys@8.38.0': dependencies: - '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/types': 8.38.0 eslint-visitor-keys: 4.2.1 '@typestrong/fs-fixture-builder@https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe': {} @@ -1895,14 +1897,14 @@ snapshots: convert-source-map@2.0.0: {} - cosmiconfig@9.0.0(typescript@5.8.3): + cosmiconfig@9.0.0(typescript@5.9.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.2 cross-spawn@7.0.6: dependencies: @@ -1932,17 +1934,17 @@ snapshots: diff@7.0.0: {} - dprint@0.50.0: + dprint@0.50.1: optionalDependencies: - '@dprint/darwin-arm64': 0.50.0 - '@dprint/darwin-x64': 0.50.0 - '@dprint/linux-arm64-glibc': 0.50.0 - '@dprint/linux-arm64-musl': 0.50.0 - '@dprint/linux-riscv64-glibc': 0.50.0 - '@dprint/linux-x64-glibc': 0.50.0 - '@dprint/linux-x64-musl': 0.50.0 - '@dprint/win32-arm64': 0.50.0 - '@dprint/win32-x64': 0.50.0 + '@dprint/darwin-arm64': 0.50.1 + '@dprint/darwin-x64': 0.50.1 + '@dprint/linux-arm64-glibc': 0.50.1 + '@dprint/linux-arm64-musl': 0.50.1 + '@dprint/linux-riscv64-glibc': 0.50.1 + '@dprint/linux-x64-glibc': 0.50.1 + '@dprint/linux-x64-musl': 0.50.1 + '@dprint/win32-arm64': 0.50.1 + '@dprint/win32-x64': 0.50.1 eastasianwidth@0.2.0: {} @@ -1962,33 +1964,34 @@ snapshots: dependencies: is-arrayish: 0.2.1 - esbuild@0.25.5: + esbuild@0.25.8: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.5 - '@esbuild/android-arm': 0.25.5 - '@esbuild/android-arm64': 0.25.5 - '@esbuild/android-x64': 0.25.5 - '@esbuild/darwin-arm64': 0.25.5 - '@esbuild/darwin-x64': 0.25.5 - '@esbuild/freebsd-arm64': 0.25.5 - '@esbuild/freebsd-x64': 0.25.5 - '@esbuild/linux-arm': 0.25.5 - '@esbuild/linux-arm64': 0.25.5 - '@esbuild/linux-ia32': 0.25.5 - '@esbuild/linux-loong64': 0.25.5 - '@esbuild/linux-mips64el': 0.25.5 - '@esbuild/linux-ppc64': 0.25.5 - '@esbuild/linux-riscv64': 0.25.5 - '@esbuild/linux-s390x': 0.25.5 - '@esbuild/linux-x64': 0.25.5 - '@esbuild/netbsd-arm64': 0.25.5 - '@esbuild/netbsd-x64': 0.25.5 - '@esbuild/openbsd-arm64': 0.25.5 - '@esbuild/openbsd-x64': 0.25.5 - '@esbuild/sunos-x64': 0.25.5 - '@esbuild/win32-arm64': 0.25.5 - '@esbuild/win32-ia32': 0.25.5 - '@esbuild/win32-x64': 0.25.5 + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 escalade@3.2.0: {} @@ -2011,16 +2014,16 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.30.0: + eslint@9.32.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.14.0 + '@eslint/core': 0.15.1 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.30.0 - '@eslint/plugin-kit': 0.3.3 + '@eslint/js': 9.32.0 + '@eslint/plugin-kit': 0.3.4 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -2467,11 +2470,11 @@ snapshots: - supports-color - utf-8-validate - puppeteer@24.11.1(typescript@5.8.3): + puppeteer@24.11.1(typescript@5.9.2): dependencies: '@puppeteer/browsers': 2.10.5 chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) - cosmiconfig: 9.0.0(typescript@5.8.3) + cosmiconfig: 9.0.0(typescript@5.9.2) devtools-protocol: 0.0.1464554 puppeteer-core: 24.11.1 typed-query-selector: 2.12.0 @@ -2605,15 +2608,15 @@ snapshots: dependencies: is-number: 7.0.0 - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.9.2): dependencies: - typescript: 5.8.3 + typescript: 5.9.2 tslib@2.8.1: {} tsx@4.20.3: dependencies: - esbuild: 0.25.5 + esbuild: 0.25.8 get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 @@ -2624,17 +2627,18 @@ snapshots: typed-query-selector@2.12.0: {} - typescript-eslint@8.35.0(eslint@9.30.0)(typescript@5.8.3): + typescript-eslint@8.38.0(eslint@9.32.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.30.0)(typescript@5.8.3))(eslint@9.30.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.35.0(eslint@9.30.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.35.0(eslint@9.30.0)(typescript@5.8.3) - eslint: 9.30.0 - typescript: 5.8.3 + '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + eslint: 9.32.0 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - typescript@5.8.3: {} + typescript@5.9.2: {} uc.micro@2.1.0: {} From 47b5e98d04b80f177050013db321aaa1c281fbb4 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 1 Aug 2025 08:52:41 -0600 Subject: [PATCH 076/119] Fix auto discovery of entry points Resolves #2988 --- CHANGELOG.md | 8 +++ src/lib/application.ts | 6 -- src/lib/converter/factories/symbol-id.ts | 72 +----------------------- src/lib/utils/declaration-maps.ts | 71 +++++++++++++++++++++++ src/lib/utils/entry-point.ts | 19 ++++++- src/lib/utils/index.ts | 1 + 6 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 src/lib/utils/declaration-maps.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0cca41a..e54923748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ title: Changelog ## Unreleased +### Features + +- Add support for TypeScript 5.9, #2989. + +### Bug Fixes + +- Fixed automatic discovery of entry points when not running in packages mode, #2988. + ## v0.28.8 (2025-07-28) ### Features diff --git a/src/lib/application.ts b/src/lib/application.ts index 779b3310a..68f3afb35 100644 --- a/src/lib/application.ts +++ b/src/lib/application.ts @@ -43,7 +43,6 @@ import { Outputs } from "./output/output.js"; import { validateMergeModuleWith } from "./validation/unusedMergeModuleWith.js"; import { diagnostic, diagnostics } from "./utils/loggers.js"; import { ValidatingFileRegistry } from "./utils/ValidatingFileRegistry.js"; -import { addInferredDeclarationMapPaths } from "./converter/factories/symbol-id.js"; import { Internationalization } from "./internationalization/internationalization.js"; const packageInfo = JSON.parse( @@ -822,11 +821,6 @@ export class Application extends AbstractComponent< continue; } - addInferredDeclarationMapPaths( - opts.getCompilerOptions(), - opts.getFileNames(), - ); - projectsToConvert.push({ dir, options: opts }); } diff --git a/src/lib/converter/factories/symbol-id.ts b/src/lib/converter/factories/symbol-id.ts index ee7b4b74b..75c45cdfe 100644 --- a/src/lib/converter/factories/symbol-id.ts +++ b/src/lib/converter/factories/symbol-id.ts @@ -1,12 +1,9 @@ import { ReflectionSymbolId } from "#models"; -import { findPackageForPath, getCommonDirectory, getQualifiedName, normalizePath, readFile } from "#node-utils"; -import { type NormalizedPath, Validation } from "#utils"; -import { existsSync } from "fs"; -import { join, relative, resolve } from "node:path"; +import { findPackageForPath, getQualifiedName, normalizePath, resolveDeclarationMaps } from "#node-utils"; +import { type NormalizedPath } from "#utils"; +import { relative } from "node:path"; import ts from "typescript"; -const declarationMapCache = new Map(); - let transientCount = 0; const transientIds = new WeakMap(); @@ -51,66 +48,3 @@ export function createSymbolIdImpl(symbol: ts.Symbol, declaration?: ts.Declarati return id; } - -function resolveDeclarationMaps(file: string): string { - if (!/\.d\.[cm]?ts$/.test(file)) return file; - if (declarationMapCache.has(file)) return declarationMapCache.get(file)!; - - const mapFile = file + ".map"; - if (!existsSync(mapFile)) return file; - - let sourceMap: unknown; - try { - sourceMap = JSON.parse(readFile(mapFile)) as unknown; - } catch { - return file; - } - - if ( - Validation.validate( - { - file: String, - sourceRoot: Validation.optional(String), - sources: [Array, String], - }, - sourceMap, - ) - ) { - // There's a pretty large assumption in here that we only have - // 1 source file per js file. This is a pretty standard typescript approach, - // but people might do interesting things with transpilation that could break this. - let source = sourceMap.sources[0]; - - // If we have a sourceRoot, trim any leading slash from the source, and join them - // Similar to how it's done at https://github.com/mozilla/source-map/blob/58819f09018d56ef84dc41ba9c93f554e0645169/lib/util.js#L412 - if (sourceMap.sourceRoot !== undefined) { - source = source.replace(/^\//, ""); - source = join(sourceMap.sourceRoot, source); - } - - const result = resolve(mapFile, "..", source); - declarationMapCache.set(file, result); - return result; - } - - return file; -} - -// See also: inferEntryPoints in entry-point.ts -export function addInferredDeclarationMapPaths( - opts: ts.CompilerOptions, - files: readonly string[], -) { - const rootDir = opts.rootDir || getCommonDirectory(files); - const declDir = opts.declarationDir || opts.outDir || rootDir; - - for (const file of files) { - const mapFile = normalizePath( - resolve(declDir, relative(rootDir, file)).replace( - /\.([cm]?[tj]s)x?$/, - ".d.$1", - ), - ); - declarationMapCache.set(mapFile, file); - } -} diff --git a/src/lib/utils/declaration-maps.ts b/src/lib/utils/declaration-maps.ts new file mode 100644 index 000000000..a6183d6f7 --- /dev/null +++ b/src/lib/utils/declaration-maps.ts @@ -0,0 +1,71 @@ +import type ts from "typescript"; +import { existsSync } from "fs"; +import { readFile } from "./fs.js"; +import { Validation } from "#utils"; +import { join, relative, resolve } from "path"; +import { getCommonDirectory, normalizePath } from "./paths.js"; + +const declarationMapCache = new Map(); + +export function resolveDeclarationMaps(file: string): string { + if (!/\.d\.[cm]?ts$/.test(file)) return file; + if (declarationMapCache.has(file)) return declarationMapCache.get(file)!; + + const mapFile = file + ".map"; + if (!existsSync(mapFile)) return file; + + let sourceMap: unknown; + try { + sourceMap = JSON.parse(readFile(mapFile)) as unknown; + } catch { + return file; + } + + if ( + Validation.validate( + { + file: String, + sourceRoot: Validation.optional(String), + sources: [Array, String], + }, + sourceMap, + ) + ) { + // There's a pretty large assumption in here that we only have + // 1 source file per js file. This is a pretty standard typescript approach, + // but people might do interesting things with transpilation that could break this. + let source = sourceMap.sources[0]; + + // If we have a sourceRoot, trim any leading slash from the source, and join them + // Similar to how it's done at https://github.com/mozilla/source-map/blob/58819f09018d56ef84dc41ba9c93f554e0645169/lib/util.js#L412 + if (sourceMap.sourceRoot !== undefined) { + source = source.replace(/^\//, ""); + source = join(sourceMap.sourceRoot, source); + } + + const result = resolve(mapFile, "..", source); + declarationMapCache.set(file, result); + return result; + } + + return file; +} + +// See also: inferEntryPoints in entry-point.ts +export function addInferredDeclarationMapPaths( + opts: ts.CompilerOptions, + files: readonly string[], +) { + const rootDir = opts.rootDir || getCommonDirectory(files); + const declDir = opts.declarationDir || opts.outDir || rootDir; + + for (const file of files) { + const mapFile = normalizePath( + resolve(declDir, relative(rootDir, file)).replace( + /\.([cm]?[tj]s)x?$/, + ".d.$1", + ), + ); + declarationMapCache.set(mapFile, file); + } +} diff --git a/src/lib/utils/entry-point.ts b/src/lib/utils/entry-point.ts index 4a28627e1..0e1aca810 100644 --- a/src/lib/utils/entry-point.ts +++ b/src/lib/utils/entry-point.ts @@ -6,6 +6,7 @@ import { deriveRootDir, getCommonDirectory, MinimatchSet, nicePath, normalizePat import type { Options } from "./options/index.js"; import { discoverPackageJson, glob, inferPackageEntryPointPaths, isDir } from "./fs.js"; import { assertNever, type GlobString, i18n, type Logger, type NormalizedPath } from "#utils"; +import { addInferredDeclarationMapPaths, resolveDeclarationMaps } from "./declaration-maps.js"; /** * Defines how entry points are interpreted. @@ -65,7 +66,7 @@ export function inferEntryPoints(logger: Logger, options: Options, programs?: ts options, ); - // See also: addInferredDeclarationMapPaths in ReflectionSymbolId + // See also: addInferredDeclarationMapPaths in symbol-id factory const jsToTsSource = new Map(); for (const program of programs) { const opts = program.getCompilerOptions(); @@ -86,7 +87,7 @@ export function inferEntryPoints(logger: Logger, options: Options, programs?: ts for (const [name, path] of pathEntries) { // Strip leading ./ from the display name const displayName = name.replace(/^\.\/?/, ""); - const targetPath = jsToTsSource.get(path) || path; + const targetPath = jsToTsSource.get(path) || resolveDeclarationMaps(path) || path; const program = programs.find((p) => p.getSourceFile(targetPath)); if (program) { @@ -107,6 +108,10 @@ export function inferEntryPoints(logger: Logger, options: Options, programs?: ts return []; } + logger.verbose( + `Inferred entry points to be:\n\t${entryPoints.map(e => nicePath(e.sourceFile.fileName)).join("\n\t")}`, + ); + return entryPoints; } @@ -413,6 +418,11 @@ function getEntryPrograms( projectReferences: options.getProjectReferences(), }); + addInferredDeclarationMapPaths( + options.getCompilerOptions(), + options.getFileNames(), + ); + const programs = [rootProgram]; // This might be a solution style tsconfig, in which case we need to add a program for each // reference so that the converter can look through each of these. @@ -433,6 +443,11 @@ function getEntryPrograms( projectReferences: ref.commandLine.projectReferences, }), ); + + addInferredDeclarationMapPaths( + ref.commandLine.options, + ref.commandLine.fileNames, + ); } } diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 605a8b406..a48c4f739 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -44,6 +44,7 @@ export type { SortStrategy } from "./sort.js"; export * from "./entry-point.js"; +export * from "./declaration-maps.js"; export * from "./highlighter.js"; export * from "./html.js"; export * from "./tsconfig.js"; From 303eb384e47045730a7c90b62db9151793092547 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 1 Aug 2025 08:57:33 -0600 Subject: [PATCH 077/119] Fix discovery of package.json when entry points are globbed Resolves #2985 --- CHANGELOG.md | 1 + src/lib/converter/plugins/PackagePlugin.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e54923748..979f338e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ title: Changelog ### Bug Fixes - Fixed automatic discovery of entry points when not running in packages mode, #2988. +- Fixed discovery of package.json file when running with entry points containing a glob, #2985. ## v0.28.8 (2025-07-28) diff --git a/src/lib/converter/plugins/PackagePlugin.ts b/src/lib/converter/plugins/PackagePlugin.ts index 8dbe44b41..5225e910b 100644 --- a/src/lib/converter/plugins/PackagePlugin.ts +++ b/src/lib/converter/plugins/PackagePlugin.ts @@ -8,9 +8,9 @@ import { ConverterEvents } from "../converter-events.js"; import type { Converter } from "../converter.js"; import { type GlobString, i18n, MinimalSourceFile, type NormalizedPath, NormalizedPathUtils } from "#utils"; import { + deriveRootDir, discoverPackageJson, type EntryPointStrategy, - getCommonDirectory, nicePath, normalizePath, Option, @@ -79,7 +79,7 @@ export class PackagePlugin extends ConverterComponent { this.packageJson = undefined; const dirName = this.application.options.packageDir ?? - Path.resolve(getCommonDirectory(this.entryPoints.map(g => `${g}/`))); + Path.resolve(deriveRootDir(this.entryPoints)); this.application.logger.verbose( `Begin package.json search at ${nicePath(dirName)}`, From f1aee8556238f1b675ff928552c3f29c186fff69 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 1 Aug 2025 09:26:05 -0600 Subject: [PATCH 078/119] Be even more restrictive with parent links Resolves #2982 --- CHANGELOG.md | 1 + src/lib/converter/plugins/ImplementsPlugin.ts | 13 +- src/test/converter2/renderer/gh2982.ts | 6 + src/test/converter2/renderer/index.ts | 1 + .../specs/interfaces/GH2982.TMXDataNode.json | 352 ++++++++++++++++++ src/test/renderer/specs/modules.json | 60 ++- src/test/renderer/specs/modules/GH2982.json | 208 +++++++++++ .../renderer/specs/types/GH2982.TMXNode.json | 120 ++++++ 8 files changed, 744 insertions(+), 17 deletions(-) create mode 100644 src/test/converter2/renderer/gh2982.ts create mode 100644 src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json create mode 100644 src/test/renderer/specs/modules/GH2982.json create mode 100644 src/test/renderer/specs/types/GH2982.TMXNode.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 979f338e7..49ed1e43d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ title: Changelog ### Bug Fixes +- Fixed bug introduced in 0.28.8 where TypeDoc could not render docs when members inherited from a complex type alias, #2982. - Fixed automatic discovery of entry points when not running in packages mode, #2988. - Fixed discovery of package.json file when running with entry points containing a glob, #2985. diff --git a/src/lib/converter/plugins/ImplementsPlugin.ts b/src/lib/converter/plugins/ImplementsPlugin.ts index 332d1640c..cab484371 100644 --- a/src/lib/converter/plugins/ImplementsPlugin.ts +++ b/src/lib/converter/plugins/ImplementsPlugin.ts @@ -172,19 +172,24 @@ export class ImplementsPlugin extends ConverterComponent { // serialization/deserialization, might point to an unexpected location. (See the mixin // converter tests, I suspect this might actually be an indication of something else slightly // broken there, but don't want to spend more time with this right now.) + // #2982, even more unfortunately, we only want to keep the link if it is pointing to a reflection + // which will receive a link during rendering. + const isValidRef = (ref: ReferenceType) => + ref.reflection && !ref.reflection.parent?.kindOf(ReflectionKind.TypeLiteral); + for (const child of reflection.children || []) { - if (child.inheritedFrom && !child.inheritedFrom.reflection) { + if (child.inheritedFrom && !isValidRef(child.inheritedFrom)) { child.inheritedFrom = ReferenceType.createBrokenReference(child.inheritedFrom.name, project); } - if (child.overwrites && !child.overwrites.reflection) { + if (child.overwrites && !isValidRef(child.overwrites)) { child.overwrites = ReferenceType.createBrokenReference(child.overwrites.name, project); } for (const childSig of child.getAllSignatures()) { - if (childSig.inheritedFrom && !childSig.inheritedFrom.reflection) { + if (childSig.inheritedFrom && !isValidRef(childSig.inheritedFrom)) { childSig.inheritedFrom = ReferenceType.createBrokenReference(childSig.inheritedFrom.name, project); } - if (childSig.overwrites && !childSig.overwrites.reflection) { + if (childSig.overwrites && !isValidRef(childSig.overwrites)) { childSig.overwrites = ReferenceType.createBrokenReference(childSig.overwrites.name, project); } } diff --git a/src/test/converter2/renderer/gh2982.ts b/src/test/converter2/renderer/gh2982.ts new file mode 100644 index 000000000..678f957ec --- /dev/null +++ b/src/test/converter2/renderer/gh2982.ts @@ -0,0 +1,6 @@ +export type TMXNode = {} & { + base: T; +}; + +export interface TMXDataNode extends TMXNode<{ extra: any }> { +} diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 8d354b5ea..76142a6df 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -135,3 +135,4 @@ export interface DisabledGroups { } export * as ExpandType from "./expandType"; +export * as GH2982 from "./gh2982"; diff --git a/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json b/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json new file mode 100644 index 000000000..59e5f0c17 --- /dev/null +++ b/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json @@ -0,0 +1,352 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": [ + { + "li": { + "tag": "a", + "props": { + "href": "../modules/GH2982.json" + }, + "children": "GH2982" + } + }, + { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "TMXDataNode" + } + } + ] + }, + { + "h1": "Interface TMXDataNode" + } + ] + }, + { + "div.tsd-signature": [ + { + "span.tsd-signature-keyword": "interface" + }, + " ", + { + "span.tsd-kind-interface": "TMXDataNode" + }, + " ", + { + "span.tsd-signature-symbol": "{" + }, + { + "br": [] + }, + "    ", + { + "tag": "a.tsd-kind-property", + "props": { + "href": "#base" + }, + "children": "base" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-symbol": "{" + }, + " ", + { + "span.tsd-kind-property": "extra" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "any" + }, + " ", + { + "span.tsd-signature-symbol": "}" + }, + { + "span.tsd-signature-symbol": ";" + }, + { + "br": [] + }, + { + "span.tsd-signature-symbol": "}" + } + ] + }, + { + "tag": "section.tsd-panel.tsd-hierarchy", + "props": { + "data-refl": "122" + }, + "children": [ + { + "h4": [ + "Hierarchy (", + { + "tag": "a", + "props": { + "href": "../hierarchy.html#GH2982.TMXDataNode" + }, + "children": "View Summary" + }, + ")" + ] + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": [ + { + "tag": "a.tsd-signature-type.tsd-kind-type-alias", + "props": { + "href": "../types/GH2982.TMXNode.json" + }, + "children": "TMXNode" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "span.tsd-signature-symbol": "{" + }, + " ", + { + "span.tsd-kind-property": "extra" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "any" + }, + " ", + { + "span.tsd-signature-symbol": "}" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": { + "span.tsd-hierarchy-target": "TMXDataNode" + } + } + } + ] + } + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh2982.ts" + }, + "children": "gh2982.ts:5" + } + ] + } + } + }, + { + "section.tsd-panel-group.tsd-index-group": { + "section.tsd-panel.tsd-index-panel": { + "tag": "details.tsd-index-content.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary.tsd-index-summary": { + "h5.tsd-index-heading.uppercase": "Index" + } + }, + { + "div.tsd-accordion-details": { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Properties" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link.tsd-is-inherited", + "props": { + "href": "#base" + }, + "children": { + "span": "base" + } + }, + "\n" + ] + } + ] + } + } + ] + } + } + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Properties" + }, + "children": { + "h2": "Properties" + } + }, + { + "section": { + "section.tsd-panel.tsd-member.tsd-is-inherited": [ + { + "h3.tsd-anchor-link#base": [ + { + "span": "base" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#base", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-signature": [ + { + "span.tsd-kind-property": "base" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-symbol": "{" + }, + " ", + { + "span.tsd-kind-property": "extra" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "any" + }, + " ", + { + "span.tsd-signature-symbol": "}" + } + ] + }, + { + "aside.tsd-sources": [ + { + "p": "Inherited from TMXNode.base" + }, + { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh2982.ts" + }, + "children": "gh2982.ts:2" + } + ] + } + } + ] + } + ] + } + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Properties" + }, + "children": "Properties" + }, + { + "div": { + "tag": "a.tsd-is-inherited", + "props": { + "href": "#base" + }, + "children": { + "span": "base" + } + } + } + ] + } + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index 21b6084dd..e56b3d3d5 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -106,6 +106,29 @@ ] } }, + { + "dd.tsd-member-summary": [] + }, + { + "dt.tsd-member-summary#gh2982": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "modules/GH2982.json" + }, + "children": "GH2982" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#gh2982", + "aria-label": "Permalink" + } + } + ] + } + }, { "dd.tsd-member-summary": [] } @@ -551,21 +574,32 @@ "children": "Namespaces" }, { - "div": { - "tag": "a", - "props": { - "href": "#expandtype" + "div": [ + { + "tag": "a", + "props": { + "href": "#expandtype" + }, + "children": { + "span": [ + "Expand", + { + "wbr": [] + }, + "Type" + ] + } }, - "children": { - "span": [ - "Expand", - { - "wbr": [] - }, - "Type" - ] + { + "tag": "a", + "props": { + "href": "#gh2982" + }, + "children": { + "span": "GH2982" + } } - } + ] } ] }, diff --git a/src/test/renderer/specs/modules/GH2982.json b/src/test/renderer/specs/modules/GH2982.json new file mode 100644 index 000000000..40c28d9dc --- /dev/null +++ b/src/test/renderer/specs/modules/GH2982.json @@ -0,0 +1,208 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "GH2982" + } + } + }, + { + "h1": "Namespace GH2982" + } + ] + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Interfaces" + }, + "children": { + "h2": "Interfaces" + } + }, + { + "dl.tsd-member-summaries": [ + { + "dt.tsd-member-summary#tmxdatanode": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "../interfaces/GH2982.TMXDataNode.json" + }, + "children": "TMXDataNode" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#tmxdatanode", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + } + ] + } + ] + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Type Aliases" + }, + "children": { + "h2": "Type Aliases" + } + }, + { + "dl.tsd-member-summaries": [ + { + "dt.tsd-member-summary#tmxnode": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "../types/GH2982.TMXNode.json" + }, + "children": "TMXNode" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#tmxnode", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + } + ] + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": [ + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Interfaces" + }, + "children": "Interfaces" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#tmxdatanode" + }, + "children": { + "span": [ + "TMX", + { + "wbr": [] + }, + "Data", + { + "wbr": [] + }, + "Node" + ] + } + } + } + ] + }, + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Type Aliases" + }, + "children": "Type Aliases" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#tmxnode" + }, + "children": { + "span": [ + "TMX", + { + "wbr": [] + }, + "Node" + ] + } + } + } + ] + } + ] + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/types/GH2982.TMXNode.json b/src/test/renderer/specs/types/GH2982.TMXNode.json new file mode 100644 index 000000000..31febec59 --- /dev/null +++ b/src/test/renderer/specs/types/GH2982.TMXNode.json @@ -0,0 +1,120 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": [ + { + "li": { + "tag": "a", + "props": { + "href": "../modules/GH2982.json" + }, + "children": "GH2982" + } + }, + { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "TMXNode" + } + } + ] + }, + { + "h1": "Type Alias TMXNode" + } + ] + }, + { + "div.tsd-signature": [ + { + "span.tsd-kind-type-alias": "TMXNode" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-symbol": "{}" + }, + " ", + { + "span.tsd-signature-symbol": "&" + }, + " ", + { + "span.tsd-signature-symbol": "{" + }, + " ", + { + "span.tsd-kind-property": "base" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "tag": "a.tsd-signature-type.tsd-kind-type-parameter", + "props": { + "href": "#t" + }, + "children": "T" + }, + " ", + { + "span.tsd-signature-symbol": "}" + } + ] + }, + { + "section.tsd-panel": [ + { + "h4": "Type Parameters" + }, + { + "ul.tsd-type-parameter-list": { + "li": { + "span#t": { + "span.tsd-kind-type-parameter": "T" + } + } + } + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh2982.ts" + }, + "children": "gh2982.ts:1" + } + ] + } + } + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": [] + } + } + ] +} From 344f74ce459f7efb7888403937035abb3fcfe866 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 1 Aug 2025 09:55:10 -0600 Subject: [PATCH 079/119] Bump version to 0.28.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a49c7b42..98b45fece 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.8", + "version": "0.28.9", "homepage": "https://typedoc.org", "type": "module", "exports": { From e764a51f4b3ed35cd488eccd09498312d9502608 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Fri, 1 Aug 2025 15:55:59 +0000 Subject: [PATCH 080/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49ed1e43d..5a01b1d6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.9 (2025-08-01) + ### Features - Add support for TypeScript 5.9, #2989. From 7bdb05f7eb7cec6f809012831b16b54ba85f1220 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 9 Aug 2025 10:00:03 -0600 Subject: [PATCH 081/119] Fix inconsistent anchors for aliases Resolves #2990 --- CHANGELOG.md | 4 + scripts/rebuild_specs.js | 1 + .../default/partials/moduleReflection.tsx | 2 +- src/test/converter2/renderer/index.ts | 1 + .../specs/interfaces/GH2982.TMXDataNode.json | 2 +- src/test/renderer/specs/modules.json | 82 +++++++++++++++++++ 6 files changed, 90 insertions(+), 2 deletions(-) mode change 100644 => 100755 scripts/rebuild_specs.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a01b1d6d..1d2515f89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Bug Fixes + +- Fixed inconsistent anchors on module pages for re-exports, #2990. + ## v0.28.9 (2025-08-01) ### Features diff --git a/scripts/rebuild_specs.js b/scripts/rebuild_specs.js old mode 100644 new mode 100755 index cbdcba2dc..dd333c4a7 --- a/scripts/rebuild_specs.js +++ b/scripts/rebuild_specs.js @@ -1,3 +1,4 @@ +#!/usr/bin/env node // @ts-check "use strict"; diff --git a/src/lib/output/themes/default/partials/moduleReflection.tsx b/src/lib/output/themes/default/partials/moduleReflection.tsx index fb358841b..31418441d 100644 --- a/src/lib/output/themes/default/partials/moduleReflection.tsx +++ b/src/lib/output/themes/default/partials/moduleReflection.tsx @@ -75,7 +75,7 @@ export function moduleMemberSummary( context: DefaultThemeRenderContext, member: DeclarationReflection | DocumentReflection, ) { - const id = context.slugger.slug(member.name); + const id = member.isReference() ? context.getAnchor(member)! : context.slugger.slug(member.name); context.page.pageHeadings.push({ link: `#${id}`, text: getDisplayName(member), diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 76142a6df..2dbb7e53d 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -136,3 +136,4 @@ export interface DisabledGroups { export * as ExpandType from "./expandType"; export * as GH2982 from "./gh2982"; +export { box as boxAlias }; diff --git a/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json b/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json index 59e5f0c17..82cb71b0b 100644 --- a/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json +++ b/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json @@ -96,7 +96,7 @@ { "tag": "section.tsd-panel.tsd-hierarchy", "props": { - "data-refl": "122" + "data-refl": "123" }, "children": [ { diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index e56b3d3d5..5a189196d 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -516,6 +516,56 @@ ] } ] + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-References" + }, + "children": { + "h2": "References" + } + }, + { + "dl.tsd-member-summaries": [ + { + "dt.tsd-member-summary#boxalias": { + "span.tsd-member-summary-name": [ + { + "span": "boxAlias" + }, + { + "span": " → " + }, + { + "tag": "a", + "props": { + "href": "functions/box.json" + }, + "children": "box" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#boxalias", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + } + ] + } + ] } ] }, @@ -855,6 +905,38 @@ } } ] + }, + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-References" + }, + "children": "References" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#boxalias" + }, + "children": { + "span": [ + "box", + { + "wbr": [] + }, + "Alias" + ] + } + } + } + ] } ] } From 2118cc256ca0487cd9ce6a5d9e55b3f34b3f572d Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 9 Aug 2025 10:00:27 -0600 Subject: [PATCH 082/119] Avoid non-breaking spaces in renderer specs --- .../specs/interfaces/BaseInterface.json | 4 ++-- .../specs/interfaces/DisabledGroups.json | 4 ++-- .../ExpandType.ExpandedByDefault.json | 2 +- .../specs/interfaces/GH2982.TMXDataNode.json | 2 +- .../specs/interfaces/NoneCategory.json | 6 +++--- .../renderer/specs/interfaces/NoneGroup.json | 4 ++-- src/test/renderer/specs/modules.json | 2 +- .../specs/types/ExpandType.AExpanded.json | 6 +++--- .../specs/types/ExpandType.BExpanded.json | 6 +++--- .../specs/types/ExpandType.Expandable.json | 2 +- .../specs/types/ExpandType.Expandable2.json | 2 +- .../ExpandType.NestedBehavior1.AExpanded.json | 6 +++--- ...xpandType.NestedBehavior1.AllExpanded.json | 6 +++--- src/test/renderer/specs/types/Nested.json | 20 +++++++++---------- src/test/renderer/testRendererUtils.ts | 2 +- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/test/renderer/specs/interfaces/BaseInterface.json b/src/test/renderer/specs/interfaces/BaseInterface.json index 46980afe2..9ccfa435d 100644 --- a/src/test/renderer/specs/interfaces/BaseInterface.json +++ b/src/test/renderer/specs/interfaces/BaseInterface.json @@ -41,7 +41,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-call-signature", "props": { @@ -65,7 +65,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-call-signature", "props": { diff --git a/src/test/renderer/specs/interfaces/DisabledGroups.json b/src/test/renderer/specs/interfaces/DisabledGroups.json index 90d525305..14e3d537a 100644 --- a/src/test/renderer/specs/interfaces/DisabledGroups.json +++ b/src/test/renderer/specs/interfaces/DisabledGroups.json @@ -41,7 +41,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -62,7 +62,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-call-signature", "props": { diff --git a/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json b/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json index 4df2574ad..a0a0fbe17 100644 --- a/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json +++ b/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json @@ -52,7 +52,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json b/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json index 82cb71b0b..4a14d5c79 100644 --- a/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json +++ b/src/test/renderer/specs/interfaces/GH2982.TMXDataNode.json @@ -52,7 +52,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/interfaces/NoneCategory.json b/src/test/renderer/specs/interfaces/NoneCategory.json index f13e3d383..24d919665 100644 --- a/src/test/renderer/specs/interfaces/NoneCategory.json +++ b/src/test/renderer/specs/interfaces/NoneCategory.json @@ -41,7 +41,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -62,7 +62,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -83,7 +83,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/interfaces/NoneGroup.json b/src/test/renderer/specs/interfaces/NoneGroup.json index 8970f3e31..fdca9ae0d 100644 --- a/src/test/renderer/specs/interfaces/NoneGroup.json +++ b/src/test/renderer/specs/interfaces/NoneGroup.json @@ -41,7 +41,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -62,7 +62,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index 5a189196d..cc6e97839 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -541,7 +541,7 @@ "span": "boxAlias" }, { - "span": " → " + "span": " → " }, { "tag": "a", diff --git a/src/test/renderer/specs/types/ExpandType.AExpanded.json b/src/test/renderer/specs/types/ExpandType.AExpanded.json index 8b3b95b61..3fd838168 100644 --- a/src/test/renderer/specs/types/ExpandType.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.AExpanded.json @@ -61,7 +61,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -86,7 +86,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -111,7 +111,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/types/ExpandType.BExpanded.json b/src/test/renderer/specs/types/ExpandType.BExpanded.json index e8eff5cf4..2865052ec 100644 --- a/src/test/renderer/specs/types/ExpandType.BExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.BExpanded.json @@ -56,7 +56,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -81,7 +81,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -106,7 +106,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/types/ExpandType.Expandable.json b/src/test/renderer/specs/types/ExpandType.Expandable.json index be39fc09a..89de6658c 100644 --- a/src/test/renderer/specs/types/ExpandType.Expandable.json +++ b/src/test/renderer/specs/types/ExpandType.Expandable.json @@ -56,7 +56,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/types/ExpandType.Expandable2.json b/src/test/renderer/specs/types/ExpandType.Expandable2.json index 1660b2262..efcacd043 100644 --- a/src/test/renderer/specs/types/ExpandType.Expandable2.json +++ b/src/test/renderer/specs/types/ExpandType.Expandable2.json @@ -56,7 +56,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json index 678a54354..6ecd11223 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json @@ -70,7 +70,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -95,7 +95,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -120,7 +120,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json index 69dd59a7a..70b147fe8 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json @@ -65,7 +65,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -90,7 +90,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -115,7 +115,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { diff --git a/src/test/renderer/specs/types/Nested.json b/src/test/renderer/specs/types/Nested.json index add5b69d4..cef1f3e4d 100644 --- a/src/test/renderer/specs/types/Nested.json +++ b/src/test/renderer/specs/types/Nested.json @@ -55,7 +55,7 @@ { "br": [] }, - "    ", + " ", { "tag": "a.tsd-kind-property", "props": { @@ -73,7 +73,7 @@ { "br": [] }, - "        ", + " ", { "span.tsd-kind-property": "anotherValue" }, @@ -90,7 +90,7 @@ { "br": [] }, - "        ", + " ", { "span.tsd-kind-property": "emptyObject" }, @@ -107,7 +107,7 @@ { "br": [] }, - "        ", + " ", { "span.tsd-kind-property": "moreOptions" }, @@ -139,7 +139,7 @@ { "br": [] }, - "        ", + " ", { "span.tsd-kind-property": "value" }, @@ -156,7 +156,7 @@ { "br": [] }, - "    ", + " ", { "span.tsd-signature-symbol": "}" }, @@ -274,7 +274,7 @@ { "br": [] }, - "    ", + " ", { "span.tsd-kind-property": "anotherValue" }, @@ -291,7 +291,7 @@ { "br": [] }, - "    ", + " ", { "span.tsd-kind-property": "emptyObject" }, @@ -308,7 +308,7 @@ { "br": [] }, - "    ", + " ", { "span.tsd-kind-property": "moreOptions" }, @@ -340,7 +340,7 @@ { "br": [] }, - "    ", + " ", { "span.tsd-kind-property": "value" }, diff --git a/src/test/renderer/testRendererUtils.ts b/src/test/renderer/testRendererUtils.ts index 1ecdd812f..544e441a7 100644 --- a/src/test/renderer/testRendererUtils.ts +++ b/src/test/renderer/testRendererUtils.ts @@ -48,7 +48,7 @@ function collapseStrings(data: any[]): unknown { function renderElementToSnapshot(element: JsxChildren): unknown { if (typeof element === "string" || typeof element === "number" || typeof element === "bigint") { - return element.toString(); + return element.toString().replaceAll("\u00a0", " "); } if (!element || typeof element === "boolean") { From b0065957eea88187744e3cd3493d9b020ddc1409 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 9 Aug 2025 15:14:45 -0600 Subject: [PATCH 083/119] Do not parse markdown footnotes as references Resolves #2991 --- CHANGELOG.md | 1 + src/lib/converter/comments/textParser.ts | 8 +++++++- src/test/comments.test.ts | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d2515f89..a16013601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ title: Changelog ### Bug Fixes - Fixed inconsistent anchors on module pages for re-exports, #2990. +- Markdown references which appear to be footnotes will no longer be checked for links, #2991. ## v0.28.9 (2025-08-01) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 2289aaa22..4078b7aef 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -258,7 +258,13 @@ function checkReference(data: TextParserData): RelativeLink | undefined { while (/[ \t]/.test(token.text[lookahead])) { ++lookahead; } - if (token.text[lookahead] === "[") { + // #2991, we check that this reference also doesn't look like a footnote reference + // as it is unlikely that someone uses that syntax without intending for footnote behavior. + // This introduces a problem if someone has an [^ref] and doesn't intend for that to + // be interpreted as a footnote, but as a reference, but we can't have it both ways, + // and having people rename their reference to not be confused with a footnote isn't a + // horrible workaround. + if (token.text[lookahead] === "[" && token.text[lookahead + 1] !== "^") { while ( lookahead < token.text.length && /[^\n\]]/.test(token.text[lookahead]) diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index d44ab06e8..352cdba30 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1558,6 +1558,7 @@ describe("Comment Parser", () => { * [2]:<./example with space> * [3]: https://example.com * [4]: #hash + * [^footnote]: ./example.md */`); equal( @@ -1579,7 +1580,7 @@ describe("Comment Parser", () => { }, { kind: "text", - text: "\n[3]: https://example.com\n[4]: #hash", + text: "\n[3]: https://example.com\n[4]: #hash\n[^footnote]: ./example.md", }, ] satisfies CommentDisplayPart[], ); From 192d1b53864f4c21df131398c6b9719d75329277 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 9 Aug 2025 16:27:09 -0600 Subject: [PATCH 084/119] Remove test cases which use module instead of namespace Preemptively resolving breakage that will come from https://github.com/microsoft/TypeScript/issues/62211 --- src/test/converter/class/class.ts | 2 +- src/test/converter/class/specs-with-lump-categories.json | 4 ++-- src/test/converter/class/specs.json | 4 ++-- src/test/converter/enum/enum.ts | 2 +- src/test/converter/enum/specs.json | 4 ++-- src/test/converter/enum/specs.nodoc.json | 4 ++-- src/test/converter/function/function.ts | 2 +- src/test/converter/function/specs.json | 4 ++-- src/test/converter/interface/interface-implementation.ts | 2 +- src/test/converter/interface/specs.json | 2 +- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/converter/class/class.ts b/src/test/converter/class/class.ts index e46637b75..a1b693ce2 100644 --- a/src/test/converter/class/class.ts +++ b/src/test/converter/class/class.ts @@ -106,7 +106,7 @@ export interface TestSubClass { mergedMethod(); } -export module TestSubClass { +export namespace TestSubClass { /** * staticMergedMethod short text. */ diff --git a/src/test/converter/class/specs-with-lump-categories.json b/src/test/converter/class/specs-with-lump-categories.json index 96ab944f1..386ab2fe6 100644 --- a/src/test/converter/class/specs-with-lump-categories.json +++ b/src/test/converter/class/specs-with-lump-categories.json @@ -565,7 +565,7 @@ { "fileName": "class.ts", "line": 109, - "character": 14, + "character": 17, "url": "typedoc://class.ts#L109" } ] @@ -2239,7 +2239,7 @@ { "fileName": "class.ts", "line": 109, - "character": 14, + "character": 17, "url": "typedoc://class.ts#L109" } ], diff --git a/src/test/converter/class/specs.json b/src/test/converter/class/specs.json index 96ab944f1..386ab2fe6 100644 --- a/src/test/converter/class/specs.json +++ b/src/test/converter/class/specs.json @@ -565,7 +565,7 @@ { "fileName": "class.ts", "line": 109, - "character": 14, + "character": 17, "url": "typedoc://class.ts#L109" } ] @@ -2239,7 +2239,7 @@ { "fileName": "class.ts", "line": 109, - "character": 14, + "character": 17, "url": "typedoc://class.ts#L109" } ], diff --git a/src/test/converter/enum/enum.ts b/src/test/converter/enum/enum.ts index 5152f41ff..394ee4a47 100644 --- a/src/test/converter/enum/enum.ts +++ b/src/test/converter/enum/enum.ts @@ -41,7 +41,7 @@ export enum ModuleEnum { /** * This is a module extending an enumeration. */ -export module ModuleEnum { +export namespace ModuleEnum { /** * This is a variable appended to an enumeration. */ diff --git a/src/test/converter/enum/specs.json b/src/test/converter/enum/specs.json index d59f01d7e..144d9a4ff 100644 --- a/src/test/converter/enum/specs.json +++ b/src/test/converter/enum/specs.json @@ -117,7 +117,7 @@ { "fileName": "enum.ts", "line": 44, - "character": 14, + "character": 17, "url": "typedoc://enum.ts#L44" } ] @@ -313,7 +313,7 @@ { "fileName": "enum.ts", "line": 44, - "character": 14, + "character": 17, "url": "typedoc://enum.ts#L44" } ] diff --git a/src/test/converter/enum/specs.nodoc.json b/src/test/converter/enum/specs.nodoc.json index d59f01d7e..144d9a4ff 100644 --- a/src/test/converter/enum/specs.nodoc.json +++ b/src/test/converter/enum/specs.nodoc.json @@ -117,7 +117,7 @@ { "fileName": "enum.ts", "line": 44, - "character": 14, + "character": 17, "url": "typedoc://enum.ts#L44" } ] @@ -313,7 +313,7 @@ { "fileName": "enum.ts", "line": 44, - "character": 14, + "character": 17, "url": "typedoc://enum.ts#L44" } ] diff --git a/src/test/converter/function/function.ts b/src/test/converter/function/function.ts index f90d3c5af..077a9a6f7 100644 --- a/src/test/converter/function/function.ts +++ b/src/test/converter/function/function.ts @@ -161,7 +161,7 @@ export function isNonNull(arg: T | null | undefined): arg is T { /** * This is the module extending the function moduleFunction(). */ -export module moduleFunction { +export namespace moduleFunction { /** * This variable is appended to a function. */ diff --git a/src/test/converter/function/specs.json b/src/test/converter/function/specs.json index eb4d6085a..8b391258c 100644 --- a/src/test/converter/function/specs.json +++ b/src/test/converter/function/specs.json @@ -169,7 +169,7 @@ { "fileName": "function.ts", "line": 164, - "character": 14, + "character": 17, "url": "typedoc://function.ts#L164" } ] @@ -1680,7 +1680,7 @@ { "fileName": "function.ts", "line": 164, - "character": 14, + "character": 17, "url": "typedoc://function.ts#L164" } ], diff --git a/src/test/converter/interface/interface-implementation.ts b/src/test/converter/interface/interface-implementation.ts index df9f78650..aefd892fb 100644 --- a/src/test/converter/interface/interface-implementation.ts +++ b/src/test/converter/interface/interface-implementation.ts @@ -1,4 +1,4 @@ -export module Forms { +export namespace Forms { /** * Function signature of an event listener callback */ diff --git a/src/test/converter/interface/specs.json b/src/test/converter/interface/specs.json index 5ca0c0914..c554d89a9 100644 --- a/src/test/converter/interface/specs.json +++ b/src/test/converter/interface/specs.json @@ -2235,7 +2235,7 @@ { "fileName": "interface-implementation.ts", "line": 1, - "character": 14, + "character": 17, "url": "typedoc://interface-implementation.ts#L1" } ] From 1f917bf0fd50dde23796501f939716d33a0d844c Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 10 Aug 2025 12:05:49 -0600 Subject: [PATCH 085/119] Clear comment cache between conversions --- src/lib/converter/converter.ts | 6 +++++- src/lib/models/ProjectReflection.ts | 17 ++++++++++++++++- src/test/programs.ts | 2 -- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 5e6b43615..296d2c0cb 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -36,7 +36,7 @@ import { unique, } from "#utils"; import type { DocumentationEntryPoint } from "../utils/entry-point.js"; -import type { CommentParserConfig } from "./comments/index.js"; +import { clearCommentCache, type CommentParserConfig } from "./comments/index.js"; import type { CommentStyle, ValidationOptions } from "../utils/options/declaration.js"; import { parseCommentString } from "./comments/parser.js"; import { lexCommentString } from "./comments/rawLexer.js"; @@ -343,6 +343,10 @@ export class Converter extends AbstractComponent { delete this.excludeCache; delete this.externalPatternCache; + // Also clear the comment cache so if we convert this ts.Program again + // later we will re-parse comments. + clearCommentCache(); + return project; } diff --git a/src/lib/models/ProjectReflection.ts b/src/lib/models/ProjectReflection.ts index 4a6ebaa0a..bc9fd66a5 100644 --- a/src/lib/models/ProjectReflection.ts +++ b/src/lib/models/ProjectReflection.ts @@ -10,7 +10,15 @@ import { ReflectionKind } from "./kind.js"; import { Comment, type CommentDisplayPart } from "./Comment.js"; import { ReflectionSymbolId } from "./ReflectionSymbolId.js"; import type { Deserializer, JSONOutput, Serializer } from "#serialization"; -import { assertNever, DefaultMap, i18n, type NormalizedPath, removeIfPresent, StableKeyMap } from "#utils"; +import { + assertNever, + DefaultMap, + i18n, + NonEnumerable, + type NormalizedPath, + removeIfPresent, + StableKeyMap, +} from "#utils"; import type { DocumentReflection } from "./DocumentReflection.js"; import type { FileRegistry } from "./FileRegistry.js"; @@ -28,18 +36,24 @@ export class ProjectReflection extends ContainerReflection { readonly variant = "project"; // Used to resolve references. + @NonEnumerable private symbolToReflectionIdMap: Map< ReflectionSymbolId, number | number[] > = new StableKeyMap(); + @NonEnumerable private reflectionIdToSymbolIdMap = new Map(); + @NonEnumerable private removedSymbolIds = new StableKeyMap(); // Maps a reflection ID to all references eventually referring to it. + @NonEnumerable private referenceGraph?: Map; + // Maps a reflection ID to all reflections with it as their parent. + @NonEnumerable private reflectionChildren = new DefaultMap(() => []); /** @@ -49,6 +63,7 @@ export class ProjectReflection extends ContainerReflection { * * This may be replaced with a `Map` someday. */ + @NonEnumerable reflections: { [id: number]: Reflection } = {}; /** diff --git a/src/test/programs.ts b/src/test/programs.ts index c63b42f26..394f6a1d7 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -13,7 +13,6 @@ import { import type { ModelToObject } from "../lib/serialization/schema.js"; import { createAppForTesting } from "../lib/application.js"; import { existsSync } from "fs"; -import { clearCommentCache } from "../lib/converter/comments/index.js"; import { diagnostics } from "../lib/utils/loggers.js"; import { normalizePath, readFile, ValidatingFileRegistry } from "#node-utils"; @@ -175,7 +174,6 @@ export function getConverter2Project(entries: string[], folder: string) { app.options.setValue("entryPoints", entryPoints); app.files = new ValidatingFileRegistry(); - clearCommentCache(); return app.converter.convert( files.map((file, index) => { return { From 4a01cd2ecc573c4c2c33aa4aceb64ce023bb9f95 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 10 Aug 2025 12:13:53 -0600 Subject: [PATCH 086/119] Bump version to 0.28.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 98b45fece..2725280d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.9", + "version": "0.28.10", "homepage": "https://typedoc.org", "type": "module", "exports": { From 0489e32d54e52eee329719bea347d9360e2db69d Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Sun, 10 Aug 2025 18:14:40 +0000 Subject: [PATCH 087/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a16013601..5d93ff70e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.10 (2025-08-10) + ### Bug Fixes - Fixed inconsistent anchors on module pages for re-exports, #2990. From 708e77d8a0a622488bc25701142e6681ac8c6877 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 10 Aug 2025 20:27:36 -0600 Subject: [PATCH 088/119] Fix TS link resolution in first comment of file Resolves #2994 --- CHANGELOG.md | 4 ++++ src/lib/converter/comments/discovery.ts | 8 ++------ src/lib/types/ts-internal/index.d.ts | 14 -------------- src/test/converter2/issues/gh2994.d.ts | 13 +++++++++++++ src/test/issues.c2.test.ts | 8 +++++++- 5 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 src/test/converter2/issues/gh2994.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d93ff70e..a534c381a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Bug Fixes + +- Fixed link resolution not working correctly in first comment on the file in some cases, #2994. + ## v0.28.10 (2025-08-10) ### Bug Fixes diff --git a/src/lib/converter/comments/discovery.ts b/src/lib/converter/comments/discovery.ts index 9ef7fd5ea..2761125e1 100644 --- a/src/lib/converter/comments/discovery.ts +++ b/src/lib/converter/comments/discovery.ts @@ -357,15 +357,11 @@ function findJsDocForComment( if (ranges[0].kind === ts.SyntaxKind.MultiLineCommentTrivia) { const jsDocs = ts .getJSDocCommentsAndTags(node) - .map((doc) => ts.findAncestor(doc, ts.isJSDoc)) as ts.JSDoc[]; + .map((doc) => ts.findAncestor(doc, ts.isJSDoc)!); if (ts.isSourceFile(node)) { if (node.statements.length) { - jsDocs.push( - ...(ts - .getJSDocCommentsAndTags(node.statements[0]) - .map((doc) => ts.findAncestor(doc, ts.isJSDoc)) as ts.JSDoc[]), - ); + jsDocs.push(...node.statements[0].getChildren().filter(ts.isJSDoc)); } } diff --git a/src/lib/types/ts-internal/index.d.ts b/src/lib/types/ts-internal/index.d.ts index 9f10898d2..f9be4f771 100644 --- a/src/lib/types/ts-internal/index.d.ts +++ b/src/lib/types/ts-internal/index.d.ts @@ -23,22 +23,8 @@ declare module "typescript" { // https://github.com/microsoft/TypeScript/blob/v5.0.2/src/compiler/utilities.ts#L7432 export function getCheckFlags(symbol: ts.Symbol): CheckFlags; - // https://github.com/microsoft/TypeScript/blob/v5.0.2/src/compiler/utilities.ts#L4171 - export function getJSDocCommentsAndTags( - hostNode: Node, - noCache?: boolean, - ): readonly (JSDoc | JSDocTag)[]; - - export function getInterfaceBaseTypeNodes( - node: InterfaceDeclaration, - ): NodeArray | undefined; - export function getAllSuperTypeNodes(node: ts.Node): readonly ts.TypeNode[]; - export interface Signature { - thisParameter?: ts.Symbol; - } - // https://github.com/microsoft/TypeScript/blob/e213b2af3430bdc9cf5fbc76a8634d832e7aaaaa/src/compiler/types.ts#L5298-L5299 export interface UnionType { /* @internal */ diff --git a/src/test/converter2/issues/gh2994.d.ts b/src/test/converter2/issues/gh2994.d.ts new file mode 100644 index 000000000..9d4b50e16 --- /dev/null +++ b/src/test/converter2/issues/gh2994.d.ts @@ -0,0 +1,13 @@ +/** + * {@link x} <-- should be resolved with TS resolution + * @packageDocumentation + */ + +/** Required comment */ +import { x } from "gh2994"; + +export var y: 2; + +declare module "gh2994" { + var x: 1; +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 4aaf6b6f0..d5fd02caa 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -17,7 +17,7 @@ import type { InlineTagDisplayPart } from "../lib/models/Comment.js"; import { getConverter2App, getConverter2Project } from "./programs.js"; import { TestLogger } from "./TestLogger.js"; import { equalKind, getComment, getLinks, getSigComment, query, querySig, reflToTree } from "./utils.js"; -import { DefaultTheme, KindRouter, PageEvent } from "../index.js"; +import { DefaultTheme, KindRouter, PageEvent, ReflectionSymbolId } from "../index.js"; const app = getConverter2App(); @@ -2144,4 +2144,10 @@ describe("Issue Tests", () => { equal(x.inheritedFrom?.reflection?.getFullName(), undefined); equal(x.inheritedFrom?.name, "Tricky.x"); }); + + it("#2994 uses TS link resolution in first comment of file", () => { + const project = convert(); + const link = project.comment?.summary.find(part => part.kind === "inline-tag"); + ok(link?.target instanceof ReflectionSymbolId); + }); }); From ef3a42e964b711760b571716646e61e5d008ac5f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 16 Aug 2025 20:34:44 -0600 Subject: [PATCH 089/119] Convert compiler options before setting overrides Resolves #3000 --- .vscode/settings.json | 1 + CHANGELOG.md | 2 ++ src/lib/application.ts | 4 +-- src/lib/internationalization/locales/en.cts | 1 + src/lib/utils/entry-point.ts | 9 +++--- src/lib/utils/options/options.ts | 23 ++++++++++++--- src/test/converter2/issues/gh2631/crlf.md | 10 +++---- src/test/programs.ts | 4 +-- .../slow/internationalization-usage.test.ts | 5 ++-- src/test/utils/options/options.test.ts | 28 +++++++++++++++++++ .../utils/options/readers/tsconfig.test.ts | 2 +- 11 files changed, 69 insertions(+), 20 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b305aa521..fb47a1c4a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -66,6 +66,7 @@ "Msys", "nodoc", "shiki", + "srcset", "tsbuildinfo", "tsdoc", "typedoc", diff --git a/CHANGELOG.md b/CHANGELOG.md index a534c381a..f7d38b248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ title: Changelog ### Bug Fixes - Fixed link resolution not working correctly in first comment on the file in some cases, #2994. +- The `compilerOptions` option now functions properly with non-boolean options, #3000. +- Configuration errors within the `compilerOptions` option are now handled gracefully, #3000. ## v0.28.10 (2025-08-10) diff --git a/src/lib/application.ts b/src/lib/application.ts index 68f3afb35..d3574b3e5 100644 --- a/src/lib/application.ts +++ b/src/lib/application.ts @@ -496,7 +496,7 @@ export class Application extends AbstractComponent< ); } - if (Object.keys(this.options.getCompilerOptions()).length === 0) { + if (Object.keys(this.options.getCompilerOptions(this.logger)).length === 0) { this.logger.warn(i18n.no_compiler_options_set()); } @@ -525,7 +525,7 @@ export class Application extends AbstractComponent< const host = ts.createWatchCompilerHost( tsconfigFile, - this.options.fixCompilerOptions({}), + this.options.fixCompilerOptions({}, this.logger), ts.sys, ts.createEmitAndSemanticDiagnosticsBuilderProgram, (d) => diagnostic(this.logger, d), diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index 703f37664..bf367d6b7 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -242,6 +242,7 @@ export = { options_file_0_does_not_exist: `The options file {0} does not exist`, failed_read_options_file_0: `Failed to parse {0}, ensure it exists and exports an object`, + failed_to_apply_compilerOptions_overrides_0: "Failed to apply compilerOptions overrides: {0}", // plugins invalid_plugin_0_missing_load_function: `Invalid structure in plugin {0}, no load function found`, diff --git a/src/lib/utils/entry-point.ts b/src/lib/utils/entry-point.ts index 0e1aca810..38511656c 100644 --- a/src/lib/utils/entry-point.ts +++ b/src/lib/utils/entry-point.ts @@ -344,7 +344,7 @@ export function getExpandedEntryPointsForPaths( options: Options, programs = getEntryPrograms(inputFiles, logger, options), ): DocumentationEntryPoint[] { - const compilerOptions = options.getCompilerOptions(); + const compilerOptions = options.getCompilerOptions(logger); const supportedFileRegex = compilerOptions.allowJs || compilerOptions.checkJs ? /\.([cm][tj]s|[tj]sx?)$/ : /\.([cm]ts|tsx?)$/; @@ -410,16 +410,16 @@ function getEntryPrograms( const rootProgram = noTsConfigFound ? ts.createProgram({ rootNames: inputFiles, - options: options.getCompilerOptions(), + options: options.getCompilerOptions(logger), }) : ts.createProgram({ rootNames: options.getFileNames(), - options: options.getCompilerOptions(), + options: options.getCompilerOptions(logger), projectReferences: options.getProjectReferences(), }); addInferredDeclarationMapPaths( - options.getCompilerOptions(), + options.getCompilerOptions(logger), options.getFileNames(), ); @@ -438,6 +438,7 @@ function getEntryPrograms( ts.createProgram({ options: options.fixCompilerOptions( ref.commandLine.options, + logger, ), rootNames: ref.commandLine.fileNames, projectReferences: ref.commandLine.projectReferences, diff --git a/src/lib/utils/options/options.ts b/src/lib/utils/options/options.ts index 49e0798d8..797cccb01 100644 --- a/src/lib/utils/options/options.ts +++ b/src/lib/utils/options/options.ts @@ -1,4 +1,4 @@ -import type ts from "typescript"; +import ts from "typescript"; import { resolve } from "path"; import { ParameterType } from "./declaration.js"; import type { OutputSpecification } from "../index.js"; @@ -361,19 +361,34 @@ export class Options { /** * Gets the set compiler options. */ - getCompilerOptions(): ts.CompilerOptions { - return this.fixCompilerOptions(this._compilerOptions); + getCompilerOptions(logger: Logger): ts.CompilerOptions { + return this.fixCompilerOptions(this._compilerOptions, logger); } /** @internal */ fixCompilerOptions( options: Readonly, + logger: Logger, ): ts.CompilerOptions { const overrides = this.getValue("compilerOptions"); const result = { ...options }; if (overrides) { - Object.assign(result, overrides); + const tsOptions = ts.convertCompilerOptionsFromJson(overrides, ".", "typedoc-overrides.json"); + + if (tsOptions.errors.length) { + for (const error of tsOptions.errors) { + logger.error( + i18n.failed_to_apply_compilerOptions_overrides_0( + ts.flattenDiagnosticMessageText(error.messageText, "\n"), + ), + ); + } + } else { + for (const key in overrides) { + result[key] = tsOptions.options[key]; + } + } } if (this.getValue("emit") !== "both") { diff --git a/src/test/converter2/issues/gh2631/crlf.md b/src/test/converter2/issues/gh2631/crlf.md index aa988a781..3d0cf7580 100644 --- a/src/test/converter2/issues/gh2631/crlf.md +++ b/src/test/converter2/issues/gh2631/crlf.md @@ -1,5 +1,5 @@ ---- -title: "Windows Line Endings" ---- - -This file contains CRLF line endings +--- +title: "Windows Line Endings" +--- + +This file contains CRLF line endings diff --git a/src/test/programs.ts b/src/test/programs.ts index 394f6a1d7..68b27b47c 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -96,7 +96,7 @@ export function getConverterProgram() { const app = getConverterApp(); converterProgram = ts.createProgram( app.options.getFileNames(), - app.options.getCompilerOptions(), + app.options.getCompilerOptions(app.logger), ); const errors = ts.getPreEmitDiagnostics(converterProgram); @@ -134,7 +134,7 @@ export function getConverter2Program() { const app = getConverter2App(); converter2Program = ts.createProgram( app.options.getFileNames(), - app.options.getCompilerOptions(), + app.options.getCompilerOptions(app.logger), ); const errors = ts.getPreEmitDiagnostics(converter2Program); diff --git a/src/test/slow/internationalization-usage.test.ts b/src/test/slow/internationalization-usage.test.ts index d1e4efd38..b669f03f2 100644 --- a/src/test/slow/internationalization-usage.test.ts +++ b/src/test/slow/internationalization-usage.test.ts @@ -9,7 +9,8 @@ describe("Internationalization", () => { it("Does not include strings in translatable object which are unused", () => { const options = new Options(); const tsconfigReader = new TSConfigReader(); - tsconfigReader.read(options, new Logger(), process.cwd()); + const logger = new Logger(); + tsconfigReader.read(options, logger, process.cwd()); const defaultLocaleTs = join( fileURLToPath(import.meta.url), @@ -26,7 +27,7 @@ describe("Internationalization", () => { ); }, getCurrentDirectory: () => process.cwd(), - getCompilationSettings: () => options.getCompilerOptions(), + getCompilationSettings: () => options.getCompilerOptions(logger), getDefaultLibFileName: (opts) => ts.getDefaultLibFilePath(opts), fileExists: ts.sys.fileExists, readFile: ts.sys.readFile, diff --git a/src/test/utils/options/options.test.ts b/src/test/utils/options/options.test.ts index f58134f57..3366d851d 100644 --- a/src/test/utils/options/options.test.ts +++ b/src/test/utils/options/options.test.ts @@ -3,6 +3,8 @@ import { type MapDeclarationOption, type NumberDeclarationOption, Option } from import { deepStrictEqual as equal, throws } from "assert"; import type { DeclarationOption, EmitStrategy } from "../../../lib/utils/options/index.js"; import { LogLevel } from "#utils"; +import { TestLogger } from "../../TestLogger.js"; +import ts from "typescript"; describe("Options", () => { let options: Options & { @@ -180,6 +182,32 @@ describe("Options", () => { throws(() => options.reset("thisOptionDoesNotExist" as never)); }); + + it("Handles tsconfig overrides which aren't strings, #3000", () => { + const logger = new TestLogger(); + const options = new Options(); + + options.setValue("compilerOptions", { + moduleResolution: "bundler", + }); + + const compilerOptions = options.getCompilerOptions(logger); + equal(compilerOptions.moduleResolution, ts.ModuleResolutionKind.Bundler); + }); + + it("Handles tsconfig overrides which are invalid, #3000", () => { + const logger = new TestLogger(); + const options = new Options(); + + options.setValue("compilerOptions", { + moduleResolution: "bad", + }); + + const compilerOptions = options.getCompilerOptions(logger); + equal(compilerOptions.moduleResolution, undefined); + + logger.expectMessage("error: Failed to apply compilerOptions overrides: *"); + }); }); describe("Option", () => { diff --git a/src/test/utils/options/readers/tsconfig.test.ts b/src/test/utils/options/readers/tsconfig.test.ts index 5bd666e81..f49cbe42d 100644 --- a/src/test/utils/options/readers/tsconfig.test.ts +++ b/src/test/utils/options/readers/tsconfig.test.ts @@ -162,7 +162,7 @@ describe("Options - TSConfigReader", () => { compilerOptions: { strict: true }, }); await readWithProject(project); - equal(options.getCompilerOptions().strict, true); + equal(options.getCompilerOptions(logger).strict, true); }); async function testTsdoc(tsdoc: object, cb?: () => void, reset = true) { From e5cd9d1946e8ef89f3b916689108fd8b488fe920 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 16 Aug 2025 21:10:11 -0600 Subject: [PATCH 090/119] Fix rendering of optional methods Resolves #2995 --- CHANGELOG.md | 1 + src/lib/output/formatter.tsx | 1 + src/test/converter2/renderer/gh2995.ts | 3 + src/test/converter2/renderer/index.ts | 1 + .../renderer/specs/interfaces/gh2995.json | 399 ++++++++++++++++++ src/test/renderer/specs/modules.json | 32 ++ 6 files changed, 437 insertions(+) create mode 100644 src/test/converter2/renderer/gh2995.ts create mode 100644 src/test/renderer/specs/interfaces/gh2995.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d38b248..d70528b85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ title: Changelog ### Bug Fixes - Fixed link resolution not working correctly in first comment on the file in some cases, #2994. +- Optional methods are now rendered with a trailing `?` in the reflection preview and signature, #2995. - The `compilerOptions` option now functions properly with non-boolean options, #3000. - Configuration errors within the `compilerOptions` option are now handled gracefully, #3000. diff --git a/src/lib/output/formatter.tsx b/src/lib/output/formatter.tsx index 8015cb002..ba2f874b1 100644 --- a/src/lib/output/formatter.tsx +++ b/src/lib/output/formatter.tsx @@ -961,6 +961,7 @@ export class FormattedCodeBuilder { const id = this.newId(); return group(id, [ name, + sig.parent.flags.isOptional ? simpleElement(?) : emptyNode, this.typeParameters(sig), ...this.parameters(sig, id), nodes( diff --git a/src/test/converter2/renderer/gh2995.ts b/src/test/converter2/renderer/gh2995.ts new file mode 100644 index 000000000..09f1a0e0d --- /dev/null +++ b/src/test/converter2/renderer/gh2995.ts @@ -0,0 +1,3 @@ +export interface gh2995 { + optionalMethod?(filter: string, args: string[]): any; +} diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 2dbb7e53d..5d8abb16f 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -136,4 +136,5 @@ export interface DisabledGroups { export * as ExpandType from "./expandType"; export * as GH2982 from "./gh2982"; +export { gh2995 } from "./gh2995"; export { box as boxAlias }; diff --git a/src/test/renderer/specs/interfaces/gh2995.json b/src/test/renderer/specs/interfaces/gh2995.json new file mode 100644 index 000000000..c4182b433 --- /dev/null +++ b/src/test/renderer/specs/interfaces/gh2995.json @@ -0,0 +1,399 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "gh2995" + } + } + }, + { + "h1": "Interface gh2995" + } + ] + }, + { + "div.tsd-signature": [ + { + "span.tsd-signature-keyword": "interface" + }, + " ", + { + "span.tsd-kind-interface": "gh2995" + }, + " ", + { + "span.tsd-signature-symbol": "{" + }, + { + "br": [] + }, + " ", + { + "tag": "a.tsd-kind-call-signature", + "props": { + "href": "#optionalmethod-1" + }, + "children": "optionalMethod" + }, + { + "span.tsd-signature-symbol": "?" + }, + { + "span.tsd-signature-symbol": "(" + }, + { + "span.tsd-kind-parameter": "filter" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "string" + }, + { + "span.tsd-signature-symbol": "," + }, + " ", + { + "span.tsd-kind-parameter": "args" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "string" + }, + { + "span.tsd-signature-symbol": "[]" + }, + { + "span.tsd-signature-symbol": ")" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "any" + }, + { + "span.tsd-signature-symbol": ";" + }, + { + "br": [] + }, + { + "span.tsd-signature-symbol": "}" + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh2995.ts" + }, + "children": "gh2995.ts:1" + } + ] + } + } + }, + { + "section.tsd-panel-group.tsd-index-group": { + "section.tsd-panel.tsd-index-panel": { + "tag": "details.tsd-index-content.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary.tsd-index-summary": { + "h5.tsd-index-heading.uppercase": "Index" + } + }, + { + "div.tsd-accordion-details": { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Methods" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link", + "props": { + "href": "#optionalmethod" + }, + "children": { + "span": [ + "optional", + { + "wbr": [] + }, + "Method?" + ] + } + }, + "\n" + ] + } + ] + } + } + ] + } + } + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Methods" + }, + "children": { + "h2": "Methods" + } + }, + { + "section": { + "section.tsd-panel.tsd-member": [ + { + "h3.tsd-anchor-link#optionalmethod": [ + { + "code.tsd-tag": "Optional" + }, + { + "span": [ + "optional", + { + "wbr": [] + }, + "Method" + ] + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#optionalmethod", + "aria-label": "Permalink" + } + } + ] + }, + { + "ul.tsd-signatures": { + "li.": [ + { + "div.tsd-signature.tsd-anchor-link#optionalmethod-1": [ + { + "span.tsd-kind-call-signature": "optionalMethod" + }, + { + "span.tsd-signature-symbol": "?" + }, + { + "span.tsd-signature-symbol": "(" + }, + { + "span.tsd-kind-parameter": "filter" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "string" + }, + { + "span.tsd-signature-symbol": "," + }, + " ", + { + "span.tsd-kind-parameter": "args" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "string" + }, + { + "span.tsd-signature-symbol": "[]" + }, + { + "span.tsd-signature-symbol": ")" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "any" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#optionalmethod-1", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-description": [ + { + "div.tsd-parameters": [ + { + "h4.tsd-parameters-title": "Parameters" + }, + { + "ul.tsd-parameter-list": [ + { + "li": { + "span": [ + { + "span.tsd-kind-parameter": "filter" + }, + ": ", + { + "span.tsd-signature-type": "string" + } + ] + } + }, + { + "li": { + "span": [ + { + "span.tsd-kind-parameter": "args" + }, + ": ", + { + "span.tsd-signature-type": "string" + }, + { + "span.tsd-signature-symbol": "[]" + } + ] + } + } + ] + } + ] + }, + { + "h4.tsd-returns-title": [ + "Returns ", + { + "span.tsd-signature-type": "any" + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh2995.ts" + }, + "children": "gh2995.ts:2" + } + ] + } + } + } + ] + } + ] + } + } + ] + } + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Methods" + }, + "children": "Methods" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#optionalmethod" + }, + "children": { + "span": [ + "optional", + { + "wbr": [] + }, + "Method" + ] + } + } + } + ] + } + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index cc6e97839..f3dad2abe 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -356,6 +356,29 @@ { "dd.tsd-member-summary": [] }, + { + "dt.tsd-member-summary#gh2995": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "interfaces/gh2995.json" + }, + "children": "gh2995" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#gh2995", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + }, { "dt.tsd-member-summary#nonecategory": { "span.tsd-member-summary-name": [ @@ -803,6 +826,15 @@ ] } }, + { + "tag": "a", + "props": { + "href": "#gh2995" + }, + "children": { + "span": "gh2995" + } + }, { "tag": "a", "props": { From 06a4239653e206d0a8676c58320f4c3f57735053 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 16 Aug 2025 22:09:40 -0600 Subject: [PATCH 091/119] Pick up shorthand property's variable comments Resolves #2999 --- CHANGELOG.md | 5 +++++ src/lib/converter/comments/discovery.ts | 16 ++++++++++++++++ src/test/converter2/issues/gh2999.ts | 10 ++++++++++ src/test/issues.c2.test.ts | 8 ++++++++ 4 files changed, 39 insertions(+) create mode 100644 src/test/converter2/issues/gh2999.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d70528b85..e7aabc3a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ title: Changelog ## Unreleased +### Features + +- Object properties declared with shorthand property assignment will now use the variable's comment + if they do not have their own comment, #2999. + ### Bug Fixes - Fixed link resolution not working correctly in first comment on the file in some cases, #2994. diff --git a/src/lib/converter/comments/discovery.ts b/src/lib/converter/comments/discovery.ts index 2761125e1..423d5aa99 100644 --- a/src/lib/converter/comments/discovery.ts +++ b/src/lib/converter/comments/discovery.ts @@ -525,6 +525,22 @@ function declarationToCommentNodes( }); } + // #2999 automatically pick up comments from the value symbol for shorthand assignments + if (ts.isShorthandPropertyAssignment(node)) { + const sourceSymbol = checker.getShorthandAssignmentValueSymbol(node); + if (sourceSymbol?.valueDeclaration) { + const commentNode = declarationToCommentNodeIgnoringParents(sourceSymbol.valueDeclaration); + if (commentNode) { + result.push( + { + node: commentNode, + inheritedFromParentDeclaration: true, + }, + ); + } + } + } + // With overloaded functions/methods, TypeScript will use the comment on the first signature // declaration if ( diff --git a/src/test/converter2/issues/gh2999.ts b/src/test/converter2/issues/gh2999.ts new file mode 100644 index 000000000..fabc7c768 --- /dev/null +++ b/src/test/converter2/issues/gh2999.ts @@ -0,0 +1,10 @@ +/** + * A test object with property a. + */ +const LocalObject = { + a: 1, +}; + +export const Options = { + LocalObject, +}; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index d5fd02caa..496dbbeb9 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2150,4 +2150,12 @@ describe("Issue Tests", () => { const link = project.comment?.summary.find(part => part.kind === "inline-tag"); ok(link?.target instanceof ReflectionSymbolId); }); + + it("#2999 picks up parent comments from shorthand property assignments", () => { + const project = convert(); + const opts = query(project, "Options"); + equal(opts.type?.type, "reflection"); + const local = opts.type.declaration.getChildByName("LocalObject"); + equal(Comment.combineDisplayParts(local?.comment?.summary), "A test object with property a."); + }); }); From 8f47c24f17204a3e2b86b48e7f8ae6933f19ae77 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 24 Aug 2025 20:05:59 -0600 Subject: [PATCH 092/119] Fix rendering of "Type Declaration" header Ref: #3002 --- .npmrc | 3 ++ CHANGELOG.md | 1 + site/tags/primaryExport.md | 4 +- src/lib/internationalization/locales/en.cts | 2 +- .../default/partials/member.declaration.tsx | 47 ++++++++++--------- .../specs/types/ExpandType.AExpanded.json | 2 +- .../specs/types/ExpandType.BExpanded.json | 2 +- .../ExpandType.NestedBehavior1.AExpanded.json | 2 +- ...xpandType.NestedBehavior1.AllExpanded.json | 6 +-- src/test/renderer/specs/types/Nested.json | 2 +- .../renderer/specs/types/UnionComments.json | 2 +- 11 files changed, 40 insertions(+), 33 deletions(-) diff --git a/.npmrc b/.npmrc index 38a20c41b..ce1fd3424 100644 --- a/.npmrc +++ b/.npmrc @@ -6,3 +6,6 @@ audit = false # While we're on the TS beta, need this flag. legacy-peer-deps = true + +# This breaks some of the scripts in the scripts directory +update-notifier = false diff --git a/CHANGELOG.md b/CHANGELOG.md index e7aabc3a1..304366829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ title: Changelog - Optional methods are now rendered with a trailing `?` in the reflection preview and signature, #2995. - The `compilerOptions` option now functions properly with non-boolean options, #3000. - Configuration errors within the `compilerOptions` option are now handled gracefully, #3000. +- Fixed improper casing of "Type Declaration" header, #3002. ## v0.28.10 (2025-08-10) diff --git a/site/tags/primaryExport.md b/site/tags/primaryExport.md index ab3533632..c1a5b13f4 100644 --- a/site/tags/primaryExport.md +++ b/site/tags/primaryExport.md @@ -1,8 +1,8 @@ --- -title: "@primaryModifier" +title: "@primaryExport" --- -# @primaryModifier +# @primaryExport **Tag Kind:** [Modifier](../tags.md#modifier-tags) diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index bf367d6b7..dd83dca43 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -506,7 +506,7 @@ export = { // Page headings/labels theme_implements: "Implements", theme_indexable: "Indexable", - theme_type_declaration: "Type declaration", + theme_type_declaration: "Type Declaration", theme_index: "Index", theme_hierarchy: "Hierarchy", theme_hierarchy_summary: "Hierarchy Summary", diff --git a/src/lib/output/themes/default/partials/member.declaration.tsx b/src/lib/output/themes/default/partials/member.declaration.tsx index 4086ebcb8..e304723d2 100644 --- a/src/lib/output/themes/default/partials/member.declaration.tsx +++ b/src/lib/output/themes/default/partials/member.declaration.tsx @@ -1,9 +1,28 @@ -import type { DeclarationReflection } from "../../../../models/index.js"; +import type { DeclarationReflection } from "#models"; import { JSX } from "#utils"; import { FormattedCodeBuilder, FormattedCodeGenerator, type FormatterNode, Wrap } from "../../../formatter.js"; import { hasTypeParameters } from "../../lib.js"; import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js"; +function shouldRenderDefaultValue(props: DeclarationReflection) { + const defaultValue = props.defaultValue; + + if (defaultValue === undefined) { + return false; + } + + /** Fix for #2717. If type is the same as value the default value is omitted */ + if (props.type && props.type.type === "literal") { + const reflectionTypeString = props.type.toString(); + + if (reflectionTypeString === defaultValue.toString()) { + return false; + } + } + + return true; +} + export function memberDeclaration(context: DefaultThemeRenderContext, props: DeclarationReflection) { const builder = new FormattedCodeBuilder(context.router, context.model); const content: FormatterNode[] = []; @@ -11,31 +30,15 @@ export function memberDeclaration(context: DefaultThemeRenderContext, props: Dec const generator = new FormattedCodeGenerator(context.options.getValue("typePrintWidth")); generator.node({ type: "nodes", content }, Wrap.Detect); - /** Fix for #2717. If type is the same as value the default value is omitted */ - function shouldRenderDefaultValue() { - if (props.type && props.type.type === "literal") { - const reflectionTypeString = props.type.toString(); - - const defaultValue = props.defaultValue; - - if (defaultValue === undefined || reflectionTypeString === defaultValue.toString()) { - return false; - } - } - return true; - } - return ( <>
{generator.toElement()} - {!!props.defaultValue && shouldRenderDefaultValue() && ( - <> - - {" = "} - {props.defaultValue} - - + {shouldRenderDefaultValue(props) && ( + + {" = "} + {props.defaultValue} + )}
diff --git a/src/test/renderer/specs/types/ExpandType.AExpanded.json b/src/test/renderer/specs/types/ExpandType.AExpanded.json index 3fd838168..fa0d5b018 100644 --- a/src/test/renderer/specs/types/ExpandType.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.AExpanded.json @@ -271,7 +271,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": { diff --git a/src/test/renderer/specs/types/ExpandType.BExpanded.json b/src/test/renderer/specs/types/ExpandType.BExpanded.json index 2865052ec..232327a24 100644 --- a/src/test/renderer/specs/types/ExpandType.BExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.BExpanded.json @@ -318,7 +318,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": { diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json index 6ecd11223..922d86eae 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json @@ -332,7 +332,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": { diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json index 70b147fe8..bcb57d04b 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json @@ -275,7 +275,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": { @@ -359,7 +359,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": { @@ -443,7 +443,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": { diff --git a/src/test/renderer/specs/types/Nested.json b/src/test/renderer/specs/types/Nested.json index cef1f3e4d..5778fa04a 100644 --- a/src/test/renderer/specs/types/Nested.json +++ b/src/test/renderer/specs/types/Nested.json @@ -365,7 +365,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul.tsd-parameters": [ diff --git a/src/test/renderer/specs/types/UnionComments.json b/src/test/renderer/specs/types/UnionComments.json index f338513ee..06e4ab4f0 100644 --- a/src/test/renderer/specs/types/UnionComments.json +++ b/src/test/renderer/specs/types/UnionComments.json @@ -50,7 +50,7 @@ { "div.tsd-type-declaration": [ { - "h4": "Type declaration" + "h4": "Type Declaration" }, { "ul": [ From 2077c80cbbce1260fe30d75f62d841fb84eaecc8 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 24 Aug 2025 21:12:46 -0600 Subject: [PATCH 093/119] Bump version to 0.28.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2725280d5..d95921ac6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.10", + "version": "0.28.11", "homepage": "https://typedoc.org", "type": "module", "exports": { From 17347c9af22d2bbf322e3c8eaa92eae10a6f7690 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Mon, 25 Aug 2025 03:13:42 +0000 Subject: [PATCH 094/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 304366829..fe48fd9b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.11 (2025-08-25) + ### Features - Object properties declared with shorthand property assignment will now use the variable's comment From 34d2c33975e6f01c766aa9a41f219027c233c24c Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Aug 2025 18:10:32 -0600 Subject: [PATCH 095/119] Fix #3003 --- CHANGELOG.md | 5 +++++ src/lib/converter/comments/discovery.ts | 15 +++++++++++++++ src/lib/converter/symbols.ts | 12 +++++++++++- src/lib/internationalization/locales/en.cts | 2 ++ src/test/converter2/issues/gh3003/enums.ts | 9 +++++++++ src/test/converter2/issues/gh3003/index.ts | 17 +++++++++++++++++ src/test/issues.c2.test.ts | 13 +++++++++++++ src/test/utils.ts | 2 +- 8 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/test/converter2/issues/gh3003/enums.ts create mode 100644 src/test/converter2/issues/gh3003/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fe48fd9b6..2f8f9994a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ title: Changelog ## Unreleased +### Bug Fixes + +- Variables marked with `@enum` now work for symbols imported from another module, #3003. +- Improved magic introduced with #2999 to work with imported symbols, #3003. + ## v0.28.11 (2025-08-25) ### Features diff --git a/src/lib/converter/comments/discovery.ts b/src/lib/converter/comments/discovery.ts index 423d5aa99..719bf2c9e 100644 --- a/src/lib/converter/comments/discovery.ts +++ b/src/lib/converter/comments/discovery.ts @@ -4,6 +4,7 @@ import { CommentStyle } from "../../utils/options/declaration.js"; import { nicePath } from "../../utils/paths.js"; import { ok } from "assert"; import { assertNever, filter, firstDefined, i18n, type Logger } from "#utils"; +import { resolveAliasedSymbol } from "../utils/symbols.js"; const variablePropertyKinds = [ ts.SyntaxKind.PropertyDeclaration, @@ -539,6 +540,20 @@ function declarationToCommentNodes( ); } } + + // #3003 even more magic for handling an imported symbol which appears in a shorthand property assignment + const originalSymbol = sourceSymbol && resolveAliasedSymbol(sourceSymbol, checker); + if (originalSymbol !== sourceSymbol && originalSymbol?.valueDeclaration) { + const commentNode = declarationToCommentNodeIgnoringParents(originalSymbol?.valueDeclaration); + if (commentNode) { + result.push( + { + node: commentNode, + inheritedFromParentDeclaration: true, + }, + ); + } + } } // With overloaded functions/methods, TypeScript will use the comment on the first signature diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index 6ae22c7b9..7358e3e45 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -1133,7 +1133,17 @@ function convertVariableAsEnum( context.finalizeDeclarationReflection(reflection); const rc = context.withScope(reflection); - const declaration = symbol.declarations!.find(ts.isVariableDeclaration)!; + const declaration = symbol.valueDeclaration; + if (!declaration) { + context.logger.error( + i18n.converting_0_as_enum_requires_value_declaration( + symbol.name, + ), + symbol.declarations?.[0], + ); + return; + } + const type = context.checker.getTypeAtLocation(declaration); for (const prop of type.getProperties()) { diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index dd83dca43..301cbf806 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -51,6 +51,8 @@ export = { `Converting {0} as a class requires a declaration which represents a non-type value`, converting_0_as_class_without_construct_signatures: `{0} is being converted as a class, but does not have any construct signatures`, + converting_0_as_enum_requires_value_declaration: + `Converting {0} as an enum requires a declaration which represents a non-type value`, comment_for_0_should_not_contain_block_or_modifier_tags: `The comment for {0} should not contain any block or modifier tags`, diff --git a/src/test/converter2/issues/gh3003/enums.ts b/src/test/converter2/issues/gh3003/enums.ts new file mode 100644 index 000000000..32a4410ac --- /dev/null +++ b/src/test/converter2/issues/gh3003/enums.ts @@ -0,0 +1,9 @@ +/** + * This is a custom enum that is exported as a constant object. + * + * @enum + */ +export const CustomEnum = { + /** A */ + A: "A", +} as const; diff --git a/src/test/converter2/issues/gh3003/index.ts b/src/test/converter2/issues/gh3003/index.ts new file mode 100644 index 000000000..142ac290b --- /dev/null +++ b/src/test/converter2/issues/gh3003/index.ts @@ -0,0 +1,17 @@ +import { CustomEnum } from "./enums.js"; + +/** + * This is a second custom enum that is exported as a constant object. + * + * @enum + */ +const CustomEnum2 = { + ...CustomEnum, + D: "D", +} as const; + +/** @namespace */ +export const ExportedObject = { + CustomEnum, + CustomEnum2, +}; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 496dbbeb9..c91e54fe7 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2158,4 +2158,17 @@ describe("Issue Tests", () => { const local = opts.type.declaration.getChildByName("LocalObject"); equal(Comment.combineDisplayParts(local?.comment?.summary), "A test object with property a."); }); + + it("#3003 handles @enum on properties within @namespace", () => { + const project = convert(); + + const a = query(project, "ExportedObject.CustomEnum"); + equalKind(a, ReflectionKind.Enum); + equal(a.children?.map(c => c.name), ["A"]); + equal(a.children?.map(c => c.type?.toString()), ['"A"']); + const b = query(project, "ExportedObject.CustomEnum2"); + equalKind(b, ReflectionKind.Enum); + equal(b.children?.map(c => c.name), ["A", "D"]); + equal(b.children?.map(c => c.type?.toString()), ['"A"', '"D"']); + }); }); diff --git a/src/test/utils.ts b/src/test/utils.ts index 409e65795..1830ef414 100644 --- a/src/test/utils.ts +++ b/src/test/utils.ts @@ -108,7 +108,7 @@ export function equalKind(refl: Reflection, kind: ReflectionKind) { equal( refl.kind, kind, - `Expected ${ReflectionKind[kind]} but got ${ReflectionKind[refl.kind]}`, + `Expected ${ReflectionKind[kind]} but got ${ReflectionKind[refl.kind]} for ${refl.getFullName()}`, ); } From 51c7ff207732d0d2aa409577eae9e27321576456 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Aug 2025 19:49:46 -0600 Subject: [PATCH 096/119] Fix link resolution with percent encoded URLs Ref #3006 --- CHANGELOG.md | 1 + src/lib/converter/comments/textParser.ts | 20 +++++++++++-------- src/test/converter2/issues/gh3006/index.ts | 9 +++++++++ .../converter2/issues/gh3006/two words.md | 1 + src/test/issues.c2.test.ts | 11 ++++++++++ 5 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 src/test/converter2/issues/gh3006/index.ts create mode 100644 src/test/converter2/issues/gh3006/two words.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f8f9994a..d073f914b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ title: Changelog - Variables marked with `@enum` now work for symbols imported from another module, #3003. - Improved magic introduced with #2999 to work with imported symbols, #3003. +- Fixed relative link resolution to file names containing percent encoded URLs, #3006. ## v0.28.11 (2025-08-25) diff --git a/src/lib/converter/comments/textParser.ts b/src/lib/converter/comments/textParser.ts index 4078b7aef..e98f18fa9 100644 --- a/src/lib/converter/comments/textParser.ts +++ b/src/lib/converter/comments/textParser.ts @@ -223,10 +223,11 @@ function checkMarkdownLink( if (link.ok) { // Only make a relative-link display part if it's actually a relative link. // Discard protocol:// links, unix style absolute paths, and windows style absolute paths. - if (isRelativePath(link.str)) { + const decoded = decodeURI(link.str); + if (isRelativePath(decoded)) { const { target, anchor } = files.register( sourcePath, - link.str as NormalizedPath, + decoded as NormalizedPath, ) || { target: undefined, anchor: undefined }; return { pos: lookahead, @@ -284,10 +285,11 @@ function checkReference(data: TextParserData): RelativeLink | undefined { ); if (link.ok) { - if (isRelativePath(link.str)) { + const decoded = decodeURI(link.str); + if (isRelativePath(decoded)) { const { target, anchor } = files.register( sourcePath, - link.str as NormalizedPath, + decoded as NormalizedPath, ) || { target: undefined, anchor: undefined }; return { pos: lookahead, @@ -377,10 +379,11 @@ function checkAttributeDirectPath( pos: number, end: number, ): RelativeLink[] { - if (isRelativePath(text.trim())) { + const decoded = decodeURI(text.trim()); + if (isRelativePath(decoded)) { const { target, anchor } = data.files.register( data.sourcePath, - text.trim() as NormalizedPath, + decoded as NormalizedPath, ) || { target: undefined, anchor: undefined }; return [{ pos, @@ -413,11 +416,12 @@ function checkAttributeSrcSet(data: TextParserData, text: string, pos: number, _ // TypeDoc: We don't exactly match this, PR welcome! For now, just permit anything // that's not whitespace or a comma const url = text.slice(textPos).match(/^[^\t\r\f\n ,]+/); + const decoded = url && decodeURI(url[0]); - if (url && isRelativePath(url[0])) { + if (decoded && isRelativePath(decoded)) { const { target, anchor } = data.files.register( data.sourcePath, - url[0] as NormalizedPath, + decoded as NormalizedPath, ) || { target: undefined, anchor: undefined }; result.push({ pos: pos + textPos, diff --git a/src/test/converter2/issues/gh3006/index.ts b/src/test/converter2/issues/gh3006/index.ts new file mode 100644 index 000000000..990e7896a --- /dev/null +++ b/src/test/converter2/issues/gh3006/index.ts @@ -0,0 +1,9 @@ +/** + * @document two words.md + * @module + */ + +/** + * [link](./two%20words.md) + */ +export const x = 1; diff --git a/src/test/converter2/issues/gh3006/two words.md b/src/test/converter2/issues/gh3006/two words.md new file mode 100644 index 000000000..feafbd5dd --- /dev/null +++ b/src/test/converter2/issues/gh3006/two words.md @@ -0,0 +1 @@ +This document's name contains a space, #3006 diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index c91e54fe7..87f894055 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2171,4 +2171,15 @@ describe("Issue Tests", () => { equal(b.children?.map(c => c.name), ["A", "D"]); equal(b.children?.map(c => c.type?.toString()), ['"A"', '"D"']); }); + + it("#3006 handles documents containing spaces in their names", () => { + const project = convert(); + equal(["two words"], project.documents?.map(doc => doc.name)); + const doc = project.documents?.[0]; + const x = query(project, "x"); + + ok(x.comment?.summary[1].kind === "relative-link"); + ok(x.comment.summary[1].target); + ok(project.files.resolve(x.comment.summary[1].target, project) === doc); + }); }); From 0a716b2abfed0038ba450a85516b5f7cd3bb572f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Aug 2025 20:25:28 -0600 Subject: [PATCH 097/119] Fix relative links to README file Resolves #3006 --- CHANGELOG.md | 3 + eslint.config.mjs | 7 + package.json | 4 +- pnpm-lock.yaml | 144 +++++++++--------- src/index.ts | 1 + src/lib/converter/comments/parser.ts | 12 +- src/lib/converter/plugins/CommentPlugin.ts | 7 +- src/lib/converter/plugins/IncludePlugin.ts | 6 +- src/lib/models/Comment.ts | 43 +++--- src/lib/models/DeclarationReflection.ts | 6 +- src/lib/models/FileRegistry.ts | 2 +- src/lib/models/ProjectReflection.ts | 8 +- src/lib/models/Reflection.ts | 8 +- src/lib/output/themes/MarkedPlugin.tsx | 11 +- .../output/themes/default/DefaultTheme.tsx | 9 +- .../themes/default/partials/comment.tsx | 46 +++--- .../default/partials/member.declaration.tsx | 2 +- .../default/partials/moduleReflection.tsx | 2 +- .../themes/default/partials/navigation.tsx | 4 +- .../themes/default/partials/typeDetails.tsx | 39 +++-- .../themes/default/templates/reflection.tsx | 2 +- src/lib/serialization/deserializer.ts | 8 +- src/lib/serialization/schema.ts | 6 +- src/lib/utils-common/i18n.ts | 3 +- src/lib/utils-common/index.ts | 1 + src/lib/utils-common/validation.ts | 4 +- src/lib/utils/options/declaration.ts | 15 +- src/lib/utils/options/defaults.ts | 14 +- src/lib/utils/options/readers/tsconfig.ts | 12 +- src/lib/utils/options/sources/typedoc.ts | 2 +- src/test/Repository.test.ts | 2 +- src/test/converter2/renderer/index.ts | 1 + src/test/merge.test.ts | 11 +- .../renderer/specs/classes/BaseClass.json | 3 - .../renderer/specs/classes/GenericClass.json | 11 +- .../specs/classes/ModifiersClass.json | 3 - .../renderer/specs/classes/RenderClass.json | 29 +--- .../renderer/specs/enums/Enumeration.json | 6 - src/test/renderer/specs/functions/box.json | 6 - .../specs/interfaces/BaseInterface.json | 3 - .../specs/interfaces/DisabledGroups.json | 5 +- .../ExpandType.ExpandedByDefault.json | 3 - .../specs/interfaces/NoneCategory.json | 6 - .../renderer/specs/interfaces/NoneGroup.json | 3 - src/test/renderer/specs/modules.json | 11 +- .../modules/ExpandType.NestedBehavior1.json | 5 - .../specs/types/ExpandType.AExpanded.json | 8 - .../specs/types/ExpandType.BExpanded.json | 3 - .../specs/types/ExpandType.Expandable.json | 3 - .../specs/types/ExpandType.Expandable2.json | 3 - .../ExpandType.NestedBehavior1.AExpanded.json | 8 - ...xpandType.NestedBehavior1.AllExpanded.json | 9 -- src/test/renderer/specs/types/Nested.json | 20 +-- src/test/utils.ts | 2 +- 54 files changed, 255 insertions(+), 340 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d073f914b..a9e5524ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ title: Changelog - Variables marked with `@enum` now work for symbols imported from another module, #3003. - Improved magic introduced with #2999 to work with imported symbols, #3003. - Fixed relative link resolution to file names containing percent encoded URLs, #3006. +- Linking to the project's README file with a relative link will now behave as expected, #3006. +- Reduced unnecessary HTML element rendering in default theme. + API: `Reflection.hasComment` and `Comment.hasVisibleComponent` now accepts an optional `notRenderedTags` parameter. ## v0.28.11 (2025-08-25) diff --git a/eslint.config.mjs b/eslint.config.mjs index a72f396c1..ff73874f2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -53,6 +53,13 @@ const config = { // This is sometimes useful for clarity "@typescript-eslint/no-unnecessary-type-arguments": "off", + // It'd be kind of nice to be able to turn this on, but it doesn't seem to be worth the additional + // pain at this point. Turning it on adds ~300 lint errors, of which manual review found 1 bug, and + // whose suggestions would have introduced 3 bugs that I noticed, and potentially more... and it + // also didn't catch the bug which I originally turned it on for! Ideally, I'd like a version of + // this which ONLY checks for boolean comparisons of type number. + "@typescript-eslint/strict-boolean-expressions": "off", + // We still use `any` fairly frequently... "@typescript-eslint/ban-types": "off", "@typescript-eslint/no-explicit-any": "off", diff --git a/package.json b/package.json index d95921ac6..970aeb368 100644 --- a/package.json +++ b/package.json @@ -50,13 +50,13 @@ "c8": "^10.1.3", "dprint": "^0.50.1", "esbuild": "^0.25.8", - "eslint": "^9.32.0", + "eslint": "^9.34.0", "mocha": "^11.7.1", "puppeteer": "^24.11.1", "semver": "^7.7.2", "tsx": "^4.20.3", "typescript": "5.9.2", - "typescript-eslint": "^8.38.0" + "typescript-eslint": "^8.41.0" }, "files": [ "/bin", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7809dfed2..f20c82e1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,8 +70,8 @@ importers: specifier: 5.9.2 version: 5.9.2 typescript-eslint: - specifier: ^8.38.0 - version: 8.38.0(eslint@9.32.0)(typescript@5.9.2) + specifier: ^8.41.0 + version: 8.41.0(eslint@9.32.0)(typescript@5.9.2) packages: @@ -442,63 +442,63 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.38.0': - resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==} + '@typescript-eslint/eslint-plugin@8.41.0': + resolution: {integrity: sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.38.0 + '@typescript-eslint/parser': ^8.41.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.38.0': - resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==} + '@typescript-eslint/parser@8.41.0': + resolution: {integrity: sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.38.0': - resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==} + '@typescript-eslint/project-service@8.41.0': + resolution: {integrity: sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.38.0': - resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==} + '@typescript-eslint/scope-manager@8.41.0': + resolution: {integrity: sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.38.0': - resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==} + '@typescript-eslint/tsconfig-utils@8.41.0': + resolution: {integrity: sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.38.0': - resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==} + '@typescript-eslint/type-utils@8.41.0': + resolution: {integrity: sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.38.0': - resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==} + '@typescript-eslint/types@8.41.0': + resolution: {integrity: sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.38.0': - resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==} + '@typescript-eslint/typescript-estree@8.41.0': + resolution: {integrity: sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.38.0': - resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==} + '@typescript-eslint/utils@8.41.0': + resolution: {integrity: sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.38.0': - resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==} + '@typescript-eslint/visitor-keys@8.41.0': + resolution: {integrity: sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typestrong/fs-fixture-builder@https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe': @@ -1297,12 +1297,12 @@ packages: typed-query-selector@2.12.0: resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} - typescript-eslint@8.38.0: - resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==} + typescript-eslint@8.41.0: + resolution: {integrity: sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} @@ -1674,14 +1674,14 @@ snapshots: '@types/node': 18.19.113 optional: true - '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.38.0 + '@typescript-eslint/parser': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.41.0 + '@typescript-eslint/type-utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.41.0 eslint: 9.32.0 graphemer: 1.4.0 ignore: 7.0.5 @@ -1691,41 +1691,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/parser@8.41.0(eslint@9.32.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.38.0 + '@typescript-eslint/scope-manager': 8.41.0 + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.41.0 debug: 4.4.1(supports-color@8.1.1) eslint: 9.32.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.38.0(typescript@5.9.2)': + '@typescript-eslint/project-service@8.41.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2) - '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.9.2) + '@typescript-eslint/types': 8.41.0 debug: 4.4.1(supports-color@8.1.1) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.38.0': + '@typescript-eslint/scope-manager@8.41.0': dependencies: - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/visitor-keys': 8.38.0 + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/visitor-keys': 8.41.0 - '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.41.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.38.0(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.41.0(eslint@9.32.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) debug: 4.4.1(supports-color@8.1.1) eslint: 9.32.0 ts-api-utils: 2.1.0(typescript@5.9.2) @@ -1733,14 +1733,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.38.0': {} + '@typescript-eslint/types@8.41.0': {} - '@typescript-eslint/typescript-estree@8.38.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.41.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/project-service': 8.38.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.9.2) - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/visitor-keys': 8.38.0 + '@typescript-eslint/project-service': 8.41.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.9.2) + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/visitor-keys': 8.41.0 debug: 4.4.1(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 @@ -1751,20 +1751,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.38.0(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/utils@8.41.0(eslint@9.32.0)(typescript@5.9.2)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) - '@typescript-eslint/scope-manager': 8.38.0 - '@typescript-eslint/types': 8.38.0 - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.41.0 + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) eslint: 9.32.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.38.0': + '@typescript-eslint/visitor-keys@8.41.0': dependencies: - '@typescript-eslint/types': 8.38.0 + '@typescript-eslint/types': 8.41.0 eslint-visitor-keys: 4.2.1 '@typestrong/fs-fixture-builder@https://codeload.github.com/TypeStrong/fs-fixture-builder/tar.gz/34113409e3a171e68ce5e2b55461ef5c35591cfe': {} @@ -2627,12 +2627,12 @@ snapshots: typed-query-selector@2.12.0: {} - typescript-eslint@8.38.0(eslint@9.32.0)(typescript@5.9.2): + typescript-eslint@8.41.0(eslint@9.32.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/parser': 8.38.0(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.38.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/eslint-plugin': 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) eslint: 9.32.0 typescript: 5.9.2 transitivePeerDependencies: diff --git a/src/index.ts b/src/index.ts index 166802605..aefdf11bc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -141,6 +141,7 @@ export { type NormalizedPathOrModule, type NormalizedPathOrModuleOrFunction, type SymbolReference, + type TagString, type TranslatedString, translateTagName, } from "#utils"; diff --git a/src/lib/converter/comments/parser.ts b/src/lib/converter/comments/parser.ts index cc016ab2b..924054b77 100644 --- a/src/lib/converter/comments/parser.ts +++ b/src/lib/converter/comments/parser.ts @@ -2,7 +2,7 @@ import assert, { ok } from "assert"; import { parseDocument as parseYamlDoc } from "yaml"; import type { CommentContextOptionalChecker, CommentParserConfig } from "./index.js"; import { Comment, type CommentDisplayPart, CommentTag, type InlineTagDisplayPart } from "../../models/index.js"; -import type { MinimalSourceFile } from "#utils"; +import type { MinimalSourceFile, TagString } from "#utils"; import { nicePath } from "../../utils/paths.js"; import { type Token, TokenSyntaxKind } from "./lexer.js"; import { extractTagName } from "./tagName.js"; @@ -235,7 +235,7 @@ export function parseCommentString( return { content, frontmatter: frontmatterData }; } -const HAS_USER_IDENTIFIER: `@${string}`[] = [ +const HAS_USER_IDENTIFIER: TagString[] = [ "@callback", "@param", "@prop", @@ -389,7 +389,7 @@ function blockTag( content = blockContent(comment, lexer, config, i18n, warning, files); } - return new CommentTag(tagName as `@${string}`, content); + return new CommentTag(tagName as TagString, content); } /** @@ -613,11 +613,11 @@ function blockContent( next.text = "@inheritDoc"; } if (config.modifierTags.has(next.text)) { - comment.modifierTags.add(next.text as `@${string}`); + comment.modifierTags.add(next.text as TagString); break; } else if (!atNewLine && !config.blockTags.has(next.text)) { // Treat unknown tag as a modifier, but warn about it. - comment.modifierTags.add(next.text as `@${string}`); + comment.modifierTags.add(next.text as TagString); warning( i18n.treating_unrecognized_tag_0_as_modifier(next.text), next, @@ -751,7 +751,7 @@ function inlineTag( const inlineTag: InlineTagDisplayPart = { kind: "inline-tag", - tag: tagName.text as `@${string}`, + tag: tagName.text as TagString, text: content.join(""), }; if (tagName.tsLinkTarget) { diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index b59d5cd43..67c013b69 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -22,6 +22,7 @@ import { removeIf, removeIfPresent, setIntersection, + type TagString, unique, } from "#utils"; import { ConverterComponent } from "../components.js"; @@ -56,7 +57,7 @@ const NEVER_RENDERED = [ // We might make this user configurable at some point, but for now, // this set is configured here. const MUTUALLY_EXCLUSIVE_MODIFIERS = [ - new Set<`@${string}`>([ + new Set([ "@alpha", "@beta", "@experimental", @@ -122,10 +123,10 @@ const MUTUALLY_EXCLUSIVE_MODIFIERS = [ */ export class CommentPlugin extends ConverterComponent { @Option("excludeTags") - accessor excludeTags!: `@${string}`[]; + accessor excludeTags!: TagString[]; @Option("cascadedModifierTags") - accessor cascadedModifierTags!: `@${string}`[]; + accessor cascadedModifierTags!: TagString[]; @Option("excludeInternal") accessor excludeInternal!: boolean; diff --git a/src/lib/converter/plugins/IncludePlugin.ts b/src/lib/converter/plugins/IncludePlugin.ts index 4228418e3..56da69733 100644 --- a/src/lib/converter/plugins/IncludePlugin.ts +++ b/src/lib/converter/plugins/IncludePlugin.ts @@ -358,7 +358,7 @@ type RegionTagRETuple = [ (regionName: string) => RegExp, (regionName: string) => RegExp, ]; -const regionTagREsByExt: Record = { +const regionTagREsByExt: Record = { bat: [ [ (regionName) => new RegExp(`:: *#region *${regionName} *\n`, "g"), @@ -407,14 +407,14 @@ const regionTagREsByExt: Record = { ], }; regionTagREsByExt["fs"] = [ - ...regionTagREsByExt["ts"], + ...regionTagREsByExt["ts"]!, [ (regionName) => new RegExp(`(#_region) *${regionName} *\n`, "g"), (regionName) => new RegExp(`(#_endregion) *${regionName} *\n`, "g"), ], ]; regionTagREsByExt["java"] = [ - ...regionTagREsByExt["ts"], + ...regionTagREsByExt["ts"]!, [ (regionName) => new RegExp(`// * *${regionName} *\n`, "g"), (regionName) => new RegExp(`// * *${regionName} *\n`, "g"), diff --git a/src/lib/models/Comment.ts b/src/lib/models/Comment.ts index 08fb76395..70316f50b 100644 --- a/src/lib/models/Comment.ts +++ b/src/lib/models/Comment.ts @@ -1,4 +1,4 @@ -import { assertNever, i18n, NonEnumerable, type NormalizedPath, removeIf } from "#utils"; +import { assertNever, i18n, NonEnumerable, type NormalizedPath, removeIf, type TagString } from "#utils"; import type { Reflection, ReflectionId } from "./Reflection.js"; import { ReflectionSymbolId } from "./ReflectionSymbolId.js"; @@ -35,7 +35,7 @@ export type CommentDisplayPart = */ export interface InlineTagDisplayPart { kind: "inline-tag"; - tag: `@${string}`; + tag: TagString; text: string; target?: Reflection | string | ReflectionSymbolId; tsLinkText?: string; @@ -80,7 +80,7 @@ export class CommentTag { /** * The name of this tag, e.g. `@returns`, `@example` */ - tag: `@${string}`; + tag: TagString; /** * Some tags, (`@typedef`, `@param`, `@property`, etc.) may have a user defined identifier associated with them. @@ -103,7 +103,7 @@ export class CommentTag { /** * Create a new CommentTag instance. */ - constructor(tag: `@${string}`, text: CommentDisplayPart[]) { + constructor(tag: TagString, text: CommentDisplayPart[]) { this.tag = tag; this.content = text; } @@ -396,7 +396,7 @@ export class Comment { /** * All modifier tags present on the comment, e.g. `@alpha`, `@beta`. */ - modifierTags: Set<`@${string}`> = new Set(); + modifierTags: Set = new Set(); /** * Label associated with this reflection, if any (https://tsdoc.org/pages/tags/label/) @@ -436,7 +436,7 @@ export class Comment { constructor( summary: CommentDisplayPart[] = [], blockTags: CommentTag[] = [], - modifierTags: Set<`@${string}`> = new Set(), + modifierTags: Set = new Set(), ) { this.summary = summary; this.blockTags = blockTags; @@ -542,15 +542,20 @@ export class Comment { } /** - * Has this comment a visible component? + * Checks if this comment contains any visible text. * - * @returns TRUE when this comment has a visible component. + * @returns TRUE when this reflection has a visible comment. */ - hasVisibleComponent(): boolean { - return ( - this.summary.some((x) => x.kind !== "text" || x.text !== "") || - this.blockTags.length > 0 - ); + hasVisibleComponent(notRenderedTags?: readonly TagString[]): boolean { + if (this.summary.some((x) => x.kind !== "text" || x.text !== "")) { + return true; + } + + if (notRenderedTags) { + return this.blockTags.some(tag => !notRenderedTags.includes(tag.tag)); + } else { + return this.blockTags.length > 0; + } } /** @@ -559,11 +564,11 @@ export class Comment { * @param tagName The name of the tag to look for. * @returns TRUE when this comment contains a tag with the given name, otherwise FALSE. */ - hasModifier(tagName: `@${string}`): boolean { + hasModifier(tagName: TagString): boolean { return this.modifierTags.has(tagName); } - removeModifier(tagName: `@${string}`) { + removeModifier(tagName: TagString) { this.modifierTags.delete(tagName); } @@ -573,18 +578,18 @@ export class Comment { * @param tagName The name of the tag to look for. * @returns The found tag or undefined. */ - getTag(tagName: `@${string}`): CommentTag | undefined { + getTag(tagName: TagString): CommentTag | undefined { return this.blockTags.find((tag) => tag.tag === tagName); } /** * Get all tags with the given tag name. */ - getTags(tagName: `@${string}`): CommentTag[] { + getTags(tagName: TagString): CommentTag[] { return this.blockTags.filter((tag) => tag.tag === tagName); } - getIdentifiedTag(identifier: string, tagName: `@${string}`) { + getIdentifiedTag(identifier: string, tagName: TagString) { return this.blockTags.find( (tag) => tag.tag === tagName && tag.name === identifier, ); @@ -594,7 +599,7 @@ export class Comment { * Removes all block tags with the given tag name from the comment. * @param tagName */ - removeTags(tagName: `@${string}`) { + removeTags(tagName: TagString) { removeIf(this.blockTags, (tag) => tag.tag === tagName); } diff --git a/src/lib/models/DeclarationReflection.ts b/src/lib/models/DeclarationReflection.ts index 6f225773b..50b94e773 100644 --- a/src/lib/models/DeclarationReflection.ts +++ b/src/lib/models/DeclarationReflection.ts @@ -354,11 +354,7 @@ export class DeclarationReflection extends ContainerReflection { new ReflectionSymbolId(sid), ); } else { - de.logger.warn( - i18n.serialized_project_referenced_0_not_part_of_project( - id.toString(), - ), - ); + de.logger.warn(i18n.serialized_project_referenced_0_not_part_of_project(id)); } } }); diff --git a/src/lib/models/FileRegistry.ts b/src/lib/models/FileRegistry.ts index 0081c87cc..6cda93a04 100644 --- a/src/lib/models/FileRegistry.ts +++ b/src/lib/models/FileRegistry.ts @@ -85,7 +85,7 @@ export class FileRegistry { project: ProjectReflection, ): string | Reflection | undefined { const reflId = this.mediaToReflection.get(id); - if (reflId) { + if (typeof reflId === "number") { return project.getReflectionById(reflId); } return this.mediaToPath.get(id); diff --git a/src/lib/models/ProjectReflection.ts b/src/lib/models/ProjectReflection.ts index bc9fd66a5..68ae7586f 100644 --- a/src/lib/models/ProjectReflection.ts +++ b/src/lib/models/ProjectReflection.ts @@ -368,7 +368,7 @@ export class ProjectReflection extends ContainerReflection { this.reflectionIdToSymbolIdMap.set(reflection.id, id); const previous = this.symbolToReflectionIdMap.get(id); - if (previous) { + if (typeof previous !== "undefined") { if (typeof previous === "number") { this.symbolToReflectionIdMap.set(id, [previous, reflection.id]); } else { @@ -440,11 +440,7 @@ export class ProjectReflection extends ContainerReflection { if (refl) { this.registerSymbolId(refl, new ReflectionSymbolId(sid)); } else { - de.logger.warn( - i18n.serialized_project_referenced_0_not_part_of_project( - id.toString(), - ), - ); + de.logger.warn(i18n.serialized_project_referenced_0_not_part_of_project(id)); } } }); diff --git a/src/lib/models/Reflection.ts b/src/lib/models/Reflection.ts index 9468671d1..a854fcd62 100644 --- a/src/lib/models/Reflection.ts +++ b/src/lib/models/Reflection.ts @@ -1,7 +1,7 @@ import { Comment } from "./Comment.js"; import { splitUnquotedString } from "./utils.js"; import type { ProjectReflection } from "./ProjectReflection.js"; -import { i18n, type NeverIfInternal, NonEnumerable, type TranslatedString } from "#utils"; +import { i18n, type NeverIfInternal, NonEnumerable, type TagString, type TranslatedString } from "#utils"; import { ReflectionKind } from "./kind.js"; import type { Deserializer, JSONOutput, Serializer } from "#serialization"; import type { ReflectionVariant } from "./variant.js"; @@ -392,12 +392,12 @@ export abstract class Reflection { } /** - * Has this reflection a visible comment? + * Checks if this reflection has a comment which contains any visible text. * * @returns TRUE when this reflection has a visible comment. */ - hasComment(): boolean { - return this.comment ? this.comment.hasVisibleComponent() : false; + hasComment(notRenderedTags?: readonly TagString[]): boolean { + return this.comment ? this.comment.hasVisibleComponent(notRenderedTags) : false; } hasGetterOrSetter(): boolean { diff --git a/src/lib/output/themes/MarkedPlugin.tsx b/src/lib/output/themes/MarkedPlugin.tsx index 6c422dc1f..924b28ca9 100644 --- a/src/lib/output/themes/MarkedPlugin.tsx +++ b/src/lib/output/themes/MarkedPlugin.tsx @@ -232,7 +232,16 @@ export class MarkedPlugin extends ContextAwareRendererComponent { const refl = page.project.files.resolve(part.target, page.model.project); let url: string | undefined; if (typeof refl === "object") { - url = context.urlTo(refl); + // #3006, this is an unfortunate heuristic. If there is a relative link to the project + // the user probably created it by linking to the directory of the project or to + // the project's readme. Since the readme doesn't get its own reflection, we can't + // reliably disambiguate this and instead will arbitrarily decide to reference the + // root index page in this case. + if (refl.isProject()) { + url = context.relativeURL("./"); + } else { + url = context.urlTo(refl); + } } else { const fileName = page.project.files.getName(part.target); if (fileName) { diff --git a/src/lib/output/themes/default/DefaultTheme.tsx b/src/lib/output/themes/default/DefaultTheme.tsx index 3c699240a..10a2c5c99 100644 --- a/src/lib/output/themes/default/DefaultTheme.tsx +++ b/src/lib/output/themes/default/DefaultTheme.tsx @@ -15,7 +15,7 @@ import type { PageEvent, RendererEvent } from "../../events.js"; import type { MarkedPlugin } from "../../plugins/index.js"; import { DefaultThemeRenderContext } from "./DefaultThemeRenderContext.js"; import { getIcons, type Icons } from "./partials/icon.js"; -import { filterMap, JSX } from "#utils"; +import { filterMap, JSX, type TagString } from "#utils"; import { classNames, getDisplayName, toStyleClass } from "../lib.js"; import { PageKind, type Router } from "../../router.js"; import { loadHighlighter, Option } from "#node-utils"; @@ -365,8 +365,8 @@ function getReflectionClasses(reflection: Reflection, filters: Record - refl.comment?.hasModifier(key as `@${string}`) || refl.comment?.getTag(key as `@${string}`), + (refl) => refl.comment?.hasModifier(key as TagString) || refl.comment?.getTag(key as TagString), ) ) { classes.add(toStyleClass(`tsd-is-${key.substring(1)}`)); diff --git a/src/lib/output/themes/default/partials/comment.tsx b/src/lib/output/themes/default/partials/comment.tsx index 1f0109381..46731e511 100644 --- a/src/lib/output/themes/default/partials/comment.tsx +++ b/src/lib/output/themes/default/partials/comment.tsx @@ -69,30 +69,34 @@ export function commentTags(context: DefaultThemeRenderContext, props: Reflectio skipSave.forEach((skip, i) => (props.comment!.blockTags[i].skipRendering = skip)); + const tagsContents = tags.map((item) => { + const name = item.name + ? `${translateTagName(item.tag)}: ${item.name}` + : translateTagName(item.tag); + + const anchor = context.slugger.slug(name); + + return ( + <> +
+ + +
+ + ); + }); + return ( <> {beforeTags} -
- {tags.map((item) => { - const name = item.name - ? `${translateTagName(item.tag)}: ${item.name}` - : translateTagName(item.tag); - - const anchor = context.slugger.slug(name); - - return ( - <> -
- - -
- - ); - })} -
+ {tagsContents.length > 0 && ( +
+ {tagsContents} +
+ )} {afterTags} ); diff --git a/src/lib/output/themes/default/partials/member.declaration.tsx b/src/lib/output/themes/default/partials/member.declaration.tsx index e304723d2..510caa883 100644 --- a/src/lib/output/themes/default/partials/member.declaration.tsx +++ b/src/lib/output/themes/default/partials/member.declaration.tsx @@ -15,7 +15,7 @@ function shouldRenderDefaultValue(props: DeclarationReflection) { if (props.type && props.type.type === "literal") { const reflectionTypeString = props.type.toString(); - if (reflectionTypeString === defaultValue.toString()) { + if (reflectionTypeString === defaultValue) { return false; } } diff --git a/src/lib/output/themes/default/partials/moduleReflection.tsx b/src/lib/output/themes/default/partials/moduleReflection.tsx index 31418441d..89a4dd4d9 100644 --- a/src/lib/output/themes/default/partials/moduleReflection.tsx +++ b/src/lib/output/themes/default/partials/moduleReflection.tsx @@ -16,7 +16,7 @@ export function moduleReflection(context: DefaultThemeRenderContext, mod: Declar return ( <> - {mod.hasComment() && ( + {mod.hasComment(context.options.getValue("notRenderedTags")) && (
{context.commentSummary(mod)} {context.commentTags(mod)} diff --git a/src/lib/output/themes/default/partials/navigation.tsx b/src/lib/output/themes/default/partials/navigation.tsx index 480859b02..39e9efa93 100644 --- a/src/lib/output/themes/default/partials/navigation.tsx +++ b/src/lib/output/themes/default/partials/navigation.tsx @@ -1,5 +1,5 @@ import { type Reflection, ReflectionFlag, ReflectionFlags } from "../../../../models/index.js"; -import { i18n, JSX, translateTagName } from "#utils"; +import { i18n, JSX, type TagString, translateTagName } from "#utils"; import type { PageEvent, PageHeading } from "../../../events.js"; import { classNames, getDisplayName, wbr } from "../../lib.js"; import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js"; @@ -65,7 +65,7 @@ export function settings(context: DefaultThemeRenderContext) { buildFilterItem( context, filterName, - translateTagName(key as `@${string}`), + translateTagName(key as TagString), defaultFilters[key], ), ); diff --git a/src/lib/output/themes/default/partials/typeDetails.tsx b/src/lib/output/themes/default/partials/typeDetails.tsx index d0c1e1e99..fbf4a6748 100644 --- a/src/lib/output/themes/default/partials/typeDetails.tsx +++ b/src/lib/output/themes/default/partials/typeDetails.tsx @@ -7,24 +7,29 @@ import { type SignatureReflection, } from "../../../../models/index.js"; import type { ReferenceType, SomeType, TypeVisitor } from "../../../../models/types.js"; -import { assert, i18n, JSX } from "#utils"; +import { assert, i18n, JSX, type TagString } from "#utils"; import { classNames, getKindClass } from "../../lib.js"; import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js"; import { anchorTargetIfPresent } from "./anchor-icon.js"; -function renderingTypeDetailsIsUseful(container: Reflection, type: SomeType): boolean { +function renderingTypeDetailsIsUseful( + container: Reflection, + type: SomeType, + notRenderedTags: readonly TagString[], +): boolean { const isUsefulVisitor: Partial> = { array(type) { - return renderingTypeDetailsIsUseful(container, type.elementType); + return renderingTypeDetailsIsUseful(container, type.elementType, notRenderedTags); }, intersection(type) { - return type.types.some(t => renderingTypeDetailsIsUseful(container, t)); + return type.types.some(t => renderingTypeDetailsIsUseful(container, t, notRenderedTags)); }, union(type) { - return !!type.elementSummaries || type.types.some(t => renderingTypeDetailsIsUseful(container, t)); + return !!type.elementSummaries || + type.types.some(t => renderingTypeDetailsIsUseful(container, t, notRenderedTags)); }, reflection(type) { - return renderingChildIsUseful(type.declaration); + return renderingChildIsUseful(type.declaration, notRenderedTags); }, reference(type) { return shouldExpandReference(container, type); @@ -44,7 +49,7 @@ export function typeDeclaration( "typeDeclaration(reflectionOwningType, type) called incorrectly", ); - if (renderingTypeDetailsIsUseful(reflectionOwningType, type)) { + if (renderingTypeDetailsIsUseful(reflectionOwningType, type, context.options.getValue("notRenderedTags"))) { return (

{i18n.theme_type_declaration()}

@@ -194,7 +199,7 @@ export function typeDetailsIfUseful( "typeDetailsIfUseful(reflectionOwningType, type) called incorrectly", ); - if (type && renderingTypeDetailsIsUseful(reflectionOwningType, type)) { + if (type && renderingTypeDetailsIsUseful(reflectionOwningType, type, context.options.getValue("notRenderedTags"))) { return context.typeDetails(reflectionOwningType, type, false); } } @@ -314,6 +319,8 @@ function renderChild( // standard type if (child.type) { + const notRenderedTags = context.options.getValue("notRenderedTags"); + return (
  • @@ -327,7 +334,7 @@ function renderChild( {context.type(child.type)}
    {highlightOrComment(child)} - {child.getProperties().some(renderingChildIsUseful) && ( + {child.getProperties().some(prop => renderingChildIsUseful(prop, notRenderedTags)) && (
      {child.getProperties().map((c) => renderChild(context, c, renderAnchors))}
    @@ -401,7 +408,7 @@ function renderIndexSignature(context: DefaultThemeRenderContext, index: Signatu ); } -function renderingChildIsUseful(refl: DeclarationReflection) { +function renderingChildIsUseful(refl: DeclarationReflection, notRenderedTags: readonly TagString[]) { // Object types directly under a variable/type alias will always be considered useful. // This probably isn't ideal, but it is an easy thing to check when assigning URLs // in the default theme, so we'll make the assumption that those properties ought to always @@ -415,20 +422,20 @@ function renderingChildIsUseful(refl: DeclarationReflection) { return true; } - if (renderingThisChildIsUseful(refl)) { + if (renderingThisChildIsUseful(refl, notRenderedTags)) { return true; } - return refl.getProperties().some(renderingThisChildIsUseful); + return refl.getProperties().some(prop => renderingThisChildIsUseful(prop, notRenderedTags)); } -function renderingThisChildIsUseful(refl: DeclarationReflection) { - if (refl.hasComment()) return true; +function renderingThisChildIsUseful(refl: DeclarationReflection, notRenderedTags: readonly TagString[]) { + if (refl.hasComment(notRenderedTags)) return true; const declaration = refl.type?.type === "reflection" ? refl.type.declaration : refl; - if (declaration.hasComment()) return true; + if (declaration.hasComment(notRenderedTags)) return true; return declaration.getAllSignatures().some((sig) => { - return sig.hasComment() || sig.parameters?.some((p) => p.hasComment()); + return sig.hasComment(notRenderedTags) || sig.parameters?.some((p) => p.hasComment(notRenderedTags)); }); } diff --git a/src/lib/output/themes/default/templates/reflection.tsx b/src/lib/output/themes/default/templates/reflection.tsx index 6c949995d..2fd4735a3 100644 --- a/src/lib/output/themes/default/templates/reflection.tsx +++ b/src/lib/output/themes/default/templates/reflection.tsx @@ -27,7 +27,7 @@ export function reflectionTemplate(context: DefaultThemeRenderContext, props: Pa return ( <> - {props.model.hasComment() && ( + {props.model.hasComment(context.options.getValue("notRenderedTags")) && (
    {context.commentSummary(props.model)} {context.commentTags(props.model)} diff --git a/src/lib/serialization/deserializer.ts b/src/lib/serialization/deserializer.ts index d2cb57bde..141c3a29d 100644 --- a/src/lib/serialization/deserializer.ts +++ b/src/lib/serialization/deserializer.ts @@ -134,7 +134,7 @@ export class Deserializer { return new IntrinsicType(obj.name); }, literal(obj) { - if (obj.value && typeof obj.value === "object") { + if (typeof obj.value === "object" && obj.value != null) { return new LiteralType( BigInt( `${obj.value.negative ? "-" : ""}${obj.value.value}`, @@ -352,15 +352,15 @@ export class Deserializer { return project; } - revive>( + revive>( source: NonNullable, creator: (obj: T) => U, ): U; - revive>( + revive>( source: T | undefined, creator: (obj: T) => U, ): U | undefined; - revive>( + revive>( source: T | undefined, creator: (obj: T) => U, ): U | undefined { diff --git a/src/lib/serialization/schema.ts b/src/lib/serialization/schema.ts index 34debec86..d55900d71 100644 --- a/src/lib/serialization/schema.ts +++ b/src/lib/serialization/schema.ts @@ -30,7 +30,7 @@ */ import type * as M from "#models"; -import type { IfInternal, NormalizedPath } from "#utils"; +import type { IfInternal, NormalizedPath, TagString } from "#utils"; // Keep this in sync with JSON_SCHEMA_VERSION in ProjectReflection.ts export const SCHEMA_VERSION = "2.0"; @@ -365,7 +365,7 @@ export interface ReflectionFlags extends Partial> { summary: CommentDisplayPart[]; - modifierTags?: `@${string}`[]; + modifierTags?: TagString[]; } /** @category Comments */ @@ -390,7 +390,7 @@ export type CommentDisplayPart = */ export interface InlineTagDisplayPart { kind: "inline-tag"; - tag: `@${string}`; + tag: TagString; text: string; target?: string | ReflectionId | ReflectionSymbolId; tsLinkText?: string; diff --git a/src/lib/utils-common/i18n.ts b/src/lib/utils-common/i18n.ts index 3ee361204..97044c53d 100644 --- a/src/lib/utils-common/i18n.ts +++ b/src/lib/utils-common/i18n.ts @@ -1,5 +1,6 @@ // Type only import to non-bundled file // eslint-disable-next-line no-restricted-imports +import type { TagString } from "#utils"; import type { TranslationProxy } from "../internationalization/internationalization.js"; let translations: Record = {}; @@ -36,7 +37,7 @@ export const i18n = new Proxy({}, { }, }) as TranslationProxy; -export function translateTagName(tag: `@${string}`): TranslatedString { +export function translateTagName(tag: TagString): TranslatedString { const tagName = tag.substring(1); if (Object.prototype.hasOwnProperty.call(translations, `tag_${tagName}`)) { return translations[`tag_${tagName}`] as TranslatedString; diff --git a/src/lib/utils-common/index.ts b/src/lib/utils-common/index.ts index 135c9068a..be18a0756 100644 --- a/src/lib/utils-common/index.ts +++ b/src/lib/utils-common/index.ts @@ -16,3 +16,4 @@ export * from "./path.js"; export * from "./set.js"; export * from "./string.js"; export * as Validation from "./validation.js"; +export type { TagString } from "./validation.js"; diff --git a/src/lib/utils-common/validation.ts b/src/lib/utils-common/validation.ts index eca8886d9..f6c01aa33 100644 --- a/src/lib/utils-common/validation.ts +++ b/src/lib/utils-common/validation.ts @@ -119,6 +119,8 @@ export function optional(x: T): Optional { return { [opt]: x }; } -export function isTagString(x: unknown): x is `@${string}` { +export type TagString = `@${string}`; + +export function isTagString(x: unknown): x is TagString { return typeof x === "string" && /^@[a-z][a-z0-9-]*$/i.test(x); } diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 61ca04387..ef234c17d 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -11,6 +11,7 @@ import { type NormalizedPath, type NormalizedPathOrModule, type NormalizedPathOrModuleOrFunction, + type TagString, type TranslatedString, } from "#utils"; import type { TranslationProxy } from "../../internationalization/internationalization.js"; @@ -297,7 +298,7 @@ export interface TypeDocOptionMap { private?: boolean; inherited?: boolean; external?: boolean; - [tag: `@${string}`]: boolean; + [tag: TagString]: boolean; }>; searchCategoryBoosts: ManuallyValidatedOption>; searchGroupBoosts: ManuallyValidatedOption>; @@ -309,15 +310,15 @@ export interface TypeDocOptionMap { preserveLinkText: boolean; jsDocCompatibility: JsDocCompatibility; suppressCommentWarningsInDeclarationFiles: boolean; - blockTags: `@${string}`[]; - inlineTags: `@${string}`[]; - modifierTags: `@${string}`[]; - excludeTags: `@${string}`[]; - notRenderedTags: `@${string}`[]; + blockTags: TagString[]; + inlineTags: TagString[]; + modifierTags: TagString[]; + excludeTags: TagString[]; + notRenderedTags: TagString[]; externalSymbolLinkMappings: ManuallyValidatedOption< Record> >; - cascadedModifierTags: `@${string}`[]; + cascadedModifierTags: TagString[]; // Organization categorizeByGroup: boolean; diff --git a/src/lib/utils/options/defaults.ts b/src/lib/utils/options/defaults.ts index 20d6d1472..277f0e035 100644 --- a/src/lib/utils/options/defaults.ts +++ b/src/lib/utils/options/defaults.ts @@ -4,7 +4,7 @@ */ import type { BundledLanguage } from "@gerrit0/mini-shiki"; import * as TagDefaults from "./tsdoc-defaults.js"; -import type { EnumKeys } from "#utils"; +import type { EnumKeys, TagString } from "#utils"; import type { ReflectionKind } from "../../models/index.js"; export const excludeNotDocumentedKinds: readonly EnumKeys< @@ -31,7 +31,7 @@ export const excludeNotDocumentedKinds: readonly EnumKeys< "Reference", ]; -export const excludeTags: readonly `@${string}`[] = [ +export const excludeTags: readonly TagString[] = [ "@override", "@virtual", "@privateRemarks", @@ -41,17 +41,17 @@ export const excludeTags: readonly `@${string}`[] = [ "@inlineType", ]; -export const blockTags: readonly `@${string}`[] = TagDefaults.blockTags; -export const inlineTags: readonly `@${string}`[] = TagDefaults.inlineTags; -export const modifierTags: readonly `@${string}`[] = TagDefaults.modifierTags; +export const blockTags: readonly TagString[] = TagDefaults.blockTags; +export const inlineTags: readonly TagString[] = TagDefaults.inlineTags; +export const modifierTags: readonly TagString[] = TagDefaults.modifierTags; -export const cascadedModifierTags: readonly `@${string}`[] = [ +export const cascadedModifierTags: readonly TagString[] = [ "@alpha", "@beta", "@experimental", ]; -export const notRenderedTags: readonly `@${string}`[] = [ +export const notRenderedTags: readonly TagString[] = [ "@showCategories", "@showGroups", "@hideCategories", diff --git a/src/lib/utils/options/readers/tsconfig.ts b/src/lib/utils/options/readers/tsconfig.ts index 45add3be1..42d1a439f 100644 --- a/src/lib/utils/options/readers/tsconfig.ts +++ b/src/lib/utils/options/readers/tsconfig.ts @@ -5,14 +5,14 @@ import ts from "typescript"; import type { Options, OptionsReader } from "../options.js"; import { isFile } from "../../fs.js"; import { ok } from "assert"; -import { i18n, type Logger, type TranslatedString, unique, Validation } from "#utils"; +import { i18n, type Logger, type TagString, type TranslatedString, unique, Validation } from "#utils"; import { nicePath, normalizePath } from "../../paths.js"; import { createRequire } from "module"; import { tsdocBlockTags, tsdocInlineTags, tsdocModifierTags } from "../tsdoc-defaults.js"; import { findTsConfigFile, getTypeDocOptionsFromTsConfig, readTsConfig } from "../../tsconfig.js"; import { diagnostics } from "../../loggers.js"; -function isSupportForTags(obj: unknown): obj is Record<`@${string}`, boolean> { +function isSupportForTags(obj: unknown): obj is Record { return ( Validation.validate({}, obj) && Object.entries(obj).every(([key, val]) => { @@ -148,15 +148,15 @@ export class TSConfigReader implements OptionsReader { const config = this.readTsDoc(logger, tsdoc); if (!config) return; - const supported = (tag: { tagName: `@${string}` }) => { + const supported = (tag: { tagName: TagString }) => { return config.supportForTags ? !!config.supportForTags[tag.tagName] : true; }; - const blockTags: `@${string}`[] = []; - const inlineTags: `@${string}`[] = []; - const modifierTags: `@${string}`[] = []; + const blockTags: TagString[] = []; + const inlineTags: TagString[] = []; + const modifierTags: TagString[] = []; if (!config.noStandardTags) { blockTags.push(...tsdocBlockTags); diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index c1658768c..6799ee531 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -650,7 +650,7 @@ export function addTypeDocOptions(options: Pick) { }, validate(value) { const knownKeys = ["protected", "private", "inherited", "external"]; - if (!value || typeof value !== "object") { + if (typeof value !== "object" || !value) { throw new Error( i18n.option_0_must_be_an_object("visibilityFilters"), ); diff --git a/src/test/Repository.test.ts b/src/test/Repository.test.ts index f87891295..014addabb 100644 --- a/src/test/Repository.test.ts +++ b/src/test/Repository.test.ts @@ -260,7 +260,7 @@ describe("RepositoryManager - git enabled", () => { it("Handles a nested repository", () => { const sub = normalizePath(join(fix.cwd, "sub_repo/repo.txt")); - const repo = manager.getRepository(sub) as GitRepository; + const repo = manager.getRepository(sub) as GitRepository | undefined; ok(repo); equal(repo.path, normalizePath(join(fix.cwd, "sub_repo"))); equal(repo.getURL(sub, 1), "link:repo.txt"); diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 5d8abb16f..4ccad6fcd 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -131,6 +131,7 @@ export interface NoneGroup { /** @disableGroups */ export interface DisabledGroups { a: 1; + /** [link to readme #3006](./index.ts) */ b(): void; } diff --git a/src/test/merge.test.ts b/src/test/merge.test.ts index 65d58c782..b38356bc2 100644 --- a/src/test/merge.test.ts +++ b/src/test/merge.test.ts @@ -1,8 +1,9 @@ import { deepStrictEqual as equal, ok } from "assert"; import { join } from "path"; -import { Application, type DeclarationReflection, EntryPointStrategy, normalizePath, ReferenceType } from "../index.js"; +import { Application, EntryPointStrategy, normalizePath, ReferenceType } from "../index.js"; import { getConverterBase } from "./programs.js"; import { TestLogger } from "./TestLogger.js"; +import { query } from "./utils.js"; const base = getConverterBase(); @@ -29,12 +30,8 @@ describe("Merging projects", () => { ["alias", "class"], ); - const crossRef = project.getChildByName( - "alias.MergedCrossReference", - ) as DeclarationReflection; - const testClass = project.getChildByName("class.class.TestClass"); - ok(testClass, "Missing test class"); - ok(crossRef, "Missing MergedCrossReference"); + const crossRef = query(project, "alias.MergedCrossReference"); + const testClass = query(project, "class.class.TestClass"); ok(crossRef.type instanceof ReferenceType); ok( diff --git a/src/test/renderer/specs/classes/BaseClass.json b/src/test/renderer/specs/classes/BaseClass.json index 288a179ce..9aa168302 100644 --- a/src/test/renderer/specs/classes/BaseClass.json +++ b/src/test/renderer/specs/classes/BaseClass.json @@ -433,9 +433,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/classes/GenericClass.json b/src/test/renderer/specs/classes/GenericClass.json index 0322a9cfe..8e8824309 100644 --- a/src/test/renderer/specs/classes/GenericClass.json +++ b/src/test/renderer/specs/classes/GenericClass.json @@ -26,14 +26,9 @@ ] }, { - "section.tsd-panel.tsd-comment": [ - { - "div.tsd-comment.tsd-typography": "

    Generic class

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] - } - ] + "section.tsd-panel.tsd-comment": { + "div.tsd-comment.tsd-typography": "

    Generic class

    \n" + } }, { "section.tsd-panel": [ diff --git a/src/test/renderer/specs/classes/ModifiersClass.json b/src/test/renderer/specs/classes/ModifiersClass.json index 17d479fa2..5d76ca4b9 100644 --- a/src/test/renderer/specs/classes/ModifiersClass.json +++ b/src/test/renderer/specs/classes/ModifiersClass.json @@ -218,9 +218,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/classes/RenderClass.json b/src/test/renderer/specs/classes/RenderClass.json index e87d21064..05fb799e2 100644 --- a/src/test/renderer/specs/classes/RenderClass.json +++ b/src/test/renderer/specs/classes/RenderClass.json @@ -26,14 +26,9 @@ ] }, { - "section.tsd-panel.tsd-comment": [ - { - "div.tsd-comment.tsd-typography": "

    Renderer class

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] - } - ] + "section.tsd-panel.tsd-comment": { + "div.tsd-comment.tsd-typography": "

    Renderer class

    \n" + } }, { "tag": "section.tsd-panel.tsd-hierarchy", @@ -107,9 +102,6 @@ }, { "div.tsd-comment.tsd-typography": "

    Index signature

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } @@ -392,9 +384,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": [ { @@ -495,9 +484,6 @@ { "div.tsd-comment.tsd-typography": "

    Property

    \n" }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { @@ -988,9 +974,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": [ { @@ -1093,9 +1076,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { @@ -1190,9 +1170,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/enums/Enumeration.json b/src/test/renderer/specs/enums/Enumeration.json index 3c5e62ba9..35b35df3c 100644 --- a/src/test/renderer/specs/enums/Enumeration.json +++ b/src/test/renderer/specs/enums/Enumeration.json @@ -167,9 +167,6 @@ { "div.tsd-comment.tsd-typography": "

    Value1 comment

    \n" }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { @@ -221,9 +218,6 @@ { "div.tsd-comment.tsd-typography": "

    Value2 comment

    \n" }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/functions/box.json b/src/test/renderer/specs/functions/box.json index 17a67cef3..7d317b856 100644 --- a/src/test/renderer/specs/functions/box.json +++ b/src/test/renderer/specs/functions/box.json @@ -148,9 +148,6 @@ }, { "div.tsd-comment.tsd-typography": "

    Item comment

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } @@ -184,9 +181,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/interfaces/BaseInterface.json b/src/test/renderer/specs/interfaces/BaseInterface.json index 9ccfa435d..0097acc1b 100644 --- a/src/test/renderer/specs/interfaces/BaseInterface.json +++ b/src/test/renderer/specs/interfaces/BaseInterface.json @@ -357,9 +357,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/interfaces/DisabledGroups.json b/src/test/renderer/specs/interfaces/DisabledGroups.json index 14e3d537a..81ddbd754 100644 --- a/src/test/renderer/specs/interfaces/DisabledGroups.json +++ b/src/test/renderer/specs/interfaces/DisabledGroups.json @@ -247,6 +247,9 @@ }, { "div.tsd-description": [ + { + "div.tsd-comment.tsd-typography": "

    link to readme #3006

    \n" + }, { "h4.tsd-returns-title": [ "Returns ", @@ -265,7 +268,7 @@ "props": { "href": "index.ts" }, - "children": "index.ts:134" + "children": "index.ts:135" } ] } diff --git a/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json b/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json index a0a0fbe17..9ee09478a 100644 --- a/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json +++ b/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json @@ -183,9 +183,6 @@ { "div.tsd-comment.tsd-typography": "

    B

    \n" }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/interfaces/NoneCategory.json b/src/test/renderer/specs/interfaces/NoneCategory.json index 24d919665..8f9f12a08 100644 --- a/src/test/renderer/specs/interfaces/NoneCategory.json +++ b/src/test/renderer/specs/interfaces/NoneCategory.json @@ -235,9 +235,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { @@ -303,9 +300,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/interfaces/NoneGroup.json b/src/test/renderer/specs/interfaces/NoneGroup.json index fdca9ae0d..925274d34 100644 --- a/src/test/renderer/specs/interfaces/NoneGroup.json +++ b/src/test/renderer/specs/interfaces/NoneGroup.json @@ -193,9 +193,6 @@ } ] }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index f3dad2abe..35cc03b77 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -16,14 +16,9 @@ ] }, { - "section.tsd-panel.tsd-comment": [ - { - "div.tsd-comment.tsd-typography": "

    Module comment

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] - } - ] + "section.tsd-panel.tsd-comment": { + "div.tsd-comment.tsd-typography": "

    Module comment

    \n" + } }, { "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", diff --git a/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json b/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json index 6f1e2a05b..c78078397 100644 --- a/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json +++ b/src/test/renderer/specs/modules/ExpandType.NestedBehavior1.json @@ -36,11 +36,6 @@ } ] }, - { - "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": [] - } - }, { "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", "props": { diff --git a/src/test/renderer/specs/types/ExpandType.AExpanded.json b/src/test/renderer/specs/types/ExpandType.AExpanded.json index fa0d5b018..948d80e77 100644 --- a/src/test/renderer/specs/types/ExpandType.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.AExpanded.json @@ -36,11 +36,6 @@ } ] }, - { - "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": [] - } - }, { "div.tsd-signature": [ { @@ -291,9 +286,6 @@ }, { "div.tsd-comment.tsd-typography": "

    A

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } diff --git a/src/test/renderer/specs/types/ExpandType.BExpanded.json b/src/test/renderer/specs/types/ExpandType.BExpanded.json index 232327a24..5ff4ad414 100644 --- a/src/test/renderer/specs/types/ExpandType.BExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.BExpanded.json @@ -338,9 +338,6 @@ }, { "div.tsd-comment.tsd-typography": "

    B

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } diff --git a/src/test/renderer/specs/types/ExpandType.Expandable.json b/src/test/renderer/specs/types/ExpandType.Expandable.json index 89de6658c..3415947d4 100644 --- a/src/test/renderer/specs/types/ExpandType.Expandable.json +++ b/src/test/renderer/specs/types/ExpandType.Expandable.json @@ -187,9 +187,6 @@ { "div.tsd-comment.tsd-typography": "

    A

    \n" }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/types/ExpandType.Expandable2.json b/src/test/renderer/specs/types/ExpandType.Expandable2.json index efcacd043..4352329a9 100644 --- a/src/test/renderer/specs/types/ExpandType.Expandable2.json +++ b/src/test/renderer/specs/types/ExpandType.Expandable2.json @@ -187,9 +187,6 @@ { "div.tsd-comment.tsd-typography": "

    C

    \n" }, - { - "div.tsd-comment.tsd-typography": [] - }, { "aside.tsd-sources": { "ul": { diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json index 922d86eae..dc8d54c08 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json @@ -45,11 +45,6 @@ } ] }, - { - "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": [] - } - }, { "div.tsd-signature": [ { @@ -352,9 +347,6 @@ }, { "div.tsd-comment.tsd-typography": "

    B

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json index bcb57d04b..6d9e9baa2 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json @@ -295,9 +295,6 @@ }, { "div.tsd-comment.tsd-typography": "

    A

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } @@ -379,9 +376,6 @@ }, { "div.tsd-comment.tsd-typography": "

    B

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } @@ -463,9 +457,6 @@ }, { "div.tsd-comment.tsd-typography": "

    C

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } diff --git a/src/test/renderer/specs/types/Nested.json b/src/test/renderer/specs/types/Nested.json index 5778fa04a..1e79630df 100644 --- a/src/test/renderer/specs/types/Nested.json +++ b/src/test/renderer/specs/types/Nested.json @@ -26,14 +26,9 @@ ] }, { - "section.tsd-panel.tsd-comment": [ - { - "div.tsd-comment.tsd-typography": "

    Type alias with nested properties

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] - } - ] + "section.tsd-panel.tsd-comment": { + "div.tsd-comment.tsd-typography": "

    Type alias with nested properties

    \n" + } }, { "div.tsd-signature": [ @@ -389,9 +384,6 @@ }, { "div.tsd-comment.tsd-typography": "

    Another value

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] }, @@ -445,9 +437,6 @@ }, { "div.tsd-comment.tsd-typography": "

    More options

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] }, @@ -471,9 +460,6 @@ }, { "div.tsd-comment.tsd-typography": "

    Value

    \n" - }, - { - "div.tsd-comment.tsd-typography": [] } ] } diff --git a/src/test/utils.ts b/src/test/utils.ts index 1830ef414..40b801487 100644 --- a/src/test/utils.ts +++ b/src/test/utils.ts @@ -60,7 +60,7 @@ export function querySig( ): SignatureReflection { const decl = query(project, name); ok( - decl.signatures?.length ?? 0 > index, + (decl.signatures?.length ?? 0) > index, `Reflection "${name}" does not contain signature`, ); return decl.signatures![index]; From bfed02dbb36c0cb0d0fb508da913c89e6ff2a0b6 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Aug 2025 20:29:08 -0600 Subject: [PATCH 098/119] Upgrade deps --- package.json | 12 +- pnpm-lock.yaml | 438 ++++++++++++++++++++++++------------------------- 2 files changed, 225 insertions(+), 225 deletions(-) diff --git a/package.json b/package.json index 970aeb368..a1139a432 100644 --- a/package.json +++ b/package.json @@ -31,17 +31,17 @@ "pnpm": ">= 10" }, "dependencies": { - "@gerrit0/mini-shiki": "^3.9.0", + "@gerrit0/mini-shiki": "^3.12.0", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", - "yaml": "^2.8.0" + "yaml": "^2.8.1" }, "peerDependencies": { "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x" }, "devDependencies": { - "@eslint/js": "^9.32.0", + "@eslint/js": "^9.34.0", "@types/lunr": "^2.3.7", "@types/markdown-it": "^14.1.2", "@types/mocha": "^10.0.10", @@ -49,12 +49,12 @@ "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe", "c8": "^10.1.3", "dprint": "^0.50.1", - "esbuild": "^0.25.8", + "esbuild": "^0.25.9", "eslint": "^9.34.0", "mocha": "^11.7.1", - "puppeteer": "^24.11.1", + "puppeteer": "^24.17.1", "semver": "^7.7.2", - "tsx": "^4.20.3", + "tsx": "^4.20.5", "typescript": "5.9.2", "typescript-eslint": "^8.41.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f20c82e1e..b2ad20010 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@gerrit0/mini-shiki': - specifier: ^3.9.0 - version: 3.9.0 + specifier: ^3.12.0 + version: 3.12.0 lunr: specifier: ^2.3.9 version: 2.3.9 @@ -21,12 +21,12 @@ importers: specifier: ^9.0.5 version: 9.0.5 yaml: - specifier: ^2.8.0 - version: 2.8.0 + specifier: ^2.8.1 + version: 2.8.1 devDependencies: '@eslint/js': - specifier: ^9.32.0 - version: 9.32.0 + specifier: ^9.34.0 + version: 9.34.0 '@types/lunr': specifier: ^2.3.7 version: 2.3.7 @@ -49,29 +49,29 @@ importers: specifier: ^0.50.1 version: 0.50.1 esbuild: - specifier: ^0.25.8 - version: 0.25.8 + specifier: ^0.25.9 + version: 0.25.9 eslint: - specifier: ^9.32.0 - version: 9.32.0 + specifier: ^9.34.0 + version: 9.34.0 mocha: specifier: ^11.7.1 version: 11.7.1 puppeteer: - specifier: ^24.11.1 - version: 24.11.1(typescript@5.9.2) + specifier: ^24.17.1 + version: 24.17.1(typescript@5.9.2) semver: specifier: ^7.7.2 version: 7.7.2 tsx: - specifier: ^4.20.3 - version: 4.20.3 + specifier: ^4.20.5 + version: 4.20.5 typescript: specifier: 5.9.2 version: 5.9.2 typescript-eslint: specifier: ^8.41.0 - version: 8.41.0(eslint@9.32.0)(typescript@5.9.2) + version: 8.41.0(eslint@9.34.0)(typescript@5.9.2) packages: @@ -132,158 +132,158 @@ packages: cpu: [x64] os: [win32] - '@esbuild/aix-ppc64@0.25.8': - resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.8': - resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.8': - resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.8': - resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.8': - resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.8': - resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.8': - resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.8': - resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.8': - resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.8': - resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.8': - resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.8': - resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.8': - resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.8': - resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.8': - resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.8': - resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.8': - resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.8': - resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.8': - resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.8': - resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.8': - resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.8': - resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.8': - resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.8': - resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.8': - resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.8': - resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -302,32 +302,32 @@ packages: resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.0': - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.32.0': - resolution: {integrity: sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==} + '@eslint/js@9.34.0': + resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.4': - resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@gerrit0/mini-shiki@3.9.0': - resolution: {integrity: sha512-p58r5PE/hIKtE7aYzeDYZr4DPrOidwoUFPX3p6rPEZWtb7zWX3b7Bu9LZ17XODFiRO4x/VzTE15KYNEaZ3khuw==} + '@gerrit0/mini-shiki@3.12.0': + resolution: {integrity: sha512-CF1vkfe2ViPtmoFEvtUWilEc4dOCiFzV8+J7/vEISSsslKQ97FjeTPNMCqUhZEiKySmKRgK3UO/CxtkyOp7DvA==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -383,22 +383,22 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@puppeteer/browsers@2.10.5': - resolution: {integrity: sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==} + '@puppeteer/browsers@2.10.8': + resolution: {integrity: sha512-f02QYEnBDE0p8cteNoPYHHjbDuwyfbe4cCIVlNi8/MRicIxFW4w4CfgU0LNgWEID6s06P+hRJ1qjpBLMhPRCiQ==} engines: {node: '>=18'} hasBin: true - '@shikijs/engine-oniguruma@3.9.1': - resolution: {integrity: sha512-WPlL/xqviwS3te4unSGGGfflKsuHLMI6tPdNYvgz/IygcBT6UiwDFSzjBKyebwi5GGSlXsjjdoJLIBnAplmEZw==} + '@shikijs/engine-oniguruma@3.12.0': + resolution: {integrity: sha512-IfDl3oXPbJ/Jr2K8mLeQVpnF+FxjAc7ZPDkgr38uEw/Bg3u638neSrpwqOTnTHXt1aU0Fk1/J+/RBdst1kVqLg==} - '@shikijs/langs@3.9.1': - resolution: {integrity: sha512-Vyy2Yv9PP3Veh3VSsIvNncOR+O93wFsNYgN2B6cCCJlS7H9SKFYc55edsqernsg8WT/zam1cfB6llJsQWLnVhA==} + '@shikijs/langs@3.12.0': + resolution: {integrity: sha512-HIca0daEySJ8zuy9bdrtcBPhcYBo8wR1dyHk1vKrOuwDsITtZuQeGhEkcEfWc6IDyTcom7LRFCH6P7ljGSCEiQ==} - '@shikijs/themes@3.9.1': - resolution: {integrity: sha512-zAykkGECNICCMXpKeVvq04yqwaSuAIvrf8MjsU5bzskfg4XreU+O0B5wdNCYRixoB9snd3YlZ373WV5E/g5T9A==} + '@shikijs/themes@3.12.0': + resolution: {integrity: sha512-/lxvQxSI5s4qZLV/AuFaA4Wt61t/0Oka/P9Lmpr1UV+HydNCczO3DMHOC/CsXCCpbv4Zq8sMD0cDa7mvaVoj0Q==} - '@shikijs/types@3.9.1': - resolution: {integrity: sha512-rqM3T7a0iM1oPKz9iaH/cVgNX9Vz1HERcUcXJ94/fulgVdwqfnhXzGxO4bLrAnh/o5CPLy3IcYedogfV+Ns0Qg==} + '@shikijs/types@3.12.0': + resolution: {integrity: sha512-jsFzm8hCeTINC3OCmTZdhR9DOl/foJWplH2Px0bTi4m8z59fnsueLsweX82oGcjRQ7mfQAluQYKGoH2VzsWY4A==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -628,8 +628,8 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - chromium-bidi@5.1.0: - resolution: {integrity: sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==} + chromium-bidi@8.0.0: + resolution: {integrity: sha512-d1VmE0FD7lxZQHzcDUCKZSNRtRwISXDsdg4HjdTR5+Ll5nQ/vzU12JeNmupD6VWffrPSlrnGhEWlLESKH3VO+g==} peerDependencies: devtools-protocol: '*' @@ -687,8 +687,8 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - devtools-protocol@0.0.1464554: - resolution: {integrity: sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw==} + devtools-protocol@0.0.1475386: + resolution: {integrity: sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==} diff@7.0.0: resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} @@ -721,8 +721,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - esbuild@0.25.8: - resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} engines: {node: '>=18'} hasBin: true @@ -751,8 +751,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.32.0: - resolution: {integrity: sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==} + eslint@9.34.0: + resolution: {integrity: sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1149,12 +1149,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - puppeteer-core@24.11.1: - resolution: {integrity: sha512-I0Gv3jWBRY9E3NTBElp7br7Gaid5RbFTxCRRMHym1kCf0ompO0Pel4REGsGDwMWkg3uwFzIH7t7qXs3T4DKRWA==} + puppeteer-core@24.17.1: + resolution: {integrity: sha512-Msh/kf9k1XFN0wuKiT4/npMmMWOT7kPBEUw01gWvRoKOOoz3It9TEmWjnt4Gl4eO+p73VMrvR+wfa0dm9rfxjw==} engines: {node: '>=18'} - puppeteer@24.11.1: - resolution: {integrity: sha512-QbccB/LgxX4tSZRzr9KQ1Jajdvu3n35Dlf/Otjz0QfR+6mDoZdMWLcWF94uQoC3OJerCyYm5hlU2Ru4nBoId2A==} + puppeteer@24.17.1: + resolution: {integrity: sha512-KIuX0w+0um4TUbm55yFl2WIsbgjya2BHIgW9ylTuhavtwjXCOM7lMo9oLR1jQnCxrFvm9h/Yeb+zfs4nlgntPg==} engines: {node: '>=18'} hasBin: true @@ -1259,8 +1259,8 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - tar-fs@3.0.10: - resolution: {integrity: sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==} + tar-fs@3.1.0: + resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} @@ -1285,8 +1285,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.20.3: - resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} engines: {node: '>=18.0.0'} hasBin: true @@ -1361,8 +1361,8 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yaml@2.8.0: - resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} engines: {node: '>= 14.6'} hasBin: true @@ -1427,87 +1427,87 @@ snapshots: '@dprint/win32-x64@0.50.1': optional: true - '@esbuild/aix-ppc64@0.25.8': + '@esbuild/aix-ppc64@0.25.9': optional: true - '@esbuild/android-arm64@0.25.8': + '@esbuild/android-arm64@0.25.9': optional: true - '@esbuild/android-arm@0.25.8': + '@esbuild/android-arm@0.25.9': optional: true - '@esbuild/android-x64@0.25.8': + '@esbuild/android-x64@0.25.9': optional: true - '@esbuild/darwin-arm64@0.25.8': + '@esbuild/darwin-arm64@0.25.9': optional: true - '@esbuild/darwin-x64@0.25.8': + '@esbuild/darwin-x64@0.25.9': optional: true - '@esbuild/freebsd-arm64@0.25.8': + '@esbuild/freebsd-arm64@0.25.9': optional: true - '@esbuild/freebsd-x64@0.25.8': + '@esbuild/freebsd-x64@0.25.9': optional: true - '@esbuild/linux-arm64@0.25.8': + '@esbuild/linux-arm64@0.25.9': optional: true - '@esbuild/linux-arm@0.25.8': + '@esbuild/linux-arm@0.25.9': optional: true - '@esbuild/linux-ia32@0.25.8': + '@esbuild/linux-ia32@0.25.9': optional: true - '@esbuild/linux-loong64@0.25.8': + '@esbuild/linux-loong64@0.25.9': optional: true - '@esbuild/linux-mips64el@0.25.8': + '@esbuild/linux-mips64el@0.25.9': optional: true - '@esbuild/linux-ppc64@0.25.8': + '@esbuild/linux-ppc64@0.25.9': optional: true - '@esbuild/linux-riscv64@0.25.8': + '@esbuild/linux-riscv64@0.25.9': optional: true - '@esbuild/linux-s390x@0.25.8': + '@esbuild/linux-s390x@0.25.9': optional: true - '@esbuild/linux-x64@0.25.8': + '@esbuild/linux-x64@0.25.9': optional: true - '@esbuild/netbsd-arm64@0.25.8': + '@esbuild/netbsd-arm64@0.25.9': optional: true - '@esbuild/netbsd-x64@0.25.8': + '@esbuild/netbsd-x64@0.25.9': optional: true - '@esbuild/openbsd-arm64@0.25.8': + '@esbuild/openbsd-arm64@0.25.9': optional: true - '@esbuild/openbsd-x64@0.25.8': + '@esbuild/openbsd-x64@0.25.9': optional: true - '@esbuild/openharmony-arm64@0.25.8': + '@esbuild/openharmony-arm64@0.25.9': optional: true - '@esbuild/sunos-x64@0.25.8': + '@esbuild/sunos-x64@0.25.9': optional: true - '@esbuild/win32-arm64@0.25.8': + '@esbuild/win32-arm64@0.25.9': optional: true - '@esbuild/win32-ia32@0.25.8': + '@esbuild/win32-ia32@0.25.9': optional: true - '@esbuild/win32-x64@0.25.8': + '@esbuild/win32-x64@0.25.9': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.32.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0)': dependencies: - eslint: 9.32.0 + eslint: 9.34.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -1520,9 +1520,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.0': {} + '@eslint/config-helpers@0.3.1': {} - '@eslint/core@0.15.1': + '@eslint/core@0.15.2': dependencies: '@types/json-schema': 7.0.15 @@ -1540,21 +1540,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.32.0': {} + '@eslint/js@9.34.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.4': + '@eslint/plugin-kit@0.3.5': dependencies: - '@eslint/core': 0.15.1 + '@eslint/core': 0.15.2 levn: 0.4.1 - '@gerrit0/mini-shiki@3.9.0': + '@gerrit0/mini-shiki@3.12.0': dependencies: - '@shikijs/engine-oniguruma': 3.9.1 - '@shikijs/langs': 3.9.1 - '@shikijs/themes': 3.9.1 - '@shikijs/types': 3.9.1 + '@shikijs/engine-oniguruma': 3.12.0 + '@shikijs/langs': 3.12.0 + '@shikijs/themes': 3.12.0 + '@shikijs/types': 3.12.0 '@shikijs/vscode-textmate': 10.0.2 '@humanfs/core@0.19.1': {} @@ -1605,33 +1605,33 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@puppeteer/browsers@2.10.5': + '@puppeteer/browsers@2.10.8': dependencies: debug: 4.4.1(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 semver: 7.7.2 - tar-fs: 3.0.10 + tar-fs: 3.1.0 yargs: 17.7.2 transitivePeerDependencies: - bare-buffer - supports-color - '@shikijs/engine-oniguruma@3.9.1': + '@shikijs/engine-oniguruma@3.12.0': dependencies: - '@shikijs/types': 3.9.1 + '@shikijs/types': 3.12.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.9.1': + '@shikijs/langs@3.12.0': dependencies: - '@shikijs/types': 3.9.1 + '@shikijs/types': 3.12.0 - '@shikijs/themes@3.9.1': + '@shikijs/themes@3.12.0': dependencies: - '@shikijs/types': 3.9.1 + '@shikijs/types': 3.12.0 - '@shikijs/types@3.9.1': + '@shikijs/types@3.12.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -1674,15 +1674,15 @@ snapshots: '@types/node': 18.19.113 optional: true - '@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.41.0(eslint@9.34.0)(typescript@5.9.2) '@typescript-eslint/scope-manager': 8.41.0 - '@typescript-eslint/type-utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/type-utils': 8.41.0(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.41.0(eslint@9.34.0)(typescript@5.9.2) '@typescript-eslint/visitor-keys': 8.41.0 - eslint: 9.32.0 + eslint: 9.34.0 graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -1691,14 +1691,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.41.0(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/parser@8.41.0(eslint@9.34.0)(typescript@5.9.2)': dependencies: '@typescript-eslint/scope-manager': 8.41.0 '@typescript-eslint/types': 8.41.0 '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) '@typescript-eslint/visitor-keys': 8.41.0 debug: 4.4.1(supports-color@8.1.1) - eslint: 9.32.0 + eslint: 9.34.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -1721,13 +1721,13 @@ snapshots: dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.41.0(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.41.0(eslint@9.34.0)(typescript@5.9.2)': dependencies: '@typescript-eslint/types': 8.41.0 '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.41.0(eslint@9.34.0)(typescript@5.9.2) debug: 4.4.1(supports-color@8.1.1) - eslint: 9.32.0 + eslint: 9.34.0 ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: @@ -1751,13 +1751,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.41.0(eslint@9.32.0)(typescript@5.9.2)': + '@typescript-eslint/utils@8.41.0(eslint@9.34.0)(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0) '@typescript-eslint/scope-manager': 8.41.0 '@typescript-eslint/types': 8.41.0 '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) - eslint: 9.32.0 + eslint: 9.34.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -1875,9 +1875,9 @@ snapshots: dependencies: readdirp: 4.1.2 - chromium-bidi@5.1.0(devtools-protocol@0.0.1464554): + chromium-bidi@8.0.0(devtools-protocol@0.0.1475386): dependencies: - devtools-protocol: 0.0.1464554 + devtools-protocol: 0.0.1475386 mitt: 3.0.1 zod: 3.25.67 @@ -1930,7 +1930,7 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - devtools-protocol@0.0.1464554: {} + devtools-protocol@0.0.1475386: {} diff@7.0.0: {} @@ -1964,34 +1964,34 @@ snapshots: dependencies: is-arrayish: 0.2.1 - esbuild@0.25.8: + esbuild@0.25.9: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.8 - '@esbuild/android-arm': 0.25.8 - '@esbuild/android-arm64': 0.25.8 - '@esbuild/android-x64': 0.25.8 - '@esbuild/darwin-arm64': 0.25.8 - '@esbuild/darwin-x64': 0.25.8 - '@esbuild/freebsd-arm64': 0.25.8 - '@esbuild/freebsd-x64': 0.25.8 - '@esbuild/linux-arm': 0.25.8 - '@esbuild/linux-arm64': 0.25.8 - '@esbuild/linux-ia32': 0.25.8 - '@esbuild/linux-loong64': 0.25.8 - '@esbuild/linux-mips64el': 0.25.8 - '@esbuild/linux-ppc64': 0.25.8 - '@esbuild/linux-riscv64': 0.25.8 - '@esbuild/linux-s390x': 0.25.8 - '@esbuild/linux-x64': 0.25.8 - '@esbuild/netbsd-arm64': 0.25.8 - '@esbuild/netbsd-x64': 0.25.8 - '@esbuild/openbsd-arm64': 0.25.8 - '@esbuild/openbsd-x64': 0.25.8 - '@esbuild/openharmony-arm64': 0.25.8 - '@esbuild/sunos-x64': 0.25.8 - '@esbuild/win32-arm64': 0.25.8 - '@esbuild/win32-ia32': 0.25.8 - '@esbuild/win32-x64': 0.25.8 + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 escalade@3.2.0: {} @@ -2014,16 +2014,16 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.32.0: + eslint@9.34.0: dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.15.1 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.32.0 - '@eslint/plugin-kit': 0.3.4 + '@eslint/js': 9.34.0 + '@eslint/plugin-kit': 0.3.5 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -2456,12 +2456,12 @@ snapshots: punycode@2.3.1: {} - puppeteer-core@24.11.1: + puppeteer-core@24.17.1: dependencies: - '@puppeteer/browsers': 2.10.5 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) + '@puppeteer/browsers': 2.10.8 + chromium-bidi: 8.0.0(devtools-protocol@0.0.1475386) debug: 4.4.1(supports-color@8.1.1) - devtools-protocol: 0.0.1464554 + devtools-protocol: 0.0.1475386 typed-query-selector: 2.12.0 ws: 8.18.3 transitivePeerDependencies: @@ -2470,13 +2470,13 @@ snapshots: - supports-color - utf-8-validate - puppeteer@24.11.1(typescript@5.9.2): + puppeteer@24.17.1(typescript@5.9.2): dependencies: - '@puppeteer/browsers': 2.10.5 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) + '@puppeteer/browsers': 2.10.8 + chromium-bidi: 8.0.0(devtools-protocol@0.0.1475386) cosmiconfig: 9.0.0(typescript@5.9.2) - devtools-protocol: 0.0.1464554 - puppeteer-core: 24.11.1 + devtools-protocol: 0.0.1475386 + puppeteer-core: 24.17.1 typed-query-selector: 2.12.0 transitivePeerDependencies: - bare-buffer @@ -2578,7 +2578,7 @@ snapshots: dependencies: has-flag: 4.0.0 - tar-fs@3.0.10: + tar-fs@3.1.0: dependencies: pump: 3.0.3 tar-stream: 3.1.7 @@ -2614,9 +2614,9 @@ snapshots: tslib@2.8.1: {} - tsx@4.20.3: + tsx@4.20.5: dependencies: - esbuild: 0.25.8 + esbuild: 0.25.9 get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 @@ -2627,13 +2627,13 @@ snapshots: typed-query-selector@2.12.0: {} - typescript-eslint@8.41.0(eslint@9.32.0)(typescript@5.9.2): + typescript-eslint@8.41.0(eslint@9.34.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.32.0)(typescript@5.9.2))(eslint@9.32.0)(typescript@5.9.2) - '@typescript-eslint/parser': 8.41.0(eslint@9.32.0)(typescript@5.9.2) + '@typescript-eslint/eslint-plugin': 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0)(typescript@5.9.2))(eslint@9.34.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.41.0(eslint@9.34.0)(typescript@5.9.2) '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.41.0(eslint@9.32.0)(typescript@5.9.2) - eslint: 9.32.0 + '@typescript-eslint/utils': 8.41.0(eslint@9.34.0)(typescript@5.9.2) + eslint: 9.34.0 typescript: 5.9.2 transitivePeerDependencies: - supports-color @@ -2680,7 +2680,7 @@ snapshots: y18n@5.0.8: {} - yaml@2.8.0: {} + yaml@2.8.1: {} yargs-parser@21.1.1: {} From 0c3ad25a27b443b7a9048ad740a7461a5d6c2264 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Aug 2025 20:31:39 -0600 Subject: [PATCH 099/119] Fix new ESLint issues --- .../themes/default/partials/member.signature.body.tsx | 2 +- src/lib/output/themes/default/partials/typeDetails.tsx | 8 ++++---- src/lib/utils-common/i18n.ts | 2 +- src/lib/utils/options/readers/arguments.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/output/themes/default/partials/member.signature.body.tsx b/src/lib/output/themes/default/partials/member.signature.body.tsx index f22486924..627721300 100644 --- a/src/lib/output/themes/default/partials/member.signature.body.tsx +++ b/src/lib/output/themes/default/partials/member.signature.body.tsx @@ -25,7 +25,7 @@ export function memberSignatureBody(
  • {context.reflectionFlags(item)} - {!!item.flags.isRest && ...} + {item.flags.isRest && ...} {item.name} {": "} {context.type(item.type)} diff --git a/src/lib/output/themes/default/partials/typeDetails.tsx b/src/lib/output/themes/default/partials/typeDetails.tsx index fbf4a6748..829a81de8 100644 --- a/src/lib/output/themes/default/partials/typeDetails.tsx +++ b/src/lib/output/themes/default/partials/typeDetails.tsx @@ -295,9 +295,9 @@ function renderChild( return (
  • - {!!child.flags.isRest && ...} + {child.flags.isRest && ...} {child.name} - {!!child.flags.isOptional && "?"}: function + {child.flags.isOptional && "?"}: function
    {context.memberSignatures(child)} @@ -325,10 +325,10 @@ function renderChild(
  • {context.reflectionFlags(child)} - {!!child.flags.isRest && ...} + {child.flags.isRest && ...} {child.name} - {!!child.flags.isOptional && "?"} + {child.flags.isOptional && "?"} {": "} {context.type(child.type)} diff --git a/src/lib/utils-common/i18n.ts b/src/lib/utils-common/i18n.ts index 97044c53d..22129a2dc 100644 --- a/src/lib/utils-common/i18n.ts +++ b/src/lib/utils-common/i18n.ts @@ -1,7 +1,7 @@ // Type only import to non-bundled file // eslint-disable-next-line no-restricted-imports -import type { TagString } from "#utils"; import type { TranslationProxy } from "../internationalization/internationalization.js"; +import type { TagString } from "./validation.js"; let translations: Record = {}; diff --git a/src/lib/utils/options/readers/arguments.ts b/src/lib/utils/options/readers/arguments.ts index 9b277d526..2e8dbb3c6 100644 --- a/src/lib/utils/options/readers/arguments.ts +++ b/src/lib/utils/options/readers/arguments.ts @@ -81,7 +81,7 @@ export class ArgumentsReader implements OptionsReader { decl.type === ParameterType.Boolean || decl.type === ParameterType.Flags ) { - const value = String(this.args[index]).toLowerCase(); + const value = String(this.args.at(index)).toLowerCase(); if (value === "true" || value === "false") { trySet(decl.name, value === "true"); @@ -114,7 +114,7 @@ export class ArgumentsReader implements OptionsReader { if (decl && decl.type === ParameterType.Flags) { const flagName = name.split(".", 2)[1]; - const value = String(this.args[index]).toLowerCase(); + const value = String(this.args.at(index)).toLowerCase(); if (value === "true" || value === "false") { trySet(decl.name, { [flagName]: value === "true" }); From 40f17fba3a5073c832e8102c8a7125464b2c28a0 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 31 Aug 2025 20:36:48 -0600 Subject: [PATCH 100/119] Bump version to 0.28.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1139a432..a10ecdb0a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.11", + "version": "0.28.12", "homepage": "https://typedoc.org", "type": "module", "exports": { From b3a4869bcbc054a37d0fbb0bc71339325a26f2ee Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Mon, 1 Sep 2025 02:37:35 +0000 Subject: [PATCH 101/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e5524ac..02338a966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.12 (2025-09-01) + ### Bug Fixes - Variables marked with `@enum` now work for symbols imported from another module, #3003. From 1269e3ab6d169e89724328ada21a14ecaba89525 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Tue, 2 Sep 2025 08:31:10 -0600 Subject: [PATCH 102/119] Make parent links even more restrictive Resolves #3007 --- CHANGELOG.md | 4 + scripts/testcase.js | 3 +- src/lib/converter/plugins/ImplementsPlugin.ts | 9 +- src/test/converter2/renderer/gh3007.ts | 16 + src/test/converter2/renderer/index.ts | 1 + .../specs/classes/GH3007.DOMBase.json | 505 ++++++++++++++++++ .../specs/classes/GH3007.DOMClass.json | 288 ++++++++++ .../specs/interfaces/GH3007.DOMIterable.json | 343 ++++++++++++ src/test/renderer/specs/modules.json | 32 ++ src/test/renderer/specs/modules/GH3007.json | 244 +++++++++ 10 files changed, 1441 insertions(+), 4 deletions(-) create mode 100644 src/test/converter2/renderer/gh3007.ts create mode 100644 src/test/renderer/specs/classes/GH3007.DOMBase.json create mode 100644 src/test/renderer/specs/classes/GH3007.DOMClass.json create mode 100644 src/test/renderer/specs/interfaces/GH3007.DOMIterable.json create mode 100644 src/test/renderer/specs/modules/GH3007.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 02338a966..aa89c3007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ title: Changelog ## Unreleased +### Bug Fixes + +- Fixed bug introduced in 0.28.8 where TypeDoc could not render docs with some mixin classes, #3007. + ## v0.28.12 (2025-09-01) ### Bug Fixes diff --git a/scripts/testcase.js b/scripts/testcase.js index 2a5c0d744..0756dae45 100755 --- a/scripts/testcase.js +++ b/scripts/testcase.js @@ -66,7 +66,8 @@ async function main() { const ext = process.argv[3] ? `.${process.argv[3]}` : guessExtension(code); const file = `src/test/converter2/issues/gh${issue}${ext}`; await writeFile(file, code.content); - await exec(`code ${file} src/test/issues.c2.test.ts`); + console.log(file); + console.log("src/test/issues.c2.test.ts"); } void main(); diff --git a/src/lib/converter/plugins/ImplementsPlugin.ts b/src/lib/converter/plugins/ImplementsPlugin.ts index cab484371..383a4ac2c 100644 --- a/src/lib/converter/plugins/ImplementsPlugin.ts +++ b/src/lib/converter/plugins/ImplementsPlugin.ts @@ -172,10 +172,13 @@ export class ImplementsPlugin extends ConverterComponent { // serialization/deserialization, might point to an unexpected location. (See the mixin // converter tests, I suspect this might actually be an indication of something else slightly // broken there, but don't want to spend more time with this right now.) - // #2982, even more unfortunately, we only want to keep the link if it is pointing to a reflection - // which will receive a link during rendering. + // #2982/#3007, even more unfortunately, we only want to keep the link if it is pointing + // to a reflection which will receive a link during rendering, we pick this based on it being + // the type of member we expect to point to. const isValidRef = (ref: ReferenceType) => - ref.reflection && !ref.reflection.parent?.kindOf(ReflectionKind.TypeLiteral); + !!ref.reflection?.parent?.kindOf( + ReflectionKind.ClassOrInterface | ReflectionKind.Method | ReflectionKind.Constructor, + ); for (const child of reflection.children || []) { if (child.inheritedFrom && !isValidRef(child.inheritedFrom)) { diff --git a/src/test/converter2/renderer/gh3007.ts b/src/test/converter2/renderer/gh3007.ts new file mode 100644 index 000000000..5b00f7d70 --- /dev/null +++ b/src/test/converter2/renderer/gh3007.ts @@ -0,0 +1,16 @@ +interface MixinConstructor U, U> { + new (...args: ConstructorParameters): U; +} + +export declare class DOMBase { + [Symbol.iterator](): Iterator; +} + +export interface DOMIterable extends Partial> { +} + +declare const DOMClass_base: MixinConstructor & object>; + +export declare class DOMClass extends DOMClass_base { + private constructor(); +} diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 4ccad6fcd..05a839724 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -138,4 +138,5 @@ export interface DisabledGroups { export * as ExpandType from "./expandType"; export * as GH2982 from "./gh2982"; export { gh2995 } from "./gh2995"; +export * as GH3007 from "./gh3007"; export { box as boxAlias }; diff --git a/src/test/renderer/specs/classes/GH3007.DOMBase.json b/src/test/renderer/specs/classes/GH3007.DOMBase.json new file mode 100644 index 000000000..18378c675 --- /dev/null +++ b/src/test/renderer/specs/classes/GH3007.DOMBase.json @@ -0,0 +1,505 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": [ + { + "li": { + "tag": "a", + "props": { + "href": "../modules/GH3007.json" + }, + "children": "GH3007" + } + }, + { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "DOMBase" + } + } + ] + }, + { + "h1": "Class DOMBase" + } + ] + }, + { + "section.tsd-panel": [ + { + "h4": "Type Parameters" + }, + { + "ul.tsd-type-parameter-list": { + "li": { + "span#t": [ + { + "span.tsd-kind-type-parameter": "T" + }, + " ", + { + "span.tsd-signature-keyword": "extends" + }, + " ", + { + "span.tsd-signature-type": "Node" + } + ] + } + } + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3007.ts" + }, + "children": "gh3007.ts:5" + } + ] + } + } + }, + { + "section.tsd-panel-group.tsd-index-group": { + "section.tsd-panel.tsd-index-panel": { + "tag": "details.tsd-index-content.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary.tsd-index-summary": { + "h5.tsd-index-heading.uppercase": "Index" + } + }, + { + "div.tsd-accordion-details": [ + { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Constructors" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link", + "props": { + "href": "#constructor" + }, + "children": { + "span": "constructor" + } + }, + "\n" + ] + } + ] + }, + { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Methods" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link", + "props": { + "href": "#iterator" + }, + "children": { + "span": "[iterator]" + } + }, + "\n" + ] + } + ] + } + ] + } + ] + } + } + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Constructors" + }, + "children": { + "h2": "Constructors" + } + }, + { + "section": { + "section.tsd-panel.tsd-member": [ + { + "h3.tsd-anchor-link#constructor": [ + { + "span": "constructor" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#constructor", + "aria-label": "Permalink" + } + } + ] + }, + { + "ul.tsd-signatures": { + "li.": [ + { + "div.tsd-signature.tsd-anchor-link#constructordombase": [ + { + "span.tsd-signature-keyword": "new" + }, + " ", + { + "span.tsd-kind-constructor-signature": "DOMBase" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "tag": "a.tsd-signature-type.tsd-kind-type-parameter", + "props": { + "href": "#constructordombaset" + }, + "children": "T" + }, + " ", + { + "span.tsd-signature-keyword": "extends" + }, + " ", + { + "span.tsd-signature-type": "Node" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "span.tsd-signature-symbol": "()" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "tag": "a.tsd-signature-type.tsd-kind-class", + "props": { + "href": "" + }, + "children": "DOMBase" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "tag": "a.tsd-signature-type.tsd-kind-type-parameter", + "props": { + "href": "#constructordombaset" + }, + "children": "T" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#constructordombase", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-description": [ + { + "section.tsd-panel": [ + { + "h4": "Type Parameters" + }, + { + "ul.tsd-type-parameter-list": { + "li": { + "span#constructordombaset": [ + { + "span.tsd-kind-type-parameter": "T" + }, + " ", + { + "span.tsd-signature-keyword": "extends" + }, + " ", + { + "span.tsd-signature-type": "Node" + } + ] + } + } + } + ] + }, + { + "h4.tsd-returns-title": [ + "Returns ", + { + "tag": "a.tsd-signature-type.tsd-kind-class", + "props": { + "href": "" + }, + "children": "DOMBase" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "tag": "a.tsd-signature-type.tsd-kind-type-parameter", + "props": { + "href": "#constructordombaset" + }, + "children": "T" + }, + { + "span.tsd-signature-symbol": ">" + } + ] + } + ] + } + ] + } + } + ] + } + } + ] + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Methods" + }, + "children": { + "h2": "Methods" + } + }, + { + "section": { + "section.tsd-panel.tsd-member": [ + { + "h3.tsd-anchor-link#iterator": [ + { + "span": "[iterator]" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#iterator", + "aria-label": "Permalink" + } + } + ] + }, + { + "ul.tsd-signatures": { + "li.": [ + { + "div.tsd-signature.tsd-anchor-link#iterator-1": [ + { + "span.tsd-kind-call-signature": "\"[iterator]\"" + }, + { + "span.tsd-signature-symbol": "()" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "Iterator" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "tag": "a.tsd-signature-type.tsd-kind-type-parameter", + "props": { + "href": "#constructordombaset" + }, + "children": "T" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#iterator-1", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-description": [ + { + "h4.tsd-returns-title": [ + "Returns ", + { + "span.tsd-signature-type": "Iterator" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "tag": "a.tsd-signature-type.tsd-kind-type-parameter", + "props": { + "href": "#constructordombaset" + }, + "children": "T" + }, + { + "span.tsd-signature-symbol": ">" + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3007.ts" + }, + "children": "gh3007.ts:6" + } + ] + } + } + } + ] + } + ] + } + } + ] + } + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": [ + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Constructors" + }, + "children": "Constructors" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#constructor" + }, + "children": { + "span": "constructor" + } + } + } + ] + }, + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Methods" + }, + "children": "Methods" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#iterator" + }, + "children": { + "span": "[iterator]" + } + } + } + ] + } + ] + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/classes/GH3007.DOMClass.json b/src/test/renderer/specs/classes/GH3007.DOMClass.json new file mode 100644 index 000000000..8bfe89f19 --- /dev/null +++ b/src/test/renderer/specs/classes/GH3007.DOMClass.json @@ -0,0 +1,288 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": [ + { + "li": { + "tag": "a", + "props": { + "href": "../modules/GH3007.json" + }, + "children": "GH3007" + } + }, + { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "DOMClass" + } + } + ] + }, + { + "h1": "Class DOMClass" + } + ] + }, + { + "tag": "section.tsd-panel.tsd-hierarchy", + "props": { + "data-refl": "146" + }, + "children": [ + { + "h4": "Hierarchy" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": [ + { + "span.tsd-signature-type": "DOMClass_base" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": { + "span.tsd-hierarchy-target": "DOMClass" + } + } + } + ] + } + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3007.ts" + }, + "children": "gh3007.ts:14" + } + ] + } + } + }, + { + "section.tsd-panel-group.tsd-index-group": { + "section.tsd-panel.tsd-index-panel": { + "tag": "details.tsd-index-content.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary.tsd-index-summary": { + "h5.tsd-index-heading.uppercase": "Index" + } + }, + { + "div.tsd-accordion-details": { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Methods" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link.tsd-is-inherited", + "props": { + "href": "#iterator" + }, + "children": { + "span": "[iterator]" + } + }, + "\n" + ] + } + ] + } + } + ] + } + } + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Methods" + }, + "children": { + "h2": "Methods" + } + }, + { + "section": { + "section.tsd-panel.tsd-member.tsd-is-inherited": [ + { + "h3.tsd-anchor-link#iterator": [ + { + "span": "[iterator]" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#iterator", + "aria-label": "Permalink" + } + } + ] + }, + { + "ul.tsd-signatures.tsd-is-inherited": { + "li.tsd-is-inherited": [ + { + "div.tsd-signature.tsd-anchor-link#iterator-1": [ + { + "span.tsd-kind-call-signature": "\"[iterator]\"" + }, + { + "span.tsd-signature-symbol": "()" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "span.tsd-signature-type": "Iterator" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "span.tsd-signature-type": "Node" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#iterator-1", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-description": [ + { + "h4.tsd-returns-title": [ + "Returns ", + { + "span.tsd-signature-type": "Iterator" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "span.tsd-signature-type": "Node" + }, + { + "span.tsd-signature-symbol": ">" + } + ] + }, + { + "aside.tsd-sources": [ + { + "p": "Inherited from DOMClass_base.[iterator]" + }, + { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3007.ts" + }, + "children": "gh3007.ts:6" + } + ] + } + } + ] + } + ] + } + ] + } + } + ] + } + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Methods" + }, + "children": "Methods" + }, + { + "div": { + "tag": "a.tsd-is-inherited", + "props": { + "href": "#iterator" + }, + "children": { + "span": "[iterator]" + } + } + } + ] + } + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/interfaces/GH3007.DOMIterable.json b/src/test/renderer/specs/interfaces/GH3007.DOMIterable.json new file mode 100644 index 000000000..e907f6478 --- /dev/null +++ b/src/test/renderer/specs/interfaces/GH3007.DOMIterable.json @@ -0,0 +1,343 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": [ + { + "li": { + "tag": "a", + "props": { + "href": "../modules/GH3007.json" + }, + "children": "GH3007" + } + }, + { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "DOMIterable" + } + } + ] + }, + { + "h1": "Interface DOMIterable" + } + ] + }, + { + "div.tsd-signature": [ + { + "span.tsd-signature-keyword": "interface" + }, + " ", + { + "span.tsd-kind-interface": "DOMIterable" + }, + " ", + { + "span.tsd-signature-symbol": "{" + }, + { + "br": [] + }, + " ", + { + "tag": "a.tsd-kind-property", + "props": { + "href": "#iterator" + }, + "children": "\"[iterator]\"" + }, + { + "span.tsd-signature-symbol": "?:" + }, + " ", + { + "span.tsd-signature-symbol": "()" + }, + " ", + { + "span.tsd-signature-symbol": "=>" + }, + " ", + { + "span.tsd-signature-type": "Iterator" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "span.tsd-signature-type": "Node" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "span.tsd-signature-symbol": ";" + }, + { + "br": [] + }, + { + "span.tsd-signature-symbol": "}" + } + ] + }, + { + "tag": "section.tsd-panel.tsd-hierarchy", + "props": { + "data-refl": "142" + }, + "children": [ + { + "h4": "Hierarchy" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": [ + { + "span.tsd-signature-type": "Partial" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "tag": "a.tsd-signature-type.tsd-kind-class", + "props": { + "href": "../classes/GH3007.DOMBase.json" + }, + "children": "DOMBase" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "span.tsd-signature-type": "Node" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "span.tsd-signature-symbol": ">" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": { + "span.tsd-hierarchy-target": "DOMIterable" + } + } + } + ] + } + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3007.ts" + }, + "children": "gh3007.ts:9" + } + ] + } + } + }, + { + "section.tsd-panel-group.tsd-index-group": { + "section.tsd-panel.tsd-index-panel": { + "tag": "details.tsd-index-content.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary.tsd-index-summary": { + "h5.tsd-index-heading.uppercase": "Index" + } + }, + { + "div.tsd-accordion-details": { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Properties" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link.tsd-is-inherited", + "props": { + "href": "#iterator" + }, + "children": { + "span": "[iterator]?" + } + }, + "\n" + ] + } + ] + } + } + ] + } + } + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Properties" + }, + "children": { + "h2": "Properties" + } + }, + { + "section": { + "section.tsd-panel.tsd-member.tsd-is-inherited": [ + { + "h3.tsd-anchor-link#iterator": [ + { + "code.tsd-tag": "Optional" + }, + { + "span": "[iterator]" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#iterator", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-signature": [ + { + "span.tsd-kind-property": "\"[iterator]\"" + }, + { + "span.tsd-signature-symbol": "?:" + }, + " ", + { + "span.tsd-signature-symbol": "()" + }, + " ", + { + "span.tsd-signature-symbol": "=>" + }, + " ", + { + "span.tsd-signature-type": "Iterator" + }, + { + "span.tsd-signature-symbol": "<" + }, + { + "span.tsd-signature-type": "Node" + }, + { + "span.tsd-signature-symbol": ">" + } + ] + }, + { + "aside.tsd-sources": [ + { + "p": "Inherited from Partial.[iterator]" + }, + { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3007.ts" + }, + "children": "gh3007.ts:6" + } + ] + } + } + ] + } + ] + } + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Properties" + }, + "children": "Properties" + }, + { + "div": { + "tag": "a.tsd-is-inherited", + "props": { + "href": "#iterator" + }, + "children": { + "span": "[iterator]" + } + } + } + ] + } + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index 35cc03b77..a11456c96 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -124,6 +124,29 @@ ] } }, + { + "dd.tsd-member-summary": [] + }, + { + "dt.tsd-member-summary#gh3007": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "modules/GH3007.json" + }, + "children": "GH3007" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#gh3007", + "aria-label": "Permalink" + } + } + ] + } + }, { "dd.tsd-member-summary": [] } @@ -666,6 +689,15 @@ "children": { "span": "GH2982" } + }, + { + "tag": "a", + "props": { + "href": "#gh3007" + }, + "children": { + "span": "GH3007" + } } ] } diff --git a/src/test/renderer/specs/modules/GH3007.json b/src/test/renderer/specs/modules/GH3007.json new file mode 100644 index 000000000..f409f158f --- /dev/null +++ b/src/test/renderer/specs/modules/GH3007.json @@ -0,0 +1,244 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "GH3007" + } + } + }, + { + "h1": "Namespace GH3007" + } + ] + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Classes" + }, + "children": { + "h2": "Classes" + } + }, + { + "dl.tsd-member-summaries": [ + { + "dt.tsd-member-summary#dombase": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "../classes/GH3007.DOMBase.json" + }, + "children": "DOMBase" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#dombase", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + }, + { + "dt.tsd-member-summary#domclass": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "../classes/GH3007.DOMClass.json" + }, + "children": "DOMClass" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#domclass", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + } + ] + } + ] + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Interfaces" + }, + "children": { + "h2": "Interfaces" + } + }, + { + "dl.tsd-member-summaries": [ + { + "dt.tsd-member-summary#domiterable": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "../interfaces/GH3007.DOMIterable.json" + }, + "children": "DOMIterable" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#domiterable", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + } + ] + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": [ + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Classes" + }, + "children": "Classes" + }, + { + "div": [ + { + "tag": "a", + "props": { + "href": "#dombase" + }, + "children": { + "span": [ + "DOM", + { + "wbr": [] + }, + "Base" + ] + } + }, + { + "tag": "a", + "props": { + "href": "#domclass" + }, + "children": { + "span": [ + "DOM", + { + "wbr": [] + }, + "Class" + ] + } + } + ] + } + ] + }, + { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Interfaces" + }, + "children": "Interfaces" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#domiterable" + }, + "children": { + "span": [ + "DOM", + { + "wbr": [] + }, + "Iterable" + ] + } + } + } + ] + } + ] + } + ] + } + } + } + ] +} From e0fd143143f10538f32c63487e6bae953ac06d54 Mon Sep 17 00:00:00 2001 From: Christopher Hiller Date: Sat, 6 Sep 2025 02:52:19 -0700 Subject: [PATCH 103/119] docs(tags): fix link to packageDocumentation This link was broken. --- site/tags/packageDocumentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/tags/packageDocumentation.md b/site/tags/packageDocumentation.md index eac3d3690..f1e54c5d6 100644 --- a/site/tags/packageDocumentation.md +++ b/site/tags/packageDocumentation.md @@ -5,7 +5,7 @@ title: "@packageDocumentation" # @packageDocumentation **Tag Kind:** [Modifier](../tags.md#modifier-tags)
    -**TSDoc Reference:** [@packageDocumentation](https://tsdoc.org/pages/tags/packageDocumentation/) +**TSDoc Reference:** [@packageDocumentation](https://tsdoc.org/pages/tags/packagedocumentation/) The `@packageDocumentation` tag is used to mark a comment as referring to a file rather than the declaration following it. The TypeDoc specific [`@module`](module.md) tag can be used for the same purpose when semantically clearer. From 8f94288e5d46a37847dfd9527966e4a64076b548 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 13 Sep 2025 10:52:24 -0600 Subject: [PATCH 104/119] Fix #3012 --- CHANGELOG.md | 1 + src/lib/converter/comments/parser.ts | 7 +++++++ src/lib/converter/plugins/InheritDocPlugin.ts | 2 ++ src/lib/internationalization/locales/en.cts | 2 ++ src/test/converter2/issues/gh3012.ts | 12 ++++++++++++ src/test/issues.c2.test.ts | 9 +++++++++ 6 files changed, 33 insertions(+) create mode 100644 src/test/converter2/issues/gh3012.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index aa89c3007..d907c6674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ title: Changelog ### Bug Fixes - Fixed bug introduced in 0.28.8 where TypeDoc could not render docs with some mixin classes, #3007. +- `@inheritDoc` will now correctly overwrite `@remarks` and `@returns` blocks on the target comment, #3012. ## v0.28.12 (2025-09-01) diff --git a/src/lib/converter/comments/parser.ts b/src/lib/converter/comments/parser.ts index 924054b77..5d2adeb49 100644 --- a/src/lib/converter/comments/parser.ts +++ b/src/lib/converter/comments/parser.ts @@ -346,6 +346,13 @@ function postProcessComment( ), ); } + if ((inlineInheritDoc.length || inheritDoc.length) && returns.length) { + warning( + i18n.content_in_returns_block_overwritten_by_inheritdoc_in_comment_at_0( + getPosition(), + ), + ); + } } const aliasedTags = new Map([["@return", "@returns"]]); diff --git a/src/lib/converter/plugins/InheritDocPlugin.ts b/src/lib/converter/plugins/InheritDocPlugin.ts index 56b53409c..b9763e65f 100644 --- a/src/lib/converter/plugins/InheritDocPlugin.ts +++ b/src/lib/converter/plugins/InheritDocPlugin.ts @@ -150,6 +150,8 @@ export class InheritDocPlugin extends ConverterComponent { } target.comment.removeTags("@inheritDoc"); + target.comment.removeTags("@remarks"); + target.comment.removeTags("@returns"); target.comment.summary = Comment.cloneDisplayParts( source.comment.summary, ); diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index 301cbf806..db746e7bd 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -81,6 +81,8 @@ export = { "Content in the summary section will be overwritten by the @inheritDoc tag in comment at {0}", content_in_remarks_block_overwritten_by_inheritdoc_in_comment_at_0: "Content in the @remarks block will be overwritten by the @inheritDoc tag in comment at {0}", + content_in_returns_block_overwritten_by_inheritdoc_in_comment_at_0: + "Content in the @returns block will be overwritten by the @inheritDoc tag in comment at {0}", example_tag_literal_name: "The first line of an example tag will be taken literally as the example name, and should only contain text", inheritdoc_tag_properly_capitalized: "The @inheritDoc tag should be properly capitalized", diff --git a/src/test/converter2/issues/gh3012.ts b/src/test/converter2/issues/gh3012.ts new file mode 100644 index 000000000..6a226968d --- /dev/null +++ b/src/test/converter2/issues/gh3012.ts @@ -0,0 +1,12 @@ +/** + * @remarks DictRemarks + */ +export const DictionarySchema = {}; + +/** + * {@inheritDoc DictionarySchema} + * + * @remarks + * Alias of {@link DictionarySchema} + */ +export const NullProtoObjectSchema = DictionarySchema; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 87f894055..c645ff455 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2182,4 +2182,13 @@ describe("Issue Tests", () => { ok(x.comment.summary[1].target); ok(project.files.resolve(x.comment.summary[1].target, project) === doc); }); + + it("#3012 removes @remarks from inheriting comment", () => { + const project = convert(); + const nullProto = query(project, "NullProtoObjectSchema"); + equal(nullProto.comment?.blockTags.map(t => t.tag), ["@remarks"]); + equal(nullProto.comment?.blockTags.map(t => Comment.combineDisplayParts(t.content)), ["DictRemarks"]); + + logger.expectMessage("warn: Content in the @remarks block will be overwritten*"); + }); }); From 12a3f3aacf434a320c8db7852c8b7826cea94538 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 13 Sep 2025 10:56:53 -0600 Subject: [PATCH 105/119] Fix tests --- src/test/converter/inheritance/inherit-doc.ts | 1 - src/test/converter/inheritance/specs.json | 17 ++++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/test/converter/inheritance/inherit-doc.ts b/src/test/converter/inheritance/inherit-doc.ts index 6e0c495d2..09a330282 100644 --- a/src/test/converter/inheritance/inherit-doc.ts +++ b/src/test/converter/inheritance/inherit-doc.ts @@ -71,7 +71,6 @@ export function functionSource(arg1: T, arg2: T): string { * @typeParam T - This will be inherited * @param arg1 - This will be inherited * @param arg2 - This will be inherited - * @returns This will be inherited */ export function functionTargetLocal(arg1: T, arg2: T) { return ""; diff --git a/src/test/converter/inheritance/specs.json b/src/test/converter/inheritance/specs.json index 4e458b53e..8bb200c3e 100644 --- a/src/test/converter/inheritance/specs.json +++ b/src/test/converter/inheritance/specs.json @@ -466,9 +466,9 @@ "sources": [ { "fileName": "inherit-doc.ts", - "line": 76, + "line": 75, "character": 16, - "url": "typedoc://inherit-doc.ts#L76" + "url": "typedoc://inherit-doc.ts#L75" } ], "signatures": [ @@ -504,15 +504,6 @@ } ] }, - { - "tag": "@returns", - "content": [ - { - "kind": "text", - "text": "This will be inherited" - } - ] - }, { "tag": "@returns", "content": [ @@ -527,9 +518,9 @@ "sources": [ { "fileName": "inherit-doc.ts", - "line": 76, + "line": 75, "character": 16, - "url": "typedoc://inherit-doc.ts#L76" + "url": "typedoc://inherit-doc.ts#L75" } ], "typeParameters": [ From 3b84d2db017d8fd7803e8195200af4f84d69657f Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 13 Sep 2025 11:53:21 -0600 Subject: [PATCH 106/119] Use basePath option for relative links Resolves #3009 --- CHANGELOG.md | 6 +++ index.html | 6 --- site/declaration-references.md | 8 ++-- site/options.md | 2 +- site/options/input.md | 20 +++++++--- site/options/output.md | 7 ++-- site/options/package-options.md | 3 +- src/lib/application.ts | 10 ++++- src/lib/converter/plugins/SourcePlugin.ts | 9 +++-- src/lib/internationalization/locales/en.cts | 4 +- src/lib/utils/ValidatingFileRegistry.ts | 22 +++++++++-- src/lib/utils/entry-point.ts | 5 ++- src/lib/utils/options/declaration.ts | 3 +- src/lib/utils/options/sources/typedoc.ts | 43 +++++++++------------ src/test/converter2/issues/gh3009.ts | 7 ++++ src/test/issues.c2.test.ts | 20 +++++++++- src/test/programs.ts | 2 +- 17 files changed, 118 insertions(+), 59 deletions(-) delete mode 100644 index.html create mode 100644 src/test/converter2/issues/gh3009.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d907c6674..f97e2d1b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ title: Changelog ## Unreleased +### Features + +- The `basePath` option now also affects relative link resolution, TypeDoc will also check for + paths relative to the provided base path. If you instead want TypeDoc to only change the rendered + base path for sources, use the `displayBasePath` option, #3009. + ### Bug Fixes - Fixed bug introduced in 0.28.8 where TypeDoc could not render docs with some mixin classes, #3007. diff --git a/index.html b/index.html deleted file mode 100644 index cf7ca8870..000000000 --- a/index.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - Wagtail - - diff --git a/site/declaration-references.md b/site/declaration-references.md index 18ed72494..d0213666c 100644 --- a/site/declaration-references.md +++ b/site/declaration-references.md @@ -4,10 +4,10 @@ title: Declaration References # Declaration References -> [!note] -> If [--useTsLinkResolution](options/comments.md#usetslinkresolution) is turned on (the default) this page likely -> **does not apply** for your links. Declaration references are used only if that option is off or TypeScript -> fails to resolve a link. +> [!note] If [--useTsLinkResolution](options/comments.md#usetslinkresolution) is turned on (the default) this page +> likely **does not apply** for your links within comments (though it will be used for +> [external documents](./external-documents.md) and for the readme file). Declaration references are used only if that option is +> off or TypeScript fails to resolve a link. Some tags like [`{@link}`](tags/link.md) and [`{@inheritDoc}`](tags/inheritDoc.md) can refer to other members of the documentation. These tags use declaration references to name another declaration. diff --git a/site/options.md b/site/options.md index 8d39b3642..b5b3b9782 100644 --- a/site/options.md +++ b/site/options.md @@ -35,7 +35,7 @@ Options which control TypeDoc's HTML output. ## Comment Options -Options which control how TypeDoc parses comments. +Options which control how TypeDoc parses comments and documents. {@listOptions options/comments.md} diff --git a/site/options/input.md b/site/options/input.md index e7bdaf311..fbfdbf9a3 100644 --- a/site/options/input.md +++ b/site/options/input.md @@ -35,7 +35,7 @@ If a `"typedoc"` [conditional export](https://nodejs.org/api/packages.html#condi TypeDoc will use it instead of the `"import"` export condition. The set of entry points provided to TypeDoc determines the names displayed in the documentation. -By default, TypeDoc will derive a [basePath](output.md#basepath) based on your entry point +By default, TypeDoc will derive a [displayBasePath](output.md#displaybasepath) based on your entry point paths to determine the displayed module name, but it can be also be set with the [`@module`](../tags/module.md) tag. ## entryPointStrategy @@ -311,7 +311,8 @@ If you are updating documentation for a forked package, you probably want to pas typedoc --disableGit ``` -Prevents TypeDoc from using Git to try to determine if sources can be linked, with this enabled, sources will always be linked, even if not part of a git repo. +Prevents TypeDoc from using Git to try to determine if sources can be linked, with this enabled, sources will always be +linked, even if not part of a git repo. ## readme @@ -319,6 +320,15 @@ Prevents TypeDoc from using Git to try to determine if sources can be linked, wi typedoc --readme ``` -Path to the readme file that should be displayed on the index page. If set to -`none`, or no readme file is automatically discovered, the index page will be -disabled. +Path to the readme file that should be displayed on the index page. If set to `none`, or no readme file is automatically +discovered, the index page will be disabled. + +## basePath + +```bash +typedoc --basePath ./ +``` + +Path to a directory containing asset files which will be checked when resolving relative paths of links and images +within documentation comments and external documents. If specified, this will also be used for the default value of +the [displayBasePath](output.md#displaybasepath) option. diff --git a/site/options/output.md b/site/options/output.md index d01692514..fbd6df90a 100644 --- a/site/options/output.md +++ b/site/options/output.md @@ -318,14 +318,15 @@ export default { }; ``` -## basePath +## displayBasePath ```bash -$ typedoc --basePath ./ --entryPoints src/index.ts +$ typedoc --displayBasePath ./ --entryPoints src/index.ts ``` Specifies the base path to be used when displaying file paths. If not set, TypeDoc will guess by taking the lowest -common directory to all source files. In the above example, TypeDoc would display links to `index.ts` rather than `src/index.ts`. +common directory to all source files. In the above example, TypeDoc would display links to `index.ts` rather than `src/index.ts` +if `displayBasePath` was not specified. Defaults to the value of [basePath](input.md#basepath) > [!note] > This option only affects displayed paths. It _does not_ affect where TypeDoc will create links to. diff --git a/site/options/package-options.md b/site/options/package-options.md index ea055071f..46aa2ea6c 100644 --- a/site/options/package-options.md +++ b/site/options/package-options.md @@ -53,6 +53,7 @@ at the root level. The following tables indicate where an option should be set. | [`gitRemote`](input.md#gitremote) | Package | | | [`disableGit`](input.md#disablegit) | Package | | | [`readme`](input.md#readme) | Both | Root: Site readme, Package: Package readme | +| [`basePath`](input.md#basepath) | Both | Root: Site readme, documents, Package: Package readme, documentation comments, documents | ## Output Options @@ -77,7 +78,7 @@ at the root level. The following tables indicate where an option should be set. | [`customFooterHtmlDisableWrapper`](output.md#customfooterhtmldisablewrapper) | Root | | | [`markdownItOptions`](output.md#markdownitoptions) | Root | | | [`markdownItLoader`](output.md#markdownitloader) | Root | | -| [`basePath`](output.md#basepath) | Both | Used to determine file names of entry points and documents | +| [`displayBasePath`](output.md#displaybasepath) | Both | Used to determine file names of entry points and documents | | [`cname`](output.md#cname) | Root | | | [`favicon`](output.md#favicon) | Root | | | [`sourceLinkExternal`](output.md#sourcelinkexternal) | Root | | diff --git a/src/lib/application.ts b/src/lib/application.ts index d3574b3e5..75b5cd214 100644 --- a/src/lib/application.ts +++ b/src/lib/application.ts @@ -148,6 +148,10 @@ export class Application extends AbstractComponent< options = new Options(); + /** + * Due for deprecation in 0.29, use the reference to this on {@link ProjectReflection}, + * this was the wrong place for this member to live. + */ files: FileRegistry = new ValidatingFileRegistry(); /** @internal */ @@ -273,6 +277,10 @@ export class Application extends AbstractComponent< this.logger.level = this.options.getValue("logLevel"); } + if (this.files instanceof ValidatingFileRegistry) { + this.files.basePath = this.options.getValue("basePath"); + } + for ( const [lang, locales] of Object.entries( this.options.getValue("locales"), @@ -827,7 +835,7 @@ export class Application extends AbstractComponent< for (const { dir, options } of projectsToConvert) { this.logger.info(i18n.converting_project_at_0(nicePath(dir))); this.options = options; - this.files = new ValidatingFileRegistry(); + this.files = new ValidatingFileRegistry(options.getValue("basePath")); let project = await this.convert(); if (project) { this.validate(project); diff --git a/src/lib/converter/plugins/SourcePlugin.ts b/src/lib/converter/plugins/SourcePlugin.ts index 203e30e84..95d5b2e44 100644 --- a/src/lib/converter/plugins/SourcePlugin.ts +++ b/src/lib/converter/plugins/SourcePlugin.ts @@ -10,7 +10,7 @@ import { SourceReference } from "../../models/index.js"; import { gitIsInstalled, RepositoryManager } from "../utils/repository.js"; import { ConverterEvents } from "../converter-events.js"; import type { Converter } from "../converter.js"; -import { i18n, type NormalizedPath } from "#utils"; +import { i18n } from "#utils"; /** * A handler that attaches source file information to reflections. @@ -31,8 +31,9 @@ export class SourcePlugin extends ConverterComponent { @Option("sourceLinkTemplate") accessor sourceLinkTemplate!: string; - @Option("basePath") - accessor basePath!: NormalizedPath; + get displayBasePath() { + return this.application.options.getValue("displayBasePath") || this.application.options.getValue("basePath"); + } /** * All file names to find the base path from. @@ -157,7 +158,7 @@ export class SourcePlugin extends ConverterComponent { ); } - const basePath = this.basePath || getCommonDirectory([...this.fileNames]); + const basePath = this.displayBasePath || getCommonDirectory([...this.fileNames]); this.repositories ||= new RepositoryManager( basePath, this.gitRevision, diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index db746e7bd..b412c9741 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -311,12 +311,14 @@ export = { "Use the specified remote for linking to GitHub/Bitbucket source files. Has no effect if disableGit or disableSources is set", help_disableGit: "Assume that all can be linked to with the sourceLinkTemplate, sourceLinkTemplate must be set if this is enabled. {path} will be rooted at basePath", - help_basePath: "Specifies the base path to be used when displaying file paths", + help_displayBasePath: + "Specifies the base path to be used when displaying file paths. If not specified, basePath is used.", help_excludeTags: "Remove the listed block/modifier tags from doc comments", help_notRenderedTags: "Tags which will be preserved in doc comments, but not rendered when creating output", help_cascadedModifierTags: "Modifier tags which should be copied to all children of the parent reflection", help_readme: "Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page", + help_basePath: "Specifies a path which links may be resolved relative to.", help_cname: "Set the CNAME file text, it's useful for custom domains on GitHub Pages", help_favicon: "Path to favicon to include as the site icon", help_sourceLinkExternal: diff --git a/src/lib/utils/ValidatingFileRegistry.ts b/src/lib/utils/ValidatingFileRegistry.ts index 0a52b52ca..0e26dc72a 100644 --- a/src/lib/utils/ValidatingFileRegistry.ts +++ b/src/lib/utils/ValidatingFileRegistry.ts @@ -4,17 +4,33 @@ import { i18n, type NormalizedPath, NormalizedPathUtils } from "#utils"; import { existsSync } from "fs"; export class ValidatingFileRegistry extends FileRegistry { + basePath: NormalizedPath; + + constructor(basePath: NormalizedPath = "") { + super(); + this.basePath = basePath; + } + override register( sourcePath: NormalizedPath, relativePath: NormalizedPath, ): { target: FileId; anchor: string | undefined } | undefined { - const absolute = NormalizedPathUtils.resolve(NormalizedPathUtils.dirname(sourcePath), relativePath); - const absoluteWithoutAnchor = absolute.replace(/#.*/, ""); + let absolute = NormalizedPathUtils.resolve(NormalizedPathUtils.dirname(sourcePath), relativePath); + let absoluteWithoutAnchor = absolute.replace(/#.*/, ""); // Note: We allow paths to directories to be registered here, but the AssetsPlugin will not // copy them to the output path. This is so that we can link to directories and associate them // with reflections in packages mode. if (!existsSync(absoluteWithoutAnchor)) { - return; + // If the relative path didn't exist normally, also check the path relative to the assetBasePath option + if (this.basePath != "") { + absolute = NormalizedPathUtils.resolve(this.basePath, relativePath); + absoluteWithoutAnchor = absolute.replace(/#.*/, ""); + if (!existsSync(absoluteWithoutAnchor)) { + return; + } + } else { + return; + } } return this.registerAbsolute(absolute); } diff --git a/src/lib/utils/entry-point.ts b/src/lib/utils/entry-point.ts index 38511656c..35f90d8b0 100644 --- a/src/lib/utils/entry-point.ts +++ b/src/lib/utils/entry-point.ts @@ -196,7 +196,7 @@ export function getDocumentEntryPoints( options, supportedFileRegex, ); - const baseDir = options.getValue("basePath") || getCommonDirectory(expanded); + const baseDir = options.getValue("displayBasePath") || options.getValue("basePath") || getCommonDirectory(expanded); return expanded.map((path) => { return { displayName: relative(baseDir, path).replace(/\.[^.]+$/, ""), @@ -293,7 +293,8 @@ function getEntryPointsForPaths( options: Options, programs = getEntryPrograms(inputFiles, logger, options), ): DocumentationEntryPoint[] { - const baseDir = options.getValue("basePath") || getCommonDirectory(inputFiles); + const baseDir = options.getValue("displayBasePath") || options.getValue("basePath") || + getCommonDirectory(inputFiles); const entryPoints: DocumentationEntryPoint[] = []; let expandSuggestion = true; diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index ef234c17d..e1bb8a352 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -222,6 +222,7 @@ export interface TypeDocOptionMap { gitRevision: string; gitRemote: string; readme: string; + basePath: NormalizedPath; // Output outputs: ManuallyValidatedOption>; @@ -261,7 +262,7 @@ export interface TypeDocOptionMap { * strictly typed here. */ markdownItLoader: ManuallyValidatedOption<(parser: any) => void>; - basePath: NormalizedPath; + displayBasePath: NormalizedPath; cname: string; favicon: NormalizedPath; githubPages: boolean; diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 6799ee531..80826f26d 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -20,9 +20,7 @@ function makeTagArrayValidator(name: keyof TypeDocOptionMap) { // For convenience, added in the same order as they are documented on the website. export function addTypeDocOptions(options: Pick) { - /////////////////////////// - // Configuration Options // - /////////////////////////// + // MARK: Configuration Options options.addDeclaration({ type: ParameterType.Path, @@ -96,9 +94,7 @@ export function addTypeDocOptions(options: Pick) { }, }); - /////////////////////////// - ////// Input Options ////// - /////////////////////////// + // MARK: Input Options options.addDeclaration({ name: "entryPoints", @@ -232,10 +228,18 @@ export function addTypeDocOptions(options: Pick) { } }, }); + options.addDeclaration({ + name: "readme", + help: () => i18n.help_readme(), + type: ParameterType.Path, + }); + options.addDeclaration({ + name: "basePath", + help: () => i18n.help_basePath(), + type: ParameterType.Path, + }); - /////////////////////////// - ///// Output Options ////// - /////////////////////////// + // MARK: Output Options options.addDeclaration({ name: "outputs", @@ -454,13 +458,8 @@ export function addTypeDocOptions(options: Pick) { type: ParameterType.Boolean, }); options.addDeclaration({ - name: "basePath", - help: () => i18n.help_basePath(), - type: ParameterType.Path, - }); - options.addDeclaration({ - name: "readme", - help: () => i18n.help_readme(), + name: "displayBasePath", + help: () => i18n.help_displayBasePath(), type: ParameterType.Path, }); options.addDeclaration({ @@ -720,9 +719,7 @@ export function addTypeDocOptions(options: Pick) { type: ParameterType.Boolean, }); - /////////////////////////// - ///// Comment Options ///// - /////////////////////////// + // MARK: Comment Options options.addDeclaration({ name: "jsDocCompatibility", @@ -807,9 +804,7 @@ export function addTypeDocOptions(options: Pick) { validate: makeTagArrayValidator("cascadedModifierTags"), }); - /////////////////////////// - // Organization Options /// - /////////////////////////// + // MARK: Organization Options options.addDeclaration({ name: "categorizeByGroup", @@ -884,9 +879,7 @@ export function addTypeDocOptions(options: Pick) { }, }); - /////////////////////////// - ///// General Options ///// - /////////////////////////// + // MARK: General Options options.addDeclaration({ name: "watch", diff --git a/src/test/converter2/issues/gh3009.ts b/src/test/converter2/issues/gh3009.ts new file mode 100644 index 000000000..ac9e243da --- /dev/null +++ b/src/test/converter2/issues/gh3009.ts @@ -0,0 +1,7 @@ +/** + * [link with base path](src/test/converter2/issues/gh3009.ts) + * [link direct](gh3009.ts) + * + * [asset link](CHANGELOG.md) + */ +export const x = 1; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index c645ff455..cdb558765 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -17,7 +17,9 @@ import type { InlineTagDisplayPart } from "../lib/models/Comment.js"; import { getConverter2App, getConverter2Project } from "./programs.js"; import { TestLogger } from "./TestLogger.js"; import { equalKind, getComment, getLinks, getSigComment, query, querySig, reflToTree } from "./utils.js"; -import { DefaultTheme, KindRouter, PageEvent, ReflectionSymbolId } from "../index.js"; +import { DefaultTheme, type FileId, KindRouter, PageEvent, ReflectionSymbolId } from "../index.js"; +import { normalizePath, TYPEDOC_ROOT } from "#node-utils"; +import { NormalizedPathUtils } from "#utils"; const app = getConverter2App(); @@ -2183,6 +2185,22 @@ describe("Issue Tests", () => { ok(project.files.resolve(x.comment.summary[1].target, project) === doc); }); + it("#3009 supports a base path for resolving relative links", () => { + app.options.setValue("basePath", TYPEDOC_ROOT); + const project = convert(); + const x = query(project, "x"); + const links = x.comment?.summary.filter(part => part.kind === "relative-link"); + equal(links?.length, 3); + equal(links[0].target, 1); + equal(links[1].target, 1); + equal(links[2].target, 2); + ok(app.files.resolve(1 as FileId, project) == project); + equal( + app.files.resolve(2 as FileId, project), + NormalizedPathUtils.resolve(normalizePath(TYPEDOC_ROOT), normalizePath("CHANGELOG.md")), + ); + }); + it("#3012 removes @remarks from inheriting comment", () => { const project = convert(); const nullProto = query(project, "NullProtoObjectSchema"); diff --git a/src/test/programs.ts b/src/test/programs.ts index 68b27b47c..d34b5315a 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -173,7 +173,7 @@ export function getConverter2Project(entries: string[], folder: string) { ok(entryPoints.length > 0, "Expected at least one entry point"); app.options.setValue("entryPoints", entryPoints); - app.files = new ValidatingFileRegistry(); + app.files = new ValidatingFileRegistry(app.options.getValue("basePath")); return app.converter.convert( files.map((file, index) => { return { From 33e4eb95ec2db9b5cb2bdf3416142006755e73f3 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 14 Sep 2025 17:41:03 -0600 Subject: [PATCH 107/119] Fix externalSymbolLinkMappings with inherited methods Fixes #3014 --- .gitignore | 1 + CHANGELOG.md | 1 + src/lib/converter/plugins/ImplementsPlugin.ts | 48 ++- src/lib/converter/types.ts | 2 + src/lib/models/types.ts | 6 +- .../themes/default/partials/typeAndParent.tsx | 26 +- src/test/converter/inheritance/specs.json | 6 +- src/test/converter/mixin/specs.json | 30 +- src/test/converter2/renderer/gh3014.ts | 7 + src/test/converter2/renderer/index.ts | 1 + .../node_modules/@typedoc/gh3014/index.d.ts | 3 + .../node_modules/@typedoc/gh3014/package.json | 3 + src/test/converter2/typedoc.json | 4 + src/test/models/types.test.ts | 4 +- src/test/output/formatter.test.ts | 10 +- src/test/renderer/specs/classes/GH3014.json | 286 ++++++++++++++++++ src/test/renderer/specs/modules.json | 32 ++ 17 files changed, 432 insertions(+), 38 deletions(-) create mode 100644 src/test/converter2/renderer/gh3014.ts create mode 100644 src/test/converter2/renderer/node_modules/@typedoc/gh3014/index.d.ts create mode 100644 src/test/converter2/renderer/node_modules/@typedoc/gh3014/package.json create mode 100644 src/test/renderer/specs/classes/GH3014.json diff --git a/.gitignore b/.gitignore index 0ed4251ff..a223dce66 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ yarn-error.log /src/test/renderer/testProject/json.json **/node_modules/ !src/test/converter2/behavior/node_modules/ +!src/test/converter2/renderer/node_modules/ /coverage/ /dist/ /docs diff --git a/CHANGELOG.md b/CHANGELOG.md index f97e2d1b9..c453ce415 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ title: Changelog - Fixed bug introduced in 0.28.8 where TypeDoc could not render docs with some mixin classes, #3007. - `@inheritDoc` will now correctly overwrite `@remarks` and `@returns` blocks on the target comment, #3012. +- The `externalSymbolLinkMappings` option now works properly on links pointing to inherited/overwritten signatures, #3014. ## v0.28.12 (2025-09-01) diff --git a/src/lib/converter/plugins/ImplementsPlugin.ts b/src/lib/converter/plugins/ImplementsPlugin.ts index 383a4ac2c..29bb78146 100644 --- a/src/lib/converter/plugins/ImplementsPlugin.ts +++ b/src/lib/converter/plugins/ImplementsPlugin.ts @@ -13,7 +13,7 @@ import { ReferenceType, ReflectionType, type SomeType, type Type } from "../../m import { filterMap, type TranslatedString, zip } from "#utils"; import { ConverterComponent } from "../components.js"; import type { Context } from "../context.js"; -import { getHumanName } from "../../utils/index.js"; +import { findPackageForPath, getHumanName } from "../../utils/index.js"; import { ConverterEvents } from "../converter-events.js"; import type { Converter } from "../converter.js"; @@ -182,18 +182,34 @@ export class ImplementsPlugin extends ConverterComponent { for (const child of reflection.children || []) { if (child.inheritedFrom && !isValidRef(child.inheritedFrom)) { - child.inheritedFrom = ReferenceType.createBrokenReference(child.inheritedFrom.name, project); + child.inheritedFrom = ReferenceType.createBrokenReference( + child.inheritedFrom.name, + project, + child.inheritedFrom.package, + ); } if (child.overwrites && !isValidRef(child.overwrites)) { - child.overwrites = ReferenceType.createBrokenReference(child.overwrites.name, project); + child.overwrites = ReferenceType.createBrokenReference( + child.overwrites.name, + project, + child.overwrites.package, + ); } for (const childSig of child.getAllSignatures()) { if (childSig.inheritedFrom && !isValidRef(childSig.inheritedFrom)) { - childSig.inheritedFrom = ReferenceType.createBrokenReference(childSig.inheritedFrom.name, project); + childSig.inheritedFrom = ReferenceType.createBrokenReference( + childSig.inheritedFrom.name, + project, + childSig.inheritedFrom.package, + ); } if (childSig.overwrites && !isValidRef(childSig.overwrites)) { - childSig.overwrites = ReferenceType.createBrokenReference(childSig.overwrites.name, project); + childSig.overwrites = ReferenceType.createBrokenReference( + childSig.overwrites.name, + project, + childSig.overwrites.package, + ); } } } @@ -522,6 +538,18 @@ export class ImplementsPlugin extends ConverterComponent { } } +function getConstructorPackagePath(context: Context, clause: ts.ExpressionWithTypeArguments): string | undefined { + const symbol = context.getSymbolAtLocation(clause.expression); + if (!symbol) return undefined; + + const resolvedSymbol = context.resolveAliasedSymbol(symbol); + + const symbolPath = resolvedSymbol?.declarations?.[0]?.getSourceFile().fileName; + if (!symbolPath) return undefined; + + return findPackageForPath(symbolPath)?.[0]; +} + function constructorInheritance( context: Context, reflection: DeclarationReflection, @@ -533,17 +561,21 @@ function constructorInheritance( ); if (!extendsClause) return; - const name = `${extendsClause.types[0].getText()}.constructor`; + const extendsType = extendsClause.types[0]; + const refPackage = getConstructorPackagePath(context, extendsType); + + const name = `${extendsType.getText()}.constructor`; const key = constructorDecl ? "overwrites" : "inheritedFrom"; reflection[key] ??= ReferenceType.createBrokenReference( name, context.project, + refPackage, ); for (const sig of reflection.signatures ?? []) { - sig[key] ??= ReferenceType.createBrokenReference(name, context.project); + sig[key] ??= ReferenceType.createBrokenReference(name, context.project, refPackage); } } @@ -576,7 +608,7 @@ function createLink( const rootSymbols = context.checker.getRootSymbols(symbol); const ref = rootSymbols.length && rootSymbols[0] != symbol ? context.createSymbolReference(rootSymbols[0], context, name) - : ReferenceType.createBrokenReference(name, context.project); + : ReferenceType.createBrokenReference(name, context.project, undefined); link(reflection); link(reflection.getSignature); diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 56a08bafb..8cd71c246 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -705,6 +705,7 @@ const queryConverter: TypeConverter = { ReferenceType.createBrokenReference( node.exprName.getText(), context.project, + undefined, ), ); } @@ -786,6 +787,7 @@ const referenceConverter: TypeConverter< const ref = ReferenceType.createBrokenReference( context.checker.typeToString(type), context.project, + undefined, ); ref.refersToTypeParameter = true; return ref; diff --git a/src/lib/models/types.ts b/src/lib/models/types.ts index a1e99c80e..b7cf99729 100644 --- a/src/lib/models/types.ts +++ b/src/lib/models/types.ts @@ -960,8 +960,10 @@ export class ReferenceType extends Type { * later during conversion. * @internal */ - static createBrokenReference(name: string, project: ProjectReflection) { - return new ReferenceType(name, -1 as ReflectionId, project, name); + static createBrokenReference(name: string, project: ProjectReflection, packageName: string | undefined) { + const ref = new ReferenceType(name, -1 as ReflectionId, project, name); + ref.package = packageName; + return ref; } protected override getTypeString() { diff --git a/src/lib/output/themes/default/partials/typeAndParent.tsx b/src/lib/output/themes/default/partials/typeAndParent.tsx index 3cac22a88..18cfb10d1 100644 --- a/src/lib/output/themes/default/partials/typeAndParent.tsx +++ b/src/lib/output/themes/default/partials/typeAndParent.tsx @@ -1,5 +1,5 @@ import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js"; -import { ArrayType, ReferenceType, SignatureReflection, type Type } from "../../../../models/index.js"; +import { ArrayType, ReferenceType, SignatureReflection, type Type } from "#models"; import { JSX } from "#utils"; export const typeAndParent = (context: DefaultThemeRenderContext, props: Type): JSX.Element => { @@ -12,15 +12,23 @@ export const typeAndParent = (context: DefaultThemeRenderContext, props: Type): ); } - if (props instanceof ReferenceType && props.reflection) { - const refl = props.reflection instanceof SignatureReflection ? props.reflection.parent : props.reflection; - const parent = refl.parent!; + if (props instanceof ReferenceType) { + if (props.reflection) { + const refl = props.reflection instanceof SignatureReflection ? props.reflection.parent : props.reflection; + const parent = refl.parent!; - return ( - <> - {{parent.name}}.{{refl.name}} - - ); + return ( + <> + {{parent.name}}.{{refl.name}} + + ); + } else if (props.externalUrl) { + if (props.externalUrl === "#") { + return <>{props.toString()}; + } else { + return {props.name}; + } + } } return <>{props.toString()}; diff --git a/src/test/converter/inheritance/specs.json b/src/test/converter/inheritance/specs.json index 8bb200c3e..74aabc605 100644 --- a/src/test/converter/inheritance/specs.json +++ b/src/test/converter/inheritance/specs.json @@ -671,14 +671,16 @@ "inheritedFrom": { "type": "reference", "target": -1, - "name": "My.constructor" + "name": "My.constructor", + "package": "typedoc" } } ], "inheritedFrom": { "type": "reference", "target": -1, - "name": "My.constructor" + "name": "My.constructor", + "package": "typedoc" } }, { diff --git a/src/test/converter/mixin/specs.json b/src/test/converter/mixin/specs.json index 146ed2189..9ec8e447f 100644 --- a/src/test/converter/mixin/specs.json +++ b/src/test/converter/mixin/specs.json @@ -426,14 +426,16 @@ "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin2(Mixin1Func(Base)).method1" + "name": "Mixin2(Mixin1Func(Base)).method1", + "package": "typedoc" } } ], "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin2(Mixin1Func(Base)).method1" + "name": "Mixin2(Mixin1Func(Base)).method1", + "package": "typedoc" } }, { @@ -493,14 +495,16 @@ "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin2(Mixin1Func(Base)).method2" + "name": "Mixin2(Mixin1Func(Base)).method2", + "package": "typedoc" } } ], "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin2(Mixin1Func(Base)).method2" + "name": "Mixin2(Mixin1Func(Base)).method2", + "package": "typedoc" } } ], @@ -875,14 +879,16 @@ "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin.method1" + "name": "Mixin.method1", + "package": "typedoc" } } ], "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin.method1" + "name": "Mixin.method1", + "package": "typedoc" } } ], @@ -1139,14 +1145,16 @@ "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin.method1" + "name": "Mixin.method1", + "package": "typedoc" } } ], "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin.method1" + "name": "Mixin.method1", + "package": "typedoc" } }, { @@ -1206,14 +1214,16 @@ "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin.method2" + "name": "Mixin.method2", + "package": "typedoc" } } ], "inheritedFrom": { "type": "reference", "target": -1, - "name": "Mixin.method2" + "name": "Mixin.method2", + "package": "typedoc" } } ], diff --git a/src/test/converter2/renderer/gh3014.ts b/src/test/converter2/renderer/gh3014.ts new file mode 100644 index 000000000..acd3ea124 --- /dev/null +++ b/src/test/converter2/renderer/gh3014.ts @@ -0,0 +1,7 @@ +import { GH3014Base } from "@typedoc/gh3014"; + +export class GH3014 extends GH3014Base { + constructor() { + super(); + } +} diff --git a/src/test/converter2/renderer/index.ts b/src/test/converter2/renderer/index.ts index 05a839724..d43e25ccc 100644 --- a/src/test/converter2/renderer/index.ts +++ b/src/test/converter2/renderer/index.ts @@ -139,4 +139,5 @@ export * as ExpandType from "./expandType"; export * as GH2982 from "./gh2982"; export { gh2995 } from "./gh2995"; export * as GH3007 from "./gh3007"; +export { GH3014 } from "./gh3014"; export { box as boxAlias }; diff --git a/src/test/converter2/renderer/node_modules/@typedoc/gh3014/index.d.ts b/src/test/converter2/renderer/node_modules/@typedoc/gh3014/index.d.ts new file mode 100644 index 000000000..5eec32d15 --- /dev/null +++ b/src/test/converter2/renderer/node_modules/@typedoc/gh3014/index.d.ts @@ -0,0 +1,3 @@ +export class GH3014Base { + constructor(); +} diff --git a/src/test/converter2/renderer/node_modules/@typedoc/gh3014/package.json b/src/test/converter2/renderer/node_modules/@typedoc/gh3014/package.json new file mode 100644 index 000000000..c204a035b --- /dev/null +++ b/src/test/converter2/renderer/node_modules/@typedoc/gh3014/package.json @@ -0,0 +1,3 @@ +{ + "name": "@typedoc/gh3014" +} diff --git a/src/test/converter2/typedoc.json b/src/test/converter2/typedoc.json index 9e56b27f9..bcdc04f8d 100644 --- a/src/test/converter2/typedoc.json +++ b/src/test/converter2/typedoc.json @@ -27,6 +27,10 @@ // used by {@link !Promise} "global": { "Promise": "#" + }, + "@typedoc/gh3014": { + "GH3014Base": "typedoc://GH3014Base", + "GH3014Base.constructor": "typedoc://GH3014Base.constructor" } } } diff --git a/src/test/models/types.test.ts b/src/test/models/types.test.ts index 966a68c97..d9dd945a5 100644 --- a/src/test/models/types.test.ts +++ b/src/test/models/types.test.ts @@ -266,7 +266,7 @@ describe("Type.toString", () => { const project = new ProjectReflection("test", new FileRegistry()); const type = new T.OptionalType( new T.QueryType( - T.ReferenceType.createBrokenReference("X", project), + T.ReferenceType.createBrokenReference("X", project, undefined), ), ); equal(type.toString(), "typeof X?"); @@ -287,7 +287,7 @@ describe("Type.toString", () => { const project = new ProjectReflection("test", new FileRegistry()); const type = new T.TypeOperatorType( new T.QueryType( - T.ReferenceType.createBrokenReference("X", project), + T.ReferenceType.createBrokenReference("X", project, undefined), ), "keyof", ); diff --git a/src/test/output/formatter.test.ts b/src/test/output/formatter.test.ts index d8b9db60f..cfe2f4a4b 100644 --- a/src/test/output/formatter.test.ts +++ b/src/test/output/formatter.test.ts @@ -228,7 +228,7 @@ describe("Formatter", () => { it("Handles query types", () => { const project = new ProjectReflection("", new FileRegistry()); const type = new QueryType( - ReferenceType.createBrokenReference("x", project), + ReferenceType.createBrokenReference("x", project, undefined), ); const text = renderElementToText(renderType(type)); equal(text, `typeof x`); @@ -236,7 +236,7 @@ describe("Formatter", () => { it("Handles a simple reference type", () => { const project = new ProjectReflection("", new FileRegistry()); - const type = ReferenceType.createBrokenReference("x", project); + const type = ReferenceType.createBrokenReference("x", project, undefined); const text = renderElementToText(renderType(type)); equal(text, `x`); }); @@ -277,7 +277,7 @@ describe("Formatter", () => { it("Handles a reference type pointing to an external url", () => { const project = new ProjectReflection("", new FileRegistry()); - const type = ReferenceType.createBrokenReference("x", project); + const type = ReferenceType.createBrokenReference("x", project, undefined); type.externalUrl = "https://example.com"; const text = renderElementToText(renderType(type)); equal(text, `x`); @@ -285,7 +285,7 @@ describe("Formatter", () => { it("Handles a reference type targeting a type parameter", () => { const project = new ProjectReflection("", new FileRegistry()); - const type = ReferenceType.createBrokenReference("x", project); + const type = ReferenceType.createBrokenReference("x", project, undefined); type.refersToTypeParameter = true; const text = renderElementToText(renderType(type)); equal(text, `x`); @@ -293,7 +293,7 @@ describe("Formatter", () => { it("Handles a reference type with type arguments", () => { const project = new ProjectReflection("", new FileRegistry()); - const type = ReferenceType.createBrokenReference("x", project); + const type = ReferenceType.createBrokenReference("x", project, undefined); type.typeArguments = [ new LiteralType(123), new LiteralType(456), diff --git a/src/test/renderer/specs/classes/GH3014.json b/src/test/renderer/specs/classes/GH3014.json new file mode 100644 index 000000000..f764dc955 --- /dev/null +++ b/src/test/renderer/specs/classes/GH3014.json @@ -0,0 +1,286 @@ +{ + "div.container.container-main": [ + { + "div.col-content": [ + { + "div.tsd-page-title": [ + { + "tag": "ul.tsd-breadcrumb", + "props": { + "aria-label": "Breadcrumb" + }, + "children": { + "li": { + "tag": "a", + "props": { + "href": "", + "aria-current": "page" + }, + "children": "GH3014" + } + } + }, + { + "h1": "Class GH3014" + } + ] + }, + { + "tag": "section.tsd-panel.tsd-hierarchy", + "props": { + "data-refl": "151" + }, + "children": [ + { + "h4": "Hierarchy" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": [ + { + "tag": "a.tsd-signature-type.external", + "props": { + "href": "typedoc://GH3014Base", + "target": "_blank" + }, + "children": "GH3014Base" + }, + { + "ul.tsd-hierarchy": { + "li.tsd-hierarchy-item": { + "span.tsd-hierarchy-target": "GH3014" + } + } + } + ] + } + } + ] + }, + { + "aside.tsd-sources": { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3014.ts" + }, + "children": "gh3014.ts:3" + } + ] + } + } + }, + { + "section.tsd-panel-group.tsd-index-group": { + "section.tsd-panel.tsd-index-panel": { + "tag": "details.tsd-index-content.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary.tsd-index-summary": { + "h5.tsd-index-heading.uppercase": "Index" + } + }, + { + "div.tsd-accordion-details": { + "section.tsd-index-section": [ + { + "h3.tsd-index-heading": "Constructors" + }, + { + "div.tsd-index-list": [ + { + "tag": "a.tsd-index-link", + "props": { + "href": "#constructor" + }, + "children": { + "span": "constructor" + } + }, + "\n" + ] + } + ] + } + } + ] + } + } + }, + { + "tag": "details.tsd-panel-group.tsd-member-group.tsd-accordion", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Constructors" + }, + "children": { + "h2": "Constructors" + } + }, + { + "section": { + "section.tsd-panel.tsd-member": [ + { + "h3.tsd-anchor-link#constructor": [ + { + "span": "constructor" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#constructor", + "aria-label": "Permalink" + } + } + ] + }, + { + "ul.tsd-signatures": { + "li.": [ + { + "div.tsd-signature.tsd-anchor-link#constructorgh3014": [ + { + "span.tsd-signature-keyword": "new" + }, + " ", + { + "span.tsd-kind-constructor-signature": "GH3014" + }, + { + "span.tsd-signature-symbol": "()" + }, + { + "span.tsd-signature-symbol": ":" + }, + " ", + { + "tag": "a.tsd-signature-type.tsd-kind-class", + "props": { + "href": "" + }, + "children": "GH3014" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#constructorgh3014", + "aria-label": "Permalink" + } + } + ] + }, + { + "div.tsd-description": [ + { + "h4.tsd-returns-title": [ + "Returns ", + { + "tag": "a.tsd-signature-type.tsd-kind-class", + "props": { + "href": "" + }, + "children": "GH3014" + } + ] + }, + { + "aside.tsd-sources": [ + { + "p": [ + "Overrides ", + { + "tag": "a.external", + "props": { + "href": "typedoc://GH3014Base.constructor", + "target": "_blank" + }, + "children": "GH3014Base.constructor" + } + ] + }, + { + "ul": { + "li": [ + "Defined in ", + { + "tag": "a", + "props": { + "href": "gh3014.ts" + }, + "children": "gh3014.ts:4" + } + ] + } + } + ] + } + ] + } + ] + } + } + ] + } + } + ] + } + ] + }, + { + "div.col-sidebar": { + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": { + "tag": "details.tsd-accordion.tsd-page-navigation-section", + "props": { + "open": true + }, + "children": [ + { + "tag": "summary.tsd-accordion-summary", + "props": { + "data-key": "section-Constructors" + }, + "children": "Constructors" + }, + { + "div": { + "tag": "a", + "props": { + "href": "#constructor" + }, + "children": { + "span": "constructor" + } + } + } + ] + } + } + ] + } + } + } + ] +} diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index a11456c96..2c201b7c5 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -261,6 +261,29 @@ { "dd.tsd-member-summary": [] }, + { + "dt.tsd-member-summary#gh3014": { + "span.tsd-member-summary-name": [ + { + "tag": "a", + "props": { + "href": "classes/GH3014.json" + }, + "children": "GH3014" + }, + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#gh3014", + "aria-label": "Permalink" + } + } + ] + } + }, + { + "dd.tsd-member-summary": [] + }, { "dt.tsd-member-summary#modifiersclass": { "span.tsd-member-summary-name": [ @@ -774,6 +797,15 @@ ] } }, + { + "tag": "a", + "props": { + "href": "#gh3014" + }, + "children": { + "span": "GH3014" + } + }, { "tag": "a", "props": { From a57c48e3aa5232f87051874a197b65177672a946 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 14 Sep 2025 17:47:09 -0600 Subject: [PATCH 108/119] Bump version to 0.28.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a10ecdb0a..6d8ff462d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.12", + "version": "0.28.13", "homepage": "https://typedoc.org", "type": "module", "exports": { From 724090664126797f9ada54a1bdeaf5dc3db77385 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Sun, 14 Sep 2025 23:48:03 +0000 Subject: [PATCH 109/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c453ce415..13d8a23db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.13 (2025-09-14) + ### Features - The `basePath` option now also affects relative link resolution, TypeDoc will also check for From bd7888a189352bfaed88793aff6f051a0b6a7ea7 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sun, 14 Sep 2025 19:39:08 -0600 Subject: [PATCH 110/119] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d6c7ab481..16c72ff2d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -38,7 +38,7 @@ If this is not possible, include at least: ## Environment -- Typedoc version: +- TypeDoc version: - TypeScript version: - Node.js version: - OS: From 54af135a17af1a5595166c7cc12650ac014b6028 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 27 Sep 2025 20:52:23 -0600 Subject: [PATCH 111/119] Introduce preservedTypeAnnotationTags option Resolves #3020 Total time spent: 32 minutes --- CHANGELOG.md | 6 +++++ site/options/comments.md | 16 ++++++++++-- site/options/package-options.md | 1 + src/lib/converter/comments/index.ts | 1 + src/lib/converter/comments/parser.ts | 23 +++++++++++++++-- src/lib/converter/converter.ts | 9 +++---- src/lib/internationalization/locales/en.cts | 1 + src/lib/models/Comment.ts | 11 ++++++++ .../themes/default/partials/comment.tsx | 1 + src/lib/serialization/schema.ts | 2 +- src/lib/utils/options/declaration.ts | 1 + src/lib/utils/options/defaults.ts | 2 ++ src/lib/utils/options/sources/typedoc.ts | 7 ++++++ src/test/comments.test.ts | 2 ++ src/test/converter/comment/comment.ts | 2 ++ src/test/converter/comment/specs.json | 25 +++++++++++-------- src/test/converter2/issues/gh3020.ts | 7 ++++++ src/test/issues.c2.test.ts | 11 ++++++++ src/test/programs.ts | 4 ++- 19 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 src/test/converter2/issues/gh3020.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 13d8a23db..f55f1ee13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ title: Changelog ## Unreleased +### Features + +- Introduced the `preservedTypeAnnotationTags` option to specify tags whose type annotations should + be copied to the output documentation, #3020. + API: Introduced `typeAnnotation` on `CommentTag` + ## v0.28.13 (2025-09-14) ### Features diff --git a/site/options/comments.md b/site/options/comments.md index 4e7caea52..c90abd7f2 100644 --- a/site/options/comments.md +++ b/site/options/comments.md @@ -190,7 +190,7 @@ Note that `@deprecated` is a block tag, not a modifier tag, so should not be spe ## excludeTags ```bash -$ typedoc --excludeTags apidefine +$ typedoc --excludeTags @apidefine ``` Specify tags that should be removed from doc comments when parsing. @@ -199,7 +199,7 @@ Useful if your project uses [apiDoc](https://apidocjs.com/) for documenting REST ## notRenderedTags ```bash -$ typedoc --notRenderedTags beta +$ typedoc --notRenderedTags @beta ``` Specify tags which should be preserved in the doc comments, but not rendered @@ -207,6 +207,18 @@ when creating output. This is intended to support tags which carry some meaning about how to render a member or instructions for TypeDoc to do something after a package has been deserialized from JSON in packages mode. +## preservedTypeAnnotationTags + +```json +// typedoc.json +{ + "preservedTypeAnnotationTags": ["@fires"] +} +``` + +Specify block tags whose type annotations should be preserved by TypeDoc's parser, +leading to their content being included in the rendered documentation. + ## externalSymbolLinkMappings ```json diff --git a/site/options/package-options.md b/site/options/package-options.md index 46aa2ea6c..047552aee 100644 --- a/site/options/package-options.md +++ b/site/options/package-options.md @@ -121,6 +121,7 @@ at the root level. The following tables indicate where an option should be set. | [`cascadedModifierTags`](comments.md#cascadedmodifiertags) | Package | | | [`excludeTags`](comments.md#excludetags) | Package | | | [`notRenderedTags`](comments.md#notrenderedtags) | Root | | +| [`preservedTypeAnnotationTags`](comments.md#preservedtypeannotationtags) | Package | | | [`externalSymbolLinkMappings`](comments.md#externalsymbollinkmappings) | Both | Unresolved links are checked both when converting and when merging projects | ## Organization Options diff --git a/src/lib/converter/comments/index.ts b/src/lib/converter/comments/index.ts index 7b2b69db7..faf890547 100644 --- a/src/lib/converter/comments/index.ts +++ b/src/lib/converter/comments/index.ts @@ -19,6 +19,7 @@ export interface CommentParserConfig { blockTags: Set; inlineTags: Set; modifierTags: Set; + preservedTypeAnnotationTags: Set; jsDocCompatibility: JsDocCompatibility; suppressCommentWarningsInDeclarationFiles: boolean; useTsLinkResolution: boolean; diff --git a/src/lib/converter/comments/parser.ts b/src/lib/converter/comments/parser.ts index 5d2adeb49..4453666c6 100644 --- a/src/lib/converter/comments/parser.ts +++ b/src/lib/converter/comments/parser.ts @@ -380,7 +380,22 @@ function blockTag( let content: CommentDisplayPart[]; if (tagName === "@example") { return exampleBlock(comment, lexer, config, i18n, warning, files); - } else if ( + } + + let typeAnnotation: string | undefined; + if ( + !lexer.done() && + config.preservedTypeAnnotationTags.has(tagName) + ) { + if (lexer.peek().kind === TokenSyntaxKind.Text && /^\s+$/.test(lexer.peek().text)) { + lexer.take(); + } + if (lexer.peek().kind === TokenSyntaxKind.TypeAnnotation) { + typeAnnotation = lexer.take().text; + } + } + + if ( ["@default", "@defaultValue"].includes(tagName) && config.jsDocCompatibility.defaultTag ) { @@ -396,7 +411,11 @@ function blockTag( content = blockContent(comment, lexer, config, i18n, warning, files); } - return new CommentTag(tagName as TagString, content); + const tag = new CommentTag(tagName as TagString, content); + if (typeAnnotation) { + tag.typeAnnotation = typeAnnotation; + } + return tag; } /** diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 296d2c0cb..9efdab032 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -785,12 +785,9 @@ export class Converter extends AbstractComponent { private _buildCommentParserConfig() { this._config = { blockTags: new Set(this.application.options.getValue("blockTags")), - inlineTags: new Set( - this.application.options.getValue("inlineTags"), - ), - modifierTags: new Set( - this.application.options.getValue("modifierTags"), - ), + inlineTags: new Set(this.application.options.getValue("inlineTags")), + modifierTags: new Set(this.application.options.getValue("modifierTags")), + preservedTypeAnnotationTags: new Set(this.application.options.getValue("preservedTypeAnnotationTags")), jsDocCompatibility: this.application.options.getValue("jsDocCompatibility"), suppressCommentWarningsInDeclarationFiles: this.application.options.getValue( "suppressCommentWarningsInDeclarationFiles", diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index b412c9741..ee7c4f032 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -316,6 +316,7 @@ export = { help_excludeTags: "Remove the listed block/modifier tags from doc comments", help_notRenderedTags: "Tags which will be preserved in doc comments, but not rendered when creating output", help_cascadedModifierTags: "Modifier tags which should be copied to all children of the parent reflection", + help_preservedTypeAnnotationTags: "Block tags whose type annotations should be preserved in the output.", help_readme: "Path to the readme file that should be displayed on the index page. Pass `none` to disable the index page and start the documentation on the globals page", help_basePath: "Specifies a path which links may be resolved relative to.", diff --git a/src/lib/models/Comment.ts b/src/lib/models/Comment.ts index 70316f50b..dea38cbf3 100644 --- a/src/lib/models/Comment.ts +++ b/src/lib/models/Comment.ts @@ -88,6 +88,12 @@ export class CommentTag { */ name?: string; + /** + * Optional type annotation associated with this tag. TypeDoc will remove type annotations unless explicitly + * requested by the user with the `preservedTypeAnnotationTags` option. + */ + typeAnnotation?: string; + /** * The actual body text of this tag. */ @@ -130,6 +136,9 @@ export class CommentTag { if (this.name) { tag.name = this.name; } + if (this.typeAnnotation) { + tag.typeAnnotation = this.typeAnnotation; + } return tag; } @@ -138,12 +147,14 @@ export class CommentTag { tag: this.tag, name: this.name, content: Comment.serializeDisplayParts(this.content), + typeAnnotation: this.typeAnnotation, }; } fromObject(de: Deserializer, obj: JSONOutput.CommentTag) { // tag already set by Comment.fromObject this.name = obj.name; + this.typeAnnotation = obj.typeAnnotation; this.content = Comment.deserializeDisplayParts(de, obj.content); } } diff --git a/src/lib/output/themes/default/partials/comment.tsx b/src/lib/output/themes/default/partials/comment.tsx index 46731e511..dc7a4d128 100644 --- a/src/lib/output/themes/default/partials/comment.tsx +++ b/src/lib/output/themes/default/partials/comment.tsx @@ -83,6 +83,7 @@ export function commentTags(context: DefaultThemeRenderContext, props: Reflectio {name} {anchorIcon(context, anchor)}
    + {item.typeAnnotation && {item.typeAnnotation}}
  • diff --git a/src/lib/serialization/schema.ts b/src/lib/serialization/schema.ts index d55900d71..f1fe07a74 100644 --- a/src/lib/serialization/schema.ts +++ b/src/lib/serialization/schema.ts @@ -369,7 +369,7 @@ export interface Comment extends Partial> { } /** @category Comments */ -export interface CommentTag extends S { +export interface CommentTag extends S { content: CommentDisplayPart[]; } diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index e1bb8a352..8a8337fb3 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -320,6 +320,7 @@ export interface TypeDocOptionMap { Record> >; cascadedModifierTags: TagString[]; + preservedTypeAnnotationTags: TagString[]; // Organization categorizeByGroup: boolean; diff --git a/src/lib/utils/options/defaults.ts b/src/lib/utils/options/defaults.ts index 277f0e035..3751c3183 100644 --- a/src/lib/utils/options/defaults.ts +++ b/src/lib/utils/options/defaults.ts @@ -51,6 +51,8 @@ export const cascadedModifierTags: readonly TagString[] = [ "@experimental", ]; +export const preservedTypeAnnotationTags: readonly TagString[] = []; + export const notRenderedTags: readonly TagString[] = [ "@showCategories", "@showGroups", diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 80826f26d..1ef0e7821 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -803,6 +803,13 @@ export function addTypeDocOptions(options: Pick) { defaultValue: OptionDefaults.cascadedModifierTags, validate: makeTagArrayValidator("cascadedModifierTags"), }); + options.addDeclaration({ + name: "preservedTypeAnnotationTags", + help: () => i18n.help_preservedTypeAnnotationTags(), + type: ParameterType.Array, + defaultValue: OptionDefaults.preservedTypeAnnotationTags, + validate: makeTagArrayValidator("preservedTypeAnnotationTags"), + }); // MARK: Organization Options diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts index 352cdba30..8a95a98c0 100644 --- a/src/test/comments.test.ts +++ b/src/test/comments.test.ts @@ -1145,6 +1145,7 @@ describe("Comment Parser", () => { "@event", "@packageDocumentation", ]), + preservedTypeAnnotationTags: new Set(["@fires"]), jsDocCompatibility: { defaultTag: true, exampleTag: true, @@ -1877,6 +1878,7 @@ describe("Raw Comment Parser", () => { "@event", "@packageDocumentation", ]), + preservedTypeAnnotationTags: new Set(["@fires"]), jsDocCompatibility: { defaultTag: true, exampleTag: true, diff --git a/src/test/converter/comment/comment.ts b/src/test/converter/comment/comment.ts index 919d7ac50..785827ab1 100644 --- a/src/test/converter/comment/comment.ts +++ b/src/test/converter/comment/comment.ts @@ -32,6 +32,8 @@ import "./comment2"; * * @groupDescription Methods * Methods description! + * + * @gh3020 {type annotation} */ export class CommentedClass { /** diff --git a/src/test/converter/comment/specs.json b/src/test/converter/comment/specs.json index 711f06806..509d4b241 100644 --- a/src/test/converter/comment/specs.json +++ b/src/test/converter/comment/specs.json @@ -66,6 +66,11 @@ "text": "Methods\nMethods description!" } ] + }, + { + "tag": "@gh3020", + "content": [], + "typeAnnotation": "{type annotation}" } ] }, @@ -109,9 +114,9 @@ "sources": [ { "fileName": "comment.ts", - "line": 40, + "line": 42, "character": 4, - "url": "typedoc://comment.ts#L40" + "url": "typedoc://comment.ts#L42" } ], "type": { @@ -128,9 +133,9 @@ "sources": [ { "fileName": "comment.ts", - "line": 80, + "line": 82, "character": 4, - "url": "typedoc://comment.ts#L80" + "url": "typedoc://comment.ts#L82" } ], "signatures": [ @@ -151,9 +156,9 @@ "sources": [ { "fileName": "comment.ts", - "line": 80, + "line": 82, "character": 4, - "url": "typedoc://comment.ts#L80" + "url": "typedoc://comment.ts#L82" } ], "parameters": [ @@ -217,9 +222,9 @@ "sources": [ { "fileName": "comment.ts", - "line": 36, + "line": 38, "character": 13, - "url": "typedoc://comment.ts#L36" + "url": "typedoc://comment.ts#L38" } ] }, @@ -232,9 +237,9 @@ "sources": [ { "fileName": "comment.ts", - "line": 89, + "line": 91, "character": 12, - "url": "typedoc://comment.ts#L89" + "url": "typedoc://comment.ts#L91" } ], "type": { diff --git a/src/test/converter2/issues/gh3020.ts b/src/test/converter2/issues/gh3020.ts new file mode 100644 index 000000000..e27024db3 --- /dev/null +++ b/src/test/converter2/issues/gh3020.ts @@ -0,0 +1,7 @@ +/** + * Component demo. + * + * @fires {CustomEvent<{id: string, source: Element}>} item-click - event when item is clicked. + */ +export class ButtonControlElement extends Object { +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index cdb558765..4b8ce14a4 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2209,4 +2209,15 @@ describe("Issue Tests", () => { logger.expectMessage("warn: Content in the @remarks block will be overwritten*"); }); + + it("#3020 permits preserving type annotations", () => { + app.options.setValue("blockTags", ["@fires"]); + app.options.setValue("preservedTypeAnnotationTags", ["@fires"]); + const project = convert(); + const btn = query(project, "ButtonControlElement"); + + equal(btn.comment?.blockTags.length, 1); + equal(btn.comment.blockTags[0].tag, "@fires"); + equal(btn.comment.blockTags[0].typeAnnotation, "{CustomEvent<{id: string, source: Element}>}"); + }); }); diff --git a/src/test/programs.ts b/src/test/programs.ts index d34b5315a..bff126163 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -14,7 +14,7 @@ import type { ModelToObject } from "../lib/serialization/schema.js"; import { createAppForTesting } from "../lib/application.js"; import { existsSync } from "fs"; import { diagnostics } from "../lib/utils/loggers.js"; -import { normalizePath, readFile, ValidatingFileRegistry } from "#node-utils"; +import { normalizePath, OptionDefaults, readFile, ValidatingFileRegistry } from "#node-utils"; let converterApp: Application | undefined; let converterProgram: ts.Program | undefined; @@ -42,6 +42,8 @@ export function getConverterApp() { gitRevision: "fake", readme: "none", skipErrorChecking: true, + preservedTypeAnnotationTags: ["@gh3020"], + blockTags: [...OptionDefaults.blockTags, "@gh3020"], } satisfies TypeDocOptions, ) ) { From efd06b1d657bba21f75154321407273a6e790925 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 27 Sep 2025 21:12:57 -0600 Subject: [PATCH 112/119] Fix conversion of auto accessor types Resolves #3019 Total time spent: 20 minutes --- CHANGELOG.md | 4 ++++ scripts/testcase.js | 14 ++++++++------ src/lib/converter/symbols.ts | 14 ++++++++++++++ .../class/specs-with-lump-categories.json | 6 +++++- src/test/converter/class/specs.json | 6 +++++- src/test/converter2/issues/gh3019.ts | 4 ++++ src/test/issues.c2.test.ts | 6 ++++++ 7 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 src/test/converter2/issues/gh3019.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f55f1ee13..7815092d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ title: Changelog be copied to the output documentation, #3020. API: Introduced `typeAnnotation` on `CommentTag` +## Bug Fixes + +- Fixed conversion of auto-accessor types on properties with the `accessor` keyword, #3019. + ## v0.28.13 (2025-09-14) ### Features diff --git a/scripts/testcase.js b/scripts/testcase.js index 0756dae45..8239e71cc 100755 --- a/scripts/testcase.js +++ b/scripts/testcase.js @@ -56,16 +56,18 @@ async function main() { ["ts", "tsx", "js", "jsx"].includes(tok.info || ""), ) || tokens.find((tok) => tok.tag === "code"); + /** @type {string} */ + let file; if (!code) { console.log("No codeblock found"); - const file = `src/test/converter2/issues/gh${issue}.ts`; - await exec(`code ${file} src/test/issues.c2.test.ts`); - return; + file = `src/test/converter2/issues/gh${issue}.ts`; + await writeFile(file, ""); + } else { + const ext = process.argv[3] ? `.${process.argv[3]}` : guessExtension(code); + file = `src/test/converter2/issues/gh${issue}${ext}`; + await writeFile(file, code.content); } - const ext = process.argv[3] ? `.${process.argv[3]}` : guessExtension(code); - const file = `src/test/converter2/issues/gh${issue}${ext}`; - await writeFile(file, code.content); console.log(file); console.log("src/test/issues.c2.test.ts"); } diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index 7358e3e45..8835d6535 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -1378,6 +1378,20 @@ function convertAccessor( const declaration = symbol.getDeclarations()?.[0]; if (declaration) { setModifiers(symbol, declaration, reflection); + + // #3019, auto accessors `accessor x: string` get the symbol flag for + // an accessor, but they don't have get/set accessors, so the need a type + // set on the accessor reflection structure. + if ( + ts.isPropertyDeclaration(declaration) && + declaration.modifiers?.some(n => n.kind === ts.SyntaxKind.AccessorKeyword) + ) { + reflection.type = context.converter.convertType( + context.withScope(reflection), + context.checker.getTypeOfSymbol(symbol), + declaration.type, + ); + } } context.finalizeDeclarationReflection(reflection); diff --git a/src/test/converter/class/specs-with-lump-categories.json b/src/test/converter/class/specs-with-lump-categories.json index 386ab2fe6..e63c9b6a6 100644 --- a/src/test/converter/class/specs-with-lump-categories.json +++ b/src/test/converter/class/specs-with-lump-categories.json @@ -4824,7 +4824,11 @@ "character": 13, "url": "typedoc://getter-setter.ts#L22" } - ] + ], + "type": { + "type": "intrinsic", + "name": "string" + } }, { "id": 190, diff --git a/src/test/converter/class/specs.json b/src/test/converter/class/specs.json index 386ab2fe6..e63c9b6a6 100644 --- a/src/test/converter/class/specs.json +++ b/src/test/converter/class/specs.json @@ -4824,7 +4824,11 @@ "character": 13, "url": "typedoc://getter-setter.ts#L22" } - ] + ], + "type": { + "type": "intrinsic", + "name": "string" + } }, { "id": 190, diff --git a/src/test/converter2/issues/gh3019.ts b/src/test/converter2/issues/gh3019.ts new file mode 100644 index 000000000..0dc0ee36c --- /dev/null +++ b/src/test/converter2/issues/gh3019.ts @@ -0,0 +1,4 @@ +class GH3019 { + accessor x: string = ""; + accessor y: number = 123; +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 4b8ce14a4..00af3d70b 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2210,6 +2210,12 @@ describe("Issue Tests", () => { logger.expectMessage("warn: Content in the @remarks block will be overwritten*"); }); + it("#3019 correctly parses accessor types", () => { + const project = convert(); + equal(query(project, "GH3019.x").type?.toString(), "string"); + equal(query(project, "GH3019.y").type?.toString(), "number"); + }); + it("#3020 permits preserving type annotations", () => { app.options.setValue("blockTags", ["@fires"]); app.options.setValue("preservedTypeAnnotationTags", ["@fires"]); From fdd83699ff5bfc38ea53f6ee0ac4cfd61ed5046c Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 27 Sep 2025 22:43:46 -0600 Subject: [PATCH 113/119] Improve slugger for headers containing HTML Resolves #3023 --- .gitignore | 2 +- CHANGELOG.md | 1 + scripts/rebuild_specs.js | 2 +- src/lib/output/themes/MarkedPlugin.tsx | 11 +- src/lib/output/themes/default/Slugger.ts | 18 ++- src/lib/utils-common/general.ts | 1 + .../converter2/renderer/renderer-readme.md | 3 + src/test/output/Slugger.test.ts | 11 ++ .../renderer/specs/classes/BaseClass.json | 4 +- .../renderer/specs/classes/GenericClass.json | 4 +- .../specs/classes/ModifiersClass.json | 15 ++- .../renderer/specs/classes/RenderClass.json | 28 +++- src/test/renderer/specs/documents/doc.json | 44 ++++++- .../renderer/specs/enums/Enumeration.json | 25 +++- src/test/renderer/specs/functions/box.json | 8 +- src/test/renderer/specs/index.json | 48 ++++++- .../specs/interfaces/BaseInterface.json | 4 +- .../specs/interfaces/DisabledGroups.json | 10 +- .../ExpandType.ExpandedByDefault.json | 4 +- src/test/renderer/specs/modules.json | 4 +- .../specs/types/ExpandType.AExpanded.json | 4 +- .../specs/types/ExpandType.BExpanded.json | 4 +- .../specs/types/ExpandType.Expandable.json | 4 +- .../specs/types/ExpandType.Expandable2.json | 4 +- .../ExpandType.NestedBehavior1.AExpanded.json | 4 +- ...xpandType.NestedBehavior1.AllExpanded.json | 12 +- src/test/renderer/specs/types/Nested.json | 16 ++- .../renderer/specs/types/UnionComments.json | 8 +- src/test/renderer/testRendererUtils.ts | 121 +++++++++++++++++- 29 files changed, 376 insertions(+), 48 deletions(-) create mode 100644 src/test/converter2/renderer/renderer-readme.md diff --git a/.gitignore b/.gitignore index a223dce66..fbbe7596d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,8 @@ yarn-error.log /coverage/ /dist/ /docs -/docs-site /docs2 +/docs-* /td*.json typedoc*.tgz diff --git a/CHANGELOG.md b/CHANGELOG.md index 7815092d4..a8d93e661 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ title: Changelog ## Bug Fixes - Fixed conversion of auto-accessor types on properties with the `accessor` keyword, #3019. +- Improved handling of HTML tags within headers for anchor generation, #3023. ## v0.28.13 (2025-09-14) diff --git a/scripts/rebuild_specs.js b/scripts/rebuild_specs.js index dd333c4a7..a0a00cd99 100755 --- a/scripts/rebuild_specs.js +++ b/scripts/rebuild_specs.js @@ -81,7 +81,7 @@ const conversions = [ */ function rebuildConverterTests(dirs) { const program = ts.createProgram(app.options.getFileNames(), { - ...app.options.getCompilerOptions(), + ...app.options.getCompilerOptions(app.logger), noEmit: true, }); diff --git a/src/lib/output/themes/MarkedPlugin.tsx b/src/lib/output/themes/MarkedPlugin.tsx index 924b28ca9..d1b636206 100644 --- a/src/lib/output/themes/MarkedPlugin.tsx +++ b/src/lib/output/themes/MarkedPlugin.tsx @@ -409,10 +409,19 @@ export class MarkedPlugin extends ContextAwareRendererComponent { } function getTokenTextContent(token: md.Token): string { + // If there are children, we want their text content, not the full text content if (token.children) { return token.children.map(getTokenTextContent).join(""); } - return token.content; + + // If this is a simple text fragment, use its content + if (token.type === "text") { + return token.content; + } + + // Otherwise this is some type of metadata token (e.g. header_open) + // or a HTML tag token. Don't include it in the text content. + return ""; } const kindNames = ["note", "tip", "important", "warning", "caution"]; diff --git a/src/lib/output/themes/default/Slugger.ts b/src/lib/output/themes/default/Slugger.ts index 65632cd28..2198ae58e 100644 --- a/src/lib/output/themes/default/Slugger.ts +++ b/src/lib/output/themes/default/Slugger.ts @@ -1,5 +1,5 @@ import { getSimilarValues } from "#utils"; -import type { TypeDocOptionMap } from "../../../utils/index.js"; +import type { TypeDocOptionMap } from "#node-utils"; /** * Responsible for getting a unique anchor for elements within a page. @@ -8,20 +8,28 @@ export class Slugger { private seen = new Map(); private serialize(value: string) { - // Notes: - // There are quite a few trade-offs here. + // There are quite a few trade-offs here. We used to remove HTML tags here, + // but TypeDoc now removes the HTML tags before passing text into the slug + // method, which allows us to skip doing that here. This improves the slugger + // generation for headers which look like the following: + // (html allowed in markdown) + // # test <t> + // (html disallowed in markdown) + // # test + // both of the above should slug to test-t return ( value .trim() - // remove html tags - .replace(/<[!/a-z].*?>/gi, "") // remove unwanted chars .replace( /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, "", ) + // change whitespace to dash .replace(/\s/g, "-") + // combine adjacent dashes + .replace(/--+/, "-") ); } diff --git a/src/lib/utils-common/general.ts b/src/lib/utils-common/general.ts index 1fcab7f2c..d22e76497 100644 --- a/src/lib/utils-common/general.ts +++ b/src/lib/utils-common/general.ts @@ -45,6 +45,7 @@ export function assertNever(x: never): never { export function assert(x: unknown, message = "Assertion failed"): asserts x { if (!x) { + debugger; throw new Error(message); } } diff --git a/src/test/converter2/renderer/renderer-readme.md b/src/test/converter2/renderer/renderer-readme.md new file mode 100644 index 000000000..e6cd8f720 --- /dev/null +++ b/src/test/converter2/renderer/renderer-readme.md @@ -0,0 +1,3 @@ +# gh3023 <test> + +Anchor for above heading should be `gh3023-test` diff --git a/src/test/output/Slugger.test.ts b/src/test/output/Slugger.test.ts index f11f9c1b6..f0c839a22 100644 --- a/src/test/output/Slugger.test.ts +++ b/src/test/output/Slugger.test.ts @@ -13,4 +13,15 @@ describe("Slugger", () => { equal(slugger.slug("model"), "model"); equal(slugger.slug("Model"), "Model-1"); }); + + it("Handles embedded html characters", () => { + const slugger = new Slugger({ lowercase: true }); + equal(slugger.slug("test "), "test-t"); + equal(slugger.slug("test "), "test-t-1"); + }); + + it("Handles adjacent whitespace", () => { + const slugger = new Slugger({ lowercase: true }); + equal(slugger.slug("test test2"), "test-test2"); + }); }); diff --git a/src/test/renderer/specs/classes/BaseClass.json b/src/test/renderer/specs/classes/BaseClass.json index 9aa168302..47bf55a43 100644 --- a/src/test/renderer/specs/classes/BaseClass.json +++ b/src/test/renderer/specs/classes/BaseClass.json @@ -401,7 +401,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Base class method

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Base class method" + } }, { "div.tsd-parameters": [ diff --git a/src/test/renderer/specs/classes/GenericClass.json b/src/test/renderer/specs/classes/GenericClass.json index 8e8824309..38529a94c 100644 --- a/src/test/renderer/specs/classes/GenericClass.json +++ b/src/test/renderer/specs/classes/GenericClass.json @@ -27,7 +27,9 @@ }, { "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": "

    Generic class

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Generic class" + } } }, { diff --git a/src/test/renderer/specs/classes/ModifiersClass.json b/src/test/renderer/specs/classes/ModifiersClass.json index 5d76ca4b9..e4650c528 100644 --- a/src/test/renderer/specs/classes/ModifiersClass.json +++ b/src/test/renderer/specs/classes/ModifiersClass.json @@ -204,7 +204,20 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    #2934 same page link ModifiersClass

    \n" + "div.tsd-comment.tsd-typography": { + "p": [ + "#2934 same page link", + { + "tag": "a.tsd-kind-class", + "props": { + "href": "#" + }, + "children": { + "code": "ModifiersClass" + } + } + ] + } }, { "h4.tsd-returns-title": [ diff --git a/src/test/renderer/specs/classes/RenderClass.json b/src/test/renderer/specs/classes/RenderClass.json index 05fb799e2..47a7ca9ee 100644 --- a/src/test/renderer/specs/classes/RenderClass.json +++ b/src/test/renderer/specs/classes/RenderClass.json @@ -27,7 +27,9 @@ }, { "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": "

    Renderer class

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Renderer class" + } } }, { @@ -101,7 +103,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Index signature

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Index signature" + } } ] } @@ -348,7 +352,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Ctor comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Ctor comment" + } }, { "div.tsd-parameters": [ @@ -482,7 +488,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Property

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Property" + } }, { "aside.tsd-sources": { @@ -942,7 +950,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Method comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Method comment" + } }, { "div.tsd-parameters": [ @@ -1066,7 +1076,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Sig 1 comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Sig 1 comment" + } }, { "h4.tsd-returns-title": [ @@ -1138,7 +1150,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Sig 2 comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Sig 2 comment" + } }, { "div.tsd-parameters": [ diff --git a/src/test/renderer/specs/documents/doc.json b/src/test/renderer/specs/documents/doc.json index e0a58f254..decb74ce4 100644 --- a/src/test/renderer/specs/documents/doc.json +++ b/src/test/renderer/specs/documents/doc.json @@ -21,7 +21,49 @@ } }, { - "div.tsd-panel.tsd-typography": "

    Rendered document

    \n

    Link to this doc: link

    \n
    test();\n
    \n\n
    Note

    \nThis is an alert

    \n
    \n" + "div.tsd-panel.tsd-typography": [ + { + "p": "Rendered document" + }, + { + "p": [ + "Link to this doc:", + { + "tag": "a", + "props": { + "href": "" + }, + "children": "link" + } + ] + }, + { + "pre": [ + { + "code.ts": "test();" + }, + { + "tag": "button", + "props": { + "type": "button" + }, + "children": "Copy" + } + ] + }, + { + "div.tsd-alert.tsd-alert-note": [ + { + "div.tsd-alert-title": { + "span": "Note" + } + }, + { + "p": "This is an alert" + } + ] + } + ] } ] }, diff --git a/src/test/renderer/specs/enums/Enumeration.json b/src/test/renderer/specs/enums/Enumeration.json index 35b35df3c..966a9f2d8 100644 --- a/src/test/renderer/specs/enums/Enumeration.json +++ b/src/test/renderer/specs/enums/Enumeration.json @@ -28,7 +28,18 @@ { "section.tsd-panel.tsd-comment": [ { - "div.tsd-comment.tsd-typography": "

    Enum comment Value1

    \n" + "div.tsd-comment.tsd-typography": { + "p": [ + "Enum comment", + { + "tag": "a.tsd-kind-enum-member", + "props": { + "href": "#value1" + }, + "children": "Value1" + } + ] + } }, { "div.tsd-comment.tsd-typography": { @@ -45,7 +56,9 @@ } ] }, - "

    Block tag

    \n" + { + "p": "Block tag" + } ] } } @@ -165,7 +178,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Value1 comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Value1 comment" + } }, { "aside.tsd-sources": { @@ -216,7 +231,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Value2 comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Value2 comment" + } }, { "aside.tsd-sources": { diff --git a/src/test/renderer/specs/functions/box.json b/src/test/renderer/specs/functions/box.json index 7d317b856..473fcb350 100644 --- a/src/test/renderer/specs/functions/box.json +++ b/src/test/renderer/specs/functions/box.json @@ -105,7 +105,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Signature comment\n#2921 !Promise

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Signature comment\n#2921 !Promise" + } }, { "section.tsd-panel": [ @@ -147,7 +149,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Item comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Item comment" + } } ] } diff --git a/src/test/renderer/specs/index.json b/src/test/renderer/specs/index.json index 358074286..68faaaf24 100644 --- a/src/test/renderer/specs/index.json +++ b/src/test/renderer/specs/index.json @@ -16,13 +16,57 @@ ] }, { - "div.tsd-panel.tsd-typography": "

    Readme text

    \n" + "div.tsd-panel.tsd-typography": [ + { + "h1#gh3023-test.tsd-anchor-link": [ + "gh3023 <test>", + { + "tag": "a.tsd-anchor-icon", + "props": { + "href": "#gh3023-test", + "aria-label": "Permalink" + } + } + ] + }, + { + "p": [ + "Anchor for above heading should be", + { + "code": "gh3023-test" + } + ] + } + ] } ] }, { "div.col-sidebar": { - "div.page-menu": [] + "div.page-menu": { + "tag": "details.tsd-accordion.tsd-page-navigation", + "props": { + "open": true + }, + "children": [ + { + "summary.tsd-accordion-summary": { + "h3": "On This Page" + } + }, + { + "div.tsd-accordion-details": { + "tag": "a", + "props": { + "href": "#gh3023-test" + }, + "children": { + "span": "gh3023 " + } + } + } + ] + } } } ] diff --git a/src/test/renderer/specs/interfaces/BaseInterface.json b/src/test/renderer/specs/interfaces/BaseInterface.json index 0097acc1b..984a18ba0 100644 --- a/src/test/renderer/specs/interfaces/BaseInterface.json +++ b/src/test/renderer/specs/interfaces/BaseInterface.json @@ -325,7 +325,9 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    Interface method

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Interface method" + } }, { "div.tsd-parameters": [ diff --git a/src/test/renderer/specs/interfaces/DisabledGroups.json b/src/test/renderer/specs/interfaces/DisabledGroups.json index 81ddbd754..4d7363857 100644 --- a/src/test/renderer/specs/interfaces/DisabledGroups.json +++ b/src/test/renderer/specs/interfaces/DisabledGroups.json @@ -248,7 +248,15 @@ { "div.tsd-description": [ { - "div.tsd-comment.tsd-typography": "

    link to readme #3006

    \n" + "div.tsd-comment.tsd-typography": { + "p": { + "tag": "a", + "props": { + "href": "../" + }, + "children": "link to readme #3006" + } + } }, { "h4.tsd-returns-title": [ diff --git a/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json b/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json index 9ee09478a..dd0a91628 100644 --- a/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json +++ b/src/test/renderer/specs/interfaces/ExpandType.ExpandedByDefault.json @@ -181,7 +181,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    B

    \n" + "div.tsd-comment.tsd-typography": { + "p": "B" + } }, { "aside.tsd-sources": { diff --git a/src/test/renderer/specs/modules.json b/src/test/renderer/specs/modules.json index 2c201b7c5..c8247517c 100644 --- a/src/test/renderer/specs/modules.json +++ b/src/test/renderer/specs/modules.json @@ -17,7 +17,9 @@ }, { "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": "

    Module comment

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Module comment" + } } }, { diff --git a/src/test/renderer/specs/types/ExpandType.AExpanded.json b/src/test/renderer/specs/types/ExpandType.AExpanded.json index 948d80e77..054638787 100644 --- a/src/test/renderer/specs/types/ExpandType.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.AExpanded.json @@ -285,7 +285,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    A

    \n" + "div.tsd-comment.tsd-typography": { + "p": "A" + } } ] } diff --git a/src/test/renderer/specs/types/ExpandType.BExpanded.json b/src/test/renderer/specs/types/ExpandType.BExpanded.json index 5ff4ad414..146e2c691 100644 --- a/src/test/renderer/specs/types/ExpandType.BExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.BExpanded.json @@ -337,7 +337,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    B

    \n" + "div.tsd-comment.tsd-typography": { + "p": "B" + } } ] } diff --git a/src/test/renderer/specs/types/ExpandType.Expandable.json b/src/test/renderer/specs/types/ExpandType.Expandable.json index 3415947d4..9a12bb136 100644 --- a/src/test/renderer/specs/types/ExpandType.Expandable.json +++ b/src/test/renderer/specs/types/ExpandType.Expandable.json @@ -185,7 +185,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    A

    \n" + "div.tsd-comment.tsd-typography": { + "p": "A" + } }, { "aside.tsd-sources": { diff --git a/src/test/renderer/specs/types/ExpandType.Expandable2.json b/src/test/renderer/specs/types/ExpandType.Expandable2.json index 4352329a9..5adbf8dbe 100644 --- a/src/test/renderer/specs/types/ExpandType.Expandable2.json +++ b/src/test/renderer/specs/types/ExpandType.Expandable2.json @@ -185,7 +185,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    C

    \n" + "div.tsd-comment.tsd-typography": { + "p": "C" + } }, { "aside.tsd-sources": { diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json index dc8d54c08..95b96b709 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AExpanded.json @@ -346,7 +346,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    B

    \n" + "div.tsd-comment.tsd-typography": { + "p": "B" + } } ] } diff --git a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json index 6d9e9baa2..df4424c35 100644 --- a/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json +++ b/src/test/renderer/specs/types/ExpandType.NestedBehavior1.AllExpanded.json @@ -294,7 +294,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    A

    \n" + "div.tsd-comment.tsd-typography": { + "p": "A" + } } ] } @@ -375,7 +377,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    B

    \n" + "div.tsd-comment.tsd-typography": { + "p": "B" + } } ] } @@ -456,7 +460,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    C

    \n" + "div.tsd-comment.tsd-typography": { + "p": "C" + } } ] } diff --git a/src/test/renderer/specs/types/Nested.json b/src/test/renderer/specs/types/Nested.json index 1e79630df..18f04453c 100644 --- a/src/test/renderer/specs/types/Nested.json +++ b/src/test/renderer/specs/types/Nested.json @@ -27,7 +27,9 @@ }, { "section.tsd-panel.tsd-comment": { - "div.tsd-comment.tsd-typography": "

    Type alias with nested properties

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Type alias with nested properties" + } } }, { @@ -383,7 +385,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Another value

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Another value" + } } ] }, @@ -436,7 +440,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    More options

    \n" + "div.tsd-comment.tsd-typography": { + "p": "More options" + } } ] }, @@ -459,7 +465,9 @@ ] }, { - "div.tsd-comment.tsd-typography": "

    Value

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Value" + } } ] } diff --git a/src/test/renderer/specs/types/UnionComments.json b/src/test/renderer/specs/types/UnionComments.json index 06e4ab4f0..aabbcc333 100644 --- a/src/test/renderer/specs/types/UnionComments.json +++ b/src/test/renderer/specs/types/UnionComments.json @@ -60,7 +60,9 @@ "span.tsd-signature-type": "\"abc\"" }, { - "div.tsd-comment.tsd-typography": "

    Commentary on abc

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Commentary on abc" + } } ] }, @@ -70,7 +72,9 @@ "span.tsd-signature-type": "\"def\"" }, { - "div.tsd-comment.tsd-typography": "

    Commentary on def

    \n" + "div.tsd-comment.tsd-typography": { + "p": "Commentary on def" + } } ] } diff --git a/src/test/renderer/testRendererUtils.ts b/src/test/renderer/testRendererUtils.ts index 544e441a7..14632fd4b 100644 --- a/src/test/renderer/testRendererUtils.ts +++ b/src/test/renderer/testRendererUtils.ts @@ -1,10 +1,11 @@ import { type Reflection, resetReflectionID } from "#models"; -import { loadTestHighlighter } from "#node-utils"; +import { HtmlAttributeParser, loadTestHighlighter, ParserState } from "#node-utils"; import { rm } from "node:fs/promises"; import { DefaultTheme, KindRouter, PageEvent, PageKind, type RenderTemplate } from "../../lib/output/index.js"; import { type JsxChildren, type JsxElement, JsxFragment } from "../../lib/utils-common/jsx.elements.js"; import { Raw } from "../../lib/utils-common/jsx.js"; import { getConverter2App, getConverter2Project } from "../programs.js"; +import { assert } from "#utils"; function shouldIgnoreElement(el: JsxElement) { switch (el.tag) { @@ -46,6 +47,120 @@ function collapseStrings(data: any[]): unknown { return data; } +// This is a very hacky html parser only intended to handle output from markdown-it +// for inclusion in the renderer specs. Don't use it for anything that requires actual +// security. +function parseHtmlToJsxElement(html: string): JsxChildren[] { + const stack: JsxElement[] = []; + const output: JsxChildren[] = []; + let pos = 0; + let last = 0; + + function currentChildList() { + if (stack.length) { + return stack[stack.length - 1].children; + } + return output; + } + + function skipWs() { + while (pos < html.length && /\s/.test(html[pos])) ++pos; + } + + function takeWord() { + const start = pos; + while (pos < html.length && /[a-z0-9-]/i.test(html[pos])) ++pos; + return html.slice(start, pos); + } + + function startTag() { + assert(html[pos] === "<"); + + ++pos; + skipWs(); + + const tag = takeWord(); + + const parser = new HtmlAttributeParser(html, pos); + const props: Record = {}; + + while (parser.state !== ParserState.END) { + if (parser.state === ParserState.BeforeAttributeValue) { + parser.step(); + props[parser.currentAttributeName] = parser.currentAttributeValue; + } else { + parser.step(); + } + } + + pos = parser.pos - 1; + + const element = { + tag, + props, + children: [], + }; + + if (html[pos] === "/") { + // self closing tag + currentChildList().push(element); + ++pos; + skipWs(); + } else { + currentChildList().push(element); + stack.push(element); + } + + assert(html[pos] === ">"); + ++pos; + last = pos; + } + + function endTag() { + assert(html[pos] === "<" && html[pos + 1] === "/"); + pos += 2; + skipWs(); + + const tag = takeWord(); + if (stack.length === 0 || stack[stack.length - 1].tag !== tag) { + throw new Error(`Invalid HTML, failed to match end tag: ${tag}`); + } + stack.pop(); + + skipWs(); + assert(html[pos] === ">"); + ++pos; + last = pos; + } + + function saveContent() { + if (pos !== last) { + const content = html.slice(last, pos); + currentChildList().push(content.trim()); + } + last = pos; + } + + while (pos < html.length) { + switch (html[pos]) { + case "<": + saveContent(); + if (html[pos + 1] == "/") { + endTag(); + } else { + startTag(); + } + break; + default: + ++pos; + break; + } + } + + saveContent(); + return output; +} + function renderElementToSnapshot(element: JsxChildren): unknown { if (typeof element === "string" || typeof element === "number" || typeof element === "bigint") { return element.toString().replaceAll("\u00a0", " "); @@ -67,7 +182,7 @@ function renderElementToSnapshot(element: JsxChildren): unknown { if (typeof tag === "function") { if (tag === Raw) { - return String((props as any).html); + return renderElementToSnapshot(parseHtmlToJsxElement(String((props as any).html))); } if (tag === JsxFragment) { return collapseStrings(children.flatMap(renderElementToSnapshot).filter(Boolean)); @@ -155,10 +270,10 @@ export async function buildRendererSpecs(specPath: string) { // Unfortunate not to set this in typedoc.json for converter2, but plenty // of tests expect to test the default option, not this. app2.options.setValue("categorizeByGroup", true); + app2.options.setValue("readme", "src/test/converter2/renderer/renderer-readme.md"); resetReflectionID(); const project = getConverter2Project(["renderer"], "."); - project.readme = [{ kind: "text", text: "Readme text" }]; await app2.generateDocs(project, specPath); await rm(`${specPath}/assets`, { recursive: true }); await rm(`${specPath}/.nojekyll`); From 8ff7d27bb43adb4c9bf42485edbdd59f504428ba Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 27 Sep 2025 22:50:07 -0600 Subject: [PATCH 114/119] Fix lint --- src/lib/utils-common/general.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/utils-common/general.ts b/src/lib/utils-common/general.ts index d22e76497..73f06bcad 100644 --- a/src/lib/utils-common/general.ts +++ b/src/lib/utils-common/general.ts @@ -45,6 +45,7 @@ export function assertNever(x: never): never { export function assert(x: unknown, message = "Assertion failed"): asserts x { if (!x) { + // eslint-disable-next-line no-debugger debugger; throw new Error(message); } From a85a65b145421cf8078720e893a97fa1c2b1b4db Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Sat, 4 Oct 2025 09:38:22 -0600 Subject: [PATCH 115/119] Sort `null` and `undefined` last in union types Resolves #3024 --- src/lib/converter/types.ts | 28 +++++++++++++++------- src/test/converter/function/specs.json | 32 +++++++++++++------------- src/test/converter2/issues/gh3024.ts | 5 ++++ src/test/issues.c2.test.ts | 14 ++++++++++- 4 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 src/test/converter2/issues/gh3024.ts diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 8cd71c246..34314f130 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -1132,7 +1132,7 @@ const unionConverter: TypeConverter = { convertType(context, type) { const types = type.types.map((type) => convertType(context, type)); normalizeUnion(types); - sortLiteralUnion(types); + sortUnion(types); return new UnionType(types); }, @@ -1220,18 +1220,30 @@ function kindToModifier( } } -function sortLiteralUnion(types: SomeType[]) { - if ( - types.some((t) => t.type !== "literal" || typeof t.value !== "number") - ) { +function sortUnion(types: SomeType[]) { + // If every member of the union is a literal numeric type, sort in ascending order + if (types.every(t => t.type === "literal" && typeof t.value === "number")) { + types.sort((a, b) => { + const aLit = a as LiteralType; + const bLit = b as LiteralType; + + return (aLit.value as number) - (bLit.value as number); + }); return; } + // #3024 Otherwise, leave the union in the converted order with the exception of null + // and undefined, which should be sorted last, with null before undefined. types.sort((a, b) => { - const aLit = a as LiteralType; - const bLit = b as LiteralType; + const aIsNull = a.type === "literal" && a.value === null; + const aIsUndef = a.type === "intrinsic" && a.name === "undefined"; + const bIsNull = b.type === "literal" && b.value === null; + const bIsUndef = b.type === "intrinsic" && b.name === "undefined"; + + const aWeight = aIsNull ? 1 : aIsUndef ? 2 : 0; + const bWeight = bIsNull ? 1 : bIsUndef ? 2 : 0; - return (aLit.value as number) - (bLit.value as number); + return aWeight - bWeight; }); } diff --git a/src/test/converter/function/specs.json b/src/test/converter/function/specs.json index 8b391258c..6c66ee7ef 100644 --- a/src/test/converter/function/specs.json +++ b/src/test/converter/function/specs.json @@ -841,19 +841,19 @@ "type": "union", "types": [ { - "type": "intrinsic", - "name": "undefined" + "type": "reference", + "target": 47, + "name": "T", + "package": "typedoc", + "refersToTypeParameter": true }, { "type": "literal", "value": null }, { - "type": "reference", - "target": 47, - "name": "T", - "package": "typedoc", - "refersToTypeParameter": true + "type": "intrinsic", + "name": "undefined" } ] } @@ -921,11 +921,11 @@ "types": [ { "type": "intrinsic", - "name": "undefined" + "name": "boolean" }, { "type": "intrinsic", - "name": "boolean" + "name": "undefined" } ] } @@ -1543,19 +1543,19 @@ "type": "union", "types": [ { - "type": "intrinsic", - "name": "undefined" + "type": "reference", + "target": 51, + "name": "T", + "package": "typedoc", + "refersToTypeParameter": true }, { "type": "literal", "value": null }, { - "type": "reference", - "target": 51, - "name": "T", - "package": "typedoc", - "refersToTypeParameter": true + "type": "intrinsic", + "name": "undefined" } ] } diff --git a/src/test/converter2/issues/gh3024.ts b/src/test/converter2/issues/gh3024.ts new file mode 100644 index 000000000..7d45c6c4f --- /dev/null +++ b/src/test/converter2/issues/gh3024.ts @@ -0,0 +1,5 @@ +export class GH3024 { + method(x: string | undefined) {} + method2(x: string | null) {} + method3(x: string | null | undefined) {} +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 00af3d70b..17e521b84 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -282,7 +282,7 @@ describe("Issue Tests", () => { const project = convert(); const nullableParam = query(project, "nullable").signatures?.[0] ?.parameters?.[0]; - equal(nullableParam?.type?.toString(), "null | string"); + equal(nullableParam?.type?.toString(), "string | null"); const nonNullableParam = query(project, "nonNullable").signatures?.[0] ?.parameters?.[0]; @@ -2226,4 +2226,16 @@ describe("Issue Tests", () => { equal(btn.comment.blockTags[0].tag, "@fires"); equal(btn.comment.blockTags[0].typeAnnotation, "{CustomEvent<{id: string, source: Element}>}"); }); + + it("#3024 places null and undefined last in unions converted from types", () => { + const project = convert(); + const method = querySig(project, "GH3024.method"); + equal(method.parameters?.[0].type?.toString(), "string | undefined"); + + const method2 = querySig(project, "GH3024.method2"); + equal(method2.parameters?.[0].type?.toString(), "string | null"); + + const method3 = querySig(project, "GH3024.method3"); + equal(method3.parameters?.[0].type?.toString(), "string | null | undefined"); + }); }); From 10092065d08970f1b8bddd0301fee1b1922cb1d4 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 10 Oct 2025 20:46:22 -0600 Subject: [PATCH 116/119] Implement excludePrivateClassFields resolves #3017 --- CHANGELOG.md | 1 + site/options/input.md | 11 ++++++++++- site/options/package-options.md | 1 + src/lib/converter/plugins/CommentPlugin.ts | 16 +++++++++++++++ src/lib/internationalization/locales/de.cts | 1 - src/lib/internationalization/locales/en.cts | 3 ++- src/lib/internationalization/locales/ja.cts | 1 - src/lib/internationalization/locales/ko.cts | 1 - src/lib/internationalization/locales/zh.cts | 1 - src/lib/utils/options/declaration.ts | 1 + src/lib/utils/options/sources/typedoc.ts | 6 ++++++ src/test/converter2/issues/gh3017.ts | 8 ++++++++ src/test/issues.c2.test.ts | 22 +++++++++++++++++++++ src/test/programs.ts | 1 + 14 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/test/converter2/issues/gh3017.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d93e661..87f3e19d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ title: Changelog - Introduced the `preservedTypeAnnotationTags` option to specify tags whose type annotations should be copied to the output documentation, #3020. API: Introduced `typeAnnotation` on `CommentTag` +- Added `excludePrivateClassFields` option to hide `#private` members while allowing `private` members, #3017. ## Bug Fixes diff --git a/site/options/input.md b/site/options/input.md index fbfdbf9a3..cc470cc49 100644 --- a/site/options/input.md +++ b/site/options/input.md @@ -215,7 +215,16 @@ Removes symbols annotated with the `@internal` doc tag. Defaults to true if the typedoc --excludePrivate ``` -Removes private class members from the generated documentation. Defaults to true. +Removes members marked with `private` and `#private` class fields from the generated documentation. Defaults to true. +To include `#private` class fields both this option and [excludePrivateClassFields](#excludeprivateclassfields) must be set to `false`. + +## excludePrivateClassFields + +```bash +typedoc --excludePrivateClassFields +``` + +Removes `#private` class fields from the generated documentation. Defaults to true. ## excludeProtected diff --git a/site/options/package-options.md b/site/options/package-options.md index 047552aee..1f90f4c0f 100644 --- a/site/options/package-options.md +++ b/site/options/package-options.md @@ -41,6 +41,7 @@ at the root level. The following tables indicate where an option should be set. | [`excludeNotDocumentedKinds`](input.md#excludenotdocumentedkinds) | Package | | | [`excludeInternal`](input.md#excludeinternal) | Package | | | [`excludePrivate`](input.md#excludeprivate) | Package | | +| [`excludePrivateClassFields`](input.md#excludeprivateclassfields) | Package | | | [`excludeProtected`](input.md#excludeprotected) | Package | | | [`excludeReferences`](input.md#excludereferences) | Package | | | [`excludeCategories`](input.md#excludecategories) | Package | | diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index 67c013b69..abe81d402 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -134,6 +134,9 @@ export class CommentPlugin extends ConverterComponent { @Option("excludePrivate") accessor excludePrivate!: boolean; + @Option("excludePrivateClassFields") + accessor excludePrivateClassFields!: boolean; + @Option("excludeProtected") accessor excludeProtected!: boolean; @@ -567,6 +570,19 @@ export class CommentPlugin extends ConverterComponent { return true; } + // #3017 this isn't quite right as it may incorrectly detect + // private members named with a hash which aren't actually private + // class fields (e.g. class Foo { "#hash" = 1 }) + // We can't fix this without storing more information about the name, + // which I'm going to put off until #3015 is done. + if ( + reflection.flags.hasFlag(ReflectionFlag.Private) && + reflection.name.startsWith("#") && + this.excludePrivateClassFields + ) { + return true; + } + if ( reflection.flags.hasFlag(ReflectionFlag.Protected) && this.excludeProtected diff --git a/src/lib/internationalization/locales/de.cts b/src/lib/internationalization/locales/de.cts index 3da33fda3..1138d57b9 100644 --- a/src/lib/internationalization/locales/de.cts +++ b/src/lib/internationalization/locales/de.cts @@ -284,7 +284,6 @@ export = localeUtils.buildIncompleteTranslation({ help_excludeNotDocumentedKinds: "Arten von Reflections, die von excludeNotDocumented entfernt werden können", help_excludeInternal: "Verhindert, dass Symbole in der Dokumentation erscheinen, die mit @internal markiert sind", help_excludeCategories: "Schließt Symbole aus dieser Kategorie von der Dokumentation aus", - help_excludePrivate: "Ignoriert private Variablen und Methoden, Standardwert ist true.", help_excludeProtected: "Ignoriert geschützte Variablen und Methoden", help_excludeReferences: "Wird ein Symbol mehrfach exportiert, ignoriere alle außer dem ersten Export", help_externalSymbolLinkMappings: diff --git a/src/lib/internationalization/locales/en.cts b/src/lib/internationalization/locales/en.cts index ee7c4f032..2244fe346 100644 --- a/src/lib/internationalization/locales/en.cts +++ b/src/lib/internationalization/locales/en.cts @@ -276,7 +276,8 @@ export = { help_excludeNotDocumentedKinds: "Specify the type of reflections that can be removed by excludeNotDocumented", help_excludeInternal: "Prevent symbols that are marked with @internal from being documented", help_excludeCategories: "Exclude symbols within this category from the documentation", - help_excludePrivate: "Ignore private variables and methods, defaults to true.", + help_excludePrivate: "Ignore members marked with the private keyword and #private class fields, defaults to true.", + help_excludePrivateClassFields: "Ignore #private class fields, defaults to true.", help_excludeProtected: "Ignore protected variables and methods", help_excludeReferences: "If a symbol is exported multiple times, ignore all but the first export", help_externalSymbolLinkMappings: "Define custom links for symbols not included in the documentation", diff --git a/src/lib/internationalization/locales/ja.cts b/src/lib/internationalization/locales/ja.cts index 40c7edf08..95667d273 100644 --- a/src/lib/internationalization/locales/ja.cts +++ b/src/lib/internationalization/locales/ja.cts @@ -194,7 +194,6 @@ export = localeUtils.buildIncompleteTranslation({ help_excludeNotDocumentedKinds: "excludeNotDocumented によって削除できる反射の種類を指定します", help_excludeInternal: "@internal でマークされたシンボルがドキュメント化されないようにする", help_excludeCategories: "このカテゴリ内のシンボルをドキュメントから除外する", - help_excludePrivate: "プライベート変数とメソッドを無視します。デフォルトは true です。", help_excludeProtected: "保護された変数とメソッドを無視する", help_excludeReferences: "シンボルが複数回エクスポートされた場合、最初のエクスポート以外はすべて無視されます。", help_externalSymbolLinkMappings: "ドキュメントに含まれていないシンボルのカスタムリンクを定義する", diff --git a/src/lib/internationalization/locales/ko.cts b/src/lib/internationalization/locales/ko.cts index 904ac25c2..4c62465b8 100644 --- a/src/lib/internationalization/locales/ko.cts +++ b/src/lib/internationalization/locales/ko.cts @@ -62,7 +62,6 @@ export = localeUtils.buildIncompleteTranslation({ help_excludeNotDocumentedKinds: "excludeNotDocumented로 제거될 리플렉션 유형을 지정합니다", help_excludeInternal: "@internal로 표시된 심볼이 문서화되지 않도록 방지합니다", help_excludeCategories: "문서에서 제외할 카테고리 내의 심볼을 제외합니다", - help_excludePrivate: "비공개 변수와 메서드를 무시합니다. 기본값은 true입니다.", help_excludeProtected: "보호된 변수와 메서드를 무시합니다", help_excludeReferences: "심볼이 여러 번 내보내진 경우 첫 번째 내보내기를 제외하고 모두 무시합니다", help_externalSymbolLinkMappings: "문서에 포함되지 않은 심볼에 대한 사용자 정의 링크를 정의합니다", diff --git a/src/lib/internationalization/locales/zh.cts b/src/lib/internationalization/locales/zh.cts index 5a0293423..729577b9c 100644 --- a/src/lib/internationalization/locales/zh.cts +++ b/src/lib/internationalization/locales/zh.cts @@ -233,7 +233,6 @@ export = localeUtils.buildIncompleteTranslation({ help_excludeNotDocumentedKinds: "指定可以通过 excludeNotDocumented 删除的反射类型", help_excludeInternal: "防止标有 @internal 的符号被记录", help_excludeCategories: "从文档中排除此类别中的符号", - help_excludePrivate: "忽略私有变量和方法,默认为 true。", help_excludeProtected: "忽略受保护的变量和方法", help_excludeReferences: "如果一个符号被导出多次,则忽略除第一次导出之外的所有导出", help_externalSymbolLinkMappings: "为文档中未包含的符号定义自定义链接", diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 8a8337fb3..ef5613ca8 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -207,6 +207,7 @@ export interface TypeDocOptionMap { excludeNotDocumented: boolean; excludeNotDocumentedKinds: ReflectionKind.KindString[]; excludeInternal: boolean; + excludePrivateClassFields: boolean; excludePrivate: boolean; excludeProtected: boolean; excludeReferences: boolean; diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 1ef0e7821..f572bfd33 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -189,6 +189,12 @@ export function addTypeDocOptions(options: Pick) { type: ParameterType.Boolean, defaultValue: true, }); + options.addDeclaration({ + name: "excludePrivateClassFields", + help: () => i18n.help_excludePrivateClassFields(), + type: ParameterType.Boolean, + defaultValue: true, + }); options.addDeclaration({ name: "excludeProtected", help: () => i18n.help_excludeProtected(), diff --git a/src/test/converter2/issues/gh3017.ts b/src/test/converter2/issues/gh3017.ts new file mode 100644 index 000000000..83627d4ac --- /dev/null +++ b/src/test/converter2/issues/gh3017.ts @@ -0,0 +1,8 @@ +export class GH3017 { + /** @hidden */ + constructor() {} + #hashPriv = 1; + private priv = 2; + protected prot = 3; + public pub = 4; +} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 17e521b84..835475153 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2210,6 +2210,28 @@ describe("Issue Tests", () => { logger.expectMessage("warn: Content in the @remarks block will be overwritten*"); }); + it("#3017 supports excluding #private fields only", () => { + app.options.setValue("excludePrivate", false); + app.options.setValue("excludePrivateClassFields", false); + let project = convert(); + equal(query(project, "GH3017").children?.map(c => c.name), ["#hashPriv", "priv", "prot", "pub"]); + + app.options.setValue("excludePrivate", false); + app.options.setValue("excludePrivateClassFields", true); + project = convert(); + equal(query(project, "GH3017").children?.map(c => c.name), ["priv", "prot", "pub"]); + + app.options.setValue("excludePrivate", true); + app.options.setValue("excludePrivateClassFields", false); + project = convert(); + equal(query(project, "GH3017").children?.map(c => c.name), ["prot", "pub"]); + + app.options.setValue("excludePrivate", true); + app.options.setValue("excludePrivateClassFields", true); + project = convert(); + equal(query(project, "GH3017").children?.map(c => c.name), ["prot", "pub"]); + }); + it("#3019 correctly parses accessor types", () => { const project = convert(); equal(query(project, "GH3019.x").type?.toString(), "string"); diff --git a/src/test/programs.ts b/src/test/programs.ts index bff126163..1074a8cbd 100644 --- a/src/test/programs.ts +++ b/src/test/programs.ts @@ -35,6 +35,7 @@ export function getConverterApp() { excludeExternals: true, disableSources: false, excludePrivate: false, + excludePrivateClassFields: false, tsconfig: join(getConverterBase(), "tsconfig.json"), externalPattern: ["**/node_modules/**"], plugin: [], From 4ca04c14d676e0dadc1cc63e05a060283e242ea6 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 10 Oct 2025 22:22:00 -0600 Subject: [PATCH 117/119] Handle `@this` tag, improved destructured parameters Resolves #3026 --- CHANGELOG.md | 2 + package.json | 4 +- site/tags.md | 2 +- site/tags/param.md | 17 ++- src/lib/converter/factories/signature.ts | 13 +-- src/lib/converter/plugins/CommentPlugin.ts | 37 ++++--- src/lib/utils/options/tsdoc-defaults.ts | 1 + src/test/TestLogger.ts | 6 +- src/test/behavior.c2.test.ts | 37 ++----- .../behavior/destructuredParamRenames.ts | 101 ++++-------------- src/test/converter2/issues/gh3026.js | 7 ++ src/test/issues.c2.test.ts | 12 +++ tsdoc.json | 5 + 13 files changed, 104 insertions(+), 140 deletions(-) create mode 100644 src/test/converter2/issues/gh3026.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 87f3e19d5..dbba2ccb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,13 @@ title: Changelog be copied to the output documentation, #3020. API: Introduced `typeAnnotation` on `CommentTag` - Added `excludePrivateClassFields` option to hide `#private` members while allowing `private` members, #3017. +- Added support for TypeScript's `@this` tag for JS files which describe `this` parameters, #3026. ## Bug Fixes - Fixed conversion of auto-accessor types on properties with the `accessor` keyword, #3019. - Improved handling of HTML tags within headers for anchor generation, #3023. +- Improved support for detecting destructured parameters and renaming them to the name used in the doc comment, #3026. ## v0.28.13 (2025-09-14) diff --git a/package.json b/package.json index 6d8ff462d..2c382287d 100644 --- a/package.json +++ b/package.json @@ -73,9 +73,9 @@ "test": "mocha --config .config/mocha.fast.json", "test:cov": "c8 -r lcov mocha --config .config/mocha.fast.json", "doc:c": "node bin/typedoc --tsconfig src/test/converter/tsconfig.json", - "doc:cd": "node --inspect-brk bin/typedoc --tsconfig src/test/converter/tsconfig.json", + "doc:cd": "node --inspect-brk dist/lib/cli.js --tsconfig src/test/converter/tsconfig.json", "doc:c2": "node bin/typedoc --options src/test/converter2 --tsconfig src/test/converter2/tsconfig.json", - "doc:c2d": "node --inspect-brk bin/typedoc --options src/test/converter2 --tsconfig src/test/converter2/tsconfig.json", + "doc:c2d": "node --inspect-brk dist/lib/cli.js --options src/test/converter2 --tsconfig src/test/converter2/tsconfig.json", "example": "cd example && node ../bin/typedoc", "test:full": "c8 -r lcov -r text-summary mocha --config .config/mocha.full.json", "rebuild_specs": "node scripts/rebuild_specs.js", diff --git a/site/tags.md b/site/tags.md index 66787ce67..db8e485fb 100644 --- a/site/tags.md +++ b/site/tags.md @@ -122,7 +122,7 @@ examples for how to use the export ([`@example`](./tags/example.md)). - [`@license`](./tags/license.md) - [`@mergeModuleWith`](./tags/mergeModuleWith.md) - [`@module`](./tags/module.md) -- [`@param`](./tags/param.md) +- [`@param`, `@this`](./tags/param.md) - [`@preventExpand`](./tags/expand.md#preventexpand) - [`@preventInline`](./tags/inline.md#preventinline) - [`@privateRemarks`](./tags/privateRemarks.md) diff --git a/site/tags/param.md b/site/tags/param.md index 1a074f162..d98216e9b 100644 --- a/site/tags/param.md +++ b/site/tags/param.md @@ -2,7 +2,7 @@ title: "@param" --- -# @param +# @param and @this **Tag Kind:** [Block](../tags.md#block-tags)
    **TSDoc Reference:** [@param](https://tsdoc.org/pages/tags/param/) @@ -54,6 +54,21 @@ export function configure({ value }: { value: string }) {} export function configure(options: { value: string }) {} ``` +## `this` Parameters + +Functions which use `this` in JavaScript files may use TypeScript's `@this` tag to define the type of their `this` +parameter. TypeDoc will check for `@this` tags and use their content in the description of the `this` parameter. + +```js +/** + * @this {Request} parameter description for `this` + * @param {Response} response parameter description for `response` + */ +export function hello(response) { + response.write(`Hello ${this.query.name || "world!"}`); +} +``` + ## TSDoc Compatibility The TSDoc standard requires that the `@param` tag _not_ include types and that diff --git a/src/lib/converter/factories/signature.ts b/src/lib/converter/factories/signature.ts index f3dc84e0a..9e5f43587 100644 --- a/src/lib/converter/factories/signature.ts +++ b/src/lib/converter/factories/signature.ts @@ -163,7 +163,7 @@ export function createConstructSignatureWithType( } } - const parameterSymbols: Array = signature.thisParameter + const parameterSymbols = signature.thisParameter ? [signature.thisParameter, ...signature.parameters] : [...signature.parameters]; @@ -207,7 +207,7 @@ export function createConstructSignatureWithType( function convertParameters( context: Context, sigRef: SignatureReflection, - parameters: readonly (ts.Symbol & { type?: ts.Type })[], + parameters: readonly ts.Symbol[], parameterNodes: | readonly ts.ParameterDeclaration[] | readonly ts.JSDocParameterTag[] @@ -226,7 +226,7 @@ function convertParameters( ts.isJSDocParameterTag(declaration), ); const paramRefl = new ParameterReflection( - /__\d+/.test(param.name) ? "__namedParameters" : param.name, + /^__\d+$/.test(param.name) ? "__namedParameters" : param.name, ReflectionKind.Parameter, sigRef, ); @@ -256,7 +256,7 @@ function convertParameters( typeNode = declaration.typeExpression?.type; } } else { - type = param.type; + type = context.checker.getTypeOfSymbol(param); } if ( @@ -295,8 +295,9 @@ function convertParameters( paramRefl.setFlag(ReflectionFlag.Optional, isOptional); // If we have no declaration, then this is an implicitly defined parameter in JS land - // because the method body uses `arguments`... which is always a rest argument - let isRest = true; + // because the method body uses `arguments`... which is always a rest argument, + // unless it is a this parameter defined with @this in JSDoc. + let isRest = param.name !== "this"; if (declaration) { isRest = ts.isParameter(declaration) ? !!declaration.dotDotDotToken diff --git a/src/lib/converter/plugins/CommentPlugin.ts b/src/lib/converter/plugins/CommentPlugin.ts index abe81d402..56f77f15c 100644 --- a/src/lib/converter/plugins/CommentPlugin.ts +++ b/src/lib/converter/plugins/CommentPlugin.ts @@ -37,9 +37,9 @@ import { CategoryPlugin } from "./CategoryPlugin.js"; * (for JS users) will be consumed by TypeScript and need not be preserved * in the comment. * - * Note that param/arg/argument/return/returns are not present. + * Note that param/arg/argument/return/returns/this are not present. * These tags will have their type information stripped when parsing, but still - * provide useful information for documentation. + * may provide useful information for documentation. */ const NEVER_RENDERED = [ "@augments", @@ -48,7 +48,6 @@ const NEVER_RENDERED = [ "@constructor", "@enum", "@extends", - "@this", "@type", "@typedef", "@jsx", @@ -479,26 +478,29 @@ export class CommentPlugin extends ConverterComponent { ) { if (!comment) return; - signature.parameters?.forEach((parameter, index) => { - if (parameter.name === "__namedParameters") { - const commentParams = comment.blockTags.filter( - (tag) => tag.tag === "@param" && !tag.name?.includes("."), - ); - if ( - signature.parameters?.length === commentParams.length && - commentParams[index].name - ) { - parameter.name = commentParams[index].name!; - } + const unusedCommentParams = comment.blockTags.filter( + (tag) => + tag.tag === "@param" && tag.name && !tag.name.includes(".") && + !signature.parameters?.some(p => p.name === tag.name), + ); + + signature.parameters?.forEach((parameter) => { + if (parameter.name === "__namedParameters" && unusedCommentParams.length) { + parameter.name = unusedCommentParams[0].name!; + unusedCommentParams.splice(0, 1); } const tag = comment.getIdentifiedTag(parameter.name, "@param"); if (tag) { - parameter.comment = new Comment( - Comment.cloneDisplayParts(tag.content), - ); + parameter.comment = new Comment(Comment.cloneDisplayParts(tag.content)); parameter.comment.sourcePath = comment.sourcePath; + } else if (parameter.name === "this") { + const thisTag = comment.getTag("@this"); + if (thisTag) { + parameter.comment = new Comment(Comment.cloneDisplayParts(thisTag.content)); + parameter.comment.sourcePath = comment.sourcePath; + } } }); @@ -516,6 +518,7 @@ export class CommentPlugin extends ConverterComponent { this.validateParamTags(signature, comment, signature.parameters || []); + comment.removeTags("@this"); comment.removeTags("@param"); comment.removeTags("@typeParam"); comment.removeTags("@template"); diff --git a/src/lib/utils/options/tsdoc-defaults.ts b/src/lib/utils/options/tsdoc-defaults.ts index 92e19f7c9..1aeabc65a 100644 --- a/src/lib/utils/options/tsdoc-defaults.ts +++ b/src/lib/utils/options/tsdoc-defaults.ts @@ -39,6 +39,7 @@ export const blockTags = [ "@since", "@sortStrategy", "@template", // Alias for @typeParam + "@this", "@type", "@typedef", "@summary", diff --git a/src/test/TestLogger.ts b/src/test/TestLogger.ts index f94670d1b..0975b1692 100644 --- a/src/test/TestLogger.ts +++ b/src/test/TestLogger.ts @@ -41,10 +41,8 @@ export class TestLogger extends Logger { } } - expectNoOtherMessages({ ignoreDebug } = { ignoreDebug: true }) { - const messages = ignoreDebug - ? this.messages.filter((msg) => !msg.startsWith("debug")) - : this.messages; + expectNoOtherMessages() { + const messages = this.messages.filter((msg) => !msg.startsWith("debug")); ok( messages.length === 0, diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index d1b18e0a7..c35d2e91a 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -1118,36 +1118,17 @@ describe("Behavior Tests", () => { const params = (name: string) => querySig(project, name).parameters?.map((p) => p.name); - equal(params("functionWithADestructuredParameter"), [ - "destructuredParam", - ]); + equal(params("singleParam"), ["params"]); - equal(params("functionWithADestructuredParameterAndExtraParameters"), [ - "__namedParameters", - "extraParameter", - ]); + equal(params("extraParam"), ["params", "extraParameter"]); - equal( - params( - "functionWithADestructuredParameterAndAnExtraParamDirective", - ), - ["__namedParameters"], - ); - - const logs = [ - 'warn: The signature functionWithADestructuredParameterAndExtraParameters has an @param with name "destructuredParam", which was not used', - 'warn: The signature functionWithADestructuredParameterAndExtraParameters has an @param with name "destructuredParam.paramZ", which was not used', - 'warn: The signature functionWithADestructuredParameterAndExtraParameters has an @param with name "destructuredParam.paramG", which was not used', - 'warn: The signature functionWithADestructuredParameterAndExtraParameters has an @param with name "destructuredParam.paramA", which was not used', - 'warn: The signature functionWithADestructuredParameterAndAnExtraParamDirective has an @param with name "fakeParameter", which was not used', - 'warn: The signature functionWithADestructuredParameterAndAnExtraParamDirective has an @param with name "destructuredParam", which was not used', - 'warn: The signature functionWithADestructuredParameterAndAnExtraParamDirective has an @param with name "destructuredParam.paramZ", which was not used', - 'warn: The signature functionWithADestructuredParameterAndAnExtraParamDirective has an @param with name "destructuredParam.paramG", which was not used', - 'warn: The signature functionWithADestructuredParameterAndAnExtraParamDirective has an @param with name "destructuredParam.paramA", which was not used', - ]; - for (const log of logs) { - logger.expectMessage(log); - } + equal(params("extraParamComment"), ["params"]); + + equal(params("multiParam"), ["params", "params2", "__namedParameters"]); + + logger.expectMessage( + 'warn: The signature extraParamComment has an @param with name "fakeParameter", which was not used', + ); logger.expectNoOtherMessages(); }); diff --git a/src/test/converter2/behavior/destructuredParamRenames.ts b/src/test/converter2/behavior/destructuredParamRenames.ts index 437090c80..83fdd1600 100644 --- a/src/test/converter2/behavior/destructuredParamRenames.ts +++ b/src/test/converter2/behavior/destructuredParamRenames.ts @@ -1,95 +1,34 @@ /** - * This is a function with a destructured parameter. - * - * @param destructuredParam - This is the parameter that is destructured. - * @param destructuredParam.paramZ - This is a string parameter. - * @param destructuredParam.paramG - This is a parameter flagged with any. - * This sentence is placed in the next line. - * - * @param destructuredParam.paramA - * This is a **parameter** pointing to an interface. - * - * ``` - * const value:BaseClass = new BaseClass('test'); - * functionWithArguments('arg', 0, value); - * ``` - * - * @returns This is the return value of the function. + * @param params - desc + * @param params.a - paramZ desc */ -export function functionWithADestructuredParameter({ - paramZ, - paramG, - paramA, -}: { - paramZ: string; - paramG: any; - paramA: Object; -}): number { +export function singleParam({ a }: { a: string }) { return 0; } /** - * This is a function with a destructured parameter and additional undocumented parameters. - * The `@param` directives are ignored because we cannot be certain which parameter they refer to. - * - * @param destructuredParam - This is the parameter that is destructured. - * @param destructuredParam.paramZ - This is a string parameter. - * @param destructuredParam.paramG - This is a parameter flagged with any. - * This sentence is placed in the next line. - * - * @param destructuredParam.paramA - * This is a **parameter** pointing to an interface. - * - * ``` - * const value:BaseClass = new BaseClass('test'); - * functionWithArguments('arg', 0, value); - * ``` - * - * @returns This is the return value of the function. + * @param params - desc */ -export function functionWithADestructuredParameterAndExtraParameters( - { - paramZ, - paramG, - paramA, - }: { - paramZ: string; - paramG: any; - paramA: Object; - }, - extraParameter: string, -): number { +export function extraParam({ a }: { a: string }, extraParameter: string) { return 0; } /** - * This is a function with a destructured parameter and an extra `@param` directive with no corresponding parameter. - * The `@param` directives are ignored because we cannot be certain which corresponds to the real parameter. - * - * @param fakeParameter - This directive does not have a corresponding parameter. - * @param destructuredParam - This is the parameter that is destructured. - * @param destructuredParam.paramZ - This is a string parameter. - * @param destructuredParam.paramG - This is a parameter flagged with any. - * This sentence is placed in the next line. - * - * @param destructuredParam.paramA - * This is a **parameter** pointing to an interface. - * - * ``` - * const value:BaseClass = new BaseClass('test'); - * functionWithArguments('arg', 0, value); - * ``` - * - * @returns This is the return value of the function. + * @param params param + * @param fakeParameter param2 */ -export function functionWithADestructuredParameterAndAnExtraParamDirective({ - paramZ, - paramG, - paramA, -}: { - paramZ: string; - paramG: any; - paramA: Object; -}): number { +export function extraParamComment({ a }: { a: string }) { + return 0; +} + +/** + * @param params params + * @param params2 params2 + */ +export function multiParam( + { a }: { a: string }, + { b }: { b: number }, + { c }: { c: boolean }, +) { return 0; } diff --git a/src/test/converter2/issues/gh3026.js b/src/test/converter2/issues/gh3026.js new file mode 100644 index 000000000..ecd21a98b --- /dev/null +++ b/src/test/converter2/issues/gh3026.js @@ -0,0 +1,7 @@ +/** + * @param {object} params params + * @param {string} params.y y + * @returns {Promise} + * @this {{ x: string }} this param + */ +export async function extendedResponse({ y }) {} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 835475153..858bda67b 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -2260,4 +2260,16 @@ describe("Issue Tests", () => { const method3 = querySig(project, "GH3024.method3"); equal(method3.parameters?.[0].type?.toString(), "string | null | undefined"); }); + + it("#3026 handles @this parameter comments and destructured parameter renames with this parameters", () => { + const project = convert(); + const sig = querySig(project, "extendedResponse"); + equal(sig.parameters?.map(p => p.name), ["this", "params"]); + + const comments = sig.parameters.map(p => Comment.combineDisplayParts(p.comment?.summary)); + equal(comments, ["this param", "params"]); + + const types = sig.parameters.map(p => p.type?.toString()); + equal(types, ["{ x: string }", "{ y: string }"]); + }); }); diff --git a/tsdoc.json b/tsdoc.json index 44391fd71..07d3e835c 100644 --- a/tsdoc.json +++ b/tsdoc.json @@ -146,6 +146,11 @@ "syntaxKind": "block", "allowMultiple": true }, + { + "tagName": "@this", + "syntaxKind": "block", + "allowMultiple": false + }, { "tagName": "@linkcode", "syntaxKind": "inline", From a79f8d5e12c755b78f103f8c8732c4dcd870ca77 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 10 Oct 2025 22:25:50 -0600 Subject: [PATCH 118/119] Bump version to 0.28.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2c382287d..24524f3d9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "typedoc", "description": "Create api documentation for TypeScript projects.", - "version": "0.28.13", + "version": "0.28.14", "homepage": "https://typedoc.org", "type": "module", "exports": { From 15f0f2a4058b179e266da3e94cfa7f82b55e3bc1 Mon Sep 17 00:00:00 2001 From: TypeDoc Bot Date: Sat, 11 Oct 2025 04:26:46 +0000 Subject: [PATCH 119/119] Update changelog for release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbba2ccb9..e769444be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ title: Changelog ## Unreleased +## v0.28.14 (2025-10-11) + ### Features - Introduced the `preservedTypeAnnotationTags` option to specify tags whose type annotations should