From 2844af7516c26d3adaab8e4cda18311bbcbbc82b Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Wed, 11 May 2022 15:52:03 -0700 Subject: [PATCH 01/28] wip --- example/typedoc.json | 10 +++++++++- src/lib/output/plugins/JavascriptIndexPlugin.ts | 4 ++++ src/lib/output/themes/default/assets/bootstrap.ts | 1 + .../themes/default/assets/typedoc/components/Search.ts | 9 +++++++-- src/lib/utils/options/declaration.ts | 8 ++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index d9dd2ca10..ce9abe62f 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -2,5 +2,13 @@ "$schema": "https://typedoc.org/schema.json", "entryPoints": ["./src"], "sort": ["source-order"], - "media": "media" + "media": "media", + "search": { + "numResults": 12, + "boosts": { + "byKind": { + "class": 1.2 + } + } + } } diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 9775b9d23..961f0ce2d 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -100,7 +100,11 @@ export class JavascriptIndexPlugin extends RendererComponent { "assets", "search.js" ); + + const searchConfig = this.application.options.getValue("search"); + const jsonData = JSON.stringify({ + searchConfig, kinds, rows, index, diff --git a/src/lib/output/themes/default/assets/bootstrap.ts b/src/lib/output/themes/default/assets/bootstrap.ts index 401911905..0dcf16051 100644 --- a/src/lib/output/themes/default/assets/bootstrap.ts +++ b/src/lib/output/themes/default/assets/bootstrap.ts @@ -24,5 +24,6 @@ if (themeChoice) { } const app: Application = new Application(); +debugger; Object.defineProperty(window, "app", { value: app }); diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 95147f5e2..4cde91323 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -1,5 +1,6 @@ import { debounce } from "../utils/debounce"; import { Index } from "lunr"; +import type { SearchConfig } from "../../../../../../utils/options/declaration"; interface IDocument { id: number; @@ -14,6 +15,7 @@ interface IData { kinds: { [kind: number]: string }; rows: IDocument[]; index: object; + searchConfig: SearchConfig; } declare global { @@ -78,14 +80,16 @@ export function initSearch() { base: searchEl.dataset["base"] + "/", }; - bindEvents(searchEl, results, field, state); + bindEvents(searchEl, results, field, state, window.searchData.searchConfig); } function bindEvents( searchEl: HTMLElement, results: HTMLElement, field: HTMLInputElement, - state: SearchState + state: SearchState, + searchConfig: SearchConfig + ) { field.addEventListener( "input", @@ -156,6 +160,7 @@ function updateResults( // when the `searchText` is empty. const res = searchText ? state.index.search(`*${searchText}*`) : []; + debugger; for (let i = 0, c = Math.min(10, res.length); i < c; i++) { const row = state.data.rows[Number(res[i].ref)]; diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 5f3728207..b2a035746 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -50,6 +50,13 @@ export type TypeDocOptionValues = { : TypeDocOptionMap[K][keyof TypeDocOptionMap[K]]; }; +export interface SearchConfig { + numResults?: number; + boosts?: { + byKind?: { [K in ReflectionKind]: number} + } +} + /** * Describes all TypeDoc options. Used internally to provide better types when fetching options. * External consumers should likely use {@link TypeDocOptions} instead. @@ -107,6 +114,7 @@ export interface TypeDocOptionMap { version: boolean; showConfig: boolean; plugin: string[]; + search: unknown; logger: unknown; // string | Function logLevel: typeof LogLevel; markedOptions: unknown; From adb740e3cfb4fdd4ad4c51fb3a3ece779cdc8cf3 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Wed, 11 May 2022 17:49:10 -0700 Subject: [PATCH 02/28] kind boosting --- example/typedoc.json | 2 +- package.json | 2 +- .../output/themes/default/assets/bootstrap.ts | 1 - .../assets/typedoc/components/Search.ts | 29 +++++++++++++++---- src/lib/utils/options/sources/typedoc.ts | 5 ++++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index ce9abe62f..c7e11ed7a 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -7,7 +7,7 @@ "numResults": 12, "boosts": { "byKind": { - "class": 1.2 + "class": 10 } } } diff --git a/package.json b/package.json index f7609bbc1..de8148ebb 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "rebuild_specs": "node scripts/rebuild_specs.js", "build": "npm run build:tsc && npm run build:themes", "build:tsc": "tsc --project .", - "build:themes": "esbuild src/lib/output/themes/default/assets/bootstrap.ts --bundle --minify --outfile=static/main.js", + "build:themes": "esbuild src/lib/output/themes/default/assets/bootstrap.ts --bundle --outfile=static/main.js", "build:prod": "npm run build:prod:tsc && npm run build:themes", "build:prod:tsc": "tsc --project . --sourceMap false", "lint": "eslint . && npm run prettier -- --check .", diff --git a/src/lib/output/themes/default/assets/bootstrap.ts b/src/lib/output/themes/default/assets/bootstrap.ts index 0dcf16051..401911905 100644 --- a/src/lib/output/themes/default/assets/bootstrap.ts +++ b/src/lib/output/themes/default/assets/bootstrap.ts @@ -24,6 +24,5 @@ if (themeChoice) { } const app: Application = new Application(); -debugger; Object.defineProperty(window, "app", { value: app }); diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 4cde91323..efe63da4d 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -1,6 +1,7 @@ import { debounce } from "../utils/debounce"; import { Index } from "lunr"; import type { SearchConfig } from "../../../../../../utils/options/declaration"; +import { ReflectionKind } from "../../../../../../models/reflections/kind"; interface IDocument { id: number; @@ -94,7 +95,7 @@ function bindEvents( field.addEventListener( "input", debounce(() => { - updateResults(searchEl, results, field, state); + updateResults(searchEl, results, field, state, searchConfig); }, 200) ); @@ -144,7 +145,8 @@ function updateResults( searchEl: HTMLElement, results: HTMLElement, query: HTMLInputElement, - state: SearchState + state: SearchState, + searchConfig:SearchConfig ) { checkIndex(state, searchEl); // Don't clear results if loading state is not ready, @@ -158,10 +160,27 @@ function updateResults( // Perform a wildcard search // Set empty `res` to prevent getting random results with wildcard search // when the `searchText` is empty. - const res = searchText ? state.index.search(`*${searchText}*`) : []; + let res = searchText ? state.index.search(`*${searchText}*`) : []; + + if(searchConfig.boosts != undefined) { + + for (let i = 0; i < res.length; i++) { + const item = res[i]; + const row = state.data.rows[Number(item.ref)]; + let score = item.score; + for(let kindName in searchConfig.boosts.byKind) { + const kind: ReflectionKind = parseInt(Object.keys(ReflectionKind).find((key: any) => ReflectionKind[key].toLowerCase() === kindName.toLowerCase()), 10); + if(row.kind == kind) { + score *= searchConfig.boosts.byKind[kindName]; + } + } + item.score = score; + } + + res.sort((a,b) => b.score - a.score) + } - debugger; - for (let i = 0, c = Math.min(10, res.length); i < c; i++) { + for (let i = 0, c = Math.min( searchConfig.numResults ?? 10, res.length); i < c; i++) { const row = state.data.rows[Number(res[i].ref)]; // Bold the matched part of the query in the search results diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 78fd34d84..f9a3899e7 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -70,6 +70,11 @@ export function addTypeDocOptions(options: Pick) { help: "Ignore protected variables and methods.", type: ParameterType.Boolean, }); + options.addDeclaration({ + name: "search", + help: "Configure search behavior", + type: ParameterType.Mixed + }); options.addDeclaration({ name: "disableSources", help: "Disable setting the source of a reflection when documenting it.", From 6870ceb54ef565ecb2af39561b7221459dc39223 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 11:28:11 -0700 Subject: [PATCH 03/28] ts fixes --- src/lib/output/plugins/JavascriptIndexPlugin.ts | 5 +++-- .../default/assets/typedoc/components/Search.ts | 16 +++++++++------- src/lib/utils/options/declaration.ts | 5 +++-- tsconfig.json | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 961f0ce2d..2f85db528 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -11,6 +11,7 @@ import { Component, RendererComponent } from "../components"; import { RendererEvent } from "../events"; import { writeFileSync } from "../../utils"; import { DefaultTheme } from "../themes/default/DefaultTheme"; +import type { IDocument } from "../themes/default/assets/typedoc/components/Search"; /** * A plugin that exports an index of the project to a javascript file. @@ -63,12 +64,12 @@ export class JavascriptIndexPlugin extends RendererComponent { parent = undefined; } - const row: any = { + const row: IDocument = { id: rows.length, kind: reflection.kind, name: reflection.name, url: reflection.url, - classes: reflection.cssClasses, + classes: reflection.cssClasses ?? '', }; if (parent) { diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index efe63da4d..b1511b88e 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -3,7 +3,7 @@ import { Index } from "lunr"; import type { SearchConfig } from "../../../../../../utils/options/declaration"; import { ReflectionKind } from "../../../../../../models/reflections/kind"; -interface IDocument { +export interface IDocument { id: number; kind: number; name: string; @@ -81,7 +81,7 @@ export function initSearch() { base: searchEl.dataset["base"] + "/", }; - bindEvents(searchEl, results, field, state, window.searchData.searchConfig); + bindEvents(searchEl, results, field, state, window?.searchData?.searchConfig ?? {}); } function bindEvents( @@ -168,10 +168,12 @@ function updateResults( const item = res[i]; const row = state.data.rows[Number(item.ref)]; let score = item.score; - for(let kindName in searchConfig.boosts.byKind) { - const kind: ReflectionKind = parseInt(Object.keys(ReflectionKind).find((key: any) => ReflectionKind[key].toLowerCase() === kindName.toLowerCase()), 10); + + for(let kindName in searchConfig.boosts.byKind ?? {}) { + const kind: ReflectionKind = parseInt(Object.keys(ReflectionKind) + .find((key: string) => (ReflectionKind[key as keyof typeof ReflectionKind]).toString().toLowerCase() === kindName.toLowerCase()) ?? '', 10); if(row.kind == kind) { - score *= searchConfig.boosts.byKind[kindName]; + score *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; } } item.score = score; @@ -223,11 +225,11 @@ function setCurrentResult(results: HTMLElement, dir: number) { // current with the arrow keys. if (dir === 1) { do { - rel = rel.nextElementSibling; + rel = rel.nextElementSibling ?? undefined; } while (rel instanceof HTMLElement && rel.offsetParent == null); } else { do { - rel = rel.previousElementSibling; + rel = rel.previousElementSibling ?? undefined; } while (rel instanceof HTMLElement && rel.offsetParent == null); } diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index b2a035746..2bb663168 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -3,7 +3,7 @@ import type { LogLevel } from "../loggers"; import type { SortStrategy } from "../sort"; import { isAbsolute, join, resolve } from "path"; import type { EntryPointStrategy } from "../entry-point"; -import type { ReflectionKind } from "../../models/reflections/kind"; +import { ReflectionKind } from "../../models/reflections/kind"; export const EmitStrategy = { true: true, // Alias for both, for backwards compatibility until 0.23 @@ -50,10 +50,11 @@ export type TypeDocOptionValues = { : TypeDocOptionMap[K][keyof TypeDocOptionMap[K]]; }; +const Kinds = Object.values(ReflectionKind); export interface SearchConfig { numResults?: number; boosts?: { - byKind?: { [K in ReflectionKind]: number} + byKind?: { [key: typeof Kinds[number]]: number} } } diff --git a/tsconfig.json b/tsconfig.json index 96b7610d4..e7ccf6f57 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "CommonJS", - "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], + "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string", "dom"], "target": "es2019", // Add our `ts` internal types From 5ec06bcf471c70440d469fb64e57f9fc61706a73 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 12:38:43 -0700 Subject: [PATCH 04/28] boost on category --- example/typedoc.json | 6 ++++- .../output/plugins/JavascriptIndexPlugin.ts | 1 + .../assets/typedoc/components/Search.ts | 26 ++++++++++++++++--- src/lib/utils/options/declaration.ts | 4 ++- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index c7e11ed7a..1ddb61f19 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -6,8 +6,12 @@ "search": { "numResults": 12, "boosts": { + "exactMatch": 2, "byKind": { - "class": 10 + "class": 1.2 + }, + "byCategory": { + "Lang": 10 } } } diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 2f85db528..4336ed7cb 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -70,6 +70,7 @@ export class JavascriptIndexPlugin extends RendererComponent { name: reflection.name, url: reflection.url, classes: reflection.cssClasses ?? '', + categories: (reflection.categories ?? []).map((category) => category.title) }; if (parent) { diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index b1511b88e..44ce8d9d4 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -10,6 +10,7 @@ export interface IDocument { url: string; classes: string; parent?: string; + categories: Array } interface IData { @@ -167,16 +168,33 @@ function updateResults( for (let i = 0; i < res.length; i++) { const item = res[i]; const row = state.data.rows[Number(item.ref)]; - let score = item.score; + let boost = 1; + // boost by exact match on name + if(searchConfig.boosts.exactMatch && row.name === searchText) { + boost *= searchConfig.boosts.exactMatch; + } + + // boost by kind for(let kindName in searchConfig.boosts.byKind ?? {}) { const kind: ReflectionKind = parseInt(Object.keys(ReflectionKind) - .find((key: string) => (ReflectionKind[key as keyof typeof ReflectionKind]).toString().toLowerCase() === kindName.toLowerCase()) ?? '', 10); + .find((key: string) => (ReflectionKind[key as keyof typeof ReflectionKind]) + .toString() + .toLowerCase() === kindName.toLowerCase()) ?? '', 10); if(row.kind == kind) { - score *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; + boost *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; + } + } + + // boost by category + for(let categoryTitle in searchConfig.boosts?.byCategory ?? []) { + if(row.categories.indexOf(categoryTitle) > -1) { + debugger; + boost *= searchConfig.boosts.byCategory?.[categoryTitle] ?? 1; } } - item.score = score; + + item.score *= boost; } res.sort((a,b) => b.score - a.score) diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 2bb663168..d1566a889 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -54,7 +54,9 @@ const Kinds = Object.values(ReflectionKind); export interface SearchConfig { numResults?: number; boosts?: { - byKind?: { [key: typeof Kinds[number]]: number} + exactMatch?: number, + byKind?: { [key: typeof Kinds[number]]: number}, + byCategory?: { [key: string]: number } } } From 84ccef0739bc149abb58f2d24701015f25a6632f Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 12:44:11 -0700 Subject: [PATCH 05/28] restore minify --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index de8148ebb..f7609bbc1 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "rebuild_specs": "node scripts/rebuild_specs.js", "build": "npm run build:tsc && npm run build:themes", "build:tsc": "tsc --project .", - "build:themes": "esbuild src/lib/output/themes/default/assets/bootstrap.ts --bundle --outfile=static/main.js", + "build:themes": "esbuild src/lib/output/themes/default/assets/bootstrap.ts --bundle --minify --outfile=static/main.js", "build:prod": "npm run build:prod:tsc && npm run build:themes", "build:prod:tsc": "tsc --project . --sourceMap false", "lint": "eslint . && npm run prettier -- --check .", From 356ab3d56cd57a9e0aeb451caaf997d7d7b834e1 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 12:55:19 -0700 Subject: [PATCH 06/28] remove debugger --- .../output/themes/default/assets/typedoc/components/Search.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 44ce8d9d4..1adc868af 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -189,7 +189,6 @@ function updateResults( // boost by category for(let categoryTitle in searchConfig.boosts?.byCategory ?? []) { if(row.categories.indexOf(categoryTitle) > -1) { - debugger; boost *= searchConfig.boosts.byCategory?.[categoryTitle] ?? 1; } } From aa7483f5b99c2dd48ef2a490ffab43f167450429 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 13:04:36 -0700 Subject: [PATCH 07/28] ignore case --- .../output/themes/default/assets/typedoc/components/Search.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 1adc868af..24bd9d775 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -171,7 +171,7 @@ function updateResults( let boost = 1; // boost by exact match on name - if(searchConfig.boosts.exactMatch && row.name === searchText) { + if(searchConfig.boosts.exactMatch && row.name.toLowerCase() === searchText.toLowerCase()) { boost *= searchConfig.boosts.exactMatch; } From ca602404ed47386b50e872f2641938eab661263e Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 14:55:37 -0700 Subject: [PATCH 08/28] shorten imports --- src/lib/output/plugins/JavascriptIndexPlugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 4336ed7cb..b3c2773bc 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -5,8 +5,8 @@ import { DeclarationReflection, ProjectReflection, ReflectionKind, -} from "../../models/reflections/index"; -import { GroupPlugin } from "../../converter/plugins/GroupPlugin"; +} from "../../models"; +import { GroupPlugin } from "../../converter/plugins"; import { Component, RendererComponent } from "../components"; import { RendererEvent } from "../events"; import { writeFileSync } from "../../utils"; From d9a60fc47acbf41fd4ca7ed17bdbbeef7ade79a7 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 17:42:48 -0700 Subject: [PATCH 09/28] lint --- .../output/plugins/JavascriptIndexPlugin.ts | 6 +- .../assets/typedoc/components/Search.ts | 55 ++++++++++++------- src/lib/utils/options/declaration.ts | 8 +-- src/lib/utils/options/sources/typedoc.ts | 2 +- tsconfig.json | 35 +++++++----- 5 files changed, 67 insertions(+), 39 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index b3c2773bc..87d4cda23 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -69,8 +69,10 @@ export class JavascriptIndexPlugin extends RendererComponent { kind: reflection.kind, name: reflection.name, url: reflection.url, - classes: reflection.cssClasses ?? '', - categories: (reflection.categories ?? []).map((category) => category.title) + classes: reflection.cssClasses ?? "", + categories: (reflection.categories ?? []).map( + (category) => category.title + ), }; if (parent) { diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 24bd9d775..5da1b0b29 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -10,7 +10,7 @@ export interface IDocument { url: string; classes: string; parent?: string; - categories: Array + categories: Array; } interface IData { @@ -82,7 +82,13 @@ export function initSearch() { base: searchEl.dataset["base"] + "/", }; - bindEvents(searchEl, results, field, state, window?.searchData?.searchConfig ?? {}); + bindEvents( + searchEl, + results, + field, + state, + window?.searchData?.searchConfig ?? {} + ); } function bindEvents( @@ -91,7 +97,6 @@ function bindEvents( field: HTMLInputElement, state: SearchState, searchConfig: SearchConfig - ) { field.addEventListener( "input", @@ -147,7 +152,7 @@ function updateResults( results: HTMLElement, query: HTMLInputElement, state: SearchState, - searchConfig:SearchConfig + searchConfig: SearchConfig ) { checkIndex(state, searchEl); // Don't clear results if loading state is not ready, @@ -163,43 +168,55 @@ function updateResults( // when the `searchText` is empty. let res = searchText ? state.index.search(`*${searchText}*`) : []; - if(searchConfig.boosts != undefined) { - + if (searchConfig.boosts != undefined) { for (let i = 0; i < res.length; i++) { const item = res[i]; const row = state.data.rows[Number(item.ref)]; let boost = 1; // boost by exact match on name - if(searchConfig.boosts.exactMatch && row.name.toLowerCase() === searchText.toLowerCase()) { + if ( + searchConfig.boosts.exactMatch && + row.name.toLowerCase() === searchText.toLowerCase() + ) { boost *= searchConfig.boosts.exactMatch; } // boost by kind - for(let kindName in searchConfig.boosts.byKind ?? {}) { - const kind: ReflectionKind = parseInt(Object.keys(ReflectionKind) - .find((key: string) => (ReflectionKind[key as keyof typeof ReflectionKind]) - .toString() - .toLowerCase() === kindName.toLowerCase()) ?? '', 10); - if(row.kind == kind) { - boost *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; + for (let kindName in searchConfig.boosts.byKind ?? {}) { + const kind: ReflectionKind = parseInt( + Object.keys(ReflectionKind).find( + (key: string) => + ReflectionKind[key as keyof typeof ReflectionKind] + .toString() + .toLowerCase() === kindName.toLowerCase() + ) ?? "", + 10 + ); + if (row.kind == kind) { + boost *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; } } // boost by category - for(let categoryTitle in searchConfig.boosts?.byCategory ?? []) { - if(row.categories.indexOf(categoryTitle) > -1) { - boost *= searchConfig.boosts.byCategory?.[categoryTitle] ?? 1; + for (let categoryTitle in searchConfig.boosts?.byCategory ?? []) { + if (row.categories.indexOf(categoryTitle) > -1) { + boost *= + searchConfig.boosts.byCategory?.[categoryTitle] ?? 1; } } item.score *= boost; } - res.sort((a,b) => b.score - a.score) + res.sort((a, b) => b.score - a.score); } - for (let i = 0, c = Math.min( searchConfig.numResults ?? 10, res.length); i < c; i++) { + for ( + let i = 0, c = Math.min(searchConfig.numResults ?? 10, res.length); + i < c; + i++ + ) { const row = state.data.rows[Number(res[i].ref)]; // Bold the matched part of the query in the search results diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index d1566a889..071eca74c 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -54,10 +54,10 @@ const Kinds = Object.values(ReflectionKind); export interface SearchConfig { numResults?: number; boosts?: { - exactMatch?: number, - byKind?: { [key: typeof Kinds[number]]: number}, - byCategory?: { [key: string]: number } - } + exactMatch?: number; + byKind?: { [key: typeof Kinds[number]]: number }; + byCategory?: { [key: string]: number }; + }; } /** diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index f9a3899e7..65d548682 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -73,7 +73,7 @@ export function addTypeDocOptions(options: Pick) { options.addDeclaration({ name: "search", help: "Configure search behavior", - type: ParameterType.Mixed + type: ParameterType.Mixed, }); options.addDeclaration({ name: "disableSources", diff --git a/tsconfig.json b/tsconfig.json index e7ccf6f57..5cf309372 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,31 +1,40 @@ { "compilerOptions": { "module": "CommonJS", - "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string", "dom"], + "lib": [ + "es2019", + "es2020.promise", + "es2020.bigint", + "es2020.string", + "dom" + ], "target": "es2019", - // Add our `ts` internal types - "typeRoots": ["node_modules/@types", "src/lib/types"], - "types": ["node", "glob", "lunr", "marked", "minimatch", "mocha"], - + "typeRoots": [ + "node_modules/@types", + "src/lib/types" + ], + "types": [ + "node", + "glob", + "lunr", + "marked", + "minimatch", + "mocha" + ], // Speed up dev compilation time "incremental": true, "tsBuildInfoFile": "node_modules/.cache/.tsbuildinfo", - "experimentalDecorators": true, - "strict": true, "alwaysStrict": true, - // For tests "resolveJsonModule": true, - // Linting "noUnusedLocals": true, "noUnusedParameters": true, "forceConsistentCasingInFileNames": true, "importsNotUsedAsValues": "error", - // Library "preserveConstEnums": true, "declaration": true, @@ -33,17 +42,17 @@ "isolatedModules": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, - // Output "outDir": "dist/", "rootDir": "src/", "newLine": "LF", - "jsx": "react", "jsxFactory": "JSX.createElement", "jsxFragmentFactory": "JSX.Fragment" }, - "include": ["src"], + "include": [ + "src" + ], "exclude": [ "src/lib/output/themes/default/assets", "src/test/converter", From 8d2a6e5c9053e3def9c4ec2cf7052e0d567b8355 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Thu, 12 May 2022 17:45:47 -0700 Subject: [PATCH 10/28] lint --- tsconfig.json | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 5cf309372..3b0a377b9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,18 +10,8 @@ ], "target": "es2019", // Add our `ts` internal types - "typeRoots": [ - "node_modules/@types", - "src/lib/types" - ], - "types": [ - "node", - "glob", - "lunr", - "marked", - "minimatch", - "mocha" - ], + "typeRoots": ["node_modules/@types", "src/lib/types"], + "types": ["node", "glob", "lunr", "marked", "minimatch", "mocha"], // Speed up dev compilation time "incremental": true, "tsBuildInfoFile": "node_modules/.cache/.tsbuildinfo", @@ -50,9 +40,7 @@ "jsxFactory": "JSX.createElement", "jsxFragmentFactory": "JSX.Fragment" }, - "include": [ - "src" - ], + "include": ["src"], "exclude": [ "src/lib/output/themes/default/assets", "src/test/converter", From 009a24420a513d8ca8bf9a19fdda9f00733caf3f Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Sun, 15 May 2022 15:27:38 -0700 Subject: [PATCH 11/28] don't write empty string --- src/lib/output/plugins/JavascriptIndexPlugin.ts | 2 +- .../output/themes/default/assets/typedoc/components/Search.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 87d4cda23..2ac619f46 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -69,7 +69,7 @@ export class JavascriptIndexPlugin extends RendererComponent { kind: reflection.kind, name: reflection.name, url: reflection.url, - classes: reflection.cssClasses ?? "", + classes: reflection.cssClasses, categories: (reflection.categories ?? []).map( (category) => category.title ), diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 5da1b0b29..3b9ab975d 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -8,7 +8,7 @@ export interface IDocument { kind: number; name: string; url: string; - classes: string; + classes?: string; parent?: string; categories: Array; } @@ -229,7 +229,7 @@ function updateResults( } const item = document.createElement("li"); - item.classList.value = row.classes; + item.classList.value = row.classes ?? ''; const anchor = document.createElement("a"); anchor.href = state.base + row.url; From 545fc883895fcdf2e55bd79de96482e2648c8376 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Sun, 15 May 2022 15:53:12 -0700 Subject: [PATCH 12/28] boost on exact by default --- example/typedoc.json | 1 - .../assets/typedoc/components/Search.ts | 69 +++++++++---------- src/lib/utils/options/declaration.ts | 1 - 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index 1ddb61f19..500f83ea2 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -6,7 +6,6 @@ "search": { "numResults": 12, "boosts": { - "exactMatch": 2, "byKind": { "class": 1.2 }, diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 3b9ab975d..9c8fc340a 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -168,50 +168,47 @@ function updateResults( // when the `searchText` is empty. let res = searchText ? state.index.search(`*${searchText}*`) : []; - if (searchConfig.boosts != undefined) { - for (let i = 0; i < res.length; i++) { - const item = res[i]; - const row = state.data.rows[Number(item.ref)]; - let boost = 1; - - // boost by exact match on name - if ( - searchConfig.boosts.exactMatch && - row.name.toLowerCase() === searchText.toLowerCase() - ) { - boost *= searchConfig.boosts.exactMatch; - } + for (let i = 0; i < res.length; i++) { + const item = res[i]; + const row = state.data.rows[Number(item.ref)]; + let boost = 1; + + // boost by exact match on name + if(row.name + .toLowerCase() + .startsWith(searchText.toLowerCase())) { + boost *= (1 / Math.abs(row.name.length - searchText.length) * 10) + } - // boost by kind - for (let kindName in searchConfig.boosts.byKind ?? {}) { - const kind: ReflectionKind = parseInt( - Object.keys(ReflectionKind).find( - (key: string) => - ReflectionKind[key as keyof typeof ReflectionKind] - .toString() - .toLowerCase() === kindName.toLowerCase() - ) ?? "", - 10 - ); - if (row.kind == kind) { - boost *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; - } + // boost by kind + for (let kindName in searchConfig.boosts?.byKind ?? {}) { + const kind: ReflectionKind = parseInt( + Object.keys(ReflectionKind).find( + (key: string) => + ReflectionKind[key as keyof typeof ReflectionKind] + .toString() + .toLowerCase() === kindName.toLowerCase() + ) ?? "", + 10 + ); + if (row.kind == kind) { + boost *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; } + } - // boost by category - for (let categoryTitle in searchConfig.boosts?.byCategory ?? []) { - if (row.categories.indexOf(categoryTitle) > -1) { - boost *= - searchConfig.boosts.byCategory?.[categoryTitle] ?? 1; - } + // boost by category + for (let categoryTitle in searchConfig.boosts?.byCategory ?? []) { + if (row.categories.indexOf(categoryTitle) > -1) { + boost *= + searchConfig.boosts?.byCategory?.[categoryTitle] ?? 1; } - - item.score *= boost; } - res.sort((a, b) => b.score - a.score); + item.score *= boost; } + res.sort((a, b) => b.score - a.score); + for ( let i = 0, c = Math.min(searchConfig.numResults ?? 10, res.length); i < c; diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 071eca74c..c150bc931 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -54,7 +54,6 @@ const Kinds = Object.values(ReflectionKind); export interface SearchConfig { numResults?: number; boosts?: { - exactMatch?: number; byKind?: { [key: typeof Kinds[number]]: number }; byCategory?: { [key: string]: number }; }; From 6a41cee5aab213bbe29a49b6616bf442251f0ad0 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Sun, 15 May 2022 19:48:56 -0700 Subject: [PATCH 13/28] precompute category boosts --- example/src/classes/Customer.ts | 2 + example/src/reactComponents.tsx | 5 ++ example/typedoc.json | 4 +- src/lib/converter/context.ts | 7 +++ src/lib/converter/plugins/CategoryPlugin.ts | 54 ++++++++++++++----- src/lib/models/reflections/container.ts | 6 +++ .../output/plugins/JavascriptIndexPlugin.ts | 4 +- .../assets/typedoc/components/Search.ts | 19 +++---- 8 files changed, 71 insertions(+), 30 deletions(-) diff --git a/example/src/classes/Customer.ts b/example/src/classes/Customer.ts index afe1122a3..1c42329e7 100644 --- a/example/src/classes/Customer.ts +++ b/example/src/classes/Customer.ts @@ -2,6 +2,8 @@ * An abstract base class for the customer entity in our application. * * Notice how TypeDoc shows the inheritance hierarchy for our class. + * + * @category Model */ export abstract class Customer { /** A public readonly property. */ diff --git a/example/src/reactComponents.tsx b/example/src/reactComponents.tsx index 2806a0571..6df57b24e 100644 --- a/example/src/reactComponents.tsx +++ b/example/src/reactComponents.tsx @@ -36,6 +36,8 @@ export interface CardAProps { * This is our recommended way to define React components as it makes your code * more readable. The minor drawback is you must click the `CardAProps` link to * see the component's props. + * + * @category Component */ export function CardA({ children, variant = "primary" }: PropsWithChildren): ReactElement { return
{children}
; @@ -66,6 +68,8 @@ export function CardA({ children, variant = "primary" }: PropsWithChildren): ReactElement { return
; diff --git a/example/typedoc.json b/example/typedoc.json index 500f83ea2..5d369f9ee 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -3,6 +3,7 @@ "entryPoints": ["./src"], "sort": ["source-order"], "media": "media", + "categorizeByGroup": false, "search": { "numResults": 12, "boosts": { @@ -10,7 +11,8 @@ "class": 1.2 }, "byCategory": { - "Lang": 10 + "Component": 2, + "Model": 1.2 } } } diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index 63a90f498..eb10bd94f 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -14,6 +14,7 @@ import type { Converter } from "./converter"; import { isNamedNode } from "./utils/nodes"; import { ConverterEvents } from "./converter-events"; import { resolveAliasedSymbol } from "./utils/symbols"; +import type { SearchConfig } from "../utils/options/declaration"; /** * The context describes the current state the converter is in. @@ -118,6 +119,12 @@ export class Context { return this.converter.application.options.getCompilerOptions(); } + getSearchOptions(): SearchConfig { + return this.converter.application.options.getValue( + "search" + ) as SearchConfig; + } + /** * Return the type declaration of the given node. * diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index 2a2276753..ea1b4c4f6 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -4,12 +4,12 @@ import { DeclarationReflection, CommentTag, } from "../../models"; -import { ReflectionCategory } from "../../models/ReflectionCategory"; +import { ReflectionCategory } from "../../models"; import { Component, ConverterComponent } from "../components"; import { Converter } from "../converter"; import type { Context } from "../context"; import { BindOption } from "../../utils"; -import type { Comment } from "../../models/comments/index"; +import type { Comment } from "../../models"; /** * A handler that sorts and categorizes the found reflections in the resolving phase. @@ -66,9 +66,12 @@ export class CategoryPlugin extends ConverterComponent { * @param context The context object describing the current state the converter is in. * @param reflection The reflection that is currently resolved. */ - private onResolve(_context: Context, reflection: Reflection) { + private onResolve(context: Context, reflection: Reflection) { if (reflection instanceof ContainerReflection) { - this.categorize(reflection); + this.categorize( + reflection, + context.getSearchOptions().boosts?.byCategory ?? {} + ); } } @@ -79,18 +82,27 @@ export class CategoryPlugin extends ConverterComponent { */ private onEndResolve(context: Context) { const project = context.project; - this.categorize(project); + this.categorize( + project, + context.getSearchOptions().boosts?.byCategory ?? {} + ); } - private categorize(obj: ContainerReflection) { + private categorize( + obj: ContainerReflection, + categorySearchBoosts: { [key: string]: number } + ) { if (this.categorizeByGroup) { - this.groupCategorize(obj); + this.groupCategorize(obj, categorySearchBoosts); } else { - this.lumpCategorize(obj); + CategoryPlugin.lumpCategorize(obj, categorySearchBoosts); } } - private groupCategorize(obj: ContainerReflection) { + private groupCategorize( + obj: ContainerReflection, + categorySearchBoosts: { [key: string]: number } + ) { if (!obj.groups || obj.groups.length === 0) { return; } @@ -98,7 +110,8 @@ export class CategoryPlugin extends ConverterComponent { if (group.categories) return; group.categories = CategoryPlugin.getReflectionCategories( - group.children + group.children, + categorySearchBoosts ); if (group.categories && group.categories.length > 1) { group.categories.sort(CategoryPlugin.sortCatCallback); @@ -112,11 +125,17 @@ export class CategoryPlugin extends ConverterComponent { }); } - private lumpCategorize(obj: ContainerReflection) { + static lumpCategorize( + obj: ContainerReflection, + categorySearchBoosts: { [key: string]: number } + ) { if (!obj.children || obj.children.length === 0 || obj.categories) { return; } - obj.categories = CategoryPlugin.getReflectionCategories(obj.children); + obj.categories = CategoryPlugin.getReflectionCategories( + obj.children, + categorySearchBoosts + ); if (obj.categories && obj.categories.length > 1) { obj.categories.sort(CategoryPlugin.sortCatCallback); } else if ( @@ -132,10 +151,13 @@ export class CategoryPlugin extends ConverterComponent { * Create a categorized representation of the given list of reflections. * * @param reflections The reflections that should be categorized. + * @param categorySearchBoosts A user-supplied map of category titles, for computing a + * relevance boost to be used when searching * @returns An array containing all children of the given reflection categorized */ static getReflectionCategories( - reflections: DeclarationReflection[] + reflections: DeclarationReflection[], + categorySearchBoosts: { [key: string]: number } ): ReflectionCategory[] { const categories: ReflectionCategory[] = []; let defaultCat: ReflectionCategory | undefined; @@ -154,11 +176,17 @@ export class CategoryPlugin extends ConverterComponent { categories.push(defaultCat); } } + defaultCat.children.push(child); return; } for (const childCat of childCategories) { let category = categories.find((cat) => cat.title === childCat); + + const catBoost = categorySearchBoosts[category?.title ?? -1]; + if (catBoost != undefined) { + child.categoryBoost = catBoost; + } if (category) { category.children.push(child); continue; diff --git a/src/lib/models/reflections/container.ts b/src/lib/models/reflections/container.ts index ae4455fcd..a7e01d377 100644 --- a/src/lib/models/reflections/container.ts +++ b/src/lib/models/reflections/container.ts @@ -20,6 +20,12 @@ export class ContainerReflection extends Reflection { */ categories?: ReflectionCategory[]; + /** + * A precomputed boost derived from the searchCategoryBoosts typedoc.json setting, to be used when + * boosting search relevance scores at runtime. + */ + categoryBoost?: number; + /** * Return a list of all children of a certain kind. * diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 2ac619f46..0cb36abab 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -70,9 +70,7 @@ export class JavascriptIndexPlugin extends RendererComponent { name: reflection.name, url: reflection.url, classes: reflection.cssClasses, - categories: (reflection.categories ?? []).map( - (category) => category.title - ), + categoryBoost: reflection.categoryBoost, }; if (parent) { diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 9c8fc340a..710995232 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -1,7 +1,7 @@ import { debounce } from "../utils/debounce"; import { Index } from "lunr"; import type { SearchConfig } from "../../../../../../utils/options/declaration"; -import { ReflectionKind } from "../../../../../../models/reflections/kind"; +import { ReflectionKind } from "../../../../../../models"; export interface IDocument { id: number; @@ -10,7 +10,7 @@ export interface IDocument { url: string; classes?: string; parent?: string; - categories: Array; + categoryBoost?: number; } interface IData { @@ -174,10 +174,8 @@ function updateResults( let boost = 1; // boost by exact match on name - if(row.name - .toLowerCase() - .startsWith(searchText.toLowerCase())) { - boost *= (1 / Math.abs(row.name.length - searchText.length) * 10) + if (row.name.toLowerCase().startsWith(searchText.toLowerCase())) { + boost *= 1 / (Math.abs(row.name.length - searchText.length) * 10); } // boost by kind @@ -197,12 +195,7 @@ function updateResults( } // boost by category - for (let categoryTitle in searchConfig.boosts?.byCategory ?? []) { - if (row.categories.indexOf(categoryTitle) > -1) { - boost *= - searchConfig.boosts?.byCategory?.[categoryTitle] ?? 1; - } - } + boost *= row.categoryBoost ?? 1; item.score *= boost; } @@ -226,7 +219,7 @@ function updateResults( } const item = document.createElement("li"); - item.classList.value = row.classes ?? ''; + item.classList.value = row.classes ?? ""; const anchor = document.createElement("a"); anchor.href = state.base + row.url; From ee9b66e4b553bc11edec72952f7bc629d3d07c3e Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 15:32:06 -0700 Subject: [PATCH 14/28] remove dep on ReflectionKind --- example/typedoc.json | 2 +- src/lib/output/plugins/JavascriptIndexPlugin.ts | 5 ++++- .../default/assets/typedoc/components/Search.ts | 16 ++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index 5d369f9ee..1796d2f88 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -8,7 +8,7 @@ "numResults": 12, "boosts": { "byKind": { - "class": 1.2 + "class": 1.5 }, "byCategory": { "Component": 2, diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 0cb36abab..afd981137 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -12,6 +12,7 @@ import { RendererEvent } from "../events"; import { writeFileSync } from "../../utils"; import { DefaultTheme } from "../themes/default/DefaultTheme"; import type { IDocument } from "../themes/default/assets/typedoc/components/Search"; +import type { SearchConfig } from "../../utils/options/declaration"; /** * A plugin that exports an index of the project to a javascript file. @@ -103,7 +104,9 @@ export class JavascriptIndexPlugin extends RendererComponent { "search.js" ); - const searchConfig = this.application.options.getValue("search"); + const searchConfig = this.application.options.getValue( + "search" + ) as SearchConfig; const jsonData = JSON.stringify({ searchConfig, diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 710995232..cd0ae8bd8 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -1,7 +1,6 @@ import { debounce } from "../utils/debounce"; import { Index } from "lunr"; import type { SearchConfig } from "../../../../../../utils/options/declaration"; -import { ReflectionKind } from "../../../../../../models"; export interface IDocument { id: number; @@ -175,17 +174,18 @@ function updateResults( // boost by exact match on name if (row.name.toLowerCase().startsWith(searchText.toLowerCase())) { - boost *= 1 / (Math.abs(row.name.length - searchText.length) * 10); + boost *= + 1 + 1 / (Math.abs(row.name.length - searchText.length) * 10); } // boost by kind for (let kindName in searchConfig.boosts?.byKind ?? {}) { - const kind: ReflectionKind = parseInt( - Object.keys(ReflectionKind).find( - (key: string) => - ReflectionKind[key as keyof typeof ReflectionKind] - .toString() - .toLowerCase() === kindName.toLowerCase() + const kinds = window.searchData?.kinds ?? {}; + const kind = parseInt( + Object.keys(kinds).find( + (key: any) => + kinds[key].toString().toLowerCase() === + kindName.toLowerCase() ) ?? "", 10 ); From aa9a272a15862ce4b7826d49edeee0c7ce8094ac Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 15:37:55 -0700 Subject: [PATCH 15/28] chain --- src/lib/converter/plugins/CategoryPlugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index ea1b4c4f6..b0be83761 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -70,7 +70,7 @@ export class CategoryPlugin extends ConverterComponent { if (reflection instanceof ContainerReflection) { this.categorize( reflection, - context.getSearchOptions().boosts?.byCategory ?? {} + context.getSearchOptions()?.boosts?.byCategory ?? {} ); } } @@ -84,7 +84,7 @@ export class CategoryPlugin extends ConverterComponent { const project = context.project; this.categorize( project, - context.getSearchOptions().boosts?.byCategory ?? {} + context.getSearchOptions()?.boosts?.byCategory ?? {} ); } From b68ede61878fdefdcd747f205fb0387020d1f268 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 15:44:55 -0700 Subject: [PATCH 16/28] don't export IDocument --- src/lib/output/plugins/JavascriptIndexPlugin.ts | 4 ++-- .../output/themes/default/assets/typedoc/components/Search.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index afd981137..9e4c0c30e 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -11,7 +11,7 @@ import { Component, RendererComponent } from "../components"; import { RendererEvent } from "../events"; import { writeFileSync } from "../../utils"; import { DefaultTheme } from "../themes/default/DefaultTheme"; -import type { IDocument } from "../themes/default/assets/typedoc/components/Search"; +// import type { IDocument } from "../themes/default/assets/typedoc/components/Search"; import type { SearchConfig } from "../../utils/options/declaration"; /** @@ -65,7 +65,7 @@ export class JavascriptIndexPlugin extends RendererComponent { parent = undefined; } - const row: IDocument = { + const row: any = { id: rows.length, kind: reflection.kind, name: reflection.name, diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index cd0ae8bd8..a0984e7f7 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -2,7 +2,7 @@ import { debounce } from "../utils/debounce"; import { Index } from "lunr"; import type { SearchConfig } from "../../../../../../utils/options/declaration"; -export interface IDocument { +interface IDocument { id: number; kind: number; name: string; From 735ac30ca741cf5c3cb19597efbc263a66499914 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 15:46:25 -0700 Subject: [PATCH 17/28] remove import --- src/lib/output/plugins/JavascriptIndexPlugin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 9e4c0c30e..6c40fea15 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -11,7 +11,6 @@ import { Component, RendererComponent } from "../components"; import { RendererEvent } from "../events"; import { writeFileSync } from "../../utils"; import { DefaultTheme } from "../themes/default/DefaultTheme"; -// import type { IDocument } from "../themes/default/assets/typedoc/components/Search"; import type { SearchConfig } from "../../utils/options/declaration"; /** From 9e9af66483d99722e92d4af0ad6436dcb9f2d7a8 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 15:48:41 -0700 Subject: [PATCH 18/28] remove numResults --- example/typedoc.json | 1 - .../themes/default/assets/typedoc/components/Search.ts | 6 +----- src/lib/utils/options/declaration.ts | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index 1796d2f88..1cd0f94d6 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -5,7 +5,6 @@ "media": "media", "categorizeByGroup": false, "search": { - "numResults": 12, "boosts": { "byKind": { "class": 1.5 diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index a0984e7f7..b4735033b 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -202,11 +202,7 @@ function updateResults( res.sort((a, b) => b.score - a.score); - for ( - let i = 0, c = Math.min(searchConfig.numResults ?? 10, res.length); - i < c; - i++ - ) { + for (let i = 0, c = Math.min(10, res.length); i < c; i++) { const row = state.data.rows[Number(res[i].ref)]; // Bold the matched part of the query in the search results diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index c150bc931..e0c0956f9 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -52,7 +52,6 @@ export type TypeDocOptionValues = { const Kinds = Object.values(ReflectionKind); export interface SearchConfig { - numResults?: number; boosts?: { byKind?: { [key: typeof Kinds[number]]: number }; byCategory?: { [key: string]: number }; From 8864debb0b115fd4f2ea422da0d748785abdbb9a Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 16:07:28 -0700 Subject: [PATCH 19/28] splip up SearchConfig --- example/typedoc.json | 16 ++++++---------- src/lib/converter/context.ts | 11 ++++++++--- src/lib/converter/plugins/CategoryPlugin.ts | 4 ++-- src/lib/output/plugins/JavascriptIndexPlugin.ts | 10 +++++++--- src/lib/utils/options/declaration.ts | 9 ++++----- src/lib/utils/options/sources/typedoc.ts | 9 +++++++-- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index 1cd0f94d6..592b5db27 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -4,15 +4,11 @@ "sort": ["source-order"], "media": "media", "categorizeByGroup": false, - "search": { - "boosts": { - "byKind": { - "class": 1.5 - }, - "byCategory": { - "Component": 2, - "Model": 1.2 - } - } + "searchCategoryBoosts": { + "Component": 2, + "Model": 1.2 + }, + "searchGroupBoosts": { + "class": 1.5 } } diff --git a/src/lib/converter/context.ts b/src/lib/converter/context.ts index eb10bd94f..22db86884 100644 --- a/src/lib/converter/context.ts +++ b/src/lib/converter/context.ts @@ -120,9 +120,14 @@ export class Context { } getSearchOptions(): SearchConfig { - return this.converter.application.options.getValue( - "search" - ) as SearchConfig; + return { + searchCategoryBoosts: this.converter.application.options.getValue( + "searchCategoryBoosts" + ) as SearchConfig["searchCategoryBoosts"], + searchGroupBoosts: this.converter.application.options.getValue( + "searchGroupBoosts" + ) as SearchConfig["searchGroupBoosts"], + }; } /** diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index b0be83761..a2dc4ffe2 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -70,7 +70,7 @@ export class CategoryPlugin extends ConverterComponent { if (reflection instanceof ContainerReflection) { this.categorize( reflection, - context.getSearchOptions()?.boosts?.byCategory ?? {} + context.getSearchOptions()?.searchCategoryBoosts ?? {} ); } } @@ -84,7 +84,7 @@ export class CategoryPlugin extends ConverterComponent { const project = context.project; this.categorize( project, - context.getSearchOptions()?.boosts?.byCategory ?? {} + context.getSearchOptions()?.searchCategoryBoosts ?? {} ); } diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 6c40fea15..498ea96c0 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -103,9 +103,13 @@ export class JavascriptIndexPlugin extends RendererComponent { "search.js" ); - const searchConfig = this.application.options.getValue( - "search" - ) as SearchConfig; + const searchConfig = { + searchCategoryBoosts: this.application.options.getValue( + "searchCategoryBoosts" + ), + searchGroupBoosts: + this.application.options.getValue("searchGroupBoosts"), + } as SearchConfig; const jsonData = JSON.stringify({ searchConfig, diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index e0c0956f9..99a0be810 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -52,10 +52,8 @@ export type TypeDocOptionValues = { const Kinds = Object.values(ReflectionKind); export interface SearchConfig { - boosts?: { - byKind?: { [key: typeof Kinds[number]]: number }; - byCategory?: { [key: string]: number }; - }; + searchGroupBoosts?: { [key: typeof Kinds[number]]: number }; + searchCategoryBoosts?: { [key: string]: number }; } /** @@ -115,7 +113,8 @@ export interface TypeDocOptionMap { version: boolean; showConfig: boolean; plugin: string[]; - search: unknown; + searchCategoryBoosts: unknown; + searchGroupBoosts: unknown; logger: unknown; // string | Function logLevel: typeof LogLevel; markedOptions: unknown; diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 65d548682..231a1971d 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -71,8 +71,13 @@ export function addTypeDocOptions(options: Pick) { type: ParameterType.Boolean, }); options.addDeclaration({ - name: "search", - help: "Configure search behavior", + name: "searchCategoryBoosts", + help: "Configure search to give a relevance boost to selected categories", + type: ParameterType.Mixed, + }); + options.addDeclaration({ + name: "searchGroupBoosts", + help: 'Configure search to give a relevance boost to selected kinds (eg "class")', type: ParameterType.Mixed, }); options.addDeclaration({ From d0bdf5ac1b216d6e6cf3f9c989d21503203ed000 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 16:12:52 -0700 Subject: [PATCH 20/28] fix search --- .../output/themes/default/assets/typedoc/components/Search.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index b4735033b..bf70fcdad 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -179,7 +179,7 @@ function updateResults( } // boost by kind - for (let kindName in searchConfig.boosts?.byKind ?? {}) { + for (let kindName in searchConfig.searchGroupBoosts ?? {}) { const kinds = window.searchData?.kinds ?? {}; const kind = parseInt( Object.keys(kinds).find( @@ -190,7 +190,7 @@ function updateResults( 10 ); if (row.kind == kind) { - boost *= searchConfig?.boosts?.byKind?.[kindName] ?? 1; + boost *= searchConfig?.searchGroupBoosts?.[kindName] ?? 1; } } From 26d4a9a7c974360d590fc36713bfb24f116482a7 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 20:53:47 -0700 Subject: [PATCH 21/28] remove dom --- tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 3b0a377b9..42700fd1e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,6 @@ "es2020.promise", "es2020.bigint", "es2020.string", - "dom" ], "target": "es2019", // Add our `ts` internal types From f30213b884cabddab50ac0824e1a3e8cb91afa77 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Mon, 16 May 2022 21:17:34 -0700 Subject: [PATCH 22/28] lint --- tsconfig.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 42700fd1e..21a96d728 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,7 @@ { "compilerOptions": { "module": "CommonJS", - "lib": [ - "es2019", - "es2020.promise", - "es2020.bigint", - "es2020.string", - ], + "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], "target": "es2019", // Add our `ts` internal types "typeRoots": ["node_modules/@types", "src/lib/types"], From 54abe7703e5d2ed506f83452feaa14e252197feb Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Tue, 17 May 2022 00:02:12 -0700 Subject: [PATCH 23/28] precompute all boosts --- src/lib/converter/plugins/CategoryPlugin.ts | 3 +- src/lib/models/reflections/container.ts | 2 +- .../output/plugins/JavascriptIndexPlugin.ts | 35 ++++++++-------- .../assets/typedoc/components/Search.ts | 40 +++++-------------- 4 files changed, 30 insertions(+), 50 deletions(-) diff --git a/src/lib/converter/plugins/CategoryPlugin.ts b/src/lib/converter/plugins/CategoryPlugin.ts index a2dc4ffe2..d04982163 100644 --- a/src/lib/converter/plugins/CategoryPlugin.ts +++ b/src/lib/converter/plugins/CategoryPlugin.ts @@ -185,7 +185,8 @@ export class CategoryPlugin extends ConverterComponent { const catBoost = categorySearchBoosts[category?.title ?? -1]; if (catBoost != undefined) { - child.categoryBoost = catBoost; + child.relevanceBoost = + (child.relevanceBoost ?? 1) * catBoost; } if (category) { category.children.push(child); diff --git a/src/lib/models/reflections/container.ts b/src/lib/models/reflections/container.ts index a7e01d377..ea7570e54 100644 --- a/src/lib/models/reflections/container.ts +++ b/src/lib/models/reflections/container.ts @@ -24,7 +24,7 @@ export class ContainerReflection extends Reflection { * A precomputed boost derived from the searchCategoryBoosts typedoc.json setting, to be used when * boosting search relevance scores at runtime. */ - categoryBoost?: number; + relevanceBoost?: number; /** * Return a list of all children of a certain kind. diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 498ea96c0..aef53bc72 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -11,7 +11,6 @@ import { Component, RendererComponent } from "../components"; import { RendererEvent } from "../events"; import { writeFileSync } from "../../utils"; import { DefaultTheme } from "../themes/default/DefaultTheme"; -import type { SearchConfig } from "../../utils/options/declaration"; /** * A plugin that exports an index of the project to a javascript file. @@ -43,6 +42,10 @@ export class JavascriptIndexPlugin extends RendererComponent { const rows: any[] = []; const kinds: { [K in ReflectionKind]?: string } = {}; + const kindBoosts = this.application.options.getValue( + "searchGroupBoosts" + ) as { [key: string]: number }; + for (const reflection of event.project.getReflectionsByKind( ReflectionKind.All )) { @@ -64,25 +67,32 @@ export class JavascriptIndexPlugin extends RendererComponent { parent = undefined; } + if (!kinds[reflection.kind]) { + kinds[reflection.kind] = GroupPlugin.getKindSingular( + reflection.kind + ); + + const boost = + kindBoosts[(kinds[reflection.kind] ?? "").toLowerCase()]; + if (boost != undefined) { + reflection.relevanceBoost = + (reflection.relevanceBoost ?? 1) * boost; + } + } + const row: any = { id: rows.length, kind: reflection.kind, name: reflection.name, url: reflection.url, classes: reflection.cssClasses, - categoryBoost: reflection.categoryBoost, + relevanceBoost: reflection.relevanceBoost, }; if (parent) { row.parent = parent.getFullName(); } - if (!kinds[reflection.kind]) { - kinds[reflection.kind] = GroupPlugin.getKindSingular( - reflection.kind - ); - } - rows.push(row); } @@ -103,16 +113,7 @@ export class JavascriptIndexPlugin extends RendererComponent { "search.js" ); - const searchConfig = { - searchCategoryBoosts: this.application.options.getValue( - "searchCategoryBoosts" - ), - searchGroupBoosts: - this.application.options.getValue("searchGroupBoosts"), - } as SearchConfig; - const jsonData = JSON.stringify({ - searchConfig, kinds, rows, index, diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index bf70fcdad..26392ebfe 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -1,6 +1,5 @@ import { debounce } from "../utils/debounce"; import { Index } from "lunr"; -import type { SearchConfig } from "../../../../../../utils/options/declaration"; interface IDocument { id: number; @@ -9,14 +8,13 @@ interface IDocument { url: string; classes?: string; parent?: string; - categoryBoost?: number; + relevanceBoost?: number; } interface IData { kinds: { [kind: number]: string }; rows: IDocument[]; index: object; - searchConfig: SearchConfig; } declare global { @@ -81,26 +79,19 @@ export function initSearch() { base: searchEl.dataset["base"] + "/", }; - bindEvents( - searchEl, - results, - field, - state, - window?.searchData?.searchConfig ?? {} - ); + bindEvents(searchEl, results, field, state); } function bindEvents( searchEl: HTMLElement, results: HTMLElement, field: HTMLInputElement, - state: SearchState, - searchConfig: SearchConfig + state: SearchState ) { field.addEventListener( "input", debounce(() => { - updateResults(searchEl, results, field, state, searchConfig); + updateResults(searchEl, results, field, state); }, 200) ); @@ -150,8 +141,7 @@ function updateResults( searchEl: HTMLElement, results: HTMLElement, query: HTMLInputElement, - state: SearchState, - searchConfig: SearchConfig + state: SearchState ) { checkIndex(state, searchEl); // Don't clear results if loading state is not ready, @@ -178,24 +168,12 @@ function updateResults( 1 + 1 / (Math.abs(row.name.length - searchText.length) * 10); } - // boost by kind - for (let kindName in searchConfig.searchGroupBoosts ?? {}) { - const kinds = window.searchData?.kinds ?? {}; - const kind = parseInt( - Object.keys(kinds).find( - (key: any) => - kinds[key].toString().toLowerCase() === - kindName.toLowerCase() - ) ?? "", - 10 - ); - if (row.kind == kind) { - boost *= searchConfig?.searchGroupBoosts?.[kindName] ?? 1; - } + // boost by relevanceBoost + if ((row.relevanceBoost ?? 1) > 1) { + debugger; } - // boost by category - boost *= row.categoryBoost ?? 1; + boost *= row.relevanceBoost ?? 1; item.score *= boost; } From 3601b26e5f554e73330c088ec52d3c018471c32a Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Tue, 17 May 2022 00:51:36 -0700 Subject: [PATCH 24/28] validate group boosts --- src/lib/utils/options/sources/typedoc.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index 231a1971d..ce9c73e06 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -79,6 +79,21 @@ export function addTypeDocOptions(options: Pick) { name: "searchGroupBoosts", help: 'Configure search to give a relevance boost to selected kinds (eg "class")', type: ParameterType.Mixed, + validate(value: unknown) { + const validValues = Object.values(ReflectionKind) + .filter((v) => typeof v === "string") + .map((v) => v.toString().toLowerCase()); + + for (const kindName in value as { [key: string]: number }) { + if (validValues.indexOf(kindName) < 0) { + throw new Error( + `'${kindName}' is an invalid value for 'searchGroupBoosts'. Must be one of: ${validValues.join( + ", " + )}` + ); + } + } + }, }); options.addDeclaration({ name: "disableSources", From ae839ef005734fdd97d4cd22d0a364a30fbccb40 Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Tue, 17 May 2022 01:00:19 -0700 Subject: [PATCH 25/28] coalesce --- src/lib/output/plugins/JavascriptIndexPlugin.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index aef53bc72..72a183aeb 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -42,9 +42,10 @@ export class JavascriptIndexPlugin extends RendererComponent { const rows: any[] = []; const kinds: { [K in ReflectionKind]?: string } = {}; - const kindBoosts = this.application.options.getValue( - "searchGroupBoosts" - ) as { [key: string]: number }; + const kindBoosts = + (this.application.options.getValue("searchGroupBoosts") as { + [key: string]: number; + }) ?? {}; for (const reflection of event.project.getReflectionsByKind( ReflectionKind.All From d6e5b18b8882115d6e6a7e8ea596f1b6060e425c Mon Sep 17 00:00:00 2001 From: Max Loeb Date: Tue, 17 May 2022 09:46:42 -0700 Subject: [PATCH 26/28] use original case --- example/typedoc.json | 2 +- src/lib/output/plugins/JavascriptIndexPlugin.ts | 3 +-- .../output/themes/default/assets/typedoc/components/Search.ts | 4 ---- src/lib/utils/options/sources/typedoc.ts | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/example/typedoc.json b/example/typedoc.json index 592b5db27..5c68ffca2 100644 --- a/example/typedoc.json +++ b/example/typedoc.json @@ -9,6 +9,6 @@ "Model": 1.2 }, "searchGroupBoosts": { - "class": 1.5 + "Class": 1.5 } } diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 72a183aeb..504555bd6 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -73,8 +73,7 @@ export class JavascriptIndexPlugin extends RendererComponent { reflection.kind ); - const boost = - kindBoosts[(kinds[reflection.kind] ?? "").toLowerCase()]; + const boost = kindBoosts[kinds[reflection.kind] ?? ""]; if (boost != undefined) { reflection.relevanceBoost = (reflection.relevanceBoost ?? 1) * boost; diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 26392ebfe..5d817e79d 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -169,10 +169,6 @@ function updateResults( } // boost by relevanceBoost - if ((row.relevanceBoost ?? 1) > 1) { - debugger; - } - boost *= row.relevanceBoost ?? 1; item.score *= boost; diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index ce9c73e06..a10edf54d 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -82,7 +82,7 @@ export function addTypeDocOptions(options: Pick) { validate(value: unknown) { const validValues = Object.values(ReflectionKind) .filter((v) => typeof v === "string") - .map((v) => v.toString().toLowerCase()); + .map((v) => v.toString()); for (const kindName in value as { [key: string]: number }) { if (validValues.indexOf(kindName) < 0) { From 09d7c2614f17b2c7221cb955e2fa680a4372e5d6 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Mon, 30 May 2022 10:42:14 -0600 Subject: [PATCH 27/28] Add additional validation for boost options --- .../output/plugins/JavascriptIndexPlugin.ts | 13 +++--- .../assets/typedoc/components/Search.ts | 4 +- src/lib/utils/options/sources/typedoc.ts | 43 ++++++++++++++----- .../utils/options/default-options.test.ts | 32 ++++++++++++++ 4 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/lib/output/plugins/JavascriptIndexPlugin.ts b/src/lib/output/plugins/JavascriptIndexPlugin.ts index 504555bd6..91dededb8 100644 --- a/src/lib/output/plugins/JavascriptIndexPlugin.ts +++ b/src/lib/output/plugins/JavascriptIndexPlugin.ts @@ -64,6 +64,7 @@ export class JavascriptIndexPlugin extends RendererComponent { } let parent = reflection.parent; + let boost = reflection.relevanceBoost ?? 1; if (parent instanceof ProjectReflection) { parent = undefined; } @@ -73,10 +74,9 @@ export class JavascriptIndexPlugin extends RendererComponent { reflection.kind ); - const boost = kindBoosts[kinds[reflection.kind] ?? ""]; - if (boost != undefined) { - reflection.relevanceBoost = - (reflection.relevanceBoost ?? 1) * boost; + const kindBoost = kindBoosts[kinds[reflection.kind] ?? ""]; + if (kindBoost != undefined) { + boost *= kindBoost; } } @@ -86,9 +86,12 @@ export class JavascriptIndexPlugin extends RendererComponent { name: reflection.name, url: reflection.url, classes: reflection.cssClasses, - relevanceBoost: reflection.relevanceBoost, }; + if (boost !== 1) { + row.boost = boost; + } + if (parent) { row.parent = parent.getFullName(); } diff --git a/src/lib/output/themes/default/assets/typedoc/components/Search.ts b/src/lib/output/themes/default/assets/typedoc/components/Search.ts index 5d817e79d..07de070cb 100644 --- a/src/lib/output/themes/default/assets/typedoc/components/Search.ts +++ b/src/lib/output/themes/default/assets/typedoc/components/Search.ts @@ -8,7 +8,7 @@ interface IDocument { url: string; classes?: string; parent?: string; - relevanceBoost?: number; + boost?: number; } interface IData { @@ -169,7 +169,7 @@ function updateResults( } // boost by relevanceBoost - boost *= row.relevanceBoost ?? 1; + boost *= row.boost ?? 1; item.score *= boost; } diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index a10edf54d..e62192f5c 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -74,17 +74,36 @@ export function addTypeDocOptions(options: Pick) { name: "searchCategoryBoosts", help: "Configure search to give a relevance boost to selected categories", type: ParameterType.Mixed, + validate(value) { + if (!isObject(value)) { + throw new Error( + "The 'searchCategoryBoosts' option must be a non-array object." + ); + } + + if (Object.values(value).some((x) => typeof x !== "number")) { + throw new Error( + "All values of 'searchCategoryBoosts' must be numbers." + ); + } + }, }); options.addDeclaration({ name: "searchGroupBoosts", help: 'Configure search to give a relevance boost to selected kinds (eg "class")', type: ParameterType.Mixed, validate(value: unknown) { + if (!isObject(value)) { + throw new Error( + "The 'searchGroupBoosts' option must be a non-array object." + ); + } + const validValues = Object.values(ReflectionKind) .filter((v) => typeof v === "string") .map((v) => v.toString()); - for (const kindName in value as { [key: string]: number }) { + for (const kindName in value) { if (validValues.indexOf(kindName) < 0) { throw new Error( `'${kindName}' is an invalid value for 'searchGroupBoosts'. Must be one of: ${validValues.join( @@ -93,6 +112,12 @@ export function addTypeDocOptions(options: Pick) { ); } } + + if (Object.values(value).some((x) => typeof x !== "number")) { + throw new Error( + "All values of 'searchGroupBoosts' must be numbers." + ); + } }, }); options.addDeclaration({ @@ -341,11 +366,7 @@ export function addTypeDocOptions(options: Pick) { help: "Specify the options passed to Marked, the Markdown parser used by TypeDoc.", type: ParameterType.Mixed, validate(value) { - if ( - typeof value !== "object" || - Array.isArray(value) || - value == null - ) { + if (!isObject(value)) { throw new Error( "The 'markedOptions' option must be a non-array object." ); @@ -357,11 +378,7 @@ export function addTypeDocOptions(options: Pick) { help: "Selectively override the TypeScript compiler options used by TypeDoc.", type: ParameterType.Mixed, validate(value) { - if ( - typeof value !== "object" || - Array.isArray(value) || - value == null - ) { + if (!isObject(value)) { throw new Error( "The 'compilerOptions' option must be a non-array object." ); @@ -429,3 +446,7 @@ export function addTypeDocOptions(options: Pick) { }, }); } + +function isObject(x: unknown): x is Record { + return !!x && typeof x === "object" && !Array.isArray(x); +} diff --git a/src/test/utils/options/default-options.test.ts b/src/test/utils/options/default-options.test.ts index 5a2a23fac..750aac1c2 100644 --- a/src/test/utils/options/default-options.test.ts +++ b/src/test/utils/options/default-options.test.ts @@ -76,4 +76,36 @@ describe("Default Options", () => { ); }); }); + + describe("searchCategoryBoosts", () => { + it("Should disallow non-objects", () => { + throws(() => opts.setValue("searchCategoryBoosts", null as never)); + }); + + it("Should disallow non-numbers", () => { + throws(() => + opts.setValue("searchCategoryBoosts", { + cat: true, + }) + ); + }); + }); + + describe("searchGroupBoosts", () => { + it("Should disallow non-objects", () => { + throws(() => opts.setValue("searchGroupBoosts", null as never)); + }); + + it("Should disallow invalid kind names", () => { + throws(() => opts.setValue("searchGroupBoosts", { Enum2: 123 })); + }); + + it("Should disallow non-numbers", () => { + throws(() => opts.setValue("searchGroupBoosts", { Enum: true })); + }); + + it("Should allow groups", () => { + doesNotThrow(() => opts.setValue("searchGroupBoosts", { Enum: 5 })); + }); + }); }); From 45bf9dac200a4d9e8eff44e8b8aa03511ff6a343 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Mon, 30 May 2022 10:53:16 -0600 Subject: [PATCH 28/28] Fix CI --- src/lib/utils/sort.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/sort.ts b/src/lib/utils/sort.ts index d06afe635..d012a137f 100644 --- a/src/lib/utils/sort.ts +++ b/src/lib/utils/sort.ts @@ -5,7 +5,7 @@ import { ReflectionKind } from "../models/reflections/kind"; import type { DeclarationReflection } from "../models/reflections/declaration"; -import { LiteralType } from "../models"; +import { LiteralType } from "../models/types"; export const SORT_STRATEGIES = [ "source-order",