33 * /reference/api/typescript/ — TypeScript API Reference landing page with search.
44 *
55 * Lists all TypeScript API modules with a client-side search bar that lets
6- * users filter across functions, handle types, types, and enums.
6+ * users filter across functions, handle members, handle types, types, and enums.
77 */
88import StarlightPage from ' @astrojs/starlight/components/StarlightPage.astro' ;
99import { tsModuleSlug , getTsModules , simplifyType } from ' @utils/ts-modules' ;
1010import { getTsApiReferenceSidebar } from ' @utils/ts-api-sidebar' ;
11+ import { buildTsApiSearchIndex } from ' @utils/ts-api-search' ;
1112import ApiSearchBar from ' @components/api-reference/ApiSearchBar.astro' ;
1213import Breadcrumb from ' @components/Breadcrumb.astro' ;
1314
@@ -23,82 +24,25 @@ const base = import.meta.env.BASE_URL.replace(/\/$/, '');
2324const allTsVersions = [... new Set (sorted .map (p => p .package .version ).filter (Boolean ))] as string [];
2425
2526/* ── Build the search index ─────────────────────────────────────── */
26-
27- interface IndexEntry {
28- /** Short name */
29- n: string ;
30- /** Fully-qualified name or signature */
31- f: string ;
32- /** Kind (method, handle, dto, enum, property, etc.) */
33- k: string ;
34- /** Package name */
35- p: string ;
36- /** Description text */
37- s: string ;
38- /** Parent type name (for capabilities on handle types) */
39- t? : string ;
40- }
41-
42- const index: IndexEntry [] = [];
27+ const index = buildTsApiSearchIndex (sorted , base );
4328let functionCount = 0 ;
4429let typeCount = 0 ;
4530
4631for (const pkg of sorted ) {
47- const pkgName = pkg .package .name ;
48- const pkgVersion = pkg .package .version ;
49-
50- // Index functions
5132 for (const func of pkg .functions ?? []) {
5233 functionCount ++ ;
53- index .push ({
54- n: func .name ,
55- f: func .signature ?? func .qualifiedName ?? func .name ,
56- k: func .kind === ' PropertyGetter' ? ' property' :
57- func .kind === ' PropertySetter' ? ' property' :
58- func .kind === ' InstanceMethod' ? ' method' : ' method' ,
59- p: pkgName ,
60- s: func .description ?? ' ' ,
61- ... (pkgVersion ? { v: pkgVersion } : {}),
62- });
6334 }
6435
65- // Index handle types
6636 for (const handle of pkg .handleTypes ?? []) {
6737 typeCount ++ ;
68- index .push ({
69- n: handle .name ,
70- f: handle .fullName ?? handle .name ,
71- k: handle .isInterface ? ' interface' : ' handle' ,
72- p: pkgName ,
73- s: ` Handle type${handle .isInterface ? ' (interface)' : ' ' } ` ,
74- ... (pkgVersion ? { v: pkgVersion } : {}),
75- });
7638 }
7739
78- // Index DTO types
7940 for (const dto of pkg .dtoTypes ?? []) {
8041 typeCount ++ ;
81- index .push ({
82- n: dto .name ,
83- f: dto .fullName ?? dto .name ,
84- k: ' type' ,
85- p: pkgName ,
86- s: ` Type with ${dto .fields ?.length ?? 0 } fields ` ,
87- ... (pkgVersion ? { v: pkgVersion } : {}),
88- });
8942 }
9043
91- // Index enum types
9244 for (const enumType of pkg .enumTypes ?? []) {
9345 typeCount ++ ;
94- index .push ({
95- n: enumType .name ,
96- f: enumType .fullName ?? enumType .name ,
97- k: ' enum' ,
98- p: pkgName ,
99- s: ` Enum: ${(enumType .members ?? []).join (' , ' )} ` ,
100- ... (pkgVersion ? { v: pkgVersion } : {}),
101- });
10246 }
10347}
10448
@@ -134,7 +78,7 @@ const indexJson = JSON.stringify(index);
13478
13579 <ApiSearchBar
13680 id =" ts-api"
137- placeholder =" Search functions, types, enums …"
81+ placeholder =" Search functions, methods, properties, types …"
13882 kinds ={ allKinds }
13983 versions ={ allTsVersions }
14084 defaultStatsText ={ ` ${functionCount .toLocaleString ()} functions and ${typeCount .toLocaleString ()} types across ${sorted .length .toLocaleString ()} modules ` }
@@ -185,15 +129,18 @@ const indexJson = JSON.stringify(index);
185129 k: string;
186130 p: string;
187131 s: string;
132+ h: string;
188133 t?: string;
189134 v?: string;
135+ m?: boolean;
190136 }
191137
192138 const PAGE_SIZE = 10;
193139 const DEBOUNCE_MS = 250;
194140 function fmtNum(n: number): string { return n.toLocaleString(); }
195141
196142 const KIND_COLORS: Record<string, string> = {
143+ function: '#3b82f6',
197144 method: '#3b82f6',
198145 property: '#10b981',
199146 handle: '#8b5cf6',
@@ -293,12 +240,12 @@ const indexJson = JSON.stringify(index);
293240 const visibleEntries = this.activeVersions === null
294241 ? this.index
295242 : this.index.filter(entry => entry.v && this.activeVersions!.has(entry.v));
296- const functionKinds = new Set(['method ', 'property ']);
243+ const functionKinds = new Set(['function ', 'method ']);
297244
298245 return {
299- packageCount: new Set(visibleEntries.map(entry => entry.p)).size,
300- functionCount: visibleEntries.filter(entry => functionKinds.has(entry.k)).length,
301- typeCount: visibleEntries.filter(entry => !functionKinds.has(entry.k)).length,
246+ packageCount: new Set(visibleEntries.filter(entry => !entry.m). map(entry => entry.p)).size,
247+ functionCount: visibleEntries.filter(entry => !entry.m && functionKinds.has(entry.k)).length,
248+ typeCount: visibleEntries.filter(entry => !entry.m && ! functionKinds.has(entry.k)).length,
302249 };
303250 }
304251
@@ -461,6 +408,7 @@ const indexJson = JSON.stringify(index);
461408 const fullLower = entry.f.toLowerCase();
462409 const descLower = entry.s.toLowerCase();
463410 const pkgLower = entry.p.toLowerCase();
411+ const parentLower = entry.t?.toLowerCase() ?? '';
464412
465413 let score = 0;
466414 for (const token of tokens) {
@@ -469,6 +417,7 @@ const indexJson = JSON.stringify(index);
469417 else if (nameLower.includes(token)) score += 40;
470418 else if (this.camelMatch(entry.n, token)) score += 55;
471419 else if (fullLower.includes(token)) score += 30;
420+ else if (parentLower.includes(token)) score += 25;
472421 else if (pkgLower.includes(token)) score += 15;
473422 else if (descLower.includes(token)) score += 10;
474423 else { score = 0; break; }
@@ -546,26 +495,21 @@ const indexJson = JSON.stringify(index);
546495
547496 private renderResult(entry: IndexEntry, tokens: string[]): string {
548497 const kindColor = KIND_COLORS[entry.k] ?? KIND_COLORS['method'];
549- const pkgSlug = entry.p.toLowerCase();
550- const itemSlug = entry.n.toLowerCase().replace(/[^a-z0-9]+/g, '-');
551-
552- // Only types (handle, interface, type, enum) have detail pages
553- const TYPE_KINDS = new Set(['handle', 'interface', 'type', 'enum']);
554- const isType = TYPE_KINDS.has(entry.k);
555- const href = isType ? `${this.base}/reference/api/typescript/${pkgSlug}/${itemSlug}/` : null;
556- const tag = href ? 'a' : 'div';
557- const hrefAttr = href ? ` href="${this.esc(href)}"` : '';
498+ const kindClass = entry.k.replace(/\s+/g, '-');
558499
559- return `<${tag}${hrefAttr} class="api-list-item api-search-result" title="${this.esc(entry.f)} — ${this.esc(entry.p)}">
500+ return `<a href="${this.esc(entry.h)}" class="api-list-item api-search-result" title="${this.esc(entry.f)} — ${this.esc(entry.p)}${entry.t ? ` (${this.esc(entry.t)})` : '' }">
560501 <div class="api-list-header">
561- <span class="api-search-result-name" style="color: ${kindColor}">${this.highlight(entry.n, tokens)}</span>
562- <span class="api-kind-micro kind-${entry.k}">${this.esc(entry.k)}</span>
502+ <span class="api-result-name-wrap">
503+ <span class="api-search-result-name" style="color: ${kindColor}">${this.highlight(entry.n, tokens)}</span>
504+ ${entry.t ? `<span class="api-result-dot"> · </span><span class="api-result-parent">${this.esc(entry.t)}</span>` : ''}
505+ </span>
506+ <span class="api-kind-micro kind-${kindClass}">${this.esc(entry.k)}</span>
563507 <div class="trailing">
564508 <span class="api-list-meta" title="${this.esc(entry.p)}">${this.esc(entry.p)}</span>
565509 </div>
566510 </div>
567511 ${entry.s ? `<div class="api-list-desc">${this.esc(entry.s)}</div>` : ''}
568- </${tag} >`;
512+ </a >`;
569513 }
570514
571515 private highlight(text: string, tokens: string[]): string {
0 commit comments