fumadocs-core 15.5.3 → 15.5.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.
- package/dist/{chunk-KNWSJ4IF.js → chunk-3NX26V7I.js} +40 -9
- package/dist/highlight/client.js +7 -15
- package/dist/highlight/index.d.ts +2 -1
- package/dist/highlight/index.js +1 -1
- package/dist/mdx-plugins/index.js +1 -1
- package/dist/source/index.d.ts +6 -5
- package/dist/source/index.js +90 -54
- package/package.json +10 -10
|
@@ -8,18 +8,49 @@ var defaultThemes = {
|
|
|
8
8
|
};
|
|
9
9
|
var highlighters = /* @__PURE__ */ new Map();
|
|
10
10
|
async function _highlight(code, options) {
|
|
11
|
-
const {
|
|
12
|
-
|
|
11
|
+
const {
|
|
12
|
+
lang: initialLang,
|
|
13
|
+
fallbackLanguage,
|
|
14
|
+
components: _,
|
|
15
|
+
engine = "oniguruma",
|
|
16
|
+
...rest
|
|
17
|
+
} = options;
|
|
18
|
+
let lang = initialLang;
|
|
19
|
+
let themes;
|
|
20
|
+
let themesToLoad;
|
|
13
21
|
if ("theme" in options && options.theme) {
|
|
14
22
|
themes = { theme: options.theme };
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
themesToLoad = [themes.theme];
|
|
24
|
+
} else {
|
|
25
|
+
themes = {
|
|
26
|
+
themes: "themes" in options && options.themes ? options.themes : defaultThemes
|
|
27
|
+
};
|
|
28
|
+
themesToLoad = Object.values(themes.themes).filter((v) => v !== void 0);
|
|
29
|
+
}
|
|
30
|
+
let highlighter;
|
|
31
|
+
if (typeof engine === "string") {
|
|
32
|
+
highlighter = await getHighlighter(engine, {
|
|
33
|
+
langs: [],
|
|
34
|
+
themes: themesToLoad
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
highlighter = await getHighlighter("custom", {
|
|
38
|
+
engine,
|
|
39
|
+
langs: [],
|
|
40
|
+
themes: themesToLoad
|
|
41
|
+
});
|
|
42
|
+
if (process.env.NODE_ENV === "development") {
|
|
43
|
+
console.warn(
|
|
44
|
+
"[Fumadocs `highlight()`] Avoid passing `engine` directly. For custom engines, use `shiki` directly instead."
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
await highlighter.loadLanguage(lang);
|
|
50
|
+
} catch {
|
|
51
|
+
lang = fallbackLanguage ?? "text";
|
|
52
|
+
await highlighter.loadLanguage(lang);
|
|
17
53
|
}
|
|
18
|
-
const highlighter = await getHighlighter("custom", {
|
|
19
|
-
engine,
|
|
20
|
-
langs: [lang],
|
|
21
|
-
themes: "theme" in themes ? [themes.theme] : Object.values(themes.themes).filter((v) => v !== void 0)
|
|
22
|
-
});
|
|
23
54
|
return highlighter.codeToHast(code, {
|
|
24
55
|
lang,
|
|
25
56
|
...rest,
|
package/dist/highlight/client.js
CHANGED
|
@@ -3,29 +3,18 @@ import {
|
|
|
3
3
|
_highlight,
|
|
4
4
|
_renderHighlight,
|
|
5
5
|
highlight
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-3NX26V7I.js";
|
|
7
7
|
|
|
8
8
|
// src/highlight/client.tsx
|
|
9
9
|
import {
|
|
10
10
|
use,
|
|
11
|
+
useEffect,
|
|
11
12
|
useId,
|
|
12
|
-
useLayoutEffect,
|
|
13
13
|
useMemo,
|
|
14
14
|
useRef,
|
|
15
15
|
useState
|
|
16
16
|
} from "react";
|
|
17
17
|
import { jsx } from "react/jsx-runtime";
|
|
18
|
-
var jsEngine;
|
|
19
|
-
function getHighlightOptions(from) {
|
|
20
|
-
if (from.engine) return from;
|
|
21
|
-
jsEngine ??= import("shiki/engine/javascript").then(
|
|
22
|
-
(res) => res.createJavaScriptRegexEngine()
|
|
23
|
-
);
|
|
24
|
-
return {
|
|
25
|
-
...from,
|
|
26
|
-
engine: jsEngine
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
18
|
function useShiki(code, {
|
|
30
19
|
withPrerenderScript = false,
|
|
31
20
|
loading,
|
|
@@ -36,7 +25,10 @@ function useShiki(code, {
|
|
|
36
25
|
() => deps ? JSON.stringify(deps) : `${options.lang}:${code}`,
|
|
37
26
|
[code, deps, options.lang]
|
|
38
27
|
);
|
|
39
|
-
const shikiOptions =
|
|
28
|
+
const shikiOptions = {
|
|
29
|
+
...options,
|
|
30
|
+
engine: options.engine ?? "js"
|
|
31
|
+
};
|
|
40
32
|
const currentTask = useRef({
|
|
41
33
|
key,
|
|
42
34
|
aborted: false
|
|
@@ -51,7 +43,7 @@ function useShiki(code, {
|
|
|
51
43
|
currentTask.current = void 0;
|
|
52
44
|
return loading;
|
|
53
45
|
});
|
|
54
|
-
|
|
46
|
+
useEffect(() => {
|
|
55
47
|
if (currentTask.current?.key === key) return;
|
|
56
48
|
if (currentTask.current) {
|
|
57
49
|
currentTask.current.aborted = true;
|
|
@@ -4,8 +4,9 @@ import { Components } from 'hast-util-to-jsx-runtime';
|
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
|
|
6
6
|
type HighlightOptionsCommon = CodeToHastOptionsCommon<BundledLanguage> & CodeOptionsMeta & {
|
|
7
|
-
engine?: Awaitable<RegexEngine>;
|
|
7
|
+
engine?: 'js' | 'oniguruma' | Awaitable<RegexEngine>;
|
|
8
8
|
components?: Partial<Components>;
|
|
9
|
+
fallbackLanguage?: BundledLanguage;
|
|
9
10
|
};
|
|
10
11
|
type HighlightOptionsThemes = CodeOptionsThemes<BundledTheme>;
|
|
11
12
|
type HighlightOptions = HighlightOptionsCommon & (HighlightOptionsThemes | Record<never, never>);
|
package/dist/highlight/index.js
CHANGED
package/dist/source/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare class FileSystem<File> {
|
|
|
22
22
|
|
|
23
23
|
interface LoadOptions {
|
|
24
24
|
transformers?: Transformer[];
|
|
25
|
+
buildFiles: (files: VirtualFile[]) => (MetaFile | PageFile)[];
|
|
25
26
|
}
|
|
26
27
|
type ContentStorage = FileSystem<MetaFile | PageFile>;
|
|
27
28
|
interface MetaFile<Data extends MetaData = MetaData> {
|
|
@@ -41,7 +42,7 @@ type Transformer = (context: {
|
|
|
41
42
|
storage: ContentStorage;
|
|
42
43
|
options: LoadOptions;
|
|
43
44
|
}) => void;
|
|
44
|
-
declare function loadFiles(files: VirtualFile[],
|
|
45
|
+
declare function loadFiles(files: VirtualFile[], options: LoadOptions): ContentStorage;
|
|
45
46
|
|
|
46
47
|
interface FileInfo {
|
|
47
48
|
/**
|
|
@@ -201,14 +202,14 @@ interface LoaderOutput<Config extends LoaderConfig> {
|
|
|
201
202
|
generateParams: <TSlug extends string = 'slug', TLang extends string = 'lang'>(slug?: TSlug, lang?: TLang) => (Record<TSlug, string[]> & Record<TLang, string>)[];
|
|
202
203
|
}
|
|
203
204
|
declare function createGetUrl(baseUrl: string, i18n?: I18nConfig): UrlFn;
|
|
204
|
-
/**
|
|
205
|
-
* Convert file path into slugs, also encode non-ASCII characters, so they can work in pathname
|
|
206
|
-
*/
|
|
207
|
-
declare function getSlugs(info: FileInfo): string[];
|
|
208
205
|
declare function loader<Config extends SourceConfig, I18n extends I18nConfig | undefined = undefined>(options: LoaderOptions<Config, I18n>): LoaderOutput<{
|
|
209
206
|
source: Config;
|
|
210
207
|
i18n: I18n extends I18nConfig ? true : false;
|
|
211
208
|
}>;
|
|
209
|
+
/**
|
|
210
|
+
* Convert file path into slugs, also encode non-ASCII characters, so they can work in pathname
|
|
211
|
+
*/
|
|
212
|
+
declare function getSlugs(file: string | FileInfo): string[];
|
|
212
213
|
|
|
213
214
|
interface MetaData {
|
|
214
215
|
icon?: string | undefined;
|
package/dist/source/index.js
CHANGED
|
@@ -265,18 +265,15 @@ var FileSystem = class {
|
|
|
265
265
|
};
|
|
266
266
|
|
|
267
267
|
// src/source/load-files.ts
|
|
268
|
-
function loadFiles(files,
|
|
268
|
+
function loadFiles(files, options) {
|
|
269
269
|
const { transformers = [] } = options;
|
|
270
270
|
const storage = new FileSystem();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
path: parsedPath
|
|
278
|
-
})
|
|
279
|
-
);
|
|
271
|
+
const normalized = files.map((file) => ({
|
|
272
|
+
...file,
|
|
273
|
+
path: normalizePath(file.path)
|
|
274
|
+
}));
|
|
275
|
+
for (const item of options.buildFiles(normalized)) {
|
|
276
|
+
storage.write(item.path, item);
|
|
280
277
|
}
|
|
281
278
|
for (const transformer of transformers) {
|
|
282
279
|
transformer({
|
|
@@ -286,14 +283,14 @@ function loadFiles(files, buildFile, options) {
|
|
|
286
283
|
}
|
|
287
284
|
return storage;
|
|
288
285
|
}
|
|
289
|
-
function loadFilesI18n(files,
|
|
290
|
-
const parser =
|
|
286
|
+
function loadFilesI18n(files, options, i18n) {
|
|
287
|
+
const parser = i18n.parser === "dir" ? dirParser : dotParser;
|
|
291
288
|
const storages = {};
|
|
292
|
-
for (const lang of
|
|
289
|
+
for (const lang of i18n.languages) {
|
|
293
290
|
storages[lang] = loadFiles(
|
|
294
291
|
files.flatMap((file) => {
|
|
295
292
|
const [path, locale] = parser(normalizePath(file.path));
|
|
296
|
-
if ((locale ??
|
|
293
|
+
if ((locale ?? i18n.defaultLanguage) === lang) {
|
|
297
294
|
return {
|
|
298
295
|
...file,
|
|
299
296
|
path
|
|
@@ -301,7 +298,6 @@ function loadFilesI18n(files, buildFile, options) {
|
|
|
301
298
|
}
|
|
302
299
|
return [];
|
|
303
300
|
}),
|
|
304
|
-
buildFile,
|
|
305
301
|
options
|
|
306
302
|
);
|
|
307
303
|
}
|
|
@@ -391,16 +387,6 @@ function createGetUrl(baseUrl, i18n) {
|
|
|
391
387
|
return `/${paths.filter((v) => v.length > 0).join("/")}`;
|
|
392
388
|
};
|
|
393
389
|
}
|
|
394
|
-
function getSlugs(info) {
|
|
395
|
-
const slugs = [];
|
|
396
|
-
for (const seg of info.dirname.split("/")) {
|
|
397
|
-
if (seg.length > 0 && !/^\(.+\)$/.test(seg)) slugs.push(encodeURI(seg));
|
|
398
|
-
}
|
|
399
|
-
if (info.name !== "index") {
|
|
400
|
-
slugs.push(encodeURI(info.name));
|
|
401
|
-
}
|
|
402
|
-
return slugs;
|
|
403
|
-
}
|
|
404
390
|
function loader(options) {
|
|
405
391
|
return createOutput(options);
|
|
406
392
|
}
|
|
@@ -408,35 +394,67 @@ function createOutput(options) {
|
|
|
408
394
|
if (!options.url && !options.baseUrl) {
|
|
409
395
|
console.warn("`loader()` now requires a `baseUrl` option to be defined.");
|
|
410
396
|
}
|
|
411
|
-
const {
|
|
397
|
+
const {
|
|
398
|
+
source,
|
|
399
|
+
baseUrl = "/",
|
|
400
|
+
i18n,
|
|
401
|
+
slugs: slugsFn,
|
|
402
|
+
url: getUrl = createGetUrl(baseUrl ?? "/", i18n),
|
|
403
|
+
transformers
|
|
404
|
+
} = options;
|
|
412
405
|
const defaultLanguage = i18n?.defaultLanguage ?? "";
|
|
413
|
-
const getUrl = options.url ?? createGetUrl(options.baseUrl ?? "/", options.i18n);
|
|
414
406
|
const files = typeof source.files === "function" ? source.files() : source.files;
|
|
415
|
-
function
|
|
416
|
-
|
|
407
|
+
function buildFiles(files2) {
|
|
408
|
+
const indexFiles = [];
|
|
409
|
+
const taken = /* @__PURE__ */ new Set();
|
|
410
|
+
for (const file of files2) {
|
|
411
|
+
if (file.type !== "page" || file.slugs) continue;
|
|
412
|
+
if (isIndex(file.path) && !slugsFn) {
|
|
413
|
+
indexFiles.push(file);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
file.slugs = slugsFn ? slugsFn(parseFilePath(file.path)) : getSlugs(file.path);
|
|
417
|
+
const key = file.slugs.join("/");
|
|
418
|
+
if (taken.has(key)) throw new Error("Duplicated slugs");
|
|
419
|
+
taken.add(key);
|
|
420
|
+
}
|
|
421
|
+
for (const file of indexFiles) {
|
|
422
|
+
file.slugs = getSlugs(file.path);
|
|
423
|
+
if (taken.has(file.slugs.join("/"))) file.slugs.push("index");
|
|
424
|
+
}
|
|
425
|
+
return files2.map((file) => {
|
|
426
|
+
if (file.type === "page") {
|
|
427
|
+
return {
|
|
428
|
+
format: "page",
|
|
429
|
+
path: file.path,
|
|
430
|
+
slugs: file.slugs,
|
|
431
|
+
data: file.data,
|
|
432
|
+
absolutePath: file.absolutePath ?? ""
|
|
433
|
+
};
|
|
434
|
+
}
|
|
417
435
|
return {
|
|
418
|
-
format: "
|
|
436
|
+
format: "meta",
|
|
419
437
|
path: file.path,
|
|
420
|
-
|
|
421
|
-
data: file.data
|
|
422
|
-
absolutePath: file.absolutePath ?? ""
|
|
438
|
+
absolutePath: file.absolutePath ?? "",
|
|
439
|
+
data: file.data
|
|
423
440
|
};
|
|
424
|
-
}
|
|
425
|
-
return {
|
|
426
|
-
format: "meta",
|
|
427
|
-
path: file.path,
|
|
428
|
-
absolutePath: file.absolutePath ?? "",
|
|
429
|
-
data: file.data
|
|
430
|
-
};
|
|
441
|
+
});
|
|
431
442
|
}
|
|
432
|
-
const storages = i18n ? loadFilesI18n(
|
|
433
|
-
|
|
434
|
-
|
|
443
|
+
const storages = i18n ? loadFilesI18n(
|
|
444
|
+
files,
|
|
445
|
+
{
|
|
446
|
+
buildFiles,
|
|
447
|
+
transformers
|
|
448
|
+
},
|
|
449
|
+
{
|
|
435
450
|
...i18n,
|
|
436
451
|
parser: i18n.parser ?? "dot"
|
|
437
452
|
}
|
|
438
|
-
|
|
439
|
-
"": loadFiles(files,
|
|
453
|
+
) : {
|
|
454
|
+
"": loadFiles(files, {
|
|
455
|
+
transformers,
|
|
456
|
+
buildFiles
|
|
457
|
+
})
|
|
440
458
|
};
|
|
441
459
|
const walker = indexPages(storages, getUrl, i18n);
|
|
442
460
|
const builder = createPageTreeBuilder(getUrl);
|
|
@@ -464,20 +482,19 @@ function createOutput(options) {
|
|
|
464
482
|
pageTree = v;
|
|
465
483
|
},
|
|
466
484
|
getPageByHref(href, { dir = "", language } = {}) {
|
|
467
|
-
const pages = this.getPages(language);
|
|
468
485
|
const [value, hash] = href.split("#", 2);
|
|
469
486
|
let target;
|
|
470
487
|
if (value.startsWith(".") && (value.endsWith(".md") || value.endsWith(".mdx"))) {
|
|
471
|
-
const
|
|
472
|
-
target =
|
|
488
|
+
const path = joinPath(dir, value);
|
|
489
|
+
target = walker.pathToPage.get(`${language}.${path}`);
|
|
473
490
|
} else {
|
|
474
|
-
target =
|
|
491
|
+
target = this.getPages(language).find((item) => item.url === value);
|
|
475
492
|
}
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
493
|
+
if (target)
|
|
494
|
+
return {
|
|
495
|
+
page: target,
|
|
496
|
+
hash
|
|
497
|
+
};
|
|
481
498
|
},
|
|
482
499
|
getPages(language = defaultLanguage) {
|
|
483
500
|
const pages = [];
|
|
@@ -555,6 +572,25 @@ function fileToPage(file, getUrl, locale) {
|
|
|
555
572
|
locale
|
|
556
573
|
};
|
|
557
574
|
}
|
|
575
|
+
var GroupRegex = /^\(.+\)$/;
|
|
576
|
+
function isIndex(file) {
|
|
577
|
+
return basename(file, extname(file)) === "index";
|
|
578
|
+
}
|
|
579
|
+
function getSlugs(file) {
|
|
580
|
+
if (typeof file !== "string") return getSlugs(file.path);
|
|
581
|
+
const dir = dirname(file);
|
|
582
|
+
const name = basename(file, extname(file));
|
|
583
|
+
const slugs = [];
|
|
584
|
+
for (const seg of dir.split("/")) {
|
|
585
|
+
if (seg.length > 0 && !GroupRegex.test(seg)) slugs.push(encodeURI(seg));
|
|
586
|
+
}
|
|
587
|
+
if (GroupRegex.test(name))
|
|
588
|
+
throw new Error(`Cannot use folder group in file names: ${file}`);
|
|
589
|
+
if (name !== "index") {
|
|
590
|
+
slugs.push(encodeURI(name));
|
|
591
|
+
}
|
|
592
|
+
return slugs;
|
|
593
|
+
}
|
|
558
594
|
export {
|
|
559
595
|
FileSystem,
|
|
560
596
|
createGetUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-core",
|
|
3
|
-
"version": "15.5.
|
|
3
|
+
"version": "15.5.4",
|
|
4
4
|
"description": "The library for building a documentation website in Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@formatjs/intl-localematcher": "^0.6.1",
|
|
89
89
|
"@orama/orama": "^3.1.6",
|
|
90
|
-
"@shikijs/rehype": "^3.
|
|
91
|
-
"@shikijs/transformers": "^3.
|
|
90
|
+
"@shikijs/rehype": "^3.7.0",
|
|
91
|
+
"@shikijs/transformers": "^3.7.0",
|
|
92
92
|
"github-slugger": "^2.0.0",
|
|
93
93
|
"hast-util-to-estree": "^3.1.3",
|
|
94
94
|
"hast-util-to-jsx-runtime": "^2.3.6",
|
|
@@ -99,24 +99,24 @@
|
|
|
99
99
|
"remark-gfm": "^4.0.1",
|
|
100
100
|
"remark-rehype": "^11.1.2",
|
|
101
101
|
"scroll-into-view-if-needed": "^3.1.0",
|
|
102
|
-
"shiki": "^3.
|
|
102
|
+
"shiki": "^3.7.0",
|
|
103
103
|
"unist-util-visit": "^5.0.0"
|
|
104
104
|
},
|
|
105
105
|
"devDependencies": {
|
|
106
106
|
"@mdx-js/mdx": "^3.1.0",
|
|
107
107
|
"@oramacloud/client": "^2.1.4",
|
|
108
|
-
"@tanstack/react-router": "^1.121.
|
|
108
|
+
"@tanstack/react-router": "^1.121.27",
|
|
109
109
|
"@types/estree-jsx": "^1.0.5",
|
|
110
110
|
"@types/hast": "^3.0.4",
|
|
111
111
|
"@types/mdast": "^4.0.3",
|
|
112
112
|
"@types/negotiator": "^0.6.4",
|
|
113
|
-
"@types/node": "24.0.
|
|
113
|
+
"@types/node": "24.0.3",
|
|
114
114
|
"@types/react": "^19.1.8",
|
|
115
115
|
"@types/react-dom": "^19.1.6",
|
|
116
|
-
"algoliasearch": "5.
|
|
116
|
+
"algoliasearch": "5.29.0",
|
|
117
117
|
"mdast-util-mdx-jsx": "^3.2.0",
|
|
118
118
|
"mdast-util-mdxjs-esm": "^2.0.1",
|
|
119
|
-
"next": "^15.3.
|
|
119
|
+
"next": "^15.3.4",
|
|
120
120
|
"react-router": "^7.6.2",
|
|
121
121
|
"remark-mdx": "^3.1.0",
|
|
122
122
|
"typescript": "^5.8.3",
|
|
@@ -127,11 +127,11 @@
|
|
|
127
127
|
},
|
|
128
128
|
"peerDependencies": {
|
|
129
129
|
"@oramacloud/client": "1.x.x || 2.x.x",
|
|
130
|
+
"@types/react": "*",
|
|
130
131
|
"algoliasearch": "5.x.x",
|
|
131
132
|
"next": "14.x.x || 15.x.x",
|
|
132
133
|
"react": "18.x.x || 19.x.x",
|
|
133
|
-
"react-dom": "18.x.x || 19.x.x"
|
|
134
|
-
"@types/react": "*"
|
|
134
|
+
"react-dom": "18.x.x || 19.x.x"
|
|
135
135
|
},
|
|
136
136
|
"peerDependenciesMeta": {
|
|
137
137
|
"@types/react": {
|