fumadocs-core 15.3.4 → 15.4.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,25 +23,25 @@ function groupResults(hits) {
23
23
  }
24
24
  return grouped;
25
25
  }
26
- async function searchDocs(index, query, tag, options) {
27
- let filters = options?.filters;
28
- if (tag) filters = filters ? `tag:${tag} AND (${filters})` : `tag:${tag}`;
29
- if (query.length === 0) {
30
- const result2 = await index.search(query, {
31
- distinct: 1,
32
- hitsPerPage: 8,
33
- ...options,
34
- filters
26
+ async function searchDocs(query, tag, locale, { indexName, onSearch, client }) {
27
+ if (query.length > 0) {
28
+ const result = onSearch ? await onSearch(query, tag, locale) : await client.searchForHits({
29
+ requests: [
30
+ {
31
+ type: "default",
32
+ indexName,
33
+ query,
34
+ distinct: 5,
35
+ hitsPerPage: 10,
36
+ filters: tag ? `tag:${tag}` : void 0
37
+ }
38
+ ]
35
39
  });
36
- return groupResults(result2.hits).filter((hit) => hit.type === "page");
40
+ return groupResults(result.results[0].hits).filter(
41
+ (hit) => hit.type === "page"
42
+ );
37
43
  }
38
- const result = await index.search(query, {
39
- distinct: 5,
40
- hitsPerPage: 10,
41
- ...options,
42
- filters
43
- });
44
- return groupResults(result.hits);
44
+ return [];
45
45
  }
46
46
  export {
47
47
  groupResults,
@@ -0,0 +1,16 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { VFile } from 'vfile';
4
+ import { Components } from 'hast-util-to-jsx-runtime';
5
+ import { PluggableList } from 'unified';
6
+
7
+ interface MarkdownProps {
8
+ components?: Components;
9
+ }
10
+ declare function Markdown({ children: content, remarkPlugins, rehypePlugins, ...options }: MarkdownProps & {
11
+ remarkPlugins?: PluggableList;
12
+ rehypePlugins?: PluggableList;
13
+ children: string | VFile;
14
+ }): Promise<react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | (string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined)>;
15
+
16
+ export { Markdown };
@@ -0,0 +1,30 @@
1
+ import "../chunk-MLKGABMK.js";
2
+
3
+ // src/content/index.ts
4
+ import { remark } from "remark";
5
+ import remarkGfm from "remark-gfm";
6
+ import remarkRehype from "remark-rehype";
7
+ import { toJsxRuntime } from "hast-util-to-jsx-runtime";
8
+ import * as JsxRuntime from "react/jsx-runtime";
9
+ function rehypeReact(options = {}) {
10
+ this.compiler = (tree, file) => {
11
+ return toJsxRuntime(tree, {
12
+ development: false,
13
+ filePath: file.path,
14
+ ...JsxRuntime,
15
+ ...options
16
+ });
17
+ };
18
+ }
19
+ async function Markdown({
20
+ children: content,
21
+ remarkPlugins = [],
22
+ rehypePlugins = [],
23
+ ...options
24
+ }) {
25
+ const processor = remark().use(remarkGfm).use(remarkPlugins).use(remarkRehype).use(rehypePlugins).use(rehypeReact, options);
26
+ return (await processor.process(content)).result;
27
+ }
28
+ export {
29
+ Markdown
30
+ };
@@ -4,9 +4,12 @@ import 'shiki';
4
4
  import 'shiki/themes';
5
5
  import 'hast-util-to-jsx-runtime';
6
6
 
7
- declare function useShiki(code: string, { defaultValue, withPrerenderScript, ...options }: HighlightOptions & {
7
+ declare function useShiki(code: string, { withPrerenderScript, loading, ...options }: HighlightOptions & {
8
8
  withPrerenderScript?: boolean;
9
- defaultValue?: ReactNode;
9
+ /**
10
+ * Displayed before highlighter is loaded.
11
+ */
12
+ loading?: ReactNode;
10
13
  }, deps?: DependencyList): ReactNode;
11
14
 
12
15
  export { useShiki };
@@ -8,30 +8,28 @@ import "../chunk-MLKGABMK.js";
8
8
 
9
9
  // src/highlight/client.tsx
10
10
  import {
11
+ use,
11
12
  useId,
13
+ useLayoutEffect,
12
14
  useMemo,
13
15
  useRef,
14
- use,
15
- useState,
16
- useLayoutEffect
16
+ useState
17
17
  } from "react";
18
18
  import { jsx } from "react/jsx-runtime";
19
19
  var jsEngine;
20
20
  function getHighlightOptions(from) {
21
21
  if (from.engine) return from;
22
- if (!jsEngine) {
23
- jsEngine = import("shiki/engine/javascript").then(
24
- (res) => res.createJavaScriptRegexEngine()
25
- );
26
- }
22
+ jsEngine ??= import("shiki/engine/javascript").then(
23
+ (res) => res.createJavaScriptRegexEngine()
24
+ );
27
25
  return {
28
26
  ...from,
29
27
  engine: jsEngine
30
28
  };
31
29
  }
32
30
  function useShiki(code, {
33
- defaultValue,
34
31
  withPrerenderScript = false,
32
+ loading,
35
33
  ...options
36
34
  }, deps) {
37
35
  const markupId = useId();
@@ -45,7 +43,6 @@ function useShiki(code, {
45
43
  aborted: false
46
44
  });
47
45
  const [rendered, setRendered] = useState(() => {
48
- if (defaultValue) return defaultValue;
49
46
  const element = withPrerenderScript && typeof document !== "undefined" ? document.querySelector(`[data-markup-id="${markupId}"]`) : null;
50
47
  const attr = element?.getAttribute("data-markup");
51
48
  if (attr) {
@@ -53,9 +50,7 @@ function useShiki(code, {
53
50
  return renderHighlightWithMarkup(markupId, hast, shikiOptions, attr);
54
51
  }
55
52
  currentTask.current = void 0;
56
- const Pre = options.components?.pre ?? "pre";
57
- const Code = options.components?.code ?? "code";
58
- return /* @__PURE__ */ jsx(Pre, { children: /* @__PURE__ */ jsx(Code, { children: code }) });
53
+ return loading;
59
54
  });
60
55
  useLayoutEffect(() => {
61
56
  if (currentTask.current?.key === key) return;
@@ -1,4 +1,4 @@
1
- import { SearchClient, SearchIndex } from 'algoliasearch';
1
+ import { Algoliasearch } from 'algoliasearch';
2
2
  import { S as StructuredData } from '../remark-structure-DVje0Sib.js';
3
3
  import 'mdast';
4
4
  import 'unified';
@@ -27,9 +27,15 @@ interface DocumentRecord {
27
27
  }
28
28
  interface SyncOptions {
29
29
  /**
30
- * Index Name for documents
30
+ * Index Name for documents.
31
+ *
32
+ * @deprecated Use `indexName` instead
31
33
  */
32
34
  document?: string;
35
+ /**
36
+ * Index Name for documents.
37
+ */
38
+ indexName?: string;
33
39
  /**
34
40
  * Search indexes
35
41
  */
@@ -41,9 +47,9 @@ interface SyncOptions {
41
47
  * @param client - Algolia Admin Client
42
48
  * @param options - Index Options
43
49
  */
44
- declare function sync(client: SearchClient, options: SyncOptions): Promise<void>;
45
- declare function setIndexSettings(index: SearchIndex): Promise<void>;
46
- declare function updateDocuments(index: SearchIndex, documents: DocumentRecord[]): Promise<void>;
50
+ declare function sync(client: Algoliasearch, options: SyncOptions): Promise<void>;
51
+ declare function setIndexSettings(client: Algoliasearch, indexName: string): Promise<void>;
52
+ declare function updateDocuments(client: Algoliasearch, indexName: string, documents: DocumentRecord[]): Promise<void>;
47
53
  interface BaseIndex {
48
54
  objectID: string;
49
55
  title: string;
@@ -2,18 +2,26 @@ import "../chunk-MLKGABMK.js";
2
2
 
3
3
  // src/search/algolia.ts
4
4
  async function sync(client, options) {
5
- const { document = "document", documents } = options;
6
- const index = client.initIndex(document);
7
- await setIndexSettings(index);
8
- await updateDocuments(index, documents);
5
+ const { document = "document", indexName = document, documents } = options;
6
+ await setIndexSettings(client, indexName);
7
+ await updateDocuments(client, indexName, documents);
9
8
  }
10
- async function setIndexSettings(index) {
11
- await index.setSettings({
12
- attributeForDistinct: "page_id",
13
- attributesToRetrieve: ["title", "section", "content", "url", "section_id"],
14
- searchableAttributes: ["title", "section", "content"],
15
- attributesToSnippet: [],
16
- attributesForFaceting: ["tag"]
9
+ async function setIndexSettings(client, indexName) {
10
+ await client.setSettings({
11
+ indexName,
12
+ indexSettings: {
13
+ attributeForDistinct: "page_id",
14
+ attributesToRetrieve: [
15
+ "title",
16
+ "section",
17
+ "content",
18
+ "url",
19
+ "section_id"
20
+ ],
21
+ searchableAttributes: ["title", "section", "content"],
22
+ attributesToSnippet: [],
23
+ attributesForFaceting: ["tag"]
24
+ }
17
25
  });
18
26
  }
19
27
  function toIndex(page) {
@@ -35,20 +43,24 @@ function toIndex(page) {
35
43
  }
36
44
  if (page.description)
37
45
  indexes.push(createIndex(void 0, void 0, page.description));
38
- page.structured.contents.forEach((p) => {
39
- const heading = p.heading ? page.structured.headings.find((h) => p.heading === h.id) : null;
46
+ const { headings, contents } = page.structured;
47
+ for (const p of contents) {
48
+ const heading = p.heading ? headings.find((h) => p.heading === h.id) : null;
40
49
  const index = createIndex(heading?.content, heading?.id, p.content);
41
50
  if (heading && !scannedHeadings.has(heading.id)) {
42
51
  scannedHeadings.add(heading.id);
43
52
  indexes.push(createIndex(heading.content, heading.id, heading.content));
44
53
  }
45
54
  indexes.push(index);
46
- });
55
+ }
47
56
  return indexes;
48
57
  }
49
- async function updateDocuments(index, documents) {
58
+ async function updateDocuments(client, indexName, documents) {
50
59
  const objects = documents.flatMap(toIndex);
51
- await index.replaceAllObjects(objects);
60
+ await client.replaceAllObjects({
61
+ indexName,
62
+ objects
63
+ });
52
64
  }
53
65
  export {
54
66
  setIndexSettings,
@@ -1,9 +1,10 @@
1
1
  import { S as SortedResult } from '../types-Ch8gnVgO.js';
2
2
  import { AnyOrama } from '@orama/orama';
3
- import { SearchOptions } from '@algolia/client-search';
4
- import { SearchIndex } from 'algoliasearch/lite';
5
- import '../remark-structure-DVje0Sib.js';
3
+ import { BaseIndex } from './algolia.js';
4
+ import { LiteClient, SearchResponse } from 'algoliasearch/lite';
6
5
  import { OramaClient, ClientSearchParams } from '@oramacloud/client';
6
+ import 'algoliasearch';
7
+ import '../remark-structure-DVje0Sib.js';
7
8
  import 'mdast';
8
9
  import 'unified';
9
10
  import 'mdast-util-mdx-jsx';
@@ -23,8 +24,12 @@ interface StaticOptions {
23
24
  initOrama?: (locale?: string) => AnyOrama | Promise<AnyOrama>;
24
25
  }
25
26
 
26
- interface AlgoliaOptions extends SearchOptions {
27
- index: SearchIndex;
27
+ interface AlgoliaOptions {
28
+ indexName: string;
29
+ client: LiteClient;
30
+ onSearch?: (query: string, tag?: string, locale?: string) => Promise<{
31
+ results: SearchResponse<BaseIndex>[];
32
+ }>;
28
33
  }
29
34
 
30
35
  interface OramaCloudOptions {
@@ -48,9 +48,8 @@ function useDocsSearch(client, locale, tag, delayMs = 100, allowEmpty = false, k
48
48
  return fetchDocs(debouncedValue, locale, tag, client);
49
49
  }
50
50
  if (client.type === "algolia") {
51
- const { index, type: _, ...rest } = client;
52
- const { searchDocs } = await import("../algolia-NTWLS6J3.js");
53
- return searchDocs(index, debouncedValue, tag, rest);
51
+ const { searchDocs } = await import("../algolia-RQ5VQJAB.js");
52
+ return searchDocs(debouncedValue, tag, locale, client);
54
53
  }
55
54
  if (client.type === "orama-cloud") {
56
55
  const { searchDocs } = await import("../orama-cloud-USLSOSXS.js");
@@ -32,8 +32,8 @@ function flattenTree(tree) {
32
32
  function findNeighbour(tree, url, options) {
33
33
  const { separateRoot = true } = options ?? {};
34
34
  const roots = separateRoot ? getPageTreeRoots(tree) : [tree];
35
- const lists = roots.map((node) => flattenTree(node.children));
36
- for (const list of lists) {
35
+ for (const root of roots) {
36
+ const list = flattenTree(root.children);
37
37
  for (let i = 0; i < list.length; i++) {
38
38
  if (list[i].url === url) {
39
39
  return {
@@ -119,6 +119,7 @@ interface LoaderOutput<Config extends LoaderConfig> {
119
119
  pageTree: Config['i18n'] extends true ? Record<string, Root> : Root;
120
120
  getPageTree: (locale?: string) => Root;
121
121
  getPageByHref: (href: string, options?: {
122
+ language?: string;
122
123
  /**
123
124
  * resolve relative file paths in `href` from specified dirname, must be a virtual path.
124
125
  */
@@ -18,42 +18,46 @@ var excludePrefix = "!";
18
18
  function isPageFile(node) {
19
19
  return "data" in node && node.format === "page";
20
20
  }
21
- function buildAll(nodes, ctx, skipIndex) {
21
+ function buildAll(nodes, ctx, filter, reversed = false) {
22
+ const { localeStorage } = ctx;
22
23
  const output = [];
23
- const folders = [];
24
- for (const node of [...nodes].sort(
25
- (a, b) => a.file.name.localeCompare(b.file.name)
26
- )) {
24
+ for (const node of [...nodes].sort((a, b) => {
25
+ let result;
26
+ if (isPageFile(a) && "children" in b) result = -1;
27
+ else if ("children" in a && isPageFile(b)) result = 1;
28
+ else result = a.file.name.localeCompare(b.file.name);
29
+ return reversed ? result * -1 : result;
30
+ })) {
31
+ if (filter && !filter(node)) continue;
27
32
  if (isPageFile(node)) {
28
- const localized = ctx.localeStorage?.read(
33
+ const localized = localeStorage?.read(
29
34
  joinPath(node.file.dirname, node.file.name),
30
35
  "page"
31
36
  );
32
37
  const treeNode = buildFileNode(localized ?? node, ctx);
33
38
  if (node.file.name === "index") {
34
- if (!skipIndex) output.unshift(treeNode);
35
- } else {
36
- output.push(treeNode);
39
+ output.unshift(treeNode);
40
+ continue;
37
41
  }
38
- }
39
- if ("children" in node) {
40
- folders.push(buildFolderNode(node, false, ctx));
42
+ output.push(treeNode);
43
+ } else if ("children" in node) {
44
+ output.push(buildFolderNode(node, false, ctx));
41
45
  }
42
46
  }
43
- output.push(...folders);
44
47
  return output;
45
48
  }
46
49
  function resolveFolderItem(folder, item, ctx, idx, addedNodePaths) {
47
50
  if (item === rest || item === restReversed) return item;
51
+ const { options, storage, localeStorage } = ctx;
48
52
  let match = separator.exec(item);
49
53
  if (match?.groups) {
50
54
  const node = {
51
55
  $id: `${folder.file.path}#${idx}`,
52
56
  type: "separator",
53
- icon: ctx.options.resolveIcon?.(match.groups.icon),
57
+ icon: options.resolveIcon?.(match.groups.icon),
54
58
  name: match.groups.name
55
59
  };
56
- return [ctx.options.attachSeparator?.(node) ?? node];
60
+ return [options.attachSeparator?.(node) ?? node];
57
61
  }
58
62
  match = link.exec(item);
59
63
  if (match?.groups) {
@@ -61,14 +65,15 @@ function resolveFolderItem(folder, item, ctx, idx, addedNodePaths) {
61
65
  const isRelative = url.startsWith("/") || url.startsWith("#") || url.startsWith(".");
62
66
  const node = {
63
67
  type: "page",
64
- icon: ctx.options.resolveIcon?.(icon),
68
+ icon: options.resolveIcon?.(icon),
65
69
  name,
66
70
  url,
67
71
  external: !isRelative
68
72
  };
69
- return [ctx.options.attachFile?.(node) ?? node];
73
+ return [options.attachFile?.(node) ?? node];
70
74
  }
71
- const isExcept = item.startsWith(excludePrefix), isExtract = item.startsWith(extractPrefix);
75
+ const isExcept = item.startsWith(excludePrefix);
76
+ const isExtract = !isExcept && item.startsWith(extractPrefix);
72
77
  let filename = item;
73
78
  if (isExcept) {
74
79
  filename = item.slice(excludePrefix.length);
@@ -76,7 +81,7 @@ function resolveFolderItem(folder, item, ctx, idx, addedNodePaths) {
76
81
  filename = item.slice(extractPrefix.length);
77
82
  }
78
83
  const path = joinPath(folder.file.path, filename);
79
- const itemNode = ctx.storage.readDir(path) ?? ctx.localeStorage?.read(path, "page") ?? ctx.storage.read(path, "page");
84
+ const itemNode = storage.readDir(path) ?? localeStorage?.read(path, "page") ?? storage.read(path, "page");
80
85
  if (!itemNode) return [];
81
86
  addedNodePaths.add(itemNode.file.path);
82
87
  if (isExcept) return [];
@@ -87,71 +92,81 @@ function resolveFolderItem(folder, item, ctx, idx, addedNodePaths) {
87
92
  return [buildFileNode(itemNode, ctx)];
88
93
  }
89
94
  function buildFolderNode(folder, isGlobalRoot, ctx) {
95
+ const { storage, localeStorage, options } = ctx;
90
96
  const metaPath = joinPath(folder.file.path, "meta");
91
- const meta = ctx.localeStorage?.read(metaPath, "meta") ?? ctx.storage.read(metaPath, "meta");
97
+ const meta = localeStorage?.read(metaPath, "meta") ?? storage.read(metaPath, "meta");
92
98
  const indexPath = joinPath(folder.file.path, "index");
93
- const indexFile = ctx.localeStorage?.read(indexPath, "page") ?? ctx.storage.read(indexPath, "page");
99
+ const indexFile = localeStorage?.read(indexPath, "page") ?? storage.read(indexPath, "page");
94
100
  const isRoot = meta?.data.root ?? isGlobalRoot;
95
- const index = indexFile ? buildFileNode(indexFile, ctx) : void 0;
96
- const addedNodePaths = /* @__PURE__ */ new Set();
101
+ let index;
97
102
  let children;
98
103
  if (!meta?.data.pages) {
99
- children = buildAll(folder.children, ctx, !isRoot);
100
- } else {
101
- const resolved = meta.data.pages.flatMap((item, i) => {
102
- return resolveFolderItem(folder, item, ctx, i, addedNodePaths);
104
+ if (indexFile) index = buildFileNode(indexFile, ctx);
105
+ children = buildAll(folder.children, ctx, (node2) => {
106
+ return node2.file.name !== "index" || isRoot;
103
107
  });
104
- const restNodes = buildAll(
105
- folder.children.filter((node2) => !addedNodePaths.has(node2.file.path)),
106
- ctx,
107
- !isRoot
108
- );
109
- const nodes = resolved?.flatMap((item) => {
110
- if (item === rest) {
111
- return restNodes;
112
- } else if (item === restReversed) {
113
- return restNodes.reverse();
108
+ } else {
109
+ const addedNodePaths = /* @__PURE__ */ new Set();
110
+ const resolved = meta.data.pages.flatMap((item, i) => resolveFolderItem(folder, item, ctx, i, addedNodePaths));
111
+ if (indexFile && (isRoot || !addedNodePaths.has(indexFile.file.path))) {
112
+ index = buildFileNode(indexFile, ctx);
113
+ }
114
+ for (let i = 0; i < resolved.length; i++) {
115
+ if (resolved[i] === rest || resolved[i] === restReversed) {
116
+ resolved.splice(
117
+ i,
118
+ 1,
119
+ ...buildAll(
120
+ folder.children,
121
+ ctx,
122
+ (node2) => {
123
+ if (node2.file.name === "index" && !isRoot) return false;
124
+ return !addedNodePaths.has(node2.file.path);
125
+ },
126
+ resolved[i] === restReversed
127
+ )
128
+ );
129
+ break;
114
130
  }
115
- return item;
116
- });
117
- children = nodes ?? restNodes;
131
+ }
132
+ children = resolved;
118
133
  }
119
134
  const node = {
120
135
  type: "folder",
121
136
  name: meta?.data.title ?? index?.name ?? // resolve folder groups like (group_name)
122
137
  pathToName(group.exec(folder.file.name)?.[1] ?? folder.file.name),
123
- icon: ctx.options.resolveIcon?.(meta?.data.icon) ?? index?.icon,
138
+ icon: options.resolveIcon?.(meta?.data.icon) ?? index?.icon,
124
139
  root: meta?.data.root,
125
140
  defaultOpen: meta?.data.defaultOpen,
126
141
  description: meta?.data.description,
127
- index: isRoot || indexFile && !addedNodePaths.has(indexFile.file.path) ? index : void 0,
142
+ index,
128
143
  children,
129
144
  $id: folder.file.path,
130
- $ref: !ctx.options.noRef ? {
145
+ $ref: !options.noRef ? {
131
146
  metaFile: meta?.file.path
132
147
  } : void 0
133
148
  };
134
- return ctx.options.attachFolder?.(node, folder, meta) ?? node;
149
+ return options.attachFolder?.(node, folder, meta) ?? node;
135
150
  }
136
- function buildFileNode(file, ctx) {
151
+ function buildFileNode(file, { options, getUrl, locale }) {
137
152
  const item = {
138
153
  $id: file.file.path,
139
154
  type: "page",
140
155
  name: file.data.data.title ?? pathToName(file.file.name),
141
156
  description: file.data.data.description,
142
- icon: ctx.options.resolveIcon?.(file.data.data.icon),
143
- url: ctx.getUrl(file.data.slugs, ctx.locale),
144
- $ref: !ctx.options.noRef ? {
157
+ icon: options.resolveIcon?.(file.data.data.icon),
158
+ url: getUrl(file.data.slugs, locale),
159
+ $ref: !options.noRef ? {
145
160
  file: file.file.path
146
161
  } : void 0
147
162
  };
148
- return ctx.options.attachFile?.(item, file) ?? item;
163
+ return options.attachFile?.(item, file) ?? item;
149
164
  }
150
165
  function build(ctx) {
151
166
  const root = ctx.storage.root();
152
167
  const folder = buildFolderNode(root, true, ctx);
153
168
  return {
154
- $id: ctx.locale ? ctx.locale : "root",
169
+ $id: ctx.locale ?? "root",
155
170
  name: folder.name,
156
171
  children: folder.children
157
172
  };
@@ -208,7 +223,9 @@ function parseFilePath(path) {
208
223
  name,
209
224
  path: segments.join("/"),
210
225
  ext,
211
- flattenedPath: [dirname, name].filter((p) => p.length > 0).join("/")
226
+ get flattenedPath() {
227
+ return [dirname, name].filter((p) => p.length > 0).join("/");
228
+ }
212
229
  };
213
230
  }
214
231
  function parseFolderPath(path) {
@@ -269,7 +286,7 @@ var Storage = class {
269
286
  );
270
287
  }
271
288
  list() {
272
- return [...this.files.values()];
289
+ return Array.from(this.files.values());
273
290
  }
274
291
  makeDir(path) {
275
292
  const segments = splitPath(path);
@@ -414,13 +431,14 @@ function createOutput(options) {
414
431
  if (!options.url && !options.baseUrl) {
415
432
  console.warn("`loader()` now requires a `baseUrl` option to be defined.");
416
433
  }
417
- const { source, slugs: slugsFn = getSlugs } = options;
434
+ const { source, slugs: slugsFn = getSlugs, i18n } = options;
435
+ const defaultLanguage = i18n?.defaultLanguage ?? "";
418
436
  const getUrl = options.url ?? createGetUrl(options.baseUrl ?? "/", options.i18n);
419
437
  const files = typeof source.files === "function" ? source.files() : source.files;
420
- const storages = options.i18n ? loadFilesI18n(files, {
438
+ const storages = i18n ? loadFilesI18n(files, {
421
439
  i18n: {
422
- ...options.i18n,
423
- parser: options.i18n.parser ?? "dot"
440
+ ...i18n,
441
+ parser: i18n.parser ?? "dot"
424
442
  },
425
443
  transformers: options.transformers,
426
444
  getSlugs: slugsFn
@@ -430,17 +448,17 @@ function createOutput(options) {
430
448
  getSlugs: slugsFn
431
449
  })
432
450
  };
433
- const walker = indexPages(storages, getUrl, options.i18n);
451
+ const walker = indexPages(storages, getUrl, i18n);
434
452
  const builder = createPageTreeBuilder(getUrl);
435
453
  let pageTree;
436
454
  return {
437
- _i18n: options.i18n,
455
+ _i18n: i18n,
438
456
  get pageTree() {
439
- if (options.i18n) {
457
+ if (i18n) {
440
458
  pageTree ??= builder.buildI18n({
441
459
  storages,
442
460
  resolveIcon: options.icon,
443
- i18n: options.i18n,
461
+ i18n,
444
462
  ...options.pageTree
445
463
  });
446
464
  } else {
@@ -455,26 +473,23 @@ function createOutput(options) {
455
473
  set pageTree(v) {
456
474
  pageTree = v;
457
475
  },
458
- getPageByHref(href, { dir = "" } = {}) {
459
- const pages = Array.from(walker.pages.values());
476
+ getPageByHref(href, { dir = "", language } = {}) {
477
+ const pages = this.getPages(language);
460
478
  const [value, hash] = href.split("#", 2);
479
+ let target;
461
480
  if (value.startsWith(".") && (value.endsWith(".md") || value.endsWith(".mdx"))) {
462
481
  const hrefPath = joinPath(dir, value);
463
- const target2 = pages.find((item) => item.file.path === hrefPath);
464
- if (target2)
465
- return {
466
- page: target2,
467
- hash
468
- };
482
+ target = pages.find((item) => item.file.path === hrefPath);
483
+ } else {
484
+ target = pages.find((item) => item.url === value);
469
485
  }
470
- const target = pages.find((item) => item.url === value);
471
- if (target)
472
- return {
473
- page: target,
474
- hash
475
- };
486
+ if (!target) return;
487
+ return {
488
+ page: target,
489
+ hash
490
+ };
476
491
  },
477
- getPages(language = options.i18n?.defaultLanguage ?? "") {
492
+ getPages(language = defaultLanguage) {
478
493
  const pages = [];
479
494
  for (const key of walker.pages.keys()) {
480
495
  if (key.startsWith(`${language}.`)) pages.push(walker.pages.get(key));
@@ -492,16 +507,16 @@ function createOutput(options) {
492
507
  }
493
508
  return list;
494
509
  },
495
- getPage(slugs = [], language = options.i18n?.defaultLanguage ?? "") {
510
+ getPage(slugs = [], language = defaultLanguage) {
496
511
  return walker.pages.get(`${language}.${slugs.join("/")}`);
497
512
  },
498
- getNodeMeta(node, language = options.i18n?.defaultLanguage ?? "") {
513
+ getNodeMeta(node, language = defaultLanguage) {
499
514
  const ref = node.$ref?.metaFile;
500
515
  if (!ref) return;
501
516
  const file = storages[language].list().find((v) => v.format === "meta" && v.file.path === ref);
502
517
  if (file) return walker.getResultFromFile(file);
503
518
  },
504
- getNodePage(node, language = options.i18n?.defaultLanguage ?? "") {
519
+ getNodePage(node, language = defaultLanguage) {
505
520
  const ref = node.$ref?.file;
506
521
  if (!ref) return;
507
522
  const file = storages[language].list().find((v) => v.format === "page" && v.file.path === ref);
@@ -509,7 +524,7 @@ function createOutput(options) {
509
524
  },
510
525
  getPageTree(locale) {
511
526
  if (options.i18n) {
512
- return this.pageTree[locale ?? options.i18n.defaultLanguage];
527
+ return this.pageTree[locale ?? defaultLanguage];
513
528
  }
514
529
  return this.pageTree;
515
530
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "15.3.4",
3
+ "version": "15.4.1",
4
4
  "description": "The library for building a documentation website in Next.js",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -24,6 +24,10 @@
24
24
  "import": "./dist/toc.js",
25
25
  "types": "./dist/toc.d.ts"
26
26
  },
27
+ "./content": {
28
+ "import": "./dist/content/index.js",
29
+ "types": "./dist/content/index.d.ts"
30
+ },
27
31
  "./search/*": {
28
32
  "import": "./dist/search/*.js",
29
33
  "types": "./dist/search/*.d.ts"
@@ -89,29 +93,28 @@
89
93
  "react-remove-scroll": "^2.6.3",
90
94
  "remark": "^15.0.0",
91
95
  "remark-gfm": "^4.0.1",
96
+ "remark-rehype": "^11.1.2",
92
97
  "scroll-into-view-if-needed": "^3.1.0",
93
98
  "shiki": "^3.4.2",
94
99
  "unist-util-visit": "^5.0.0"
95
100
  },
96
101
  "devDependencies": {
97
- "@algolia/client-search": "4.24.0",
98
102
  "@mdx-js/mdx": "^3.1.0",
99
103
  "@oramacloud/client": "^2.1.4",
100
- "@tanstack/react-router": "^1.120.3",
104
+ "@tanstack/react-router": "^1.120.9",
101
105
  "@types/estree-jsx": "^1.0.5",
102
106
  "@types/hast": "^3.0.4",
103
107
  "@types/mdast": "^4.0.3",
104
108
  "@types/negotiator": "^0.6.3",
105
- "@types/node": "22.15.19",
106
- "@types/react": "^19.1.4",
109
+ "@types/node": "22.15.21",
110
+ "@types/react": "^19.1.5",
107
111
  "@types/react-dom": "^19.1.5",
108
- "algoliasearch": "4.24.0",
112
+ "algoliasearch": "5.25.0",
109
113
  "mdast-util-mdx-jsx": "^3.2.0",
110
114
  "mdast-util-mdxjs-esm": "^2.0.1",
111
115
  "next": "^15.3.2",
112
116
  "react-router": "^7.6.0",
113
117
  "remark-mdx": "^3.1.0",
114
- "remark-rehype": "^11.1.2",
115
118
  "typescript": "^5.8.3",
116
119
  "unified": "^11.0.5",
117
120
  "vfile": "^6.0.3",
@@ -120,7 +123,7 @@
120
123
  },
121
124
  "peerDependencies": {
122
125
  "@oramacloud/client": "1.x.x || 2.x.x",
123
- "algoliasearch": "4.24.0",
126
+ "algoliasearch": "5.x.x",
124
127
  "next": "14.x.x || 15.x.x",
125
128
  "react": "18.x.x || 19.x.x",
126
129
  "react-dom": "18.x.x || 19.x.x"