|
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)}...`; |
|
}); |
|
} |
|
|