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 {
return createEndpoint(server(options));
}
export function flexsearchI18n(options: I18nOptions): SearchAPI {
function serverI18n(options: I18nOptions): SearchServer {
const { indexes: inputIndexes, localeMap } = options;
async function initSearchServers() {
@@ -109,7 +105,7 @@ export function flexsearchI18n(options: I18nOptions): SearchAPI {
}
const get = initSearchServers();
return createEndpoint({
return {
async export(): Promise<ExportedData> {
const map = await get;
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);
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<
@@ -139,14 +143,19 @@ export interface FromSourceOptions<C extends LoaderConfig> extends Pick<
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>(
loader: LoaderOutput<C>,
loader: LoaderOutput<C> | (() => Awaitable<LoaderOutput<C>>),
options: FromSourceOptions<NoInfer<C>> = {},
) {
const cache = new WeakMap<LoaderOutput<C>, Promise<SearchServer>>();
const { buildIndex = buildIndexDefault, ...rest } = options;
function indexes(): Promise<IndexWithLocale[]> {
return Promise.all(
loader.getPages().map(async (page) => {
async function initServer(loader: LoaderOutput<C>): Promise<SearchServer> {
const indexes = await Promise.all(
loader.getPages().map(async (page): Promise<IndexWithLocale> => {
const index = await buildIndex(page);
return {
...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) {
return flexsearchI18n({
indexes,
i18n: loader._i18n,
...rest,
});
async function getCurrentServer() {
const l = typeof loader === 'function' ? await loader() : loader;
let server = cache.get(l);
if (!server) {
server = initServer(l);
cache.set(l, server);
}
return await server;
}
return flexsearch({
indexes,
...rest,
return createEndpoint({
async export() {
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>;
}
/**
* 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>(
source: LoaderOutput<C>,
options?: Options<C>,
): SearchAPI<OramaQueryOptions>;
export function createFromSource<C extends LoaderConfig>(
source: LoaderOutput<C>,
loader: LoaderOutput<C> | (() => Awaitable<LoaderOutput<C>>),
options: Options<C> = {},
): SearchAPI<OramaQueryOptions> {
const { buildIndex = buildIndexDefault } = options;
const cache = new WeakMap<LoaderOutput<C>, Promise<SearchServer<OramaQueryOptions>>>();
if (source._i18n) {
return createI18nSearchAPI('advanced', {
async function initServer(loader: LoaderOutput<C>) {
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,
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(
initAdvancedSearch({
...options,
async indexes() {
const indexes = source.getPages().map(async (page) => {
const index = await buildIndex(page);
if (index.breadcrumbs) return index;
return { ...index, breadcrumbs: buildBreadcrumbs(source, page) };
});
async function getCurrentServer() {
const l = typeof loader === 'function' ? await loader() : loader;
let server = cache.get(l);
if (!server) {
server = initServer(l);
cache.set(l, server);
}
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> {