import { z } from "zod"; import Slugger from "github-slugger"; import { GlobResult, PluginOptions, SearchDocument, Section, pluginOptionValidator, } from "./types.js"; type Page = z.infer; const pageValidator = z.object({ url: z.string(), frontmatter: z.record(z.any()), }); const sluggers = new Map(); 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 = {} ): Promise { 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; }