diff --git a/src/node/plugins/localSearchPlugin.ts b/src/node/plugins/localSearchPlugin.ts index e4f04a68..b282aa7b 100644 --- a/src/node/plugins/localSearchPlugin.ts +++ b/src/node/plugins/localSearchPlugin.ts @@ -5,7 +5,10 @@ import pMap from 'p-map' import path from 'path' import type { Plugin, ViteDevServer } from 'vite' import type { SiteConfig } from '../config' -import { createMarkdownRenderer } from '../markdown/markdown' +import { + createMarkdownRenderer, + type MarkdownRenderer +} from '../markdown/markdown' import { resolveSiteDataByRoute, slash, @@ -29,6 +32,26 @@ interface IndexObject { titles: string[] } +let md: MarkdownRenderer + +let flagAllIndexed = false +const taskByLocale = new Map>() +const filesByLocale = new Map>() +const indexByLocale = new Map>() + +function getIndexByLocale(locale: string, options?: any) { + let index = indexByLocale.get(locale) + if (!index) { + index = new MiniSearch({ + fields: ['title', 'titles', 'text'], + storeFields: ['title', 'titles'], + ...options + }) + indexByLocale.set(locale, index) + } + return index +} + export async function localSearchPlugin( siteConfig: SiteConfig ): Promise { @@ -48,7 +71,7 @@ export async function localSearchPlugin( } } - const md = await createMarkdownRenderer( + md ??= await createMarkdownRenderer( siteConfig.srcDir, siteConfig.markdown, siteConfig.site.base, @@ -71,21 +94,6 @@ export async function localSearchPlugin( } } - const indexByLocales = new Map>() - - function getIndexByLocale(locale: string) { - let index = indexByLocales.get(locale) - if (!index) { - index = new MiniSearch({ - fields: ['title', 'titles', 'text'], - storeFields: ['title', 'titles'], - ...options.miniSearch?.options - }) - indexByLocales.set(locale, index) - } - return index - } - function getLocaleForPath(file: string) { const relativePath = slash(path.relative(siteConfig.srcDir, file)) const siteData = resolveSiteDataByRoute(siteConfig.site, relativePath) @@ -125,21 +133,10 @@ export async function localSearchPlugin( return id } - function scanForLocales() { - for (const page of siteConfig.pages) { - const file = path.join(siteConfig.srcDir, page) - const locale = getLocaleForPath(file) - // dry-fetch the index for this locale - getIndexByLocale(locale) - } - } - - async function indexFile(page: string, parallel: boolean = false) { - const file = path.join(siteConfig.srcDir, page) - // get file metadata + async function indexFile(file: string, parallel: boolean = false) { const fileId = getDocId(file) const locale = getLocaleForPath(file) - const index = getIndexByLocale(locale) + const index = getIndexByLocale(locale, options.miniSearch?.options) // retrieve file and split into "sections" const html = await render(file) const sections = @@ -163,29 +160,28 @@ export async function localSearchPlugin( } } - async function indexAll() { - const concurrency = siteConfig.concurrency - let numIndexed = 0 - - const updateProgress = () => - updateCurrentTask( - ++numIndexed, - siteConfig.pages.length, - 'indexing local search' - ) - - const parallel = shouldUseParallel(siteConfig, 'local-search') + function indexAll() { + if (flagAllIndexed) return + flagAllIndexed = true - await pMap( - siteConfig.pages, - (page) => indexFile(page, parallel).then(updateProgress), - { concurrency } - ) + for (const page of siteConfig.pages) { + const file = path.join(siteConfig.srcDir, page) + const locale = getLocaleForPath(file) + if (!filesByLocale.has(locale)) filesByLocale.set(locale, new Set()) + filesByLocale.get(locale)!.add(file) + } + } - updateCurrentTask() + async function indexLocale(locale: string) { + let numIndexed = 0 + const update = () => + updateCurrentTask(++numIndexed, files.length, `🔍️ indexing ${locale}`) + const files = [...filesByLocale.get(locale)!] + const task = (f: string) => indexFile(f, parallel).then(update) + await pMap(files, task, { concurrency: siteConfig.concurrency }) } - let indexAllPromise: Promise | undefined + const parallel = shouldUseParallel(siteConfig, 'local-search') return { name: 'vitepress:local-search', @@ -213,28 +209,28 @@ export async function localSearchPlugin( async load(id) { if (id === LOCAL_SEARCH_INDEX_REQUEST_PATH) { - scanForLocales() + indexAll() let records: string[] = [] - for (const [locale] of indexByLocales) { + for (const [locale] of filesByLocale) { records.push( `${JSON.stringify( locale - )}: () => import('${LOCAL_SEARCH_INDEX_ID}-${locale}')` + )}: () => import('${LOCAL_SEARCH_INDEX_ID}:${locale}')` ) } return `export default {${records.join(',')}}` } else if (id.startsWith(LOCAL_SEARCH_INDEX_REQUEST_PATH)) { const locale = id.slice(LOCAL_SEARCH_INDEX_REQUEST_PATH.length + 1) - await (indexAllPromise ??= indexAll()) + await ((taskByLocale as any)[locale] ??= indexLocale(locale)) return `export default ${JSON.stringify( - JSON.stringify(indexByLocales.get(locale) ?? {}) + JSON.stringify(indexByLocale.get(locale) ?? {}) )}` } }, async handleHotUpdate({ file }) { if (file.endsWith('.md')) { - await indexFile(file) + await indexFile(file, parallel) debug('🔍️ Updated', file) onIndexUpdated() }