fumadocs-core 15.4.2 → 15.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,7 @@ function groupResults(hits) {
23
23
  }
24
24
  return grouped;
25
25
  }
26
- async function searchDocs(query, tag, locale, { indexName, onSearch, client }) {
26
+ async function searchDocs(query, { indexName, onSearch, client, locale, tag }) {
27
27
  if (query.length > 0) {
28
28
  const result = onSearch ? await onSearch(query, tag, locale) : await client.searchForHits({
29
29
  requests: [
@@ -5,17 +5,16 @@ function splitPath(path) {
5
5
  function joinPath(...paths) {
6
6
  const out = [];
7
7
  const parsed = paths.flatMap(splitPath);
8
- while (parsed.length > 0) {
9
- switch (parsed[0]) {
8
+ for (const seg of parsed) {
9
+ switch (seg) {
10
10
  case "..":
11
11
  out.pop();
12
12
  break;
13
13
  case ".":
14
14
  break;
15
15
  default:
16
- out.push(parsed[0]);
16
+ out.push(seg);
17
17
  }
18
- parsed.shift();
19
18
  }
20
19
  return out.join("/");
21
20
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Link
3
- } from "./chunk-FVY6EZ3N.js";
3
+ } from "./chunk-BBP7MIO4.js";
4
4
 
5
5
  // src/link.tsx
6
6
  import { forwardRef } from "react";
@@ -12,24 +12,22 @@ var FrameworkContext = createContext("FrameworkContext", {
12
12
  usePathname: notImplemented
13
13
  });
14
14
  function FrameworkProvider({
15
- children,
16
- ...props
15
+ Link: Link2,
16
+ useRouter: useRouter2,
17
+ useParams: useParams2,
18
+ usePathname: usePathname2,
19
+ Image: Image2,
20
+ children
17
21
  }) {
18
22
  const framework = React.useMemo(
19
23
  () => ({
20
- usePathname: props.usePathname,
21
- useRouter: props.useRouter,
22
- Link: props.Link,
23
- Image: props.Image,
24
- useParams: props.useParams
24
+ usePathname: usePathname2,
25
+ useRouter: useRouter2,
26
+ Link: Link2,
27
+ Image: Image2,
28
+ useParams: useParams2
25
29
  }),
26
- [
27
- props.Link,
28
- props.usePathname,
29
- props.useRouter,
30
- props.useParams,
31
- props.Image
32
- ]
30
+ [Link2, usePathname2, useRouter2, useParams2, Image2]
33
31
  );
34
32
  return /* @__PURE__ */ jsx(FrameworkContext.Provider, { value: framework, children });
35
33
  }
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import {
3
3
  Link
4
- } from "./chunk-NNKVN7WA.js";
4
+ } from "./chunk-5SU2O5AS.js";
5
5
  import {
6
6
  useParams
7
- } from "./chunk-FVY6EZ3N.js";
7
+ } from "./chunk-BBP7MIO4.js";
8
8
  import "./chunk-MLKGABMK.js";
9
9
 
10
10
  // src/dynamic-link.tsx
@@ -2,12 +2,12 @@ import "./chunk-MLKGABMK.js";
2
2
 
3
3
  // src/search/client/fetch.ts
4
4
  var cache = /* @__PURE__ */ new Map();
5
- async function fetchDocs(query, locale, tag, options) {
5
+ async function fetchDocs(query, { api = "/api/search", locale, tag }) {
6
6
  const params = new URLSearchParams();
7
7
  params.set("query", query);
8
8
  if (locale) params.set("locale", locale);
9
9
  if (tag) params.set("tag", tag);
10
- const key = `${options.api ?? "/api/search"}?${params}`;
10
+ const key = `${api}?${params}`;
11
11
  const cached = cache.get(key);
12
12
  if (cached) return cached;
13
13
  const res = await fetch(key);
@@ -29,7 +29,7 @@ interface Framework {
29
29
  }>;
30
30
  Image?: FC<ImageProps>;
31
31
  }
32
- declare function FrameworkProvider({ children, ...props }: Framework & {
32
+ declare function FrameworkProvider({ Link, useRouter, useParams, usePathname, Image, children, }: Framework & {
33
33
  children: ReactNode;
34
34
  }): react_jsx_runtime.JSX.Element;
35
35
  declare function usePathname(): string;
@@ -7,7 +7,7 @@ import {
7
7
  useParams,
8
8
  usePathname,
9
9
  useRouter
10
- } from "../chunk-FVY6EZ3N.js";
10
+ } from "../chunk-BBP7MIO4.js";
11
11
  import "../chunk-MLKGABMK.js";
12
12
  export {
13
13
  FrameworkProvider,
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  FrameworkProvider
4
- } from "../chunk-FVY6EZ3N.js";
4
+ } from "../chunk-BBP7MIO4.js";
5
5
  import "../chunk-MLKGABMK.js";
6
6
 
7
7
  // src/framework/next.tsx
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FrameworkProvider
3
- } from "../chunk-FVY6EZ3N.js";
3
+ } from "../chunk-BBP7MIO4.js";
4
4
  import "../chunk-MLKGABMK.js";
5
5
 
6
6
  // src/framework/react-router.tsx
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FrameworkProvider
3
- } from "../chunk-FVY6EZ3N.js";
3
+ } from "../chunk-BBP7MIO4.js";
4
4
  import "../chunk-MLKGABMK.js";
5
5
 
6
6
  // src/framework/tanstack.tsx
@@ -0,0 +1,14 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import react__default from 'react';
3
+
4
+ /**
5
+ * The built-in CSS `:empty` selector cannot detect if the children is hidden, classes such as `md:hidden` causes it to fail.
6
+ * This component is an enhancement to `empty:hidden` to fix the issue described above.
7
+ *
8
+ * This can be expensive, please avoid this whenever possible.
9
+ */
10
+ declare function HideIfEmpty({ children }: {
11
+ children: react__default.ReactNode;
12
+ }): react_jsx_runtime.JSX.Element;
13
+
14
+ export { HideIfEmpty };
@@ -0,0 +1,64 @@
1
+ "use client";
2
+ import "./chunk-MLKGABMK.js";
3
+
4
+ // src/hide-if-empty.tsx
5
+ import React from "react";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ var isEmpty = (node) => {
8
+ for (let i = 0; i < node.childNodes.length; i++) {
9
+ const child = node.childNodes.item(i);
10
+ if (child.nodeType === Node.TEXT_NODE || child.nodeType === Node.ELEMENT_NODE && window.getComputedStyle(child).display !== "none") {
11
+ return false;
12
+ }
13
+ }
14
+ return true;
15
+ };
16
+ function HideIfEmpty({ children }) {
17
+ const id = React.useId();
18
+ const [empty, setEmpty] = React.useState();
19
+ React.useEffect(() => {
20
+ const element = document.querySelector(
21
+ `[data-fdid="${id}"]`
22
+ );
23
+ if (!element) return;
24
+ const onUpdate = () => {
25
+ setEmpty(isEmpty(element));
26
+ };
27
+ const observer = new ResizeObserver(onUpdate);
28
+ observer.observe(element);
29
+ onUpdate();
30
+ return () => {
31
+ observer.disconnect();
32
+ };
33
+ }, [id]);
34
+ let child;
35
+ if (React.isValidElement(children)) {
36
+ child = React.cloneElement(children, {
37
+ ...children.props,
38
+ "data-fdid": id,
39
+ "data-empty": empty,
40
+ suppressHydrationWarning: true
41
+ });
42
+ } else {
43
+ child = React.Children.count(children) > 1 ? React.Children.only(null) : null;
44
+ }
45
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
46
+ child,
47
+ empty === void 0 && /* @__PURE__ */ jsx(
48
+ "script",
49
+ {
50
+ suppressHydrationWarning: true,
51
+ dangerouslySetInnerHTML: {
52
+ __html: `{
53
+ const element = document.querySelector('[data-fdid="${id}"]')
54
+ if (element) {
55
+ element.setAttribute('data-empty', String((${isEmpty.toString()})(element)))
56
+ }}`
57
+ }
58
+ }
59
+ )
60
+ ] });
61
+ }
62
+ export {
63
+ HideIfEmpty
64
+ };
@@ -1,4 +1,3 @@
1
- import { I as I18nConfig } from '../config-Cm58P4fz.js';
2
1
  import { NextMiddleware } from 'next/dist/server/web/types';
