File size: 1,620 Bytes
0ad74ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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)}...`;
	});
}