fumadocs-core 16.2.3 → 16.2.4

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.
@@ -1,26 +1,31 @@
1
1
  import {
2
2
  flattenNode,
3
3
  toMdxExport
4
- } from "./chunk-A4G5V4FQ.js";
4
+ } from "./chunk-VLSDGCJE.js";
5
5
 
6
6
  // src/mdx-plugins/remark-structure.ts
7
7
  import Slugger from "github-slugger";
8
8
  import { remark } from "remark";
9
9
  import remarkGfm from "remark-gfm";
10
10
  import { visit } from "unist-util-visit";
11
- function remarkStructure({
12
- types = [
11
+ var remarkStructureDefaultOptions = {
12
+ types: [
13
13
  "heading",
14
14
  "paragraph",
15
15
  "blockquote",
16
16
  "tableCell",
17
17
  "mdxJsxFlowElement"
18
18
  ],
19
- allowedMdxAttributes = (node) => {
19
+ allowedMdxAttributes: (node) => {
20
20
  if (!node.name) return false;
21
21
  return ["TypeTable", "Callout"].includes(node.name);
22
22
  },
23
- exportAs = false
23
+ exportAs: false
24
+ };
25
+ function remarkStructure({
26
+ types = remarkStructureDefaultOptions.types,
27
+ allowedMdxAttributes = remarkStructureDefaultOptions.allowedMdxAttributes,
28
+ exportAs = remarkStructureDefaultOptions.exportAs
24
29
  } = {}) {
25
30
  const slugger = new Slugger();
26
31
  if (Array.isArray(allowedMdxAttributes)) {
@@ -106,6 +111,7 @@ function structure(content, remarkPlugins = [], options = {}) {
106
111
  }
107
112
 
108
113
  export {
114
+ remarkStructureDefaultOptions,
109
115
  remarkStructure,
110
116
  structure
111
117
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  flattenNode
3
- } from "./chunk-A4G5V4FQ.js";
3
+ } from "./chunk-VLSDGCJE.js";
4
4
 
5
5
  // src/mdx-plugins/remark-heading.ts
6
6
  import Slugger from "github-slugger";
@@ -3,7 +3,7 @@ import { valueToEstree } from "estree-util-value-to-estree";
3
3
  function flattenNode(node) {
4
4
  if ("children" in node)
5
5
  return node.children.map((child) => flattenNode(child)).join("");
6
- if ("value" in node) return node.value;
6
+ if ("value" in node && typeof node.value === "string") return node.value;
7
7
  return "";
8
8
  }
9
9
  function toMdxExport(name, value) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  flattenNode
3
- } from "./chunk-A4G5V4FQ.js";
3
+ } from "./chunk-VLSDGCJE.js";
4
4
 
5
5
  // src/mdx-plugins/remark-admonition.ts
6
6
  import { visit } from "unist-util-visit";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  remarkHeading
3
- } from "../chunk-HNZEUF6C.js";
4
- import "../chunk-A4G5V4FQ.js";
3
+ } from "../chunk-MA6O2UUE.js";
4
+ import "../chunk-VLSDGCJE.js";
5
5
  import "../chunk-U67V476Y.js";
6
6
 
7
7
  // src/content/toc.ts
@@ -1,12 +1,34 @@
1
- import { NextProxy } from 'next/dist/server/web/types';
1
+ import { NextProxy } from 'next/server';
2
2
  import { I18nConfig } from './index.js';
3
+ import { NextURL } from 'next/dist/server/web/next-url';
3
4
 
4
5
  interface MiddlewareOptions extends I18nConfig {
5
6
  /**
6
- * A function that adds the locale prefix to path name
7
+ * Either:
8
+ * - A formatter object
9
+ * - A function that adds the locale prefix to pathname
7
10
  */
8
- format?: (locale: string, path: string) => string;
11
+ format?: URLFormatter | ((locale: string, pathname: string) => string);
12
+ /**
13
+ * the cookie to store locale code when `hideLocale` is set to `always`.
14
+ */
15
+ cookieName?: string;
16
+ }
17
+ interface URLFormatter {
18
+ /**
19
+ * get locale code from request URL
20
+ */
21
+ get: (url: NextURL) => string | undefined;
22
+ /**
23
+ * add locale code to request URL (which is missing the locale).
24
+ */
25
+ add: (url: NextURL, locale: string) => URL;
26
+ /**
27
+ * remove locale code from request URL
28
+ */
29
+ remove: (url: NextURL) => URL;
9
30
  }
10
- declare function createI18nMiddleware({ languages, defaultLanguage, format, hideLocale, }: MiddlewareOptions): NextProxy;
31
+ declare const DefaultFormatter: URLFormatter;
32
+ declare function createI18nMiddleware({ languages, defaultLanguage, format, cookieName, hideLocale, }: MiddlewareOptions): NextProxy;
11
33
 
12
- export { createI18nMiddleware };
34
+ export { DefaultFormatter, type URLFormatter, createI18nMiddleware };
@@ -6,58 +6,71 @@ import "../chunk-U67V476Y.js";
6
6
  // src/i18n/middleware.ts
7
7
  import { match as matchLocale } from "@formatjs/intl-localematcher";
8
8
  import { NextResponse } from "next/server";
9
- var COOKIE = "FD_LOCALE";
10
- function getLocale(request, locales, defaultLanguage) {
11
- const languages = getNegotiator(request).languages(locales);
12
- return matchLocale(languages, locales, defaultLanguage);
13
- }
14
- var defaultFormat = (locale, path) => {
15
- return `/${locale}${path}`;
9
+ var DefaultFormatter = {
10
+ get(url) {
11
+ const segs = url.pathname.split("/");
12
+ if (segs.length > 1 && segs[1]) return segs[1];
13
+ },
14
+ add(url, locale) {
15
+ const next = new URL(url);
16
+ next.pathname = `${url.basePath}/${locale}/${url.pathname}`.replaceAll(
17
+ /\/+/g,
18
+ "/"
19
+ );
20
+ return next;
21
+ },
22
+ remove(url) {
23
+ const next = new URL(url);
24
+ const pathname = url.pathname.split("/").slice(2).join("/");
25
+ next.pathname = `${url.basePath}/${pathname}`.replaceAll(/\/+/g, "/");
26
+ return next;
27
+ }
16
28
  };
17
29
  function createI18nMiddleware({
18
30
  languages,
19
31
  defaultLanguage,
20
- format = defaultFormat,
32
+ format = DefaultFormatter,
33
+ cookieName = "FD_LOCALE",
21
34
  hideLocale = "never"
22
35
  }) {
23
- function getLocaleUrl(request, locale) {
24
- const next = new URL(request.url);
25
- next.pathname = format(locale, forceSlashPrefix(request.nextUrl.pathname));
26
- return next;
36
+ let formatter;
37
+ if (typeof format === "function") {
38
+ formatter = {
39
+ ...DefaultFormatter,
40
+ add(url, locale) {
41
+ const next = new URL(url);
42
+ next.pathname = format(locale, url.pathname);
43
+ return next;
44
+ }
45
+ };
46
+ } else {
47
+ formatter = format;
27
48
  }
28
49
  return (request) => {
29
50
  const url = request.nextUrl;
30
- const pathLocale = languages.find(
31
- (locale) => url.pathname.startsWith(`/${locale}/`) || url.pathname === `/${locale}`
32
- );
51
+ let pathLocale = formatter.get(url);
52
+ if (pathLocale && !languages.includes(pathLocale)) pathLocale = void 0;
33
53
  if (!pathLocale) {
34
54
  if (hideLocale === "default-locale") {
35
- return NextResponse.rewrite(getLocaleUrl(request, defaultLanguage));
55
+ return NextResponse.rewrite(formatter.add(url, defaultLanguage));
36
56
  }
37
- const preferred = getLocale(request, languages, defaultLanguage);
57
+ const finalLanguages = getNegotiator(request).languages(languages);
58
+ const preferred = matchLocale(finalLanguages, languages, defaultLanguage);
38
59
  if (hideLocale === "always") {
39
- const locale = request.cookies.get(COOKIE)?.value ?? preferred;
40
- return NextResponse.rewrite(getLocaleUrl(request, locale));
60
+ const locale = request.cookies.get(cookieName)?.value ?? preferred;
61
+ return NextResponse.rewrite(formatter.add(url, locale));
41
62
  }
42
- return NextResponse.redirect(getLocaleUrl(request, preferred));
63
+ return NextResponse.redirect(formatter.add(url, preferred));
43
64
  }
44
65
  if (hideLocale === "always" || hideLocale === "default-locale" && pathLocale === defaultLanguage) {
45
- const res = NextResponse.redirect(
46
- new URL(
47
- forceSlashPrefix(url.pathname.slice(`/${pathLocale}`.length)),
48
- request.url
49
- )
50
- );
51
- res.cookies.set(COOKIE, pathLocale);
66
+ const res = NextResponse.redirect(formatter.remove(url));
67
+ res.cookies.set(cookieName, pathLocale);
52
68
  return res;
53
69
  }
54
70
  return NextResponse.next();
55
71
  };
56
72
  }
57
- function forceSlashPrefix(v) {
58
- if (v.startsWith("/")) return v;
59
- return "/" + v;
60
- }
61
73
  export {
74
+ DefaultFormatter,
62
75
  createI18nMiddleware
63
76
  };
@@ -1,7 +1,7 @@
1
1
  export { Options as RemarkGfmOptions, default as remarkGfm } from 'remark-gfm';
2
2
  export { CodeBlockIcon, RehypeCodeOptions, rehypeCode, rehypeCodeDefaultOptions, transformerIcon, transformerTab } from './rehype-code.js';
3
3
  export { RemarkImageOptions, remarkImage } from './remark-image.js';
4
- export { StructureOptions, StructuredData, remarkStructure, structure } from './remark-structure.js';
4
+ export { StructureOptions, StructuredData, remarkStructure, remarkStructureDefaultOptions, structure } from './remark-structure.js';
5
5
  export { RemarkHeadingOptions, remarkHeading } from './remark-heading.js';
6
6
  export { RemarkAdmonitionOptions, remarkAdmonition } from './remark-admonition.js';
7
7
  export { RemarkDirectiveAdmonitionOptions, remarkDirectiveAdmonition } from './remark-directive-admonition.js';
@@ -15,8 +15,9 @@ import {
15
15
  } from "../chunk-XJ6ZQNEX.js";
16
16
  import {
17
17
  remarkStructure,
18
+ remarkStructureDefaultOptions,
18
19
  structure
19
- } from "../chunk-SLIKY7GW.js";
20
+ } from "../chunk-JUF4WZ6G.js";
20
21
  import {
21
22
  rehypeCode,
22
23
  rehypeCodeDefaultOptions,
@@ -28,7 +29,7 @@ import {
28
29
  } from "../chunk-EFVXL2PP.js";
29
30
  import {
30
31
  remarkAdmonition
31
- } from "../chunk-JOXPHQ2R.js";
32
+ } from "../chunk-W6WTLKRA.js";
32
33
  import {
33
34
  remarkCodeTab
34
35
  } from "../chunk-CH7YHH7V.js";
@@ -45,8 +46,8 @@ import {
45
46
  import "../chunk-XN2LKXFZ.js";
46
47
  import {
47
48
  remarkHeading
48
- } from "../chunk-HNZEUF6C.js";
49
- import "../chunk-A4G5V4FQ.js";
49
+ } from "../chunk-MA6O2UUE.js";
50
+ import "../chunk-VLSDGCJE.js";
50
51
  import "../chunk-U67V476Y.js";
51
52
  export {
52
53
  generateCodeBlockTabs,
@@ -65,6 +66,7 @@ export {
65
66
  remarkNpm,
66
67
  remarkSteps,
67
68
  remarkStructure,
69
+ remarkStructureDefaultOptions,
68
70
  structure,
69
71
  transformerIcon,
70
72
  transformerTab
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  remarkAdmonition
3
- } from "../chunk-JOXPHQ2R.js";
4
- import "../chunk-A4G5V4FQ.js";
3
+ } from "../chunk-W6WTLKRA.js";
4
+ import "../chunk-VLSDGCJE.js";
5
5
  import "../chunk-U67V476Y.js";
6
6
  export {
7
7
  remarkAdmonition
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  remarkHeading
3
- } from "../chunk-HNZEUF6C.js";
4
- import "../chunk-A4G5V4FQ.js";
3
+ } from "../chunk-MA6O2UUE.js";
4
+ import "../chunk-VLSDGCJE.js";
5
5
  import "../chunk-U67V476Y.js";
6
6
  export {
7
7
  remarkHeading
@@ -52,8 +52,15 @@ declare module 'vfile' {
52
52
  structuredData: StructuredData;
53
53
  }
54
54
  }
55
+ declare const remarkStructureDefaultOptions: {
56
+ types: string[];
57
+ allowedMdxAttributes: (node: MdxJsxFlowElement) => boolean;
58
+ exportAs: false;
59
+ };
55
60
  /**
56
- * Attach structured data to VFile, you can access via `vfile.data.structuredData`.
61
+ * Extract content into structured data.
62
+ *
63
+ * By default, the output is stored into VFile (`vfile.data.structuredData`), you can specify `exportAs` to export it.
57
64
  */
58
65
  declare function remarkStructure({ types, allowedMdxAttributes, exportAs, }?: StructureOptions): Transformer<Root, Root>;
59
66
  /**
@@ -61,4 +68,4 @@ declare function remarkStructure({ types, allowedMdxAttributes, exportAs, }?: St
61
68
  */
62
69
  declare function structure(content: string, remarkPlugins?: PluggableList, options?: StructureOptions): StructuredData;
63
70
 
64
- export { type StructureOptions, type StructuredData, remarkStructure, structure };
71
+ export { type StructureOptions, type StructuredData, remarkStructure, remarkStructureDefaultOptions, structure };
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  remarkStructure,
3
+ remarkStructureDefaultOptions,
3
4
  structure
4
- } from "../chunk-SLIKY7GW.js";
5
- import "../chunk-A4G5V4FQ.js";
5
+ } from "../chunk-JUF4WZ6G.js";
6
+ import "../chunk-VLSDGCJE.js";
6
7
  import "../chunk-U67V476Y.js";
