fumadocs-core 16.8.12 → 16.9.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.
@@ -1,4 +1,4 @@
1
- import { i as Root, r as Node, t as Folder } from "./definitions-Cob-Q8-8.js";
1
+ import { i as Root, r as Node, t as Folder } from "./definitions-DQRPsvc7.js";
2
2
  import { ReactNode } from "react";
3
3
 
4
4
  //#region src/breadcrumb.d.ts
@@ -9,9 +9,9 @@ interface ID {
9
9
  }
10
10
  interface Root extends ID {
11
11
  /**
12
- * @internal meta file path
12
+ * @internal folder `$ref`
13
13
  */
14
- $ref?: string;
14
+ $ref?: Folder['$ref'];
15
15
  type?: 'root';
16
16
  name: ReactNode;
17
17
  description?: ReactNode;
@@ -46,9 +46,12 @@ interface Separator extends ID {
46
46
  }
47
47
  interface Folder extends ID {
48
48
  /**
49
- * @internal meta file path
49
+ * @internal paths of meta file & folder
50
50
  */
51
- $ref?: string;
51
+ $ref?: {
52
+ meta?: string;
53
+ folder: string;
54
+ };
52
55
  type: 'folder';
53
56
  name: ReactNode;
54
57
  description?: ReactNode;
@@ -1,2 +1,2 @@
1
- import { n as defineI18n, t as I18nConfig } from "../index-BreFfl9v.js";
2
- export { I18nConfig, defineI18n };
1
+ import { a as TranslationPreset, c as TranslationsAPIExtension, d as renderTranslation, i as TranslationObject, l as defineI18n, n as I18nConfig, o as TranslationValue, r as SingularTranslationsAPI, s as TranslationsAPI, t as I18nAPI, u as defineTranslations } from "../index-Ci-v40o3.js";
2
+ export { I18nAPI, I18nConfig, SingularTranslationsAPI, TranslationObject, TranslationPreset, TranslationValue, TranslationsAPI, TranslationsAPIExtension, defineI18n, defineTranslations, renderTranslation };
@@ -1,6 +1,83 @@
1
1
  //#region src/i18n/index.ts
2
2
  function defineI18n(config) {
3
- return config;
3
+ return {
4
+ ...config,
5
+ translations() {
6
+ const translations = {};
7
+ const stagedTranslations = {};
8
+ for (const lang of config.languages) {
9
+ translations[lang] = {};
10
+ stagedTranslations[lang] = {};
11
+ }
12
+ return {
13
+ config,
14
+ $inferLanguages: void 0,
15
+ $inferNamespaces: void 0,
16
+ get(lang) {
17
+ return translations[lang];
18
+ },
19
+ preset(lang, preset) {
20
+ const t = translations[lang];
21
+ const staged = stagedTranslations[lang];
22
+ for (const [namespace, obj] of Object.entries(preset.value)) Object.assign(t[namespace] ?? (staged[namespace] ??= {}), obj);
23
+ return this;
24
+ },
25
+ add(namespace, overrides) {
26
+ for (const [lang, values] of Object.entries(overrides)) Object.assign(translations[lang][namespace], values);
27
+ return this;
28
+ },
29
+ extend({ namespace, defaultValue }) {
30
+ for (const lang of config.languages) {
31
+ const t = translations[lang];
32
+ const staged = stagedTranslations[lang];
33
+ if (t[namespace]) continue;
34
+ t[namespace] = {
35
+ ...defaultValue,
36
+ ...staged[namespace]
37
+ };
38
+ delete staged[namespace];
39
+ }
40
+ return this;
41
+ }
42
+ };
43
+ }
44
+ };
45
+ }
46
+ /** create translations API without i18n */
47
+ function defineTranslations() {
48
+ const translations = {};
49
+ const stagedTranslations = {};
50
+ return {
51
+ $inferNamespaces: void 0,
52
+ get() {
53
+ return translations;
54
+ },
55
+ preset(preset) {
56
+ for (const [namespace, obj] of Object.entries(preset.value)) {
57
+ const t = translations[namespace] ?? (stagedTranslations[namespace] ??= {});
58
+ Object.assign(t, obj);
59
+ }
60
+ return this;
61
+ },
62
+ add(namespace, overrides) {
63
+ Object.assign(translations[namespace], overrides);
64
+ return this;
65
+ },
66
+ extend({ namespace, defaultValue }) {
67
+ if (!translations[namespace]) {
68
+ translations[namespace] = {
69
+ ...defaultValue,
70
+ ...stagedTranslations[namespace]
71
+ };
72
+ delete stagedTranslations[namespace];
73
+ }
74
+ return this;
75
+ }
76
+ };
77
+ }
78
+ function renderTranslation(label, params) {
79
+ if (params) for (const k in params) label = label.replaceAll(`{${k}}`, params[k]);
80
+ return label;
4
81
  }
5
82
  //#endregion
6
- export { defineI18n };
83
+ export { defineI18n, defineTranslations, renderTranslation };
@@ -1,4 +1,4 @@
1
- import { t as I18nConfig } from "../index-BreFfl9v.js";
1
+ import { n as I18nConfig } from "../index-Ci-v40o3.js";
2
2
  import { NextProxy } from "next/server.js";
3
3
  import { NextURL } from "next/dist/server/web/next-url.js";
4
4
 
@@ -1,8 +1,8 @@
1
- import { a as Separator, i as Root, n as Item, r as Node, t as Folder } from "./definitions-Cob-Q8-8.js";
1
+ import { a as Separator, i as Root, n as Item, r as Node, t as Folder } from "./definitions-DQRPsvc7.js";
2
2
  import { t as Awaitable } from "./types-DpbpliNk.js";
3
3
  import { i as StructuredData } from "./remark-structure-C2-K9_ko.js";
4
- import { t as I18nConfig } from "./index-BreFfl9v.js";
5
- import { n as SerializedPageTree } from "./index-D2f_P1Zo.js";
4
+ import { n as I18nConfig } from "./index-Ci-v40o3.js";
5
+ import { n as SerializedPageTree } from "./index-Doj0FeGs.js";
6
6
  import { ReactNode } from "react";
7
7
 
8
8
  //#region src/source/storage/file-system.d.ts
@@ -55,15 +55,13 @@ interface ContentStoragePageFile<Type extends string | undefined = string | unde
55
55
  //#endregion
56
56
  //#region src/source/page-tree/builder.d.ts
57
57
  interface PageTreeBuilderContext<S extends ContentStorage = ContentStorage> {
58
- idPrefix: string;
59
- noRef: boolean;
60
58
  transformers: PageTreeTransformer<S>[];
61
59
  builder: PageTreeBuilder;
62
60
  storage: S;
63
- getUrl: ResolvedLoaderConfig['url'];
64
61
  storages?: Record<string, S>;
65
62
  locale?: string;
66
63
  custom?: Record<string, unknown>;
64
+ options: PageTreeOptions<S>;
67
65
  }
68
66
  interface PageTreeTransformer<S extends ContentStorage = ContentStorage> {
69
67
  file?: (this: PageTreeBuilderContext<S>, node: Item, filePath?: string) => Item;
@@ -93,35 +91,15 @@ interface PageTreeOptions<S extends ContentStorage = ContentStorage> {
93
91
  transformers?: PageTreeTransformer<S>[];
94
92
  /** custom context */
95
93
  context?: Record<string, unknown>;
94
+ /** customize the default sorting behaviour (`localeCompare`) */
95
+ sort?: {
96
+ /** @default 'path' */by?: 'name' | 'path';
97
+ locales?: Intl.LocalesArgument;
98
+ options?: Intl.CollatorOptions;
99
+ };
96
100
  }
97
- declare class PageTreeBuilder {
98
- private readonly flattenPathToFullPath;
99
- private readonly transformers;
100
- /** virtual file path -> output page tree node (if cached) */
101
- private readonly pathToNode;
102
- /** unfinished nodes */
103
- private readonly unfinished;
104
- private readonly ownerMap;
105
- private _nextId;
106
- /** passed as additional information to transformers */
107
- private readonly ctx;
108
- private readonly storage;
109
- constructor(input: ContentStorage | [locale: string, storages: Record<string, ContentStorage>], options: PageTreeOptions);
101
+ interface PageTreeBuilder {
110
102
  resolveFlattenPath(name: string, format: string): string;
111
- /**
112
- * try to register as the owner of `node`.
113
- *
114
- * when a node is referenced by multiple folders, this determines which folder they should belong to.
115
- *
116
- * @returns whether the owner owns the node.
117
- */
118
- private own;
119
- private transferOwner;
120
- private generateId;
121
- buildPaths(paths: string[], filter?: (file: string) => boolean, reversed?: boolean): Node[];
122
- private resolveFolderItem;
123
- folder(folderPath: string): Folder | undefined;
124
- file(path: string): Item | undefined;
125
103
  root(id?: string, path?: string): Root;
126
104
  }
127
105
  //#endregion
@@ -351,6 +329,7 @@ interface MetaData {
351
329
  title?: string | undefined;
352
330
  root?: boolean | undefined;
353
331
  pages?: string[] | undefined;
332
+ pagesIndex?: string | undefined;
354
333
  defaultOpen?: boolean | undefined;
355
334
  collapsible?: boolean | undefined;
356
335
  description?: string | undefined;
@@ -1,4 +1,4 @@
1
- import { i as Root, n as Item, r as Node, t as Folder } from "./definitions-Cob-Q8-8.js";
1
+ import { i as Root, n as Item, r as Node, t as Folder } from "./definitions-DQRPsvc7.js";
2
2
 
3
3
  //#region src/page-tree/utils.d.ts
4
4
  /**
@@ -0,0 +1,87 @@
1
+ //#region src/i18n/index.d.ts
2
+ interface I18nConfig<Languages extends string = string> {
3
+ /**
4
+ * Supported locale codes.
5
+ *
6
+ * A page tree will be built for each language.
7
+ */
8
+ languages: Languages[];
9
+ /**
10
+ * Default locale if not specified
11
+ */
12
+ defaultLanguage: NoInfer<Languages>;
13
+ /**
14
+ * Don't show the locale prefix on URL.
15
+ *
16
+ * - `always`: Always hide the prefix
17
+ * - `default-locale`: Only hide the default locale
18
+ * - `never`: Never hide the prefix
19
+ *
20
+ * This API uses `NextResponse.rewrite`.
21
+ *
22
+ * @defaultValue 'never'
23
+ */
24
+ hideLocale?: 'always' | 'default-locale' | 'never';
25
+ /**
26
+ * Used by `loader()`, specify the way to parse i18n file structure.
27
+ *
28
+ * @defaultValue 'dot'
29
+ */
30
+ parser?: 'dot' | 'dir' | 'none';
31
+ /**
32
+ * the fallback language when the page has no translations available for a given locale.
33
+ *
34
+ * Default to `defaultLanguage`, no fallback when set to `null`.
35
+ */
36
+ fallbackLanguage?: NoInfer<Languages> | null;
37
+ }
38
+ interface I18nAPI<Languages extends string = string> extends I18nConfig<Languages> {
39
+ translations: () => TranslationsAPI<Languages, Record<never, TranslationObject>>;
40
+ }
41
+ declare function defineI18n<const Languages extends string>(config: I18nConfig<Languages>): I18nAPI<Languages>;
42
+ type TranslationObject = Record<string, TranslationValue>;
43
+ type TranslationValue<Params extends string = string> = string & {
44
+ _params?: Params;
45
+ };
46
+ type TranslationPreset<Namespaces extends Record<string, TranslationObject> = Record<string, TranslationObject>> = {
47
+ name: string;
48
+ value: Partial<Namespaces>;
49
+ };
50
+ interface TranslationsAPIExtension<Namespace extends string = string, Obj extends TranslationObject = TranslationObject> {
51
+ namespace: Namespace;
52
+ defaultValue: Obj;
53
+ }
54
+ interface TranslationsAPI<Languages extends string = string, Namespaces extends Record<string, TranslationObject> = Record<string, TranslationObject>> {
55
+ /** for type inference only, always `undefined` in runtime */
56
+ $inferLanguages: Languages;
57
+ /** for type inference only, always `undefined` in runtime */
58
+ $inferNamespaces: Namespaces;
59
+ config: I18nConfig<Languages>;
60
+ get: {
61
+ (lang: Languages): Namespaces;
62
+ (lang: string): Namespaces | undefined;
63
+ };
64
+ /** add extension, will be ignored if the same extension (namespace) has been registered */
65
+ extend: <N extends string, Obj extends TranslationObject>(extension: TranslationsAPIExtension<N, Obj>) => TranslationsAPI<Languages, Namespaces & { [K in N]: Obj }>;
66
+ /** add translations */
67
+ add: <N extends keyof Namespaces>(namespace: N, overrides: { [Lang in Languages]?: Partial<Namespaces[N]> }) => TranslationsAPI<Languages, Namespaces>;
68
+ /** add language pack */
69
+ preset: (lang: Languages, preset: TranslationPreset<Namespaces>) => TranslationsAPI<Languages, Namespaces>;
70
+ }
71
+ interface SingularTranslationsAPI<Namespaces extends Record<string, TranslationObject> = Record<string, TranslationObject>> {
72
+ /** for type inference only, always `undefined` in runtime */
73
+ $inferNamespaces: Namespaces;
74
+ get: () => Namespaces;
75
+ /** add extension, will be ignored if the same extension (namespace) has been registered */
76
+ extend: <N extends string, Obj extends TranslationObject>(extension: TranslationsAPIExtension<N, Obj>) => SingularTranslationsAPI<Namespaces & { [K in N]: Obj }>;
77
+ /** add translations */
78
+ add: <N extends keyof Namespaces>(namespace: N, overrides: Partial<Namespaces[N]>) => SingularTranslationsAPI<Namespaces>;
79
+ /** add language pack */
80
+ preset: (preset: TranslationPreset<Namespaces>) => SingularTranslationsAPI<Namespaces>;
81
+ }
82
+ /** create translations API without i18n */
83
+ declare function defineTranslations(): SingularTranslationsAPI<Record<never, TranslationObject>>;
84
+ declare function renderTranslation(v: TranslationValue<never>): string;
85
+ declare function renderTranslation<Params extends string>(v: TranslationValue<Params>, params: Record<Params, string>): string;
86
+ //#endregion
87
+ export { TranslationPreset as a, TranslationsAPIExtension as c, renderTranslation as d, TranslationObject as i, defineI18n as l, I18nConfig as n, TranslationValue as o, SingularTranslationsAPI as r, TranslationsAPI as s, I18nAPI as t, defineTranslations as u };
@@ -1,4 +1,4 @@
1
- import { i as Root } from "./definitions-Cob-Q8-8.js";
1
+ import { i as Root } from "./definitions-DQRPsvc7.js";
2
2
  //#region src/source/client/index.d.ts
3
3
  interface SerializedPageTree {
4
4
  $fumadocs_loader: 'page-tree';
@@ -232,11 +232,9 @@ function transformerFallback() {
232
232
  if (addedFiles.has(file)) continue;
233
233
  isolatedStorage.write(file, this.storage.read(file));
234
234
  }
235
- root.fallback = new PageTreeBuilder(isolatedStorage, {
236
- idPrefix: this.idPrefix ? `fallback:${this.idPrefix}` : "fallback",
237
- url: this.getUrl,
238
- noRef: this.noRef,
239
- transformers: this.transformers,
235
+ root.fallback = createPageTreeBuilder(isolatedStorage, {
236
+ ...this.options,
237
+ idPrefix: this.options.idPrefix ? `fallback:${this.options.idPrefix}` : "fallback",
240
238
  generateFallback: false,
241
239
  context: {
242
240
  ...this.custom,
@@ -267,40 +265,50 @@ const rest = "...";
267
265
  const restReversed = "z...a";
268
266
  const extractPrefix = "...";
269
267
  const excludePrefix = "!";
270
- var PageTreeBuilder = class {
271
- constructor(input, options) {
272
- this.flattenPathToFullPath = /* @__PURE__ */ new Map();
273
- this.transformers = [];
274
- this.pathToNode = /* @__PURE__ */ new Map();
275
- this.unfinished = /* @__PURE__ */ new WeakSet();
276
- this.ownerMap = /* @__PURE__ */ new Map();
277
- this._nextId = 0;
278
- const { transformers, url, context, generateFallback = true, idPrefix = "", noRef = false } = options;
279
- if (transformers) this.transformers.push(...transformers);
280
- if (generateFallback) this.transformers.push(transformerFallback());
281
- this.ctx = {
282
- builder: this,
283
- idPrefix,
284
- getUrl: url,
285
- storage: void 0,
286
- noRef,
287
- transformers: this.transformers,
288
- custom: context
268
+ const SymbolUnfinished = Symbol("unfinished");
269
+ const SymbolName = Symbol("name");
270
+ const SymbolOwner = Symbol("owner");
271
+ function createPageTreeBuilder(input, options) {
272
+ const flattenPathToFullPath = /* @__PURE__ */ new Map();
273
+ const transformers = [];
274
+ /** virtual file path -> output page tree node (if cached) */
275
+ const pathToNode = /* @__PURE__ */ new Map();
276
+ let _nextId = 0;
277
+ const { noRef = false, idPrefix, url: getUrl, generateFallback = true, sort: { by: sortBy = "path", locales: sortLocales, options: sortOptions } = {} } = options;
278
+ /** passed as additional information to transformers */
279
+ let ctx;
280
+ if (options.transformers) transformers.push(...options.transformers);
281
+ if (generateFallback) transformers.push(transformerFallback());
282
+ if (Array.isArray(input)) {
283
+ const [locale, storages] = input;
284
+ ctx = {
285
+ get builder() {
286
+ return builder;
287
+ },
288
+ storage: storages[locale],
289
+ storages,
290
+ locale,
291
+ transformers,
292
+ custom: options.context,
293
+ options
289
294
  };
290
- if (Array.isArray(input)) {
291
- const [locale, storages] = input;
292
- this.ctx.storage = this.storage = storages[locale];
293
- this.ctx.locale = locale;
294
- this.ctx.storages = storages;
295
- } else this.ctx.storage = this.storage = input;
296
- for (const file of this.storage.getFiles()) {
297
- const content = this.storage.read(file);
298
- const flattenPath = file.substring(0, file.length - extname(file).length);
299
- this.flattenPathToFullPath.set(flattenPath + "." + content.format, file);
300
- }
295
+ } else ctx = {
296
+ get builder() {
297
+ return builder;
298
+ },
299
+ storage: input,
300
+ transformers,
301
+ custom: options.context,
302
+ options
303
+ };
304
+ const { storage, locale } = ctx;
305
+ for (const file of storage.getFiles()) {
306
+ const content = storage.read(file);
307
+ const flattenPath = file.substring(0, file.length - extname(file).length);
308
+ flattenPathToFullPath.set(flattenPath + "." + content.format, file);
301
309
  }
302
- resolveFlattenPath(name, format) {
303
- return this.flattenPathToFullPath.get(name + "." + format) ?? name;
310
+ function resolveFlattenPath(name, format) {
311
+ return flattenPathToFullPath.get(name + "." + format) ?? name;
304
312
  }
305
313
  /**
306
314
  * try to register as the owner of `node`.
@@ -309,14 +317,14 @@ var PageTreeBuilder = class {
309
317
  *
310
318
  * @returns whether the owner owns the node.
311
319
  */
312
- own(ownerPath, node, priority) {
313
- if (this.unfinished.has(node)) return false;
314
- const existing = this.ownerMap.get(node);
320
+ function own(ownerPath, node, priority) {
321
+ if (node[SymbolUnfinished]) return false;
322
+ const existing = node[SymbolOwner];
315
323
  if (!existing) {
316
- this.ownerMap.set(node, {
324
+ node[SymbolOwner] = {
317
325
  owner: ownerPath,
318
326
  priority
319
- });
327
+ };
320
328
  return true;
321
329
  }
322
330
  if (existing.owner === ownerPath) {
@@ -324,7 +332,7 @@ var PageTreeBuilder = class {
324
332
  return true;
325
333
  }
326
334
  if (existing.priority >= priority) return false;
327
- const folder = this.pathToNode.get(existing.owner);
335
+ const folder = pathToNode.get(existing.owner);
328
336
  if (folder && folder.type === "folder") if (folder.index === node) delete folder.index;
329
337
  else {
330
338
  const idx = folder.children.indexOf(node);
@@ -334,113 +342,133 @@ var PageTreeBuilder = class {
334
342
  existing.priority = priority;
335
343
  return true;
336
344
  }
337
- transferOwner(ownerPath, node) {
338
- const existing = this.ownerMap.get(node);
345
+ function transferOwner(ownerPath, node) {
346
+ const existing = node[SymbolOwner];
339
347
  if (existing) existing.owner = ownerPath;
340
348
  }
341
- generateId(localId = `_${this._nextId++}`) {
349
+ function generateId(localId = `_${_nextId++}`) {
342
350
  let id = localId;
343
- if (this.ctx.locale) id = `${this.ctx.locale}:${id}`;
344
- if (this.ctx.idPrefix) id = `${this.ctx.idPrefix}:${id}`;
351
+ if (locale) id = `${locale}:${id}`;
352
+ if (idPrefix) id = `${idPrefix}:${id}`;
345
353
  return id;
346
354
  }
347
- buildPaths(paths, filter, reversed = false) {
348
- const items = [];
349
- const folders = [];
350
- const sortedPaths = paths.sort((a, b) => reversed ? b.localeCompare(a) : a.localeCompare(b));
351
- for (const path of sortedPaths) {
355
+ function buildPaths(paths, filter, reversed = false) {
356
+ const nodes = [];
357
+ let indexNode;
358
+ for (const path of paths) {
352
359
  if (filter && !filter(path)) continue;
353
- const fileNode = this.file(path);
360
+ const fileNode = buildFile(path);
354
361
  if (fileNode) {
355
- if (basename(path, extname(path)) === "index") items.unshift(fileNode);
356
- else items.push(fileNode);
362
+ nodes.push(fileNode);
363
+ if (!indexNode && basename(path, extname(path)) === "index") indexNode = fileNode;
357
364
  continue;
358
365
  }
359
- const dirNode = this.folder(path);
360
- if (dirNode) folders.push(dirNode);
366
+ const dirNode = buildFolder(path);
367
+ if (dirNode) nodes.push(dirNode);
368
+ }
369
+ const factor = reversed ? -1 : 1;
370
+ const useName = sortBy === "name";
371
+ return nodes.sort((a, b) => {
372
+ if (a === indexNode) return -100;
373
+ if (b === indexNode) return 100;
374
+ const aT = useName && a[SymbolName] || (a.type === "folder" ? a.$ref.folder : a.$ref);
375
+ const bT = useName && b[SymbolName] || (b.type === "folder" ? b.$ref.folder : b.$ref);
376
+ const aK = a.type === "folder" ? 10 : 0;
377
+ const bK = b.type === "folder" ? 10 : 0;
378
+ return factor * (aT.localeCompare(bT, sortLocales, sortOptions) + (aK - bK));
379
+ });
380
+ }
381
+ function resolveLink(item) {
382
+ const match = link.exec(item);
383
+ if (!match?.groups) return;
384
+ const { icon, url, name, external } = match.groups;
385
+ let node = {
386
+ $id: generateId(),
387
+ type: "page",
388
+ icon,
389
+ name,
390
+ url,
391
+ external: external ? true : void 0
392
+ };
393
+ for (const transformer of transformers) {
394
+ if (!transformer.file) continue;
395
+ node = transformer.file.call(ctx, node);
361
396
  }
362
- items.push(...folders);
363
- return items;
397
+ return node;
364
398
  }
365
- resolveFolderItem(folderPath, item, outputArray, excludedPaths) {
399
+ function resolveSeparator(item) {
400
+ const match = separator.exec(item);
401
+ if (!match?.groups) return;
402
+ let node = {
403
+ $id: generateId(),
404
+ type: "separator",
405
+ icon: match.groups.icon,
406
+ name: match.groups.name
407
+ };
408
+ for (const transformer of transformers) {
409
+ if (!transformer.separator) continue;
410
+ node = transformer.separator.call(ctx, node);
411
+ }
412
+ return node;
413
+ }
414
+ function resolveFolderItem(folderPath, item, outputArray, excludedPaths) {
366
415
  if (item === rest || item === restReversed) {
367
416
  outputArray.push(item);
368
417
  return;
369
418
  }
370
- let match = separator.exec(item);
371
- if (match?.groups) {
372
- let node = {
373
- $id: this.generateId(),
374
- type: "separator",
375
- icon: match.groups.icon,
376
- name: match.groups.name
377
- };
378
- for (const transformer of this.transformers) {
379
- if (!transformer.separator) continue;
380
- node = transformer.separator.call(this.ctx, node);
381
- }
382
- outputArray.push(node);
419
+ const separator = resolveSeparator(item);
420
+ if (separator) {
421
+ outputArray.push(separator);
383
422
  return;
384
423
  }
385
- match = link.exec(item);
386
- if (match?.groups) {
387
- const { icon, url, name, external } = match.groups;
388
- let node = {
389
- $id: this.generateId(),
390
- type: "page",
391
- icon,
392
- name,
393
- url
394
- };
395
- if (external) node.external = true;
396
- for (const transformer of this.transformers) {
397
- if (!transformer.file) continue;
398
- node = transformer.file.call(this.ctx, node);
399
- }
400
- outputArray.push(node);
424
+ const link = resolveLink(item);
425
+ if (link) {
426
+ outputArray.push(link);
401
427
  return;
402
428
  }
403
429
  if (item.startsWith(excludePrefix)) {
404
430
  const path = joinPath(folderPath, item.slice(1));
405
431
  excludedPaths.add(path);
406
- excludedPaths.add(this.resolveFlattenPath(path, "page"));
432
+ excludedPaths.add(resolveFlattenPath(path, "page"));
407
433
  return;
408
434
  }
409
435
  if (item.startsWith(extractPrefix)) {
410
436
  const path = joinPath(folderPath, item.slice(3));
411
- const node = this.folder(path);
437
+ const node = buildFolder(path);
412
438
  if (!node) return;
413
439
  const children = node.index ? [node.index, ...node.children] : node.children;
414
- if (this.own(folderPath, node, 2)) {
440
+ if (own(folderPath, node, 2)) {
415
441
  for (const child of children) {
416
- this.transferOwner(folderPath, child);
442
+ transferOwner(folderPath, child);
417
443
  outputArray.push(child);
418
444
  }
419
445
  excludedPaths.add(path);
420
- } else for (const child of children) if (this.own(folderPath, child, 2)) outputArray.push(child);
446
+ } else for (const child of children) if (own(folderPath, child, 2)) outputArray.push(child);
421
447
  return;
422
448
  }
423
449
  let path = joinPath(folderPath, item);
424
- let node = this.folder(path);
450
+ let node = buildFolder(path);
425
451
  if (!node) {
426
- path = this.resolveFlattenPath(path, "page");
427
- node = this.file(path);
452
+ path = resolveFlattenPath(path, "page");
453
+ node = buildFile(path);
428
454
  }
429
- if (!node || !this.own(folderPath, node, 2)) return;
455
+ if (!node || !own(folderPath, node, 2)) return;
430
456
  outputArray.push(node);
431
457
  excludedPaths.add(path);
432
458
  }
433
- folder(folderPath) {
434
- const cached = this.pathToNode.get(folderPath);
459
+ function buildFolder(folderPath, isGlobalRoot = false) {
460
+ const cached = pathToNode.get(folderPath);
435
461
  if (cached) return cached;
436
- const files = this.storage.readDir(folderPath);
462
+ const files = storage.readDir(folderPath);
437
463
  if (!files) return;
438
- const isGlobalRoot = folderPath === "";
439
- const metaPath = this.resolveFlattenPath(joinPath(folderPath, "meta"), "meta");
440
- const indexPath = this.resolveFlattenPath(joinPath(folderPath, "index"), "page");
441
- let meta = this.storage.read(metaPath);
442
- if (meta && meta.format !== "meta") meta = void 0;
464
+ let metaPath = resolveFlattenPath(joinPath(folderPath, "meta"), "meta");
465
+ let meta = storage.read(metaPath);
466
+ if (!meta || meta.format !== "meta") {
467
+ meta = void 0;
468
+ metaPath = void 0;
469
+ }
443
470
  const metadata = meta?.data ?? {};
471
+ const isRoot = metadata.root ?? isGlobalRoot;
444
472
  let node = {
445
473
  type: "folder",
446
474
  name: null,
@@ -449,83 +477,109 @@ var PageTreeBuilder = class {
449
477
  description: metadata.description,
450
478
  collapsible: metadata.collapsible,
451
479
  children: [],
452
- $id: this.generateId(folderPath),
453
- $ref: !this.ctx.noRef && meta ? metaPath : void 0
480
+ $id: generateId(folderPath),
481
+ $ref: {
482
+ folder: folderPath,
483
+ meta: metaPath
484
+ },
485
+ [SymbolUnfinished]: true
454
486
  };
455
- this.pathToNode.set(folderPath, node);
456
- this.unfinished.add(node);
457
- if (!(metadata.root ?? isGlobalRoot)) {
458
- const file = this.file(indexPath);
459
- if (file && this.own(folderPath, file, 0)) node.index = file;
487
+ pathToNode.set(folderPath, node);
488
+ let indexPath;
489
+ if (metadata.pagesIndex) {
490
+ const resolvedPath = resolveFlattenPath(joinPath(folderPath, metadata.pagesIndex), "page");
491
+ const page = buildFile(resolvedPath);
492
+ if (page && own(folderPath, page, 3)) {
493
+ indexPath = resolvedPath;
494
+ node.index = page;
495
+ } else node.index = resolveLink(metadata.pagesIndex);
496
+ } else if (!isRoot) {
497
+ const defaultPath = resolveFlattenPath(joinPath(folderPath, "index"), "page");
498
+ const page = buildFile(defaultPath);
499
+ if (page && own(folderPath, page, 0)) {
500
+ indexPath = defaultPath;
501
+ node.index = page;
502
+ }
460
503
  }
461
504
  if (metadata.pages) {
462
505
  const outputArray = [];
463
506
  const excludedPaths = /* @__PURE__ */ new Set();
464
- for (const item of metadata.pages) this.resolveFolderItem(folderPath, item, outputArray, excludedPaths);
465
- if (excludedPaths.has(indexPath)) delete node.index;
466
- else if (node.index) excludedPaths.add(indexPath);
507
+ for (const item of metadata.pages) resolveFolderItem(folderPath, item, outputArray, excludedPaths);
508
+ if (indexPath) if (excludedPaths.has(indexPath)) delete node.index;
509
+ else excludedPaths.add(indexPath);
467
510
  for (const item of outputArray) {
468
511
  if (item !== rest && item !== restReversed) {
469
512
  node.children.push(item);
470
513
  continue;
471
514
  }
472
- const resolvedItem = this.buildPaths(files, (file) => !excludedPaths.has(file), item === restReversed);
473
- for (const child of resolvedItem) if (this.own(folderPath, child, 0)) node.children.push(child);
515
+ const resolvedItem = buildPaths(files, (file) => !excludedPaths.has(file), item === restReversed);
516
+ for (const child of resolvedItem) if (own(folderPath, child, 0)) node.children.push(child);
474
517
  }
475
- } else for (const item of this.buildPaths(files, node.index ? (file) => file !== indexPath : void 0)) if (this.own(folderPath, item, 0)) node.children.push(item);
518
+ } else for (const item of buildPaths(files, indexPath ? (file) => file !== indexPath : void 0)) if (own(folderPath, item, 0)) node.children.push(item);
476
519
  node.icon = metadata.icon ?? node.index?.icon;
477
520
  node.name = metadata.title ?? node.index?.name;
478
- this.unfinished.delete(node);
521
+ node[SymbolName] = metadata.title ?? node.index?.[SymbolName];
479
522
  if (!node.name) {
480
523
  const folderName = basename(folderPath);
481
524
  node.name = pathToName(group.exec(folderName)?.[1] ?? folderName);
482
525
  }
483
- for (const transformer of this.transformers) {
526
+ for (const transformer of transformers) {
484
527
  if (!transformer.folder) continue;
485
- node = transformer.folder.call(this.ctx, node, folderPath, meta ? metaPath : void 0);
528
+ node = transformer.folder.call(ctx, node, folderPath, metaPath);
486
529
  }
487
- this.pathToNode.set(folderPath, node);
530
+ pathToNode.set(folderPath, node);
531
+ delete node[SymbolUnfinished];
488
532
  return node;
489
533
  }
490
- file(path) {
491
- const cached = this.pathToNode.get(path);
534
+ function buildFile(path) {
535
+ const cached = pathToNode.get(path);
492
536
  if (cached) return cached;
493
- const page = this.storage.read(path);
537
+ const page = storage.read(path);
494
538
  if (!page || page.format !== "page") return;
495
539
  const { title, description, icon } = page.data;
496
540
  let item = {
497
- $id: this.generateId(path),
541
+ $id: generateId(path),
498
542
  type: "page",
499
543
  name: title ?? pathToName(basename(path, extname(path))),
500
544
  description,
501
545
  icon,
502
- url: this.ctx.getUrl(page.slugs, this.ctx.locale),
503
- $ref: !this.ctx.noRef ? path : void 0
546
+ url: getUrl(page.slugs, ctx.locale),
547
+ $ref: path,
548
+ [SymbolName]: title
504
549
  };
505
- for (const transformer of this.transformers) {
550
+ for (const transformer of transformers) {
506
551
  if (!transformer.file) continue;
507
- item = transformer.file.call(this.ctx, item, path);
552
+ item = transformer.file.call(ctx, item, path);
508
553
  }
509
- this.pathToNode.set(path, item);
554
+ pathToNode.set(path, item);
510
555
  return item;
511
556
  }
512
- root(id = "root", path = "") {
513
- const folder = this.folder(path);
514
- let root = {
515
- type: "root",
516
- $ref: folder?.$ref,
517
- $id: this.generateId(id),
518
- name: folder?.name || "Docs",
519
- description: folder?.description,
520
- children: folder ? folder.children : []
521
- };
522
- for (const transformer of this.transformers) {
523
- if (!transformer.root) continue;
524
- root = transformer.root.call(this.ctx, root);
557
+ const builder = {
558
+ resolveFlattenPath,
559
+ root(id = "root", path = "") {
560
+ const folder = buildFolder(path, true);
561
+ for (const node of pathToNode.values()) {
562
+ delete node[SymbolName];
563
+ delete node[SymbolOwner];
564
+ if (noRef && "$ref" in node) delete node.$ref;
565
+ }
566
+ let root = {
567
+ type: "root",
568
+ $ref: folder?.$ref,
569
+ $id: generateId(id),
570
+ name: folder?.name || "Docs",
571
+ description: folder?.description,
572
+ children: folder ? folder.children : []
573
+ };
574
+ for (const transformer of transformers) {
575
+ if (!transformer.root) continue;
576
+ root = transformer.root.call(ctx, root);
577
+ }
578
+ return root;
525
579
  }
526
- return root;
527
- }
528
- };
580
+ };
581
+ return builder;
582
+ }
529
583
  /**
530
584
  * Get item name from file name
531
585
  *
@@ -623,10 +677,10 @@ function loader(...args) {
623
677
  ...pageTreeConfig,
624
678
  transformers
625
679
  };
626
- if (storage instanceof FileSystem) return pageTrees = new PageTreeBuilder(storage, options).root();
680
+ if (storage instanceof FileSystem) return pageTrees = createPageTreeBuilder(storage, options).root();
627
681
  else {
628
682
  const out = {};
629
- for (const locale in storage) out[locale] = new PageTreeBuilder([locale, storage], options).root();
683
+ for (const locale in storage) out[locale] = createPageTreeBuilder([locale, storage], options).root();
630
684
  return pageTrees = out;
631
685
  }
632
686
  }
@@ -677,8 +731,8 @@ function loader(...args) {
677
731
  },
678
732
  getNodeMeta(node, language = i18n?.defaultLanguage) {
679
733
  const ref = node.$ref;
680
- if (!ref) return;
681
- return indexer.getMeta(ref, language);
734
+ if (!ref?.meta) return;
735
+ return indexer.getMeta(ref.meta, language);
682
736
  },
683
737
  getNodePage(node, language = i18n?.defaultLanguage) {
684
738
  const ref = node.$ref;
@@ -1,3 +1,3 @@
1
- import { a as Separator, i as Root, n as Item, r as Node, t as Folder } from "../definitions-Cob-Q8-8.js";
2
- import { a as flattenTree, c as visit, i as findSiblings, n as findParent, o as getPageTreePeers, r as findPath, s as getPageTreeRoots, t as findNeighbour } from "../index-DRvW7IXg.js";
1
+ import { a as Separator, i as Root, n as Item, r as Node, t as Folder } from "../definitions-DQRPsvc7.js";
2
+ import { a as flattenTree, c as visit, i as findSiblings, n as findParent, o as getPageTreePeers, r as findPath, s as getPageTreeRoots, t as findNeighbour } from "../index-B_zmuyw1.js";
3
3
  export { Folder, Item, Node, Root, Separator, findNeighbour, findParent, findPath, findSiblings, flattenTree, getPageTreePeers, getPageTreeRoots, visit };
@@ -1,7 +1,7 @@
1
1
  import { t as Awaitable } from "../types-DpbpliNk.js";
2
- import { t as I18nConfig } from "../index-BreFfl9v.js";
3
- import { m as SharedIndex, n as SearchAPI, t as QueryOptions } from "../server-CQJ8bzkB.js";
4
- import { C as LoaderConfig, T as LoaderOutput } from "../index-VMicRewG2.js";
2
+ import { n as I18nConfig } from "../index-Ci-v40o3.js";
3
+ import { m as SharedIndex, n as SearchAPI, t as QueryOptions } from "../server-CB8UVhSB.js";
4
+ import { C as LoaderConfig, T as LoaderOutput } from "../index-8VAf9i7P2.js";
5
5
  import { DocumentData, DocumentOptions } from "flexsearch";
6
6
 
7
7
  //#region src/search/server/build-doc.d.ts
@@ -1,5 +1,5 @@
1
1
  import { r as SortedResult } from "../index-Bcms_L7o.js";
2
- import { n as SearchAPI } from "../server-CQJ8bzkB.js";
2
+ import { n as SearchAPI } from "../server-CB8UVhSB.js";
3
3
  import Mixedbread from "@mixedbread/sdk";
4
4
  import { StoreSearchResponse } from "@mixedbread/sdk/resources/stores";
5
5
 
@@ -1,2 +1,2 @@
1
- import { a as AdvancedOptions, c as SimpleOptions, d as createSearchAPI, f as initAdvancedSearch, i as AdvancedIndex, l as createFromSource, n as SearchAPI, o as ExportedData, p as initSimpleSearch, r as SearchServer, s as Index, t as QueryOptions, u as createI18nSearchAPI } from "../server-CQJ8bzkB.js";
1
+ import { a as AdvancedOptions, c as SimpleOptions, d as createSearchAPI, f as initAdvancedSearch, i as AdvancedIndex, l as createFromSource, n as SearchAPI, o as ExportedData, p as initSimpleSearch, r as SearchServer, s as Index, t as QueryOptions, u as createI18nSearchAPI } from "../server-CB8UVhSB.js";
2
2
  export { AdvancedIndex, AdvancedOptions, ExportedData, Index, QueryOptions, SearchAPI, SearchServer, SimpleOptions, createFromSource, createI18nSearchAPI, createSearchAPI, initAdvancedSearch, initSimpleSearch };
@@ -1,8 +1,8 @@
1
1
  import { t as Awaitable } from "./types-DpbpliNk.js";
2
2
  import { i as StructuredData } from "./remark-structure-C2-K9_ko.js";
3
- import { t as I18nConfig } from "./index-BreFfl9v.js";
3
+ import { n as I18nConfig } from "./index-Ci-v40o3.js";
4
4
  import { r as SortedResult } from "./index-Bcms_L7o.js";
5
- import { C as LoaderConfig, T as LoaderOutput } from "./index-VMicRewG2.js";
5
+ import { C as LoaderConfig, T as LoaderOutput } from "./index-8VAf9i7P2.js";
6
6
  import { Language, Orama, RawData, SearchParams, TypedDocument, create } from "@orama/orama";
7
7
 
8
8
  //#region src/search/orama/create-db.d.ts
@@ -1,2 +1,2 @@
1
- import { i as useFumadocsLoader, n as SerializedPageTree, r as deserializePageTree, t as Serialized } from "../../index-D2f_P1Zo.js";
1
+ import { i as useFumadocsLoader, n as SerializedPageTree, r as deserializePageTree, t as Serialized } from "../../index-Doj0FeGs.js";
2
2
  export { Serialized, SerializedPageTree, deserializePageTree, useFumadocsLoader };
@@ -1,2 +1,2 @@
1
- import { b as dynamicLoader, v as DynamicLoader, y as DynamicLoaderConfig } from "../index-VMicRewG2.js";
1
+ import { b as dynamicLoader, v as DynamicLoader, y as DynamicLoaderConfig } from "../index-8VAf9i7P2.js";
2
2
  export { DynamicLoader, DynamicLoaderConfig, dynamicLoader };
@@ -1,4 +1,4 @@
1
- import { i as isStaticSource, n as loader, r as isDynamicSource } from "../loader-Bs2iumYD.js";
1
+ import { i as isStaticSource, n as loader, r as isDynamicSource } from "../loader-AC1aN-MX.js";
2
2
  import { cache } from "react";
3
3
  //#region src/source/dynamic.ts
4
4
  function dynamicLoader(input, options) {
@@ -1,2 +1,2 @@
1
- import { A as ResolvedLoaderConfig, B as PageTreeTransformer, C as LoaderConfig, D as LoaderPluginOption, E as LoaderPlugin, H as ContentStorageMetaFile, L as PageTreeBuilder, M as loader, O as Meta, P as getSlugs, R as PageTreeBuilderContext, S as InferPageType, T as LoaderOutput, U as ContentStoragePageFile, V as ContentStorage, W as FileSystem, _ as update, a as MetaData, c as SourceConfig, d as VirtualFile, f as _SourceUpdate_, g as source, h as multiple, i as DynamicSource, j as createGetUrl, k as Page, l as SourceUnion, m as isStaticSource, n as llms, o as PageData, p as isDynamicSource, r as path_d_exports, s as Source, t as LLMsConfig, u as StaticSource, w as LoaderOptions, x as InferMetaType, z as PageTreeOptions } from "../index-VMicRewG2.js";
1
+ import { A as ResolvedLoaderConfig, B as PageTreeTransformer, C as LoaderConfig, D as LoaderPluginOption, E as LoaderPlugin, H as ContentStorageMetaFile, L as PageTreeBuilder, M as loader, O as Meta, P as getSlugs, R as PageTreeBuilderContext, S as InferPageType, T as LoaderOutput, U as ContentStoragePageFile, V as ContentStorage, W as FileSystem, _ as update, a as MetaData, c as SourceConfig, d as VirtualFile, f as _SourceUpdate_, g as source, h as multiple, i as DynamicSource, j as createGetUrl, k as Page, l as SourceUnion, m as isStaticSource, n as llms, o as PageData, p as isDynamicSource, r as path_d_exports, s as Source, t as LLMsConfig, u as StaticSource, w as LoaderOptions, x as InferMetaType, z as PageTreeOptions } from "../index-8VAf9i7P2.js";
2
2
  export { ContentStorage, ContentStorageMetaFile, ContentStoragePageFile, DynamicSource, FileSystem, InferMetaType, InferPageType, LLMsConfig, LoaderConfig, LoaderOptions, LoaderOutput, LoaderPlugin, LoaderPluginOption, Meta, MetaData, Page, PageData, PageTreeBuilder, PageTreeBuilderContext, PageTreeOptions, PageTreeTransformer, path_d_exports as PathUtils, ResolvedLoaderConfig, Source, SourceConfig, SourceUnion, StaticSource, VirtualFile, _SourceUpdate_, createGetUrl, getSlugs, isDynamicSource, isStaticSource, llms, loader, multiple, source, update };
@@ -1,5 +1,5 @@
1
1
  import { o as path_exports } from "../path-CPgAF5cw.js";
2
- import { a as multiple, c as FileSystem, i as isStaticSource, n as loader, o as source, r as isDynamicSource, s as update, t as createGetUrl } from "../loader-Bs2iumYD.js";
2
+ import { a as multiple, c as FileSystem, i as isStaticSource, n as loader, o as source, r as isDynamicSource, s as update, t as createGetUrl } from "../loader-AC1aN-MX.js";
3
3
  import { getSlugs } from "./plugins/slugs.js";
4
4
  import { llms } from "./llms.js";
5
5
  export { FileSystem, path_exports as PathUtils, createGetUrl, getSlugs, isDynamicSource, isStaticSource, llms, loader, multiple, source, update };
@@ -1,2 +1,2 @@
1
- import { n as llms, t as LLMsConfig } from "../index-VMicRewG2.js";
1
+ import { n as llms, t as LLMsConfig } from "../index-8VAf9i7P2.js";
2
2
  export { LLMsConfig, llms };
@@ -1,4 +1,4 @@
1
- import { E as LoaderPlugin } from "../../index-VMicRewG2.js";
1
+ import { E as LoaderPlugin } from "../../index-8VAf9i7P2.js";
2
2
  import { icons } from "lucide-react";
3
3
 
4
4
  //#region src/source/plugins/lucide-icons.d.ts
@@ -1,2 +1,2 @@
1
- import { F as slugsFromData, I as slugsPlugin, N as SlugFn, P as getSlugs } from "../../index-VMicRewG2.js";
1
+ import { F as slugsFromData, I as slugsPlugin, N as SlugFn, P as getSlugs } from "../../index-8VAf9i7P2.js";
2
2
  export { SlugFn, getSlugs, slugsFromData, slugsPlugin };
@@ -1,5 +1,5 @@
1
- import { a as Separator$1, n as Item$1, t as Folder$1 } from "../../definitions-Cob-Q8-8.js";
2
- import { E as LoaderPlugin } from "../../index-VMicRewG2.js";
1
+ import { a as Separator$1, n as Item$1, t as Folder$1 } from "../../definitions-DQRPsvc7.js";
2
+ import { E as LoaderPlugin } from "../../index-8VAf9i7P2.js";
3
3
  import { ReactNode } from "react";
4
4
 
5
5
  //#region src/source/plugins/status-badges.d.ts
@@ -7,6 +7,7 @@ import { z } from "zod";
7
7
  declare const metaSchema: z.ZodObject<{
8
8
  title: z.ZodOptional<z.ZodString>;
9
9
  pages: z.ZodOptional<z.ZodArray<z.ZodString>>;
10
+ pagesIndex: z.ZodOptional<z.ZodString>;
10
11
  description: z.ZodOptional<z.ZodString>;
11
12
  root: z.ZodOptional<z.ZodBoolean>;
12
13
  defaultOpen: z.ZodOptional<z.ZodBoolean>;
@@ -6,6 +6,8 @@ import { z } from "zod";
6
6
  const metaSchema = z.object({
7
7
  title: z.string().optional(),
8
8
  pages: z.array(z.string()).optional(),
9
+ /** specify a index page for folder */
10
+ pagesIndex: z.string().optional(),
9
11
  description: z.string().optional(),
10
12
  root: z.boolean().optional(),
11
13
  defaultOpen: z.boolean().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-core",
3
- "version": "16.8.12",
3
+ "version": "16.9.1",
4
4
  "description": "The React.js library for building a documentation website",
5
5
  "keywords": [
6
6
  "Docs",
@@ -1,40 +0,0 @@
1
- //#region src/i18n/index.d.ts
2
- interface I18nConfig<Languages extends string = string> {
3
- /**
4
- * Supported locale codes.
5
- *
6
- * A page tree will be built for each language.
7
- */
8
- languages: Languages[];
9
- /**
10
- * Default locale if not specified
11
- */
12
- defaultLanguage: Languages;
13
- /**
14
- * Don't show the locale prefix on URL.
15
- *
16
- * - `always`: Always hide the prefix
17
- * - `default-locale`: Only hide the default locale
18
- * - `never`: Never hide the prefix
19
- *
20
- * This API uses `NextResponse.rewrite`.
21
- *
22
- * @defaultValue 'never'
23
- */
24
- hideLocale?: 'always' | 'default-locale' | 'never';
25
- /**
26
- * Used by `loader()`, specify the way to parse i18n file structure.
27
- *
28
- * @defaultValue 'dot'
29
- */
30
- parser?: 'dot' | 'dir' | 'none';
31
- /**
32
- * the fallback language when the page has no translations available for a given locale.
33
- *
34
- * Default to `defaultLanguage`, no fallback when set to `null`.
35
- */
36
- fallbackLanguage?: Languages | null;
37
- }
38
- declare function defineI18n<Languages extends string>(config: I18nConfig<Languages>): I18nConfig<Languages>;
39
- //#endregion
40
- export { defineI18n as n, I18nConfig as t };