Core: Support function for loader option in Search API

This commit is contained in:
Fuma Nama
2026-04-18 23:06:12 +08:00
parent 023d23d35c
commit b60fa32525
3 changed files with 93 additions and 56 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Support function for loader option in Search API
+44 -20
View File
@@ -69,11 +69,7 @@ function server(options: Options): SearchServer {
}; };
} }
export function flexsearch(options: Options): SearchAPI { function serverI18n(options: I18nOptions): SearchServer {
return createEndpoint(server(options));
}
export function flexsearchI18n(options: I18nOptions): SearchAPI {
const { indexes: inputIndexes, localeMap } = options; const { indexes: inputIndexes, localeMap } = options;
async function initSearchServers() { async function initSearchServers() {
@@ -109,7 +105,7 @@ export function flexsearchI18n(options: I18nOptions): SearchAPI {
} }
const get = initSearchServers(); const get = initSearchServers();
return createEndpoint({ return {
async export(): Promise<ExportedData> { async export(): Promise<ExportedData> {
const map = await get; const map = await get;
const entries = Array.from(map.entries()).map(async ([k, v]) => { const entries = Array.from(map.entries()).map(async ([k, v]) => {
@@ -129,7 +125,15 @@ export function flexsearchI18n(options: I18nOptions): SearchAPI {
if (handler) return handler.search(query, searchOptions); if (handler) return handler.search(query, searchOptions);
return []; return [];
}, },
}); };
}
export function flexsearch(options: Options): SearchAPI {
return createEndpoint(server(options));
}
export function flexsearchI18n(options: I18nOptions): SearchAPI {
return createEndpoint(serverI18n(options));
} }
export interface FromSourceOptions<C extends LoaderConfig> extends Pick< export interface FromSourceOptions<C extends LoaderConfig> extends Pick<
@@ -139,14 +143,19 @@ export interface FromSourceOptions<C extends LoaderConfig> extends Pick<
buildIndex?: (page: Page<C['source']['pageData']>) => Awaitable<Index>; buildIndex?: (page: Page<C['source']['pageData']>) => Awaitable<Index>;
} }
/**
* create server from loader, if passed as function, the server will re-index all records once a different instance of loader is returned.
*/
export function flexsearchFromSource<C extends LoaderConfig>( export function flexsearchFromSource<C extends LoaderConfig>(
loader: LoaderOutput<C>, loader: LoaderOutput<C> | (() => Awaitable<LoaderOutput<C>>),
options: FromSourceOptions<NoInfer<C>> = {}, options: FromSourceOptions<NoInfer<C>> = {},
) { ) {
const cache = new WeakMap<LoaderOutput<C>, Promise<SearchServer>>();
const { buildIndex = buildIndexDefault, ...rest } = options; const { buildIndex = buildIndexDefault, ...rest } = options;
function indexes(): Promise<IndexWithLocale[]> {
return Promise.all( async function initServer(loader: LoaderOutput<C>): Promise<SearchServer> {
loader.getPages().map(async (page) => { const indexes = await Promise.all(
loader.getPages().map(async (page): Promise<IndexWithLocale> => {
const index = await buildIndex(page); const index = await buildIndex(page);
return { return {
...index, ...index,
@@ -155,18 +164,33 @@ export function flexsearchFromSource<C extends LoaderConfig>(
}; };
}), }),
); );
if (loader._i18n)
return serverI18n({
indexes,
i18n: loader._i18n,
...rest,
});
return server({ indexes, ...rest });
} }
if (loader._i18n) { async function getCurrentServer() {
return flexsearchI18n({ const l = typeof loader === 'function' ? await loader() : loader;
indexes, let server = cache.get(l);
i18n: loader._i18n, if (!server) {
...rest, server = initServer(l);
}); cache.set(l, server);
}
return await server;
} }
return flexsearch({ return createEndpoint({
indexes, async export() {
...rest, return (await getCurrentServer()).export();
},
async search(query, options) {
return (await getCurrentServer()).search(query, options);
},
}); });
} }
+44 -36
View File
@@ -267,52 +267,60 @@ interface Options<C extends LoaderConfig> extends Omit<AdvancedOptions, 'indexes
buildIndex?: (page: Page<C['source']['pageData']>) => Awaitable<AdvancedIndex>; buildIndex?: (page: Page<C['source']['pageData']>) => Awaitable<AdvancedIndex>;
} }
/**
* create server from loader, if passed as function, the server will re-index all records once a different instance of loader is returned.
*/
export function createFromSource<C extends LoaderConfig>( export function createFromSource<C extends LoaderConfig>(
source: LoaderOutput<C>, loader: LoaderOutput<C> | (() => Awaitable<LoaderOutput<C>>),
options?: Options<C>,
): SearchAPI<OramaQueryOptions>;
export function createFromSource<C extends LoaderConfig>(
source: LoaderOutput<C>,
options: Options<C> = {}, options: Options<C> = {},
): SearchAPI<OramaQueryOptions> { ): SearchAPI<OramaQueryOptions> {
const { buildIndex = buildIndexDefault } = options; const { buildIndex = buildIndexDefault } = options;
const cache = new WeakMap<LoaderOutput<C>, Promise<SearchServer<OramaQueryOptions>>>();
if (source._i18n) { async function initServer(loader: LoaderOutput<C>) {
return createI18nSearchAPI('advanced', { const indexes = await Promise.all(
loader.getPages().map(async (page) => {
const index = await buildIndex(page);
return {
...index,
breadcrumbs: index.breadcrumbs ?? buildBreadcrumbs(loader, page),
locale: page.locale,
};
}),
);
if (loader._i18n) {
return createI18nSearchAPI('advanced', {
...options,
indexes: indexes as WithLocale<AdvancedIndex>[],
i18n: loader._i18n,
});
}
return initAdvancedSearch({
indexes,
...options, ...options,
i18n: source._i18n,
async indexes() {
const indexes = source.getLanguages().flatMap((entry) => {
return entry.pages.map(async (page) => {
const index = await buildIndex(page);
return {
...index,
breadcrumbs: index.breadcrumbs ?? buildBreadcrumbs(source, page),
locale: entry.language,
};
});
});
return Promise.all(indexes);
},
}); });
} }
return toAPI( async function getCurrentServer() {
initAdvancedSearch({ const l = typeof loader === 'function' ? await loader() : loader;
...options, let server = cache.get(l);
async indexes() { if (!server) {
const indexes = source.getPages().map(async (page) => { server = initServer(l);
const index = await buildIndex(page); cache.set(l, server);
if (index.breadcrumbs) return index; }
return { ...index, breadcrumbs: buildBreadcrumbs(source, page) }; return await server;
}); }
return Promise.all(indexes); return toAPI({
}, async export() {
}), return (await getCurrentServer()).export();
); },
async search(query, options) {
return (await getCurrentServer()).search(query, options);
},
});
} }
function toAPI(server: SearchServer<OramaQueryOptions>): SearchAPI<OramaQueryOptions> { function toAPI(server: SearchServer<OramaQueryOptions>): SearchAPI<OramaQueryOptions> {