3
2
 
4
3
  interface MiddlewareOptions extends I18nConfig {
@@ -9,4 +8,35 @@ interface MiddlewareOptions extends I18nConfig {
9
8
  }
10
9
  declare function createI18nMiddleware({ languages, defaultLanguage, format, hideLocale, }: MiddlewareOptions): NextMiddleware;
11
10
 
12
- export { I18nConfig, createI18nMiddleware };
11
+ interface I18nConfig {
12
+ /**
13
+ * Supported locale codes.
14
+ *
15
+ * A page tree will be built for each language.
16
+ */
17
+ languages: string[];
18
+ /**
19
+ * Default locale if not specified
20
+ */
21
+ defaultLanguage: string;
22
+ /**
23
+ * Don't show the locale prefix on URL.
24
+ *
25
+ * - `always`: Always hide the prefix
26
+ * - `default-locale`: Only hide the default locale
27
+ * - `never`: Never hide the prefix
28
+ *
29
+ * This API uses `NextResponse.rewrite`.
30
+ *
31
+ * @defaultValue 'never'
32
+ */
33
+ hideLocale?: 'always' | 'default-locale' | 'never';
34
+ /**
35
+ * Used by `loader()`, specify the way to parse i18n file structure.
36
+ *
37
+ * @defaultValue 'dot'
38
+ */
39
+ parser?: 'dot' | 'dir';
40
+ }
41
+
42
+ export { type I18nConfig, createI18nMiddleware };
package/dist/link.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import {
3
3
  Link
4
- } from "./chunk-NNKVN7WA.js";
5
- import "./chunk-FVY6EZ3N.js";
4
+ } from "./chunk-5SU2O5AS.js";
5
+ import "./chunk-BBP7MIO4.js";
6
6
  import "./chunk-MLKGABMK.js";
