| import FlexSearch from "flexsearch"; | |
| export type Page = { | |
| content: string; | |
| slug: string; | |
| title: string; | |
| type: string; | |
| }; | |
| export type Result = { | |
| content: string[]; | |
| slug: string; | |
| title: string; | |
| type?: string; | |
| }; | |
| let pages_index: FlexSearch.Index; | |
| let pages: Page[]; | |
| export function create_pages_index(data: Page[]) { | |
| pages_index = new FlexSearch.Index({ tokenize: "forward" }); | |
| data.forEach((page, i) => { | |
| const item = `${page.title} ${page.content}`; | |
| pages_index.add(i, item); | |
| }); | |
| pages = data; | |
| } | |
| export function search_pages_index(search_term: string) { | |
| const match = search_term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | |
| const results = pages_index.search(match); | |
| return results | |
| .map((index) => pages[index as number]) | |
| .map(({ slug, title, content, type }) => { | |
| return { | |
| slug, | |
| title: replace_text_with_marker(title, match), | |
| content: get_matches(content, match), | |
| type | |
| }; | |
| }); | |
| } | |
| function replace_text_with_marker(text: string, match: string) { | |
| const regex = new RegExp(match, "gi"); | |
| return text.replaceAll( | |
| regex, | |
| (match) => `<span class='mark'>${match}</span>` | |
| ); | |
| } | |
| function get_matches(text: string, search_term: string, limit = 1) { | |
| const regex = new RegExp(search_term, "gi"); | |
| const indexes = []; | |
| let matches = 0; | |
| let match; | |
| while ((match = regex.exec(text)) !== null && matches < limit) { | |
| indexes.push(match.index); | |
| matches++; | |
| } | |
| return indexes.map((index) => { | |
| const start = index - 20; | |
| const end = index + 80; | |
| const excerpt = text.substring(start, end).trim(); | |
| return `...${replace_text_with_marker(excerpt, search_term)}...`; | |
| }); | |
| } | |