fumadocs-core 13.2.2 → 13.3.0
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.
- package/dist/chunk-MXOJWF66.js +67 -0
- package/dist/config-inq6kP6y.d.ts +26 -0
- package/dist/i18n/index.d.ts +12 -0
- package/dist/i18n/index.js +7 -0
- package/dist/middleware.d.ts +3 -31
- package/dist/middleware.js +3 -48
- package/dist/search/server.d.ts +25 -10
- package/dist/search/server.js +41 -11
- package/dist/server/index.d.ts +1 -0
- package/dist/sidebar.js +1 -0
- package/dist/source/index.d.ts +22 -10
- package/dist/source/index.js +54 -31
- package/dist/toc.d.ts +13 -3
- package/dist/toc.js +39 -30
- package/package.json +12 -5
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/i18n/middleware.ts
|
|
2
|
+
import { match as matchLocale } from "@formatjs/intl-localematcher";
|
|
3
|
+
import Negotiator from "negotiator";
|
|
4
|
+
import { NextResponse } from "next/server";
|
|
5
|
+
var COOKIE = "FD_LOCALE";
|
|
6
|
+
function getLocale(request, locales, defaultLanguage) {
|
|
7
|
+
const negotiatorHeaders = {};
|
|
8
|
+
request.headers.forEach((value, key) => negotiatorHeaders[key] = value);
|
|
9
|
+
const languages = new Negotiator({ headers: negotiatorHeaders }).languages(
|
|
10
|
+
locales
|
|
11
|
+
);
|
|
12
|
+
return matchLocale(languages, locales, defaultLanguage);
|
|
13
|
+
}
|
|
14
|
+
var defaultFormat = (locale, path) => {
|
|
15
|
+
return `/${locale}/${path}`;
|
|
16
|
+
};
|
|
17
|
+
function createI18nMiddleware({
|
|
18
|
+
languages,
|
|
19
|
+
defaultLanguage,
|
|
20
|
+
format = defaultFormat,
|
|
21
|
+
hideLocale = "never"
|
|
22
|
+
}) {
|
|
23
|
+
function getUrl(request, pathname, locale) {
|
|
24
|
+
if (!locale) {
|
|
25
|
+
return new URL(
|
|
26
|
+
pathname.startsWith("/") ? pathname : `/${pathname}`,
|
|
27
|
+
request.url
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return new URL(
|
|
31
|
+
format(locale, pathname.startsWith("/") ? pathname.slice(1) : pathname),
|
|
32
|
+
request.url
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return (request) => {
|
|
36
|
+
const { pathname } = request.nextUrl;
|
|
37
|
+
const pathLocale = languages.find(
|
|
38
|
+
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
|
|
39
|
+
);
|
|
40
|
+
if (!pathLocale) {
|
|
41
|
+
if (hideLocale === "default-locale") {
|
|
42
|
+
return NextResponse.rewrite(getUrl(request, pathname, defaultLanguage));
|
|
43
|
+
}
|
|
44
|
+
const preferred = getLocale(request, languages, defaultLanguage);
|
|
45
|
+
if (hideLocale === "always") {
|
|
46
|
+
const locale = request.cookies.get(COOKIE)?.value ?? preferred;
|
|
47
|
+
return NextResponse.rewrite(getUrl(request, pathname, locale));
|
|
48
|
+
}
|
|
49
|
+
return NextResponse.redirect(getUrl(request, pathname, preferred));
|
|
50
|
+
}
|
|
51
|
+
if (hideLocale === "always") {
|
|
52
|
+
const path = pathname.slice(`/${pathLocale}`.length);
|
|
53
|
+
const res = NextResponse.redirect(getUrl(request, path));
|
|
54
|
+
res.cookies.set(COOKIE, pathLocale);
|
|
55
|
+
return res;
|
|
56
|
+
}
|
|
57
|
+
if (hideLocale === "default-locale" && pathLocale === defaultLanguage) {
|
|
58
|
+
const path = pathname.slice(`/${pathLocale}`.length);
|
|
59
|
+
return NextResponse.redirect(getUrl(request, path));
|
|
60
|
+
}
|
|
61
|
+
return NextResponse.next();
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
createI18nMiddleware
|
|
67
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
|
|
26
|
+
export type { I18nConfig as I };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { I as I18nConfig } from '../config-inq6kP6y.js';
|
|
2
|
+
import { NextMiddleware } from 'next/dist/server/web/types';
|
|
3
|
+
|
|
4
|
+
interface MiddlewareOptions extends I18nConfig {
|
|
5
|
+
/**
|
|
6
|
+
* A function that adds the locale prefix to path name
|
|
7
|
+
*/
|
|
8
|
+
format?: (locale: string, path: string) => string;
|
|
9
|
+
}
|
|
10
|
+
declare function createI18nMiddleware({ languages, defaultLanguage, format, hideLocale, }: MiddlewareOptions): NextMiddleware;
|
|
11
|
+
|
|
12
|
+
export { I18nConfig, createI18nMiddleware };
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Supported locale codes
|
|
6
|
-
*/
|
|
7
|
-
languages: string[];
|
|
8
|
-
/**
|
|
9
|
-
* Default locale if not specified
|
|
10
|
-
*/
|
|
11
|
-
defaultLanguage: string;
|
|
12
|
-
/**
|
|
13
|
-
* A function that adds the locale prefix to path name
|
|
14
|
-
*/
|
|
15
|
-
format?: (locale: string, path: string) => string;
|
|
16
|
-
/**
|
|
17
|
-
* Don't show the locale prefix on URL.
|
|
18
|
-
*
|
|
19
|
-
* - `always`: Always hide the prefix
|
|
20
|
-
* - `default-locale`: Only hide the default locale
|
|
21
|
-
* - `never`: Never hide the prefix
|
|
22
|
-
*
|
|
23
|
-
* This API uses `NextResponse.rewrite`.
|
|
24
|
-
*
|
|
25
|
-
* @defaultValue 'never'
|
|
26
|
-
*/
|
|
27
|
-
hideLocale?: 'always' | 'default-locale' | 'never';
|
|
28
|
-
}
|
|
29
|
-
declare function createI18nMiddleware({ languages, defaultLanguage, format, hideLocale, }: MiddlewareOptions): NextMiddleware;
|
|
30
|
-
|
|
31
|
-
export { createI18nMiddleware };
|
|
1
|
+
export { createI18nMiddleware } from './i18n/index.js';
|
|
2
|
+
import './config-inq6kP6y.js';
|
|
3
|
+
import 'next/dist/server/web/types';
|
package/dist/middleware.js
CHANGED
|
@@ -1,52 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createI18nMiddleware
|
|
3
|
+
} from "./chunk-MXOJWF66.js";
|
|
1
4
|
import "./chunk-MLKGABMK.js";
|
|
2
|
-
|
|
3
|
-
// src/middleware.ts
|
|
4
|
-
import { match as matchLocale } from "@formatjs/intl-localematcher";
|
|
5
|
-
import Negotiator from "negotiator";
|
|
6
|
-
import { NextResponse } from "next/server";
|
|
7
|
-
function getLocale(request, locales, defaultLanguage) {
|
|
8
|
-
const negotiatorHeaders = {};
|
|
9
|
-
request.headers.forEach((value, key) => negotiatorHeaders[key] = value);
|
|
10
|
-
const languages = new Negotiator({ headers: negotiatorHeaders }).languages(
|
|
11
|
-
locales
|
|
12
|
-
);
|
|
13
|
-
return matchLocale(languages, locales, defaultLanguage);
|
|
14
|
-
}
|
|
15
|
-
var defaultFormat = (locale, path) => {
|
|
16
|
-
return `/${locale}/${path}`;
|
|
17
|
-
};
|
|
18
|
-
function createI18nMiddleware({
|
|
19
|
-
languages,
|
|
20
|
-
defaultLanguage,
|
|
21
|
-
format = defaultFormat,
|
|
22
|
-
hideLocale = "never"
|
|
23
|
-
}) {
|
|
24
|
-
function shouldHideLocale(locale) {
|
|
25
|
-
return hideLocale === "always" || hideLocale === "default-locale" && locale === defaultLanguage;
|
|
26
|
-
}
|
|
27
|
-
return (request) => {
|
|
28
|
-
const { pathname } = request.nextUrl;
|
|
29
|
-
const pathLocale = languages.find(
|
|
30
|
-
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
|
|
31
|
-
);
|
|
32
|
-
if (!pathLocale) {
|
|
33
|
-
const locale = getLocale(request, languages, defaultLanguage);
|
|
34
|
-
let path = pathname;
|
|
35
|
-
while (path.startsWith("/")) {
|
|
36
|
-
path = path.slice(1);
|
|
37
|
-
}
|
|
38
|
-
const url = new URL(format(locale, path), request.url);
|
|
39
|
-
return shouldHideLocale(locale) ? NextResponse.rewrite(url) : NextResponse.redirect(url);
|
|
40
|
-
}
|
|
41
|
-
if (hideLocale === "default-locale" && pathLocale === defaultLanguage) {
|
|
42
|
-
const path = pathname.slice(`/${pathLocale}`.length);
|
|
43
|
-
return NextResponse.redirect(
|
|
44
|
-
new URL(path.startsWith("/") ? path : `/${path}`, request.url)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
return NextResponse.next();
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
5
|
export {
|
|
51
6
|
createI18nMiddleware
|
|
52
7
|
};
|
package/dist/search/server.d.ts
CHANGED
|
@@ -1,9 +1,33 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { S as StructuredData } from '../remark-structure-Dj8oa5Ba.js';
|
|
3
3
|
import { SortedResult } from './shared.js';
|
|
4
|
+
import { I as I18nConfig } from '../config-inq6kP6y.js';
|
|
4
5
|
import 'mdast';
|
|
5
6
|
import 'unified';
|
|
6
7
|
|
|
8
|
+
interface I18nSimpleOptions extends Omit<SimpleOptions, 'language' | 'indexes'> {
|
|
9
|
+
i18n: I18nConfig;
|
|
10
|
+
indexes: WithLocale<Index>[] | Dynamic<WithLocale<Index>>;
|
|
11
|
+
}
|
|
12
|
+
interface I18nAdvancedOptions extends Omit<AdvancedOptions, 'language' | 'indexes'> {
|
|
13
|
+
i18n: I18nConfig;
|
|
14
|
+
indexes: WithLocale<AdvancedIndex>[] | Dynamic<WithLocale<AdvancedIndex>>;
|
|
15
|
+
}
|
|
16
|
+
type WithLocale<T> = T & {
|
|
17
|
+
locale: string;
|
|
18
|
+
};
|
|
19
|
+
declare function createI18nSearchAPI$1<T extends 'simple' | 'advanced'>(type: T, options: T extends 'simple' ? I18nSimpleOptions : I18nAdvancedOptions): SearchAPI;
|
|
20
|
+
|
|
21
|
+
type ToI18n<T extends {
|
|
22
|
+
indexes: unknown;
|
|
23
|
+
}> = Omit<T, 'indexes' | 'language'> & {
|
|
24
|
+
indexes: ([language: string, indexes: T['indexes']] | {
|
|
25
|
+
language: string;
|
|
26
|
+
indexes: T['indexes'];
|
|
27
|
+
})[];
|
|
28
|
+
};
|
|
29
|
+
declare function createI18nSearchAPI<T extends 'simple' | 'advanced'>(type: T, options: T extends 'simple' ? ToI18n<SimpleOptions> : ToI18n<AdvancedOptions>): SearchAPI;
|
|
30
|
+
|
|
7
31
|
interface SearchAPI {
|
|
8
32
|
GET: (request: NextRequest) => Promise<NextResponse<SortedResult[]>>;
|
|
9
33
|
search: (query: string, options?: {
|
|
@@ -29,16 +53,7 @@ interface AdvancedOptions {
|
|
|
29
53
|
tag?: boolean;
|
|
30
54
|
language?: string;
|
|
31
55
|
}
|
|
32
|
-
type ToI18n<T extends {
|
|
33
|
-
indexes: unknown;
|
|
34
|
-
}> = Omit<T, 'indexes' | 'language'> & {
|
|
35
|
-
indexes: ([language: string, indexes: T['indexes']] | {
|
|
36
|
-
language: string;
|
|
37
|
-
indexes: T['indexes'];
|
|
38
|
-
})[];
|
|
39
|
-
};
|
|
40
56
|
declare function createSearchAPI<T extends 'simple' | 'advanced'>(type: T, options: T extends 'simple' ? SimpleOptions : AdvancedOptions): SearchAPI;
|
|
41
|
-
declare function createI18nSearchAPI<T extends 'simple' | 'advanced'>(type: T, options: T extends 'simple' ? ToI18n<SimpleOptions> : ToI18n<AdvancedOptions>): SearchAPI;
|
|
42
57
|
interface Index {
|
|
43
58
|
title: string;
|
|
44
59
|
description?: string;
|
|
@@ -64,4 +79,4 @@ interface AdvancedIndex {
|
|
|
64
79
|
}
|
|
65
80
|
declare function initSearchAPIAdvanced({ indexes, language, tag, }: AdvancedOptions): SearchAPI;
|
|
66
81
|
|
|
67
|
-
export { type AdvancedIndex, type Index, createI18nSearchAPI, createSearchAPI, initSearchAPI, initSearchAPIAdvanced };
|
|
82
|
+
export { type AdvancedIndex, type AdvancedOptions, type Dynamic, type Index, type SearchAPI, type SimpleOptions, createI18nSearchAPI, createI18nSearchAPI$1 as createI18nSearchAPIExperimental, createSearchAPI, initSearchAPI, initSearchAPIAdvanced };
|
package/dist/search/server.js
CHANGED
|
@@ -2,8 +2,10 @@ import "../chunk-MLKGABMK.js";
|
|
|
2
2
|
|
|
3
3
|
// src/search/server.ts
|
|
4
4
|
import { Document } from "flexsearch";
|
|
5
|
+
|
|
6
|
+
// src/search/create-endpoint.ts
|
|
5
7
|
import { NextResponse } from "next/server";
|
|
6
|
-
function
|
|
8
|
+
function createEndpoint(search) {
|
|
7
9
|
return {
|
|
8
10
|
search,
|
|
9
11
|
async GET(request) {
|
|
@@ -18,13 +20,32 @@ function create(search) {
|
|
|
18
20
|
}
|
|
19
21
|
};
|
|
20
22
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return initSearchAPI(options);
|
|
24
|
-
}
|
|
25
|
-
return initSearchAPIAdvanced(options);
|
|
26
|
-
}
|
|
23
|
+
|
|
24
|
+
// src/search/i18n-api.ts
|
|
27
25
|
function createI18nSearchAPI(type, options) {
|
|
26
|
+
const map = /* @__PURE__ */ new Map();
|
|
27
|
+
return createEndpoint(async (query, searchOptions) => {
|
|
28
|
+
if (map.size === 0) {
|
|
29
|
+
const indexes = typeof options.indexes === "function" ? await options.indexes() : options.indexes;
|
|
30
|
+
for (const locale of options.i18n.languages) {
|
|
31
|
+
const api = createSearchAPI(type, {
|
|
32
|
+
...options,
|
|
33
|
+
language: locale,
|
|
34
|
+
indexes: indexes.filter((index) => index.locale === locale)
|
|
35
|
+
});
|
|
36
|
+
map.set(locale, api);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const handler = map.get(
|
|
40
|
+
searchOptions?.locale ?? options.i18n.defaultLanguage
|
|
41
|
+
);
|
|
42
|
+
if (handler) return handler.search(query, searchOptions);
|
|
43
|
+
return [];
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/search/legacy-i18n-api.ts
|
|
48
|
+
function createI18nSearchAPI2(type, options) {
|
|
28
49
|
const map = /* @__PURE__ */ new Map();
|
|
29
50
|
for (const entry of options.indexes) {
|
|
30
51
|
const v = Array.isArray(entry) ? { language: entry[0], indexes: entry[1] } : entry;
|
|
@@ -38,7 +59,7 @@ function createI18nSearchAPI(type, options) {
|
|
|
38
59
|
})
|
|
39
60
|
);
|
|
40
61
|
}
|
|
41
|
-
return
|
|
62
|
+
return createEndpoint(async (query, searchOptions) => {
|
|
42
63
|
if (searchOptions?.locale) {
|
|
43
64
|
const handler = map.get(searchOptions.locale);
|
|
44
65
|
if (handler) return handler.search(query, searchOptions);
|
|
@@ -46,6 +67,14 @@ function createI18nSearchAPI(type, options) {
|
|
|
46
67
|
return [];
|
|
47
68
|
});
|
|
48
69
|
}
|
|
70
|
+
|
|
71
|
+
// src/search/server.ts
|
|
72
|
+
function createSearchAPI(type, options) {
|
|
73
|
+
if (type === "simple") {
|
|
74
|
+
return initSearchAPI(options);
|
|
75
|
+
}
|
|
76
|
+
return initSearchAPIAdvanced(options);
|
|
77
|
+
}
|
|
49
78
|
function initSearchAPI({ indexes, language }) {
|
|
50
79
|
const store = ["title", "url"];
|
|
51
80
|
async function getDocument() {
|
|
@@ -99,7 +128,7 @@ function initSearchAPI({ indexes, language }) {
|
|
|
99
128
|
return index;
|
|
100
129
|
}
|
|
101
130
|
const doc = getDocument();
|
|
102
|
-
return
|
|
131
|
+
return createEndpoint(async (query) => {
|
|
103
132
|
const results = (await doc).search(query, 5, {
|
|
104
133
|
enrich: true,
|
|
105
134
|
suggest: true
|
|
@@ -189,7 +218,7 @@ function initSearchAPIAdvanced({
|
|
|
189
218
|
return index;
|
|
190
219
|
}
|
|
191
220
|
const doc = getDocument();
|
|
192
|
-
return
|
|
221
|
+
return createEndpoint(async (query, options) => {
|
|
193
222
|
const index = await doc;
|
|
194
223
|
const results = index.search(query, 5, {
|
|
195
224
|
enrich: true,
|
|
@@ -229,7 +258,8 @@ function initSearchAPIAdvanced({
|
|
|
229
258
|
});
|
|
230
259
|
}
|
|
231
260
|
export {
|
|
232
|
-
createI18nSearchAPI,
|
|
261
|
+
createI18nSearchAPI2 as createI18nSearchAPI,
|
|
262
|
+
createI18nSearchAPI as createI18nSearchAPIExperimental,
|
|
233
263
|
createSearchAPI,
|
|
234
264
|
initSearchAPI,
|
|
235
265
|
initSearchAPIAdvanced
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { a as TOCItemType, T as TableOfContents, g as getTableOfContents } from '../get-toc-CM4X3hbw.js';
|
|
2
2
|
import { N as Node, I as Item, R as Root } from '../page-tree-BTCDMLTU.js';
|
|
3
3
|
export { p as PageTree } from '../page-tree-BTCDMLTU.js';
|
|
4
|
+
export { SortedResult } from '../search/shared.js';
|
|
4
5
|
import 'react';
|
|
5
6
|
|
|
6
7
|
/**
|
package/dist/sidebar.js
CHANGED
package/dist/source/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
+
import { I as I18nConfig } from '../config-inq6kP6y.js';
|
|
2
3
|
import { R as Root, I as Item, F as Folder$1, S as Separator } from '../page-tree-BTCDMLTU.js';
|
|
3
4
|
|
|
4
5
|
interface FileInfo {
|
|
@@ -49,7 +50,7 @@ interface SourceConfig {
|
|
|
49
50
|
pageData: PageData;
|
|
50
51
|
metaData: MetaData;
|
|
51
52
|
}
|
|
52
|
-
interface LoaderOptions
|
|
53
|
+
interface LoaderOptions {
|
|
53
54
|
/**
|
|
54
55
|
* @defaultValue `''`
|
|
55
56
|
*/
|
|
@@ -67,6 +68,24 @@ interface LoaderOptions extends Pick<BuildPageTreeOptionsWithI18n, 'languages' |
|
|
|
67
68
|
* Additional options for page tree builder
|
|
68
69
|
*/
|
|
69
70
|
pageTree?: Partial<Omit<BuildPageTreeOptions, 'storage' | 'getUrl'>>;
|
|
71
|
+
/**
|
|
72
|
+
* Configure i18n
|
|
73
|
+
*/
|
|
74
|
+
i18n?: I18nConfig;
|
|
75
|
+
/**
|
|
76
|
+
* Accepted languages.
|
|
77
|
+
*
|
|
78
|
+
* A page tree will be built for each language.
|
|
79
|
+
*
|
|
80
|
+
* @deprecated Use `i18n` instead
|
|
81
|
+
*/
|
|
82
|
+
languages?: string[];
|
|
83
|
+
/**
|
|
84
|
+
* Default locale when locale is not provided.
|
|
85
|
+
*
|
|
86
|
+
* @deprecated Use `i18n` instead
|
|
87
|
+
*/
|
|
88
|
+
defaultLanguage?: string;
|
|
70
89
|
}
|
|
71
90
|
interface Source<Config extends SourceConfig> {
|
|
72
91
|
/**
|
|
@@ -109,7 +128,7 @@ declare function getSlugs(info: FileInfo): string[];
|
|
|
109
128
|
type InferSourceConfig<T> = T extends Source<infer Config> ? Config : never;
|
|
110
129
|
declare function loader<Options extends LoaderOptions>(options: Options): LoaderOutput<{
|
|
111
130
|
source: InferSourceConfig<Options['source']>;
|
|
112
|
-
i18n: Options['languages'] extends string[] ? true : false;
|
|
131
|
+
i18n: Options['i18n'] extends I18nConfig ? true : Options['languages'] extends string[] ? true : false;
|
|
113
132
|
}>;
|
|
114
133
|
|
|
115
134
|
interface MetaData {
|
|
@@ -201,14 +220,7 @@ interface BuildPageTreeOptions {
|
|
|
201
220
|
resolveIcon?: (icon: string | undefined) => ReactElement | undefined;
|
|
202
221
|
}
|
|
203
222
|
interface BuildPageTreeOptionsWithI18n extends BuildPageTreeOptions {
|
|
204
|
-
|
|
205
|
-
* Build a page tree for each language
|
|
206
|
-
*/
|
|
207
|
-
languages?: string[];
|
|
208
|
-
/**
|
|
209
|
-
* Hide the locale prefix from URLs if it is same as the specified default locale.
|
|
210
|
-
*/
|
|
211
|
-
defaultLanguage?: string;
|
|
223
|
+
i18n: I18nConfig;
|
|
212
224
|
}
|
|
213
225
|
interface PageTreeBuilder {
|
|
214
226
|
build: (options: BuildPageTreeOptions) => Root;
|
package/dist/source/index.js
CHANGED
|
@@ -6,30 +6,33 @@ import {
|
|
|
6
6
|
} from "../chunk-MLKGABMK.js";
|
|
7
7
|
|
|
8
8
|
// src/source/path.ts
|
|
9
|
-
import { parse } from "path";
|
|
10
9
|
function parseFilePath(path) {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
10
|
+
const segments = splitPath(slash(path));
|
|
11
|
+
const dirname = segments.slice(0, -1).join("/");
|
|
12
|
+
const base = segments.at(-1) ?? "";
|
|
13
|
+
const dotIdx = base.lastIndexOf(".");
|
|
14
|
+
const nameWithLocale = dotIdx !== -1 ? base.slice(0, dotIdx) : base;
|
|
15
|
+
const flattenedPath = [dirname, nameWithLocale].filter((p) => p.length > 0).join("/");
|
|
16
|
+
const [name, locale] = nameWithLocale.split(".");
|
|
15
17
|
return {
|
|
16
|
-
dirname
|
|
18
|
+
dirname,
|
|
17
19
|
name,
|
|
18
20
|
flattenedPath,
|
|
19
21
|
locale,
|
|
20
|
-
path:
|
|
22
|
+
path: segments.join("/")
|
|
21
23
|
};
|
|
22
24
|
}
|
|
23
25
|
function parseFolderPath(path) {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const [name, locale] =
|
|
26
|
+
const segments = splitPath(slash(path));
|
|
27
|
+
const base = segments.at(-1) ?? "";
|
|
28
|
+
const [name, locale] = base.split(".");
|
|
29
|
+
const flattenedPath = segments.join("/");
|
|
27
30
|
return {
|
|
28
|
-
dirname:
|
|
31
|
+
dirname: segments.slice(0, -1).join("/"),
|
|
29
32
|
name,
|
|
30
|
-
flattenedPath
|
|
33
|
+
flattenedPath,
|
|
31
34
|
locale,
|
|
32
|
-
path:
|
|
35
|
+
path: flattenedPath
|
|
33
36
|
};
|
|
34
37
|
}
|
|
35
38
|
function normalizePath(path) {
|
|
@@ -63,6 +66,8 @@ var group = /^\((?<name>.+)\)$/;
|
|
|
63
66
|
var link = /^\[(?<text>.+)]\((?<url>.+)\)$/;
|
|
64
67
|
var separator = /^---(?<name>.*?)---$/;
|
|
65
68
|
var rest = "...";
|
|
69
|
+
var extractPrefix = "...";
|
|
70
|
+
var excludePrefix = "!";
|
|
66
71
|
function buildAll(nodes, ctx, skipIndex) {
|
|
67
72
|
const output = [];
|
|
68
73
|
const folders = [];
|
|
@@ -106,12 +111,12 @@ function resolveFolderItem(folder, item, ctx, addedNodePaths) {
|
|
|
106
111
|
};
|
|
107
112
|
return [ctx.options.attachFile?.(node) ?? node];
|
|
108
113
|
}
|
|
109
|
-
const isExcept = item.startsWith(
|
|
114
|
+
const isExcept = item.startsWith(excludePrefix), isExtract = item.startsWith(extractPrefix);
|
|
110
115
|
let filename = item;
|
|
111
116
|
if (isExcept) {
|
|
112
|
-
filename = item.slice(
|
|
117
|
+
filename = item.slice(excludePrefix.length);
|
|
113
118
|
} else if (isExtract) {
|
|
114
|
-
filename = item.slice(
|
|
119
|
+
filename = item.slice(extractPrefix.length);
|
|
115
120
|
}
|
|
116
121
|
const path = resolvePath(folder.file.path, filename);
|
|
117
122
|
const itemNode = ctx.storage.readDir(path) ?? ctx.storage.read(path, "page");
|
|
@@ -173,12 +178,19 @@ function buildFolderNode(folder, isGlobalRoot, ctx) {
|
|
|
173
178
|
);
|
|
174
179
|
}
|
|
175
180
|
function buildFileNode(file, ctx) {
|
|
181
|
+
let urlLocale;
|
|
176
182
|
const localized = findLocalizedFile(file.file.flattenedPath, "page", ctx) ?? file;
|
|
183
|
+
const hideLocale = ctx.i18n?.hideLocale ?? "never";
|
|
184
|
+
if (hideLocale === "never") {
|
|
185
|
+
urlLocale = ctx.lang;
|
|
186
|
+
} else if (hideLocale === "default-locale" && ctx.lang !== ctx.i18n?.defaultLanguage) {
|
|
187
|
+
urlLocale = ctx.lang;
|
|
188
|
+
}
|
|
177
189
|
const item = {
|
|
178
190
|
type: "page",
|
|
179
191
|
name: localized.data.data.title,
|
|
180
192
|
icon: ctx.options.resolveIcon?.(localized.data.data.icon),
|
|
181
|
-
url: ctx.options.getUrl(localized.data.slugs,
|
|
193
|
+
url: ctx.options.getUrl(localized.data.slugs, urlLocale)
|
|
182
194
|
};
|
|
183
195
|
return removeUndefined(ctx.options.attachFile?.(item, file) ?? item);
|
|
184
196
|
}
|
|
@@ -199,13 +211,14 @@ function createPageTreeBuilder() {
|
|
|
199
211
|
storage: options.storage
|
|
200
212
|
});
|
|
201
213
|
},
|
|
202
|
-
buildI18n({
|
|
203
|
-
const entries = languages.map((lang) => {
|
|
214
|
+
buildI18n({ i18n, ...options }) {
|
|
215
|
+
const entries = i18n.languages.map((lang) => {
|
|
204
216
|
const tree = build({
|
|
205
|
-
lang
|
|
217
|
+
lang,
|
|
206
218
|
options,
|
|
207
219
|
builder: this,
|
|
208
|
-
storage: options.storage
|
|
220
|
+
storage: options.storage,
|
|
221
|
+
i18n
|
|
209
222
|
});
|
|
210
223
|
return [lang, tree];
|
|
211
224
|
});
|
|
@@ -324,7 +337,7 @@ function loadFiles(files, options) {
|
|
|
324
337
|
}
|
|
325
338
|
|
|
326
339
|
// src/source/loader.ts
|
|
327
|
-
function buildPageMap(storage,
|
|
340
|
+
function buildPageMap(storage, getUrl, languages = []) {
|
|
328
341
|
const map = /* @__PURE__ */ new Map();
|
|
329
342
|
const defaultMap = /* @__PURE__ */ new Map();
|
|
330
343
|
map.set("", defaultMap);
|
|
@@ -361,19 +374,30 @@ function getSlugs(info) {
|
|
|
361
374
|
);
|
|
362
375
|
}
|
|
363
376
|
function loader(options) {
|
|
377
|
+
if (options.languages) {
|
|
378
|
+
console.warn(
|
|
379
|
+
"Fumadocs: It's highly recommended to use `i18n` config instead of passing `languages` to loader."
|
|
380
|
+
);
|
|
381
|
+
return createOutput({
|
|
382
|
+
...options,
|
|
383
|
+
i18n: {
|
|
384
|
+
languages: options.languages,
|
|
385
|
+
defaultLanguage: options.defaultLanguage
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
364
389
|
return createOutput(options);
|
|
365
390
|
}
|
|
366
391
|
function createOutput({
|
|
367
392
|
source,
|
|
368
393
|
icon: resolveIcon,
|
|
369
|
-
languages,
|
|
370
394
|
rootDir = "",
|
|
371
395
|
transformers,
|
|
372
396
|
baseUrl = "/",
|
|
373
397
|
slugs: slugsFn = getSlugs,
|
|
374
398
|
url: getUrl = createGetUrl(baseUrl),
|
|
375
|
-
|
|
376
|
-
|
|
399
|
+
pageTree: pageTreeOptions = {},
|
|
400
|
+
i18n
|
|
377
401
|
}) {
|
|
378
402
|
const storage = loadFiles(
|
|
379
403
|
typeof source.files === "function" ? source.files(rootDir) : source.files,
|
|
@@ -383,25 +407,24 @@ function createOutput({
|
|
|
383
407
|
getSlugs: slugsFn
|
|
384
408
|
}
|
|
385
409
|
);
|
|
386
|
-
const i18nMap = buildPageMap(storage,
|
|
410
|
+
const i18nMap = buildPageMap(storage, getUrl, i18n?.languages);
|
|
387
411
|
const builder = createPageTreeBuilder();
|
|
388
|
-
const pageTree =
|
|
412
|
+
const pageTree = i18n === void 0 ? builder.build({
|
|
389
413
|
storage,
|
|
390
414
|
resolveIcon,
|
|
391
415
|
getUrl,
|
|
392
416
|
...pageTreeOptions
|
|
393
417
|
}) : builder.buildI18n({
|
|
394
|
-
languages,
|
|
395
418
|
storage,
|
|
396
419
|
resolveIcon,
|
|
397
420
|
getUrl,
|
|
398
|
-
|
|
421
|
+
i18n,
|
|
399
422
|
...pageTreeOptions
|
|
400
423
|
});
|
|
401
424
|
return {
|
|
402
425
|
pageTree,
|
|
403
426
|
files: storage.list(),
|
|
404
|
-
getPages(language = "") {
|
|
427
|
+
getPages(language = i18n?.defaultLanguage ?? "") {
|
|
405
428
|
return Array.from(i18nMap.get(language)?.values() ?? []);
|
|
406
429
|
},
|
|
407
430
|
getLanguages() {
|
|
@@ -415,7 +438,7 @@ function createOutput({
|
|
|
415
438
|
}
|
|
416
439
|
return list;
|
|
417
440
|
},
|
|
418
|
-
getPage(slugs = [], language = "") {
|
|
441
|
+
getPage(slugs = [], language = i18n?.defaultLanguage ?? "") {
|
|
419
442
|
return i18nMap.get(language)?.get(slugs.join("/"));
|
|
420
443
|
}
|
|
421
444
|
};
|
package/dist/toc.d.ts
CHANGED
|
@@ -3,11 +3,21 @@ import { ReactNode, RefObject, AnchorHTMLAttributes } from 'react';
|
|
|
3
3
|
import { T as TableOfContents } from './get-toc-CM4X3hbw.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
6
|
+
* The estimated active heading ID
|
|
7
7
|
*/
|
|
8
8
|
declare function useActiveAnchor(): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The id of visible anchors
|
|
11
|
+
*/
|
|
12
|
+
declare function useActiveAnchors(): string[];
|
|
9
13
|
interface AnchorProviderProps {
|
|
10
14
|
toc: TableOfContents;
|
|
15
|
+
/**
|
|
16
|
+
* Only accept one active item at most
|
|
17
|
+
*
|
|
18
|
+
* @defaultValue true
|
|
19
|
+
*/
|
|
20
|
+
single?: boolean;
|
|
11
21
|
children?: ReactNode;
|
|
12
22
|
}
|
|
13
23
|
interface ScrollProviderProps {
|
|
@@ -18,11 +28,11 @@ interface ScrollProviderProps {
|
|
|
18
28
|
children?: ReactNode;
|
|
19
29
|
}
|
|
20
30
|
declare function ScrollProvider({ containerRef, children, }: ScrollProviderProps): React.ReactElement;
|
|
21
|
-
declare function AnchorProvider({ toc, children, }: AnchorProviderProps): React.ReactElement;
|
|
31
|
+
declare function AnchorProvider({ toc, single, children, }: AnchorProviderProps): React.ReactElement;
|
|
22
32
|
interface TOCItemProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
|
|
23
33
|
href: string;
|
|
24
34
|
onActiveChange?: (v: boolean) => void;
|
|
25
35
|
}
|
|
26
36
|
declare const TOCItem: react.ForwardRefExoticComponent<TOCItemProps & react.RefAttributes<HTMLAnchorElement>>;
|
|
27
37
|
|
|
28
|
-
export { AnchorProvider, type AnchorProviderProps, ScrollProvider, type ScrollProviderProps, TOCItem, type TOCItemProps, useActiveAnchor };
|
|
38
|
+
export { AnchorProvider, type AnchorProviderProps, ScrollProvider, type ScrollProviderProps, TOCItem, type TOCItemProps, useActiveAnchor, useActiveAnchors };
|
package/dist/toc.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import {
|
|
2
3
|
useOnChange
|
|
3
4
|
} from "./chunk-KGMG4N3Y.js";
|
|
@@ -22,52 +23,59 @@ function mergeRefs(...refs) {
|
|
|
22
23
|
|
|
23
24
|
// src/utils/use-anchor-observer.ts
|
|
24
25
|
import { useEffect, useState } from "react";
|
|
25
|
-
function useAnchorObserver(watch) {
|
|
26
|
-
const [activeAnchor, setActiveAnchor] = useState();
|
|
26
|
+
function useAnchorObserver(watch, single) {
|
|
27
|
+
const [activeAnchor, setActiveAnchor] = useState([]);
|
|
27
28
|
useEffect(() => {
|
|
29
|
+
let visible = [];
|
|
28
30
|
const observer = new IntersectionObserver(
|
|
29
31
|
(entries) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
if (entry.isIntersecting && !visible.includes(entry.target.id)) {
|
|
34
|
+
visible = [...visible, entry.target.id];
|
|
35
|
+
} else if (!entry.isIntersecting && visible.includes(entry.target.id)) {
|
|
36
|
+
visible = visible.filter((v) => v !== entry.target.id);
|
|
35
37
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
}
|
|
39
|
+
if (visible.length > 0) setActiveAnchor(visible);
|
|
38
40
|
},
|
|
39
|
-
{
|
|
41
|
+
{
|
|
42
|
+
rootMargin: single ? "-80px 0% -70% 0%" : `-20px 0% -40% 0%`,
|
|
43
|
+
threshold: 1
|
|
44
|
+
}
|
|
40
45
|
);
|
|
41
|
-
|
|
46
|
+
function onScroll() {
|
|
42
47
|
const element = document.scrollingElement;
|
|
43
48
|
if (!element) return;
|
|
44
|
-
if (element.scrollTop === 0)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
if (element.scrollTop === 0 && single) setActiveAnchor(watch.slice(0, 1));
|
|
50
|
+
else if (element.scrollTop + element.clientHeight >= element.scrollHeight - 6) {
|
|
51
|
+
setActiveAnchor((active) => {
|
|
52
|
+
const last = active.at(-1);
|
|
53
|
+
return last ? watch.slice(watch.indexOf(last)) : active;
|
|
54
|
+
});
|
|
49
55
|
}
|
|
50
|
-
}
|
|
51
|
-
window.addEventListener("scroll", scroll);
|
|
56
|
+
}
|
|
52
57
|
for (const heading of watch) {
|
|
53
58
|
const element = document.getElementById(heading);
|
|
54
|
-
if (element
|
|
55
|
-
observer.observe(element);
|
|
56
|
-
}
|
|
59
|
+
if (element) observer.observe(element);
|
|
57
60
|
}
|
|
61
|
+
onScroll();
|
|
62
|
+
window.addEventListener("scroll", onScroll);
|
|
58
63
|
return () => {
|
|
59
|
-
window.removeEventListener("scroll",
|
|
64
|
+
window.removeEventListener("scroll", onScroll);
|
|
60
65
|
observer.disconnect();
|
|
61
66
|
};
|
|
62
|
-
}, [watch]);
|
|
63
|
-
return activeAnchor;
|
|
67
|
+
}, [single, watch]);
|
|
68
|
+
return single ? activeAnchor.slice(0, 1) : activeAnchor;
|
|
64
69
|
}
|
|
65
70
|
|
|
66
71
|
// src/toc.tsx
|
|
67
72
|
import { jsx } from "react/jsx-runtime";
|
|
68
|
-
var ActiveAnchorContext = createContext(
|
|
73
|
+
var ActiveAnchorContext = createContext([]);
|
|
69
74
|
var ScrollContext = createContext({ current: null });
|
|
70
75
|
function useActiveAnchor() {
|
|
76
|
+
return useContext(ActiveAnchorContext).at(-1);
|
|
77
|
+
}
|
|
78
|
+
function useActiveAnchors() {
|
|
71
79
|
return useContext(ActiveAnchorContext);
|
|
72
80
|
}
|
|
73
81
|
function ScrollProvider({
|
|
@@ -78,21 +86,21 @@ function ScrollProvider({
|
|
|
78
86
|
}
|
|
79
87
|
function AnchorProvider({
|
|
80
88
|
toc,
|
|
89
|
+
single = true,
|
|
81
90
|
children
|
|
82
91
|
}) {
|
|
83
92
|
const headings = useMemo(() => {
|
|
84
93
|
return toc.map((item) => item.url.split("#")[1]);
|
|
85
94
|
}, [toc]);
|
|
86
|
-
|
|
87
|
-
return /* @__PURE__ */ jsx(ActiveAnchorContext.Provider, { value: activeAnchor, children });
|
|
95
|
+
return /* @__PURE__ */ jsx(ActiveAnchorContext.Provider, { value: useAnchorObserver(headings, single), children });
|
|
88
96
|
}
|
|
89
97
|
var TOCItem = forwardRef(
|
|
90
98
|
({ onActiveChange, ...props }, ref) => {
|
|
91
99
|
const containerRef = useContext(ScrollContext);
|
|
92
|
-
const
|
|
100
|
+
const anchors = useActiveAnchors();
|
|
93
101
|
const anchorRef = useRef(null);
|
|
94
102
|
const mergedRef = mergeRefs(anchorRef, ref);
|
|
95
|
-
const isActive =
|
|
103
|
+
const isActive = anchors.includes(props.href.slice(1));
|
|
96
104
|
useOnChange(isActive, (v) => {
|
|
97
105
|
const element = anchorRef.current;
|
|
98
106
|
if (!element) return;
|
|
@@ -115,5 +123,6 @@ export {
|
|
|
115
123
|
AnchorProvider,
|
|
116
124
|
ScrollProvider,
|
|
117
125
|
TOCItem,
|
|
118
|
-
useActiveAnchor
|
|
126
|
+
useActiveAnchor,
|
|
127
|
+
useActiveAnchors
|
|
119
128
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-core",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.0",
|
|
4
4
|
"description": "The library for building a documentation website in Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
"./search-algolia/server": {
|
|
68
68
|
"import": "./dist/search-algolia/server.js",
|
|
69
69
|
"types": "./dist/search-algolia/server.d.ts"
|
|
70
|
+
},
|
|
71
|
+
"./i18n": {
|
|
72
|
+
"import": "./dist/i18n/index.js",
|
|
73
|
+
"types": "./dist/i18n/index.d.ts"
|
|
70
74
|
}
|
|
71
75
|
},
|
|
72
76
|
"typesVersions": {
|
|
@@ -112,6 +116,9 @@
|
|
|
112
116
|
],
|
|
113
117
|
"search-algolia/server": [
|
|
114
118
|
"./dist/search-algolia/server.d.ts"
|
|
119
|
+
],
|
|
120
|
+
"i18n": [
|
|
121
|
+
"./dist/i18n/index.d.ts"
|
|
115
122
|
]
|
|
116
123
|
}
|
|
117
124
|
},
|
|
@@ -120,8 +127,8 @@
|
|
|
120
127
|
],
|
|
121
128
|
"dependencies": {
|
|
122
129
|
"@formatjs/intl-localematcher": "^0.5.4",
|
|
123
|
-
"@shikijs/rehype": "^1.
|
|
124
|
-
"@shikijs/transformers": "^1.
|
|
130
|
+
"@shikijs/rehype": "^1.13.0",
|
|
131
|
+
"@shikijs/transformers": "^1.13.0",
|
|
125
132
|
"flexsearch": "0.7.21",
|
|
126
133
|
"github-slugger": "^2.0.0",
|
|
127
134
|
"image-size": "^1.1.1",
|
|
@@ -132,7 +139,7 @@
|
|
|
132
139
|
"remark-gfm": "^4.0.0",
|
|
133
140
|
"remark-mdx": "^3.0.1",
|
|
134
141
|
"scroll-into-view-if-needed": "^3.1.0",
|
|
135
|
-
"shiki": "^1.
|
|
142
|
+
"shiki": "^1.13.0",
|
|
136
143
|
"swr": "^2.2.5",
|
|
137
144
|
"unist-util-visit": "^5.0.0"
|
|
138
145
|
},
|
|
@@ -144,7 +151,7 @@
|
|
|
144
151
|
"@types/hast": "^3.0.4",
|
|
145
152
|
"@types/mdast": "^4.0.3",
|
|
146
153
|
"@types/negotiator": "^0.6.3",
|
|
147
|
-
"@types/node": "
|
|
154
|
+
"@types/node": "22.3.0",
|
|
148
155
|
"@types/react": "^18.3.3",
|
|
149
156
|
"@types/react-dom": "^18.3.0",
|
|
150
157
|
"algoliasearch": "^4.24.0",
|