7
7
  export {
8
8
  Link as default
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  joinPath,
7
7
  slash
8
- } from "../chunk-XMCPKVJQ.js";
8
+ } from "../chunk-3JSIVMCJ.js";
9
9
  import {
10
10
  defaultThemes,
11
11
  getHighlighter
@@ -4,9 +4,9 @@ import {
4
4
  import "./chunk-MLKGABMK.js";
5
5
 
6
6
  // src/search/client/orama-cloud.ts
7
- async function searchDocs(query, tag, options) {
7
+ async function searchDocs(query, options) {
8
8
  const list = [];
9
- const { index = "default", client, params: extraParams = {} } = options;
9
+ const { index = "default", client, params: extraParams = {}, tag } = options;
10
10
  if (index === "crawler") {
11
11
  const result2 = await client.search({
12
12
  ...extraParams,
@@ -12,21 +12,46 @@ import 'mdast-util-mdx-jsx';
12
12
  interface FetchOptions {
13
13
  /**
14
14
  * API route for search endpoint
15
+ *
16
+ * @defaultValue '/api/search'
15
17
  */
16
18
  api?: string;
19
+ /**
20
+ * Filter results with specific tag.
21
+ */
22
+ tag?: string;
23
+ /**
24
+ * Filter by locale
25
+ */
26
+ locale?: string;
17
27
  }
18
28
 
19
29
  interface StaticOptions {
20
30
  /**
21
31
  * Where to download exported search indexes (URL)
32
+ *
33
+ * @defaultValue '/api/search'
22
34
  */
23
35
  from?: string;
24
36
  initOrama?: (locale?: string) => AnyOrama | Promise<AnyOrama>;
37
+ /**
38
+ * Filter results with specific tag.
39
+ */
40
+ tag?: string;
41
+ /**
42
+ * Filter by locale (unsupported at the moment)
43
+ */
44
+ locale?: string;
25
45
  }
26
46
 
27
47
  interface AlgoliaOptions {
28
48
  indexName: string;
29
49
  client: LiteClient;
50
+ /**
51
+ * Filter results with specific tag.
52
+ */
53
+ tag?: string;
54
+ locale?: string;
30
55
  onSearch?: (query: string, tag?: string, locale?: string) => Promise<{
31
56
  results: SearchResponse<BaseIndex>[];
32
57
  }>;
@@ -41,6 +66,14 @@ interface OramaCloudOptions {
41
66
  */
42
67
  index?: 'default' | 'crawler';
43
68
  params?: ClientSearchParams;
69
+ /**
70
+ * Filter results with specific tag.
71
+ */
72
+ tag?: string;
73
+ /**
74
+ * Filter by locale (unsupported at the moment)
75
+ */
76
+ locale?: string;
44
77
  }
45
78
 
46
79
  interface UseDocsSearch {
@@ -62,13 +95,43 @@ type Client = ({
62
95
  type: 'orama-cloud';
63
96
  } & OramaCloudOptions);
64
97
  /**
65
- * @param client - search client
66
- * @param locale - Filter with locale
67
- * @param tag - Filter with specific tag
68
- * @param delayMs - The debounced delay for performing a search.
69
- * @param allowEmpty - still perform search even if query is empty
70
- * @param key - cache key
98
+ * Provide a hook to query different official search clients.
99
+ *
100
+ * Note: it will re-query when its parameters changed, make sure to use `useCallback()` on functions passed to this hook.
101
+ */
102
+ declare function useDocsSearch(clientOptions: Client & {
103
+ /**
104
+ * The debounced delay for performing a search (in ms).
105
+ * .
106
+ * @defaultValue 100
107
+ */
108
+ delayMs?: number;
109
+ /**
110
+ * still perform search even if query is empty.
111
+ *
112
+ * @defaultValue false
113
+ */
114
+ allowEmpty?: boolean;
115
+ },
116
+ /**
117
+ * @deprecated pass to `client` object instead
118
+ */
119
+ _locale?: string,
120
+ /**
121
+ * @deprecated pass to `client` object instead
122
+ */
123
+ _tag?: string,
124
+ /**
125
+ * @deprecated pass to `client` object instead
126
+ */
127
+ _delayMs?: number,
128
+ /**
129
+ * @deprecated pass to `client` object instead
130
+ */
131
+ _allowEmpty?: boolean,
132
+ /**
133
+ * @deprecated No longer used
71
134
  */
72
- declare function useDocsSearch(client: Client, locale?: string, tag?: string, delayMs?: number, allowEmpty?: boolean, key?: string): UseDocsSearch;
135
+ _key?: string): UseDocsSearch;
73
136
 
74
137
  export { type AlgoliaOptions, type Client, type FetchOptions, type OramaCloudOptions, type StaticOptions, useDocsSearch };
@@ -23,7 +23,6 @@ function useDebounce(value, delayMs = 1e3) {
23
23
  }
24
24
 
25
25
  // src/search/client.ts
26
- var staticClient;
27
26
  function isDifferentDeep(a, b) {
28
27
  if (Array.isArray(a) && Array.isArray(b)) {
29
28
  return b.length !== a.length || a.some((v, i) => isDifferentDeep(v, b[i]));
@@ -37,7 +36,14 @@ function isDifferentDeep(a, b) {
37
36
  }
38
37
  return a !== b;
39
38
  }
40
- function useDocsSearch(client, locale, tag, delayMs = 100, allowEmpty = false, key) {
39
+ function useDocsSearch(clientOptions, _locale, _tag, _delayMs = 100, _allowEmpty = false, _key) {
40
+ const {
41
+ delayMs = _delayMs ?? 100,
42
+ allowEmpty = _allowEmpty ?? false,
43
+ ...client
44
+ } = clientOptions;
45
+ client.tag ??= _tag;
46
+ client.locale ??= _locale;
41
47
  const [search, setSearch] = useState2("");
42
48
  const [results, setResults] = useState2("empty");
43
49
  const [error, setError] = useState2();
@@ -45,7 +51,7 @@ function useDocsSearch(client, locale, tag, delayMs = 100, allowEmpty = false, k
45
51
  const debouncedValue = useDebounce(search, delayMs);
46
52
  const onStart = useRef2(void 0);
47
53
  useOnChange(
48
- key ?? [client, debouncedValue, locale, tag],
54
+ [client, debouncedValue],
49
55
  () => {
50
56
  if (onStart.current) {
51
57
  onStart.current();
@@ -59,20 +65,22 @@ function useDocsSearch(client, locale, tag, delayMs = 100, allowEmpty = false, k
59
65
  async function run() {
60
66
  if (debouncedValue.length === 0 && !allowEmpty) return "empty";
61
67
  if (client.type === "fetch") {
62
- const { fetchDocs } = await import("../fetch-W5EHIBOE.js");
63
- return fetchDocs(debouncedValue, locale, tag, client);
68
+ const { fetchDocs } = await import("../fetch-M245YYDD.js");
69
+ return fetchDocs(debouncedValue, client);
64
70
  }
65
71
  if (client.type === "algolia") {
66
- const { searchDocs } = await import("../algolia-RQ5VQJAB.js");
67
- return searchDocs(debouncedValue, tag, locale, client);
72
+ const { searchDocs } = await import("../algolia-KJKVXZ5Q.js");
73
+ return searchDocs(debouncedValue, client);
68
74
  }
69
75
  if (client.type === "orama-cloud") {
70
- const { searchDocs } = await import("../orama-cloud-USLSOSXS.js");
71
- return searchDocs(debouncedValue, tag, client);
76
+ const { searchDocs } = await import("../orama-cloud-SBXZAVQC.js");
77
+ return searchDocs(debouncedValue, client);
72
78
  }
73
- const { createStaticClient } = await import("../static-VESU2S64.js");
74
- if (!staticClient) staticClient = createStaticClient(client);
75
- return staticClient.search(debouncedValue, locale, tag);
79
+ if (client.type === "static") {
80
+ const { search: search2 } = await import("../static-IM4OAJFY.js");
81
+ return search2(debouncedValue, client);
82
+ }
83
+ throw new Error("unknown search client");
76
84
  }
77
85
  void run().then((res) => {
78
86
  if (interrupt) return;
@@ -1,11 +1,12 @@
1
1
  import { TypedDocument, Orama, Language, RawData, create, SearchParams } from '@orama/orama';
2
2
  import { S as StructuredData } from '../remark-structure-DVje0Sib.js';
3
3
  import { S as SortedResult } from '../types-Ch8gnVgO.js';
4
- import { I as I18nConfig } from '../config-Cm58P4fz.js';
4
+ import { I18nConfig } from '../i18n/index.js';
5
5
  import { LoaderOutput, LoaderConfig, InferPageType } from '../source/index.js';
6
6
  import 'mdast';
7
7
  import 'unified';
8
8
  import 'mdast-util-mdx-jsx';
9
+ import 'next/dist/server/web/types';
9
10
  import 'react';
10
11
  import '../page-tree-bSt6K__E.js';
11
12
 
@@ -8,7 +8,8 @@ import { LoaderOutput, LoaderConfig, InferPageType } from '../source/index.js';
8
8
  import 'react';
9
9
  import 'unified';
10
10
  import 'vfile';
11
- import '../config-Cm58P4fz.js';
11
+ import '../i18n/index.js';
12
+ import 'next/dist/server/web/types';
12
13
 
13
14
  /**
14
15
  * Flatten tree to an array of page nodes
@@ -1,6 +1,7 @@
1
1
  import { ReactElement } from 'react';
2
- import { I as I18nConfig } from '../config-Cm58P4fz.js';
2
+ import { I18nConfig } from '../i18n/index.js';
3
3
  import { R as Root, I as Item, F as Folder$1, S as Separator } from '../page-tree-bSt6K__E.js';
4
+ import 'next/dist/server/web/types';
4
5
 
5
6
  interface FileInfo {
6
7
  /**
@@ -153,6 +154,9 @@ interface LoaderOutput<Config extends LoaderConfig> {
153
154
  generateParams: <TSlug extends string = 'slug', TLang extends string = 'lang'>(slug?: TSlug, lang?: TLang) => (Record<TSlug, string[]> & Record<TLang, string>)[];
154
155
  }
155
156
  declare function createGetUrl(baseUrl: string, i18n?: I18nConfig): UrlFn;
157
+ /**
158
+ * Convert file path into slugs, also encode non-ASCII characters, so they can work in pathname
159
+ */
156
160
  declare function getSlugs(info: FileInfo): string[];
157
161
  declare function loader<Config extends SourceConfig, I18n extends I18nConfig | undefined = undefined>(options: LoaderOptions<Config, I18n>): LoaderOutput<{
158
162
  source: Config;
@@ -2,7 +2,7 @@ import {
2
2
  joinPath,
3
3
  slash,
4
4
  splitPath
5
- } from "../chunk-XMCPKVJQ.js";
5
+ } from "../chunk-3JSIVMCJ.js";
6
6
  import {
7
7
  __export
8
8
  } from "../chunk-MLKGABMK.js";
@@ -142,25 +142,27 @@ function buildFolderNode(folder, isGlobalRoot, ctx) {
142
142
  index,
143
143
  children,
144
144
  $id: folder.file.path,
145
- $ref: !options.noRef ? {
146
- metaFile: meta?.file.path
145
+ $ref: !options.noRef && meta ? {
146
+ metaFile: metaPath
147
147
  } : void 0
148
148
  };
149
149
  return options.attachFolder?.(node, folder, meta) ?? node;
150
150
  }
151
- function buildFileNode(file, { options, getUrl, locale }) {
151
+ function buildFileNode(page, { options, getUrl, locale }) {
152
+ const file = page.file;
153
+ const { slugs, data } = page.data;
152
154
  const item = {
153
- $id: file.file.path,
155
+ $id: file.path,
154
156
  type: "page",
155
- name: file.data.data.title ?? pathToName(file.file.name),
156
- description: file.data.data.description,
157
- icon: options.resolveIcon?.(file.data.data.icon),
158
- url: getUrl(file.data.slugs, locale),
157
+ name: data.title ?? pathToName(file.name),
158
+ description: data.description,
159
+ icon: options.resolveIcon?.(data.icon),
160
+ url: getUrl(slugs, locale),
159
161
  $ref: !options.noRef ? {
160
- file: file.file.path
162
+ file: joinPath(file.dirname, file.name)
161
163
  } : void 0
162
164
  };
163
- return options.attachFile?.(item, file) ?? item;
165
+ return options.attachFile?.(item, page) ?? item;
164
166
  }
165
167
  function build(ctx) {
166
168
  const root = ctx.storage.root();
@@ -416,13 +418,14 @@ function createGetUrl(baseUrl, i18n) {
416
418
  };
417
419
  }
418
420
  function getSlugs(info) {
419
- return [...info.dirname.split("/"), info.name].filter(
420
- // filter empty folder names and file groups like (group_name)
421
- (v, i, arr) => {
422
- if (v.length === 0) return false;
423
- return i === arr.length - 1 ? v !== "index" : !/^\(.+\)$/.test(v);
424
- }
425
- );
421
+ const slugs = [];
422
+ for (const seg of info.dirname.split("/")) {
423
+ if (seg.length > 0 && !/^\(.+\)$/.test(seg)) slugs.push(encodeURI(seg));
424
+ }
425
+ if (info.name !== "index") {
426
+ slugs.push(encodeURI(info.name));
427
+ }
428
+ return slugs;
426
429
  }
427
430
  function loader(options) {
428
431
  return createOutput(options);
@@ -491,8 +494,8 @@ function createOutput(options) {
491
494
  },
492
495
  getPages(language = defaultLanguage) {
493
496
  const pages = [];
494
- for (const key of walker.pages.keys()) {
495
- if (key.startsWith(`${language}.`)) pages.push(walker.pages.get(key));
497
+ for (const [key, value] of walker.pages.entries()) {
498
+ if (key.startsWith(`${language}.`)) pages.push(value);
496
499
  }
497
500
  return pages;
498
501
  },
@@ -513,13 +516,13 @@ function createOutput(options) {
513
516
  getNodeMeta(node, language = defaultLanguage) {
514
517
  const ref = node.$ref?.metaFile;
515
518
  if (!ref) return;
516
- const file = storages[language].list().find((v) => v.format === "meta" && v.file.path === ref);
519
+ const file = storages[language].read(ref, "meta");
517
520
  if (file) return walker.getResultFromFile(file);
518
521
  },
519
522
  getNodePage(node, language = defaultLanguage) {
520
523
  const ref = node.$ref?.file;
521
524
  if (!ref) return;
522
- const file = storages[language].list().find((v) => v.format === "page" && v.file.path === ref);
525
+ const file = storages[language].read(ref, "page");
523
526
  if (file) return walker.getResultFromFile(file);
524
527
  },
525
528
  getPageTree(locale) {
@@ -0,0 +1,61 @@
1
+ import {
2
+ searchAdvanced,
3
+ searchSimple
4
+ } from "./chunk-WFUH5VBX.js";
5
+ import "./chunk-KAOEMCTI.js";
6
+ import "./chunk-MLKGABMK.js";
7
+
8
+ // src/search/client/static.ts
9
+ import { create, load } from "@orama/orama";
10
+ var cache = /* @__PURE__ */ new Map();
11
+ async function loadDB({
12
+ from = "/api/search",
13
+ initOrama = (locale) => create({ schema: { _: "string" }, language: locale })
14
+ }) {
15
+ const cacheKey = from;
16
+ const cached = cache.get(cacheKey);
17
+ if (cached) return cached;
18
+ async function init() {
19
+ const res = await fetch(from);
20
+ if (!res.ok)
21
+ throw new Error(
22
+ `failed to fetch exported search indexes from ${from}, make sure the search database is exported and available for client.`
23
+ );
24
+ const data = await res.json();
25
+ const dbs = /* @__PURE__ */ new Map();
26
+ if (data.type === "i18n") {
27
+ await Promise.all(
28
+ Object.entries(data.data).map(async ([k, v]) => {
29
+ const db2 = await initOrama(k);
30
+ load(db2, v);
31
+ dbs.set(k, {
32
+ type: v.type,
33
+ db: db2
34
+ });
35
+ })
36
+ );
37
+ return dbs;
38
+ }
39
+ const db = await initOrama();
40
+ load(db, data);
41
+ dbs.set("", {
42
+ type: data.type,
43
+ db
44
+ });
45
+ return dbs;
46
+ }
47
+ const result = init();
48
+ cache.set(cacheKey, result);
49
+ return result;
50
+ }
51
+ async function search(query, options) {
52
+ const { tag, locale } = options;
53
+ const db = (await loadDB(options)).get(locale ?? "");
54
+ if (!db) return [];
55
+ if (db.type === "simple")
56
+ return searchSimple(db, query);
57
+ return searchAdvanced(db.db, query, tag);
58
+ }
59
+ export {
60
+ search
61
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "15.4.2",
3
+ "version": "15.5.1",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -28,6 +28,10 @@
28
28
  "import": "./dist/content/index.js",
29
29
  "types": "./dist/content/index.d.ts"
30
30
  },
31
+ "./hide-if-empty": {
32
+ "import": "./dist/hide-if-empty.js",
33
+ "types": "./dist/hide-if-empty.d.ts"
34
+ },
31
35
  "./search/*": {
32
36
  "import": "./dist/search/*.js",
33
37
  "types": "./dist/search/*.d.ts"
@@ -101,18 +105,18 @@
101
105
  "devDependencies": {
102
106
  "@mdx-js/mdx": "^3.1.0",
103
107
  "@oramacloud/client": "^2.1.4",
104
- "@tanstack/react-router": "^1.120.11",
108
+ "@tanstack/react-router": "^1.120.12",
105
109
  "@types/estree-jsx": "^1.0.5",
106
110
  "@types/hast": "^3.0.4",
107
111
  "@types/mdast": "^4.0.3",
108
112
  "@types/negotiator": "^0.6.3",
109
- "@types/node": "22.15.21",
110
- "@types/react": "^19.1.5",
113
+ "@types/node": "22.15.28",
114
+ "@types/react": "^19.1.6",
111
115
  "@types/react-dom": "^19.1.5",
112
116
  "algoliasearch": "5.25.0",
113
117
  "mdast-util-mdx-jsx": "^3.2.0",
114
118
  "mdast-util-mdxjs-esm": "^2.0.1",
115
- "next": "^15.3.2",
119
+ "next": "^15.3.3",
116
120
  "react-router": "^7.6.1",
117
121
  "remark-mdx": "^3.1.0",
118
122
  "typescript": "^5.8.3",
@@ -1,32 +0,0 @@
1
- interface I18nConfig {
2
- /**
3
- * Supported locale codes.
4
- *
5
- * A page tree will be built for each language.
6
- */
7
- languages: string[];
8
- /**
9
- * Default locale if not specified
10
- */
11
- defaultLanguage: string;
12
- /**
13
- * Don't show the locale prefix on URL.
14
- *
15
- * - `always`: Always hide the prefix
16
- * - `default-locale`: Only hide the default locale
17
- * - `never`: Never hide the prefix
18
- *
19
- * This API uses `NextResponse.rewrite`.
20
- *
21
- * @defaultValue 'never'
22
- */
23
- hideLocale?: 'always' | 'default-locale' | 'never';
24
- /**
25
- * Used by `loader()`, specify the way to parse i18n file structure.
26
- *
27
- * @defaultValue 'dot'
28
- */
29
- parser?: 'dot' | 'dir';
30
- }
31
-
32
- export type { I18nConfig as I };
@@ -1,61 +0,0 @@
1
- import {
2
- searchAdvanced,
3
- searchSimple
4
- } from "./chunk-WFUH5VBX.js";
5
- import "./chunk-KAOEMCTI.js";
6
- import "./chunk-MLKGABMK.js";
7
-
8
- // src/search/client/static.ts
9
- import { create, load } from "@orama/orama";
10
- function createStaticClient({
11
- from = "/api/search",
12
- initOrama = (locale) => create({ schema: { _: "string" }, language: locale })
13
- }) {
14
- const dbs = /* @__PURE__ */ new Map();
15
- async function init() {
16
- const res = await fetch(from);
17
- if (!res.ok)
18
- throw new Error(
19
- `failed to fetch exported search indexes from ${from}, make sure the search database is exported and available for client.`
20
- );
21
- const data = await res.json();
22
- if (data.type === "i18n") {
23
- for (const [k, v] of Object.entries(data.data)) {
24
- const db = await initOrama(k);
25
- load(db, v);
26
- dbs.set(k, {
27
- type: v.type,
28
- db
29
- });
30
- }
31
- } else {
32
- const db = await initOrama();
33
- load(db, data);
34
- dbs.set("", {
35
- type: data.type,
36
- db
37
- });
38
- }
39
- }
40
- const get = init();
41
- return {
42
- async search(query, locale, tag) {
43
- await get;
44
- const cached = dbs.get(locale ?? "");
45
- if (!cached) return [];
46
- if (cached.type === "simple")
47
- return searchSimple(
48
- cached,
49
- query
50
- );
51
- return searchAdvanced(
52
- cached.db,
53
- query,
54
- tag
55
- );
56
- }
57
- };
58
- }
59
- export {
60
- createStaticClient
61
- };