Skip to content

Commit c232b04

Browse files
authored
Improve docs search (#349)
* feature: Improved documentation search * chore: Added changeset --------- Co-authored-by: ijlee2 <ijlee2@users.noreply.github.com>
1 parent 862daf3 commit c232b04

3 files changed

Lines changed: 130 additions & 0 deletions

File tree

.changeset/spotty-needles-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"docs-app-for-codemod-utils": minor
3+
---
4+
5+
Improved documentation search

docs/.vitepress/config.mts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { defineConfig } from 'vitepress';
22

3+
import { miniSearch } from './minisearch.mts';
34
import { sidebar } from './sidebar.mts';
45

56
export default defineConfig({
@@ -112,6 +113,10 @@ export default defineConfig({
112113
level: [2, 3],
113114
},
114115
search: {
116+
options: {
117+
detailedView: true,
118+
miniSearch,
119+
},
115120
provider: 'local',
116121
},
117122
sidebar,

docs/.vitepress/minisearch.mts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import type { DefaultTheme } from 'vitepress/theme';
2+
3+
function boostDocument(documentId: string, term: string): number {
4+
const [url, _sectionId] = documentId.split('#') as [
5+
string,
6+
string | undefined,
7+
];
8+
9+
const path = url.replace('/docs', '');
10+
11+
const PRIORITIZED_PATHS: Record<string, string> = {
12+
astjavascript: '/packages/codemod-utils-ast-javascript',
13+
asttemplate: '/packages/codemod-utils-ast-template',
14+
asttemplatetag: '/packages/codemod-utils-ast-template-tag',
15+
blueprints: '/packages/codemod-utils-blueprints',
16+
cli: '/packages/codemod-utils-cli',
17+
ember: '/packages/codemod-utils-ember',
18+
files: '/packages/codemod-utils-files',
19+
packagejson: '/packages/codemod-utils-package-json',
20+
tests: '/packages/codemod-utils-tests',
21+
threads: '/packages/codemod-utils-threads',
22+
};
23+
24+
const prioritizedPath = PRIORITIZED_PATHS[term.toLowerCase()];
25+
26+
if (prioritizedPath) {
27+
return path === prioritizedPath ? 10 : 0;
28+
}
29+
30+
if (path === '/') {
31+
return 0.5;
32+
}
33+
34+
if (path === '/quickstart') {
35+
return 3;
36+
}
37+
38+
if (path.startsWith('/packages/')) {
39+
return 5;
40+
}
41+
42+
if (path.startsWith('/tutorials/create-blueprints/')) {
43+
return 1.5;
44+
}
45+
46+
if (path.startsWith('/tutorials/main-tutorial/')) {
47+
return 3;
48+
}
49+
50+
if (path.startsWith('/tutorials/support-windows/')) {
51+
return 1.5;
52+
}
53+
54+
if (path.startsWith('/tutorials/update-css-files/')) {
55+
return 1.5;
56+
}
57+
58+
if (path.startsWith('/tutorials/update-template-tags/')) {
59+
return 1.5;
60+
}
61+
62+
return 1;
63+
}
64+
65+
function tokenize(text: string): string[] {
66+
/*
67+
MiniSearch's default tokenizer splits on whitespace and punctuation only, so
68+
an API name like `formatDateRange` is indexed as a single term. We split
69+
camelCase names as well, so that someone who searches for "date range" or
70+
"primary locale" lands on the same pages that `formatDateRange` and
71+
`primaryLocale` do.
72+
*/
73+
const SPACE = /[\n\r\p{Z}]+/u;
74+
75+
const PUNCTUATION = /\p{P}+/u;
76+
77+
const CAMEL_CASE_BOUNDARY =
78+
/(?<=[\p{Ll}\p{N}])(?=\p{Lu})|(?<=\p{Lu})(?=\p{Lu}\p{Ll})/u;
79+
80+
const tokens: string[] = [];
81+
82+
for (const chunk of text.split(SPACE)) {
83+
const words = chunk.split(PUNCTUATION).filter((word) => word !== '');
84+
85+
for (const word of words) {
86+
// Keep the whole name, so that searching for `formatDateRange` still works
87+
tokens.push(word);
88+
89+
const parts = word.split(CAMEL_CASE_BOUNDARY);
90+
91+
if (parts.length > 1) {
92+
tokens.push(...parts);
93+
}
94+
}
95+
96+
/*
97+
Join what punctuation split apart, so that `format-date-range` produces
98+
the same `formatdaterange` token that `formatDateRange` does. Both
99+
spellings of a name then lead to the same page. (A user who types the
100+
words with a space instead, `format date range`, still gets the looser,
101+
word-by-word match.)
102+
*/
103+
if (words.length > 1) {
104+
tokens.push(words.join(''));
105+
}
106+
}
107+
108+
return tokens;
109+
}
110+
111+
export const miniSearch: NonNullable<
112+
DefaultTheme.LocalSearchOptions['miniSearch']
113+
> = {
114+
options: {
115+
tokenize,
116+
},
117+
searchOptions: {
118+
boostDocument,
119+
},
120+
};

0 commit comments

Comments
 (0)