From 42af237eb59789b2935d4570c423f07c5ac8da39 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 11 Apr 2025 15:51:33 -0600 Subject: [PATCH 1/8] 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 2/8] 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 3/8] 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 4/8] 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 5/8] 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 6/8] 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 7/8] 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 8/8] 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.