@barnabask/astro-minisearch
Version:
Utilities to add a static full text index to an Astro project
81 lines (71 loc) • 2.25 kB
text/typescript
import { z } from "zod";
import Slugger from "github-slugger";
import {
GlobResult,
PluginOptions,
SearchDocument,
Section,
pluginOptionValidator,
} from "./types.js";
type Page = z.infer<typeof pageValidator>;
const pageValidator = z.object({
url: z.string(),
frontmatter: z.record(z.any()),
});
const sluggers = new Map<string, Slugger>();
function slugifyUrl(url: string, heading?: string): string {
if (!url || !heading || url.includes("#")) return url;
const slugger = sluggers.get(url) || new Slugger();
if (!sluggers.has(url)) sluggers.set(url, slugger);
return [url, slugger.slug(heading)].join("#");
}
/**
* Convert a single page to an array of SearchDocuments.
* @ignore
*/
export function pageToDocuments(
page: Page,
contentKey: string
): SearchDocument[] {
const { url, frontmatter } = page;
const { [contentKey]: textData, title, ...other } = frontmatter;
let sections: Section[] = [];
if (!textData) {
return [];
} else if (typeof textData === "string") {
// if plain text was a simple string, treat like an object with null key
sections = [{ heading: "", text: textData }];
} else if (Array.isArray(textData)) {
sections = textData.map(([heading, text]) => {
return { heading, text };
});
}
return sections.map(({ heading, text }) => {
return { url: slugifyUrl(url, heading), heading, title, text, ...other };
});
}
/**
* Converts glob output of static pages to an array of {@link types!SearchDocument}s.
*
* @param pages result of of [`import.meta.glob`](https://vitejs.dev/guide/features.html#glob-import)
* @param options plugin options (optional)
* @example
* ```ts
* pagesGlobToDocuments(import.meta.glob('./**\/*.md*'));
* ```
*/
export async function pagesGlobToDocuments(
pages: GlobResult,
options: Partial<PluginOptions> = {}
): Promise<SearchDocument[]> {
const documents: SearchDocument[] = [];
const opts = pluginOptionValidator.parse(options);
const contentKey = opts.contentKey;
await Promise.all(
Object.values(pages).map(async (getInfo) => {
const page: Page = pageValidator.parse(await getInfo());
documents.push(...pageToDocuments(page, contentKey));
})
);
return documents;
}