7
8
  export {
8
9
  remarkStructure,
10
+ remarkStructureDefaultOptions,
9
11
  structure
10
12
  };
@@ -1,3 +1,4 @@
1
+ import { DependencyList } from 'react';
1
2
  import { AnyOrama } from '@orama/orama';
2
3
  import '../mdx-plugins/remark-structure.js';
3
4
  import { BaseIndex } from './algolia.js';
@@ -9,7 +10,6 @@ import 'mdast';
9
10
  import 'unified';
10
11
  import 'mdast-util-mdx-jsx';
11
12
  import 'algoliasearch';
12
- import 'react';
13
13
 
14
14
  interface FetchOptions {
15
15
  /**
@@ -120,7 +120,7 @@ type Client = ({
120
120
  /**
121
121
  * Provide a hook to query different official search clients.
122
122
  *
123
- * Note: it will re-query when its parameters changed, make sure to use `useCallback()` on functions passed to this hook.
123
+ * Note: it will re-query when its parameters changed, make sure to use `useMemo()` on `clientOptions` or define `deps` array.
124
124
  */
125
125
  declare function useDocsSearch(clientOptions: Client & {
126
126
  /**
@@ -135,6 +135,6 @@ declare function useDocsSearch(clientOptions: Client & {
135
135
  * @defaultValue false
136
136
  */
137
137
  allowEmpty?: boolean;
138
- }): UseDocsSearch;
138
+ }, deps?: DependencyList): UseDocsSearch;
139
139
 
