1/** Add search exclusions during the shared build-output pass. */
2// Selectors (matched as opening-tag class strings) we want Pagefind to
3// skip. We add the attribute as `data-pagefind-ignore="all"` so any
4// headings inside also stop being indexed (otherwise headings inside a
5// collapsed code block would still show up as sub-results).
6const classMatchers = [
7 // Expressive Code wraps every fenced code block in this div.
8 "expressive-code",
9 // Starlight's TOC, sidebar, page footer, pagination, header.
10 "right-sidebar",
11 "sidebar-pane",
12 "sl-sidebar-state-persist",
13 "pagination-links",
14 "sl-mobile-toc",
15 "site-search",
16];
17const classRegex = new RegExp(
18 `<(div|nav|aside|figure|header|footer|details|site-search)([^>]*?)class="([^"]*?\\b(?:${classMatchers.join("|")})\\b[^"]*?)"([^>]*?)>`,
19 "g",
20);
21// Also tag every <pre> directly, which catches the inner code element
22// even if Expressive Code wrapping ever changes.
23const preRegex = /<pre(\s[^>]*)?>/g;
24// Provider docs auto-generated from JSDoc start with a
25// "> **Source:** `src/...`" blockquote that's noise for search excerpts.
26const sourceBlockquoteRegex =
27 /<blockquote>\s*<p><strong>Source:<\/strong>[\s\S]*?<\/p>\s*<\/blockquote>/g;
28
29export function rewriteForPagefind(html: string) {
30 let out = html.replace(classRegex, (match, tag, pre, cls, post) => {
31 if (match.includes("data-pagefind-ignore")) return match;
32 return `<${tag}${pre}class="${cls}" data-pagefind-ignore="all"${post}>`;
33 });
34 out = out.replace(preRegex, (match, attrs = "") => {
35 if (match.includes("data-pagefind-ignore")) return match;
36 return `<pre${attrs} data-pagefind-ignore="all">`;
37 });
38 out = out.replace(sourceBlockquoteRegex, (match) =>
39 match.replace("<blockquote>", '<blockquote data-pagefind-ignore="all">'),
40 );
41 return out;
42}