fumadocs-core 15.1.1 → 15.1.2
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.
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
function splitPath(path) {
|
|
3
3
|
return path.split("/").filter((p) => p.length > 0);
|
|
4
4
|
}
|
|
5
|
-
function joinPath(
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
function joinPath(...paths) {
|
|
6
|
+
const out = [];
|
|
7
|
+
const parsed = paths.flatMap(splitPath);
|
|
8
|
+
while (parsed.length > 0) {
|
|
9
|
+
switch (parsed[0]) {
|
|
9
10
|
case "..":
|
|
10
|
-
|
|
11
|
+
out.pop();
|
|
11
12
|
break;
|
|
12
13
|
case ".":
|
|
13
14
|
break;
|
|
14
15
|
default:
|
|
15
|
-
|
|
16
|
+
out.push(parsed[0]);
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
parsed.shift();
|
|
18
19
|
}
|
|
19
|
-
return
|
|
20
|
+
return out.join("/");
|
|
20
21
|
}
|
|
21
22
|
function slash(path) {
|
|
22
23
|
const isExtendedLengthPath = path.startsWith("\\\\?\\");
|
package/dist/source/index.d.ts
CHANGED
|
@@ -3,12 +3,6 @@ import { I as I18nConfig } from '../config-inq6kP6y.js';
|
|
|
3
3
|
import { R as Root, I as Item, F as Folder$1, S as Separator } from '../page-tree-9q98UqWL.js';
|
|
4
4
|
|
|
5
5
|
interface FileInfo {
|
|
6
|
-
/**
|
|
7
|
-
* locale extension from the last second `.`, like `.en`
|
|
8
|
-
*
|
|
9
|
-
* empty string if no locale
|
|
10
|
-
*/
|
|
11
|
-
locale: string;
|
|
12
6
|
/**
|
|
13
7
|
* File path without extension
|
|
14
8
|
*
|
|
@@ -47,9 +41,15 @@ declare function parseFolderPath(path: string): FolderInfo;
|
|
|
47
41
|
|
|
48
42
|
interface LoadOptions {
|
|
49
43
|
transformers?: Transformer[];
|
|
50
|
-
rootDir?: string;
|
|
51
44
|
getSlugs: (info: FileInfo) => string[];
|
|
52
45
|
}
|
|
46
|
+
interface I18nLoadOptions extends LoadOptions {
|
|
47
|
+
i18n: {
|
|
48
|
+
parser: 'dot' | 'dir';
|
|
49
|
+
languages: string[];
|
|
50
|
+
defaultLanguage: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
53
|
interface VirtualFile {
|
|
54
54
|
/**
|
|
55
55
|
* Relative path
|
|
@@ -68,7 +68,7 @@ type Transformer = (context: {
|
|
|
68
68
|
storage: Storage;
|
|
69
69
|
options: LoadOptions;
|
|
70
70
|
}) => void;
|
|
71
|
-
declare function loadFiles(files: VirtualFile[], options:
|
|
71
|
+
declare function loadFiles<O extends LoadOptions>(files: VirtualFile[], options: O): Storage;
|
|
72
72
|
|
|
73
73
|
interface LoaderConfig {
|
|
74
74
|
source: SourceConfig;
|
|
@@ -79,11 +79,6 @@ interface SourceConfig {
|
|
|
79
79
|
metaData: MetaData;
|
|
80
80
|
}
|
|
81
81
|
interface LoaderOptions {
|
|
82
|
-
/**
|
|
83
|
-
* @deprecated It is now recommended to filter files on `source` level
|
|
84
|
-
* @defaultValue `''`
|
|
85
|
-
*/
|
|
86
|
-
rootDir?: string;
|
|
87
82
|
baseUrl: string;
|
|
88
83
|
icon?: NonNullable<BuildPageTreeOptions['resolveIcon']>;
|
|
89
84
|
slugs?: LoadOptions['getSlugs'];
|
|
@@ -97,14 +92,16 @@ interface LoaderOptions {
|
|
|
97
92
|
/**
|
|
98
93
|
* Configure i18n
|
|
99
94
|
*/
|
|
100
|
-
i18n?: I18nConfig
|
|
95
|
+
i18n?: I18nConfig & {
|
|
96
|
+
parser?: I18nLoadOptions['i18n']['parser'];
|
|
97
|
+
};
|
|
101
98
|
}
|
|
102
99
|
interface Source<Config extends SourceConfig> {
|
|
103
100
|
/**
|
|
104
101
|
* @internal
|
|
105
102
|
*/
|
|
106
103
|
_config?: Config;
|
|
107
|
-
files: VirtualFile[] | ((
|
|
104
|
+
files: VirtualFile[] | (() => VirtualFile[]);
|
|
108
105
|
}
|
|
109
106
|
interface Page<Data = PageData> {
|
|
110
107
|
file: FileInfo;
|
|
@@ -234,7 +231,7 @@ declare namespace fileSystem {
|
|
|
234
231
|
export { type fileSystem_File as File, type fileSystem_Folder as Folder, type fileSystem_MetaFile as MetaFile, type fileSystem_PageFile as PageFile, fileSystem_Storage as Storage };
|
|
235
232
|
}
|
|
236
233
|
|
|
237
|
-
interface
|
|
234
|
+
interface Options {
|
|
238
235
|
/**
|
|
239
236
|
* Remove references to the file path of original nodes (`$ref`)
|
|
240
237
|
*
|
|
@@ -244,11 +241,14 @@ interface BuildPageTreeOptions {
|
|
|
244
241
|
attachFile?: (node: Item, file?: PageFile) => Item;
|
|
245
242
|
attachFolder?: (node: Folder$1, folder: Folder, meta?: MetaFile) => Folder$1;
|
|
246
243
|
attachSeparator?: (node: Separator) => Separator;
|
|
247
|
-
storage: Storage;
|
|
248
244
|
getUrl: UrlFn;
|
|
249
245
|
resolveIcon?: (icon: string | undefined) => ReactElement | undefined;
|
|
250
246
|
}
|
|
251
|
-
interface
|
|
247
|
+
interface BuildPageTreeOptions extends Options {
|
|
248
|
+
storage: Storage;
|
|
249
|
+
}
|
|
250
|
+
interface BuildPageTreeOptionsWithI18n extends Options {
|
|
251
|
+
storages: Record<string, Storage>;
|
|
252
252
|
i18n: I18nConfig;
|
|
253
253
|
}
|
|
254
254
|
interface PageTreeBuilder {
|
package/dist/source/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
joinPath,
|
|
3
3
|
slash,
|
|
4
4
|
splitPath
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-XMCPKVJQ.js";
|
|
6
6
|
import {
|
|
7
7
|
__export
|
|
8
8
|
} from "../chunk-MLKGABMK.js";
|
|
@@ -25,10 +25,8 @@ function buildAll(nodes, ctx, skipIndex) {
|
|
|
25
25
|
(a, b) => a.file.name.localeCompare(b.file.name)
|
|
26
26
|
)) {
|
|
27
27
|
if (isPageFile(node)) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const localized = ctx.storage.read(
|
|
31
|
-
joinPath(node.file.dirname, `${node.file.name}.${ctx.lang}`),
|
|
28
|
+
const localized = ctx.localeStorage?.read(
|
|
29
|
+
joinPath(node.file.dirname, node.file.name),
|
|
32
30
|
"page"
|
|
33
31
|
);
|
|
34
32
|
const treeNode = buildFileNode(localized ?? node, ctx);
|
|
@@ -83,10 +81,7 @@ function resolveFolderItem(folder, item, ctx, idx, addedNodePaths) {
|
|
|
83
81
|
filename = item.slice(extractPrefix.length);
|
|
84
82
|
}
|
|
85
83
|
const path = joinPath(folder.file.path, filename);
|
|
86
|
-
|
|
87
|
-
if (!itemNode) {
|
|
88
|
-
itemNode = (ctx.lang ? ctx.storage.read(`${path}.${ctx.lang}`, "page") : null) ?? ctx.storage.read(`${path}.${ctx.defaultLanguage}`, "page") ?? ctx.storage.read(path, "page");
|
|
89
|
-
}
|
|
84
|
+
const itemNode = ctx.storage.readDir(path) ?? ctx.localeStorage?.read(path, "page") ?? ctx.storage.read(path, "page");
|
|
90
85
|
if (!itemNode) return [];
|
|
91
86
|
addedNodePaths.add(itemNode.file.path);
|
|
92
87
|
if (isExcept) return [];
|
|
@@ -98,7 +93,7 @@ function resolveFolderItem(folder, item, ctx, idx, addedNodePaths) {
|
|
|
98
93
|
}
|
|
99
94
|
function buildFolderNode(folder, isGlobalRoot, ctx) {
|
|
100
95
|
const metaPath = joinPath(folder.file.path, "meta");
|
|
101
|
-
const meta = ctx.
|
|
96
|
+
const meta = ctx.localeStorage?.read(metaPath, "meta") ?? ctx.storage.read(metaPath, "meta");
|
|
102
97
|
const indexFile = ctx.storage.read(
|
|
103
98
|
joinPath(folder.file.path, "index"),
|
|
104
99
|
"page"
|
|
@@ -153,7 +148,7 @@ function buildFileNode(file, ctx) {
|
|
|
153
148
|
name: file.data.data.title ?? pathToName(file.file.name),
|
|
154
149
|
description: file.data.data.description,
|
|
155
150
|
icon: ctx.options.resolveIcon?.(file.data.data.icon),
|
|
156
|
-
url: ctx.options.getUrl(file.data.slugs, ctx.
|
|
151
|
+
url: ctx.options.getUrl(file.data.slugs, ctx.locale),
|
|
157
152
|
$ref: !ctx.options.noRef ? {
|
|
158
153
|
file: file.file.path
|
|
159
154
|
} : void 0
|
|
@@ -180,11 +175,11 @@ function createPageTreeBuilder() {
|
|
|
180
175
|
buildI18n({ i18n, ...options }) {
|
|
181
176
|
const entries = i18n.languages.map((lang) => {
|
|
182
177
|
const tree = build({
|
|
183
|
-
lang,
|
|
184
178
|
options,
|
|
185
179
|
builder: this,
|
|
186
|
-
|
|
187
|
-
|
|
180
|
+
locale: lang,
|
|
181
|
+
storage: options.storages[i18n.defaultLanguage],
|
|
182
|
+
localeStorage: options.storages[lang]
|
|
188
183
|
});
|
|
189
184
|
return [lang, tree];
|
|
190
185
|
});
|
|
@@ -208,29 +203,19 @@ function parseFilePath(path) {
|
|
|
208
203
|
const dirname = segments.slice(0, -1).join("/");
|
|
209
204
|
let name = segments.at(-1) ?? "";
|
|
210
205
|
let ext = "";
|
|
211
|
-
|
|
212
|
-
let dotIdx = name.lastIndexOf(".");
|
|
206
|
+
const dotIdx = name.lastIndexOf(".");
|
|
213
207
|
if (dotIdx !== -1) {
|
|
214
208
|
ext = name.substring(dotIdx);
|
|
215
209
|
name = name.substring(0, dotIdx);
|
|
216
210
|
}
|
|
217
|
-
dotIdx = name.lastIndexOf(".");
|
|
218
|
-
if (dotIdx !== -1 && isLocale(name.substring(dotIdx))) {
|
|
219
|
-
locale = name.substring(dotIdx);
|
|
220
|
-
name = name.substring(0, dotIdx);
|
|
221
|
-
}
|
|
222
211
|
return {
|
|
223
212
|
dirname,
|
|
224
213
|
name,
|
|
225
|
-
locale,
|
|
226
214
|
path: segments.join("/"),
|
|
227
215
|
ext,
|
|
228
|
-
flattenedPath: [dirname,
|
|
216
|
+
flattenedPath: [dirname, name].filter((p) => p.length > 0).join("/")
|
|
229
217
|
};
|
|
230
218
|
}
|
|
231
|
-
function isLocale(code) {
|
|
232
|
-
return code.length > 0 && !/\d+/.test(code);
|
|
233
|
-
}
|
|
234
219
|
function parseFolderPath(path) {
|
|
235
220
|
const segments = splitPath(slash(path));
|
|
236
221
|
const base = segments.at(-1) ?? "";
|
|
@@ -284,10 +269,7 @@ var Storage = class {
|
|
|
284
269
|
this.makeDir(node.file.dirname);
|
|
285
270
|
this.readDir(node.file.dirname)?.children.push(node);
|
|
286
271
|
this.files.set(
|
|
287
|
-
joinPath(
|
|
288
|
-
node.file.dirname,
|
|
289
|
-
`${node.file.name}${node.file.locale}.${node.format}`
|
|
290
|
-
),
|
|
272
|
+
joinPath(node.file.dirname, `${node.file.name}.${node.format}`),
|
|
291
273
|
node
|
|
292
274
|
);
|
|
293
275
|
}
|
|
@@ -313,20 +295,17 @@ var Storage = class {
|
|
|
313
295
|
function loadFiles(files, options) {
|
|
314
296
|
const { transformers = [] } = options;
|
|
315
297
|
const storage = new Storage();
|
|
316
|
-
const rootDir = normalizePath(options.rootDir ?? "");
|
|
317
298
|
for (const file of files) {
|
|
318
|
-
const
|
|
319
|
-
if (!normalizedPath.startsWith(rootDir)) continue;
|
|
320
|
-
const relativePath = normalizedPath.slice(rootDir.length);
|
|
299
|
+
const parsedPath = normalizePath(file.path);
|
|
321
300
|
if (file.type === "page") {
|
|
322
|
-
const slugs = file.slugs ?? options.getSlugs(parseFilePath(
|
|
323
|
-
storage.write(
|
|
301
|
+
const slugs = file.slugs ?? options.getSlugs(parseFilePath(parsedPath));
|
|
302
|
+
storage.write(parsedPath, file.type, {
|
|
324
303
|
slugs,
|
|
325
304
|
data: file.data
|
|
326
305
|
});
|
|
327
306
|
}
|
|
328
307
|
if (file.type === "meta") {
|
|
329
|
-
storage.write(
|
|
308
|
+
storage.write(parsedPath, file.type, file.data);
|
|
330
309
|
}
|
|
331
310
|
}
|
|
332
311
|
for (const transformer of transformers) {
|
|
@@ -337,27 +316,63 @@ function loadFiles(files, options) {
|
|
|
337
316
|
}
|
|
338
317
|
return storage;
|
|
339
318
|
}
|
|
319
|
+
function loadFilesI18n(files, options) {
|
|
320
|
+
const parser = options.i18n.parser === "dir" ? dirParser : dotParser;
|
|
321
|
+
const storages = {};
|
|
322
|
+
for (const lang of options.i18n.languages) {
|
|
323
|
+
storages[lang] = loadFiles(
|
|
324
|
+
files.flatMap((file) => {
|
|
325
|
+
const [path, locale] = parser(normalizePath(file.path));
|
|
326
|
+
if ((locale ?? options.i18n.defaultLanguage) === lang) {
|
|
327
|
+
return {
|
|
328
|
+
...file,
|
|
329
|
+
path
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return [];
|
|
333
|
+
}),
|
|
334
|
+
options
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
return storages;
|
|
338
|
+
}
|
|
339
|
+
function dirParser(path) {
|
|
340
|
+
const parsed = path.split("/");
|
|
341
|
+
if (parsed.length >= 2) return [parsed.slice(1).join("/"), parsed[0]];
|
|
342
|
+
return [path];
|
|
343
|
+
}
|
|
344
|
+
function dotParser(path) {
|
|
345
|
+
const segs = path.split("/");
|
|
346
|
+
if (segs.length === 0) return [path];
|
|
347
|
+
const name = segs[segs.length - 1].split(".");
|
|
348
|
+
if (name.length >= 3) {
|
|
349
|
+
const locale = name.splice(name.length - 2, 1)[0];
|
|
350
|
+
if (locale.length > 0 && !/\d+/.test(locale)) {
|
|
351
|
+
segs[segs.length - 1] = name.join(".");
|
|
352
|
+
return [segs.join("/"), locale];
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return [path];
|
|
356
|
+
}
|
|
340
357
|
|
|
341
358
|
// src/source/loader.ts
|
|
342
|
-
function indexPages(
|
|
359
|
+
function indexPages(storages, getUrl, i18n) {
|
|
343
360
|
const defaultLanguage = i18n?.defaultLanguage ?? "";
|
|
344
361
|
const map = /* @__PURE__ */ new Map();
|
|
345
362
|
const pathToFile = /* @__PURE__ */ new Map();
|
|
346
|
-
for (const item of
|
|
363
|
+
for (const item of storages[defaultLanguage].list()) {
|
|
347
364
|
if (item.format === "meta")
|
|
348
365
|
pathToFile.set(item.file.path, fileToMeta(item));
|
|
349
366
|
if (item.format === "page") {
|
|
350
|
-
if (item.file.locale.length > 0 && item.file.locale.slice(1) !== defaultLanguage)
|
|
351
|
-
continue;
|
|
352
367
|
const page = fileToPage(item, getUrl, defaultLanguage);
|
|
353
368
|
pathToFile.set(item.file.path, page);
|
|
354
369
|
map.set(`${defaultLanguage}.${page.slugs.join("/")}`, page);
|
|
355
370
|
if (!i18n) continue;
|
|
356
|
-
const
|
|
371
|
+
const path = joinPath(item.file.dirname, item.file.name);
|
|
357
372
|
for (const lang of i18n.languages) {
|
|
358
373
|
if (lang === defaultLanguage) continue;
|
|
359
374
|
const localizedPage = fileToPage(
|
|
360
|
-
|
|
375
|
+
storages[lang].read(path, "page") ?? item,
|
|
361
376
|
getUrl,
|
|
362
377
|
lang
|
|
363
378
|
);
|
|
@@ -401,17 +416,23 @@ function createOutput(options) {
|
|
|
401
416
|
if (!options.url && !options.baseUrl) {
|
|
402
417
|
console.warn("`loader()` now requires a `baseUrl` option to be defined.");
|
|
403
418
|
}
|
|
404
|
-
const { source,
|
|
419
|
+
const { source, slugs: slugsFn = getSlugs } = options;
|
|
405
420
|
const getUrl = options.url ?? createGetUrl(options.baseUrl ?? "/", options.i18n);
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
{
|
|
421
|
+
const files = typeof source.files === "function" ? source.files() : source.files;
|
|
422
|
+
const storages = options.i18n ? loadFilesI18n(files, {
|
|
423
|
+
i18n: {
|
|
424
|
+
...options.i18n,
|
|
425
|
+
parser: options.i18n.parser ?? "dot"
|
|
426
|
+
},
|
|
427
|
+
transformers: options.transformers,
|
|
428
|
+
getSlugs: slugsFn
|
|
429
|
+
}) : {
|
|
430
|
+
"": loadFiles(files, {
|
|
409
431
|
transformers: options.transformers,
|
|
410
|
-
rootDir,
|
|
411
432
|
getSlugs: slugsFn
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const walker = indexPages(
|
|
433
|
+
})
|
|
434
|
+
};
|
|
435
|
+
const walker = indexPages(storages, getUrl, options.i18n);
|
|
415
436
|
const builder = createPageTreeBuilder();
|
|
416
437
|
let pageTree;
|
|
417
438
|
return {
|
|
@@ -419,7 +440,7 @@ function createOutput(options) {
|
|
|
419
440
|
get pageTree() {
|
|
420
441
|
if (options.i18n) {
|
|
421
442
|
pageTree ??= builder.buildI18n({
|
|
422
|
-
|
|
443
|
+
storages,
|
|
423
444
|
resolveIcon: options.icon,
|
|
424
445
|
getUrl,
|
|
425
446
|
i18n: options.i18n,
|
|
@@ -427,7 +448,7 @@ function createOutput(options) {
|
|
|
427
448
|
});
|
|
428
449
|
} else {
|
|
429
450
|
pageTree ??= builder.build({
|
|
430
|
-
storage,
|
|
451
|
+
storage: storages[""],
|
|
431
452
|
resolveIcon: options.icon,
|
|
432
453
|
getUrl,
|
|
433
454
|
...options.pageTree
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-core",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.2",
|
|
4
4
|
"description": "The library for building a documentation website in Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -113,8 +113,8 @@
|
|
|
113
113
|
"@types/hast": "^3.0.4",
|
|
114
114
|
"@types/mdast": "^4.0.3",
|
|
115
115
|
"@types/negotiator": "^0.6.3",
|
|
116
|
-
"@types/node": "22.13.
|
|
117
|
-
"@types/react": "^19.0.
|
|
116
|
+
"@types/node": "22.13.11",
|
|
117
|
+
"@types/react": "^19.0.12",
|
|
118
118
|
"@types/react-dom": "^19.0.4",
|
|
119
119
|
"algoliasearch": "4.24.0",
|
|
120
120
|
"mdast-util-mdx-jsx": "^3.2.0",
|