140
140
  export { type AlgoliaOptions, type Client, type FetchOptions, type OramaCloudOptions, type StaticOptions, useDocsSearch };
@@ -22,20 +22,21 @@ function useDebounce(value, delayMs = 1e3) {
22
22
  }
23
23
 
24
24
  // src/search/client.ts
25
- function isDifferentDeep(a, b) {
25
+ function isDeepEqual(a, b) {
26
+ if (a === b) return true;
26
27
  if (Array.isArray(a) && Array.isArray(b)) {
27
- return b.length !== a.length || a.some((v, i) => isDifferentDeep(v, b[i]));
28
+ return b.length === a.length && a.every((v, i) => isDeepEqual(v, b[i]));
28
29
  }
29
30
  if (typeof a === "object" && a && typeof b === "object" && b) {
30
31
  const aKeys = Object.keys(a);
31
32
  const bKeys = Object.keys(b);
32
- return aKeys.length !== bKeys.length || aKeys.some(
33
- (key) => isDifferentDeep(a[key], b[key])
33
+ return aKeys.length === bKeys.length && aKeys.every(
34
+ (key) => Object.hasOwn(b, key) && isDeepEqual(a[key], b[key])
34
35
  );
35
36
  }
36
- return a !== b;
37
+ return false;
37
38
  }
38
- function useDocsSearch(clientOptions) {
39
+ function useDocsSearch(clientOptions, deps) {
39
40
  const { delayMs = 100, allowEmpty = false, ...client } = clientOptions;
40
41
  const [search, setSearch] = useState2("");
41
42
  const [results, setResults] = useState2("empty");
@@ -44,7 +45,7 @@ function useDocsSearch(clientOptions) {
44
45
  const debouncedValue = useDebounce(search, delayMs);
45
46
  const onStart = useRef(void 0);
46
47
  useOnChange(
47
- [client, debouncedValue],
48
+ [deps ?? clientOptions, debouncedValue],
48
49
  () => {
49
50
  if (onStart.current) {
50
51
  onStart.current();
@@ -89,7 +90,7 @@ function useDocsSearch(clientOptions) {
89
90
  setIsLoading(false);
90
91
  });
91
92
  },
92
- isDifferentDeep
93
+ deps ? void 0 : (a, b) => !isDeepEqual(a, b)
93
94
  );
94
95
  return { search, setSearch, query: { isLoading, data: results, error } };
95
96
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "16.2.3",
3
+ "version": "16.2.4",
4
4
  "description": "The React.js library for building a documentation website",
5
5
  "keywords": [
6
6
  "Fumadocs",
@@ -130,29 +130,29 @@
130
130
  },
131
131
  "devDependencies": {
132
132
  "@mdx-js/mdx": "^3.1.1",
133
- "@mixedbread/sdk": "^0.45.0",
133
+ "@mixedbread/sdk": "^0.46.0",
134
134
  "@orama/core": "^1.2.13",
135
- "@tanstack/react-router": "1.136.18",
135
+ "@tanstack/react-router": "1.140.0",
136
136
  "@types/estree-jsx": "^1.0.5",
137
137
  "@types/hast": "^3.0.4",
138
138
  "@types/mdast": "^4.0.4",
139
139
  "@types/negotiator": "^0.6.4",
140
- "@types/node": "24.10.1",
140
+ "@types/node": "24.10.2",
141
141
  "@types/react": "^19.2.7",
142
142
  "@types/react-dom": "^19.2.3",
143
- "algoliasearch": "5.45.0",
144
- "lucide-react": "^0.555.0",
143
+ "algoliasearch": "5.46.0",
144
+ "lucide-react": "^0.556.0",
145
145
  "mdast-util-mdx-jsx": "^3.2.0",
146
146
  "mdast-util-mdxjs-esm": "^2.0.1",
147
- "next": "16.0.7",
148
- "react-router": "^7.10.0",
147
+ "next": "16.0.8",
148
+ "react-router": "^7.10.1",
149
149
  "remark-directive": "^4.0.0",
150
150
  "remark-mdx": "^3.1.1",
151
151
  "remove-markdown": "^0.6.2",
152
152
  "typescript": "^5.9.3",
153
153
  "unified": "^11.0.5",
154
154
  "vfile": "^6.0.3",
155
- "waku": "^0.27.2",
155
+ "waku": "^0.27.3",
156
156
  "zod": "^4.1.13",
157
157
  "eslint-config-custom": "0.0.0",
158
158
  "tsconfig": "0.0.0"