Core: extend page tree root scope

This commit is contained in:
Fuma Nama
2026-04-06 11:25:19 +08:00
parent 3c101a5522
commit 552492732e
16 changed files with 59 additions and 31 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
extend page tree root scope
+11 -8
View File
@@ -8,7 +8,14 @@ interface ID {
}
export interface Root extends ID {
/**
* @internal meta file path
*/
$ref?: string;
type?: 'root';
name: ReactNode;
description?: ReactNode;
children: Node[];
/**
* Another page tree that won't be displayed unless being opened.
@@ -20,11 +27,9 @@ export type Node = Item | Separator | Folder;
export interface Item extends ID {
/**
* @internal
* @internal page file path
*/
$ref?: {
file: string;
};
$ref?: string;
type: 'page';
name: ReactNode;
@@ -48,11 +53,9 @@ export interface Separator extends ID {
export interface Folder extends ID {
/**
* @internal
* @internal meta file path
*/
$ref?: {
metaFile?: string;
};
$ref?: string;
type: 'folder';
name: ReactNode;
+2 -2
View File
@@ -60,7 +60,7 @@ export function getPageTreeRoots(
return roots;
});
if (!('type' in pageTree)) result.push(pageTree);
if (!pageTree.type || pageTree.type === 'root') result.push(pageTree);
return result;
}
@@ -109,7 +109,7 @@ export function findParent(
let result: PageTree.Root | PageTree.Folder | undefined;
visit(from, (node, parent) => {
if ('type' in node && node.type === 'page' && node.url === url) {
if (node.type === 'page' && node.url === url) {
result = parent;
return 'break';
}
+3 -3
View File
@@ -141,7 +141,7 @@ export interface LoaderOutput<Config extends LoaderConfig> {
) => Page<Config['source']['pageData']> | undefined;
getNodeMeta: (
node: PageTree.Folder,
node: PageTree.Folder | PageTree.Root,
language?: string,
) => Meta<Config['source']['metaData']> | undefined;
@@ -401,13 +401,13 @@ export function loader(
return indexer.getPageBySlugs(slugs, language);
},
getNodeMeta(node, language = i18n?.defaultLanguage) {
const ref = node.$ref?.metaFile;
const ref = node.$ref;
if (!ref) return;
return indexer.getMeta(ref, language);
},
getNodePage(node, language = i18n?.defaultLanguage) {
const ref = node.$ref?.file;
const ref = node.$ref;
if (!ref) return;
return indexer.getPage(ref, language);
+18 -7
View File
@@ -8,29 +8,38 @@ interface Context {
export interface LLMsConfig {
TAB?: string;
renderName?: (item: PageTree.Node | PageTree.Root, ctx: Context) => string;
renderDescription?: (item: PageTree.Item | PageTree.Folder, ctx: Context) => string;
renderDescription?: (
item: PageTree.Root | PageTree.Item | PageTree.Folder,
ctx: Context,
) => string;
}
export function llms<C extends LoaderConfig>(loader: LoaderOutput<C>, config: LLMsConfig = {}) {
const {
TAB = ' ',
renderName = (node, ctx): string => {
if (typeof node.name === 'string') return node.name;
if ('type' in node && node.type === 'page') {
if (node.type === 'page') {
const page = loader.getNodePage(node, ctx.lang);
if (page) return page.data.title ?? '';
} else if (node.type === 'separator') {
return String(node.name);
} else {
const meta = loader.getNodeMeta(node, ctx.lang);
if (meta) return meta?.data.title ?? '';
}
return String(node.name);
return typeof node.name === 'string' ? node.name : '';
},
renderDescription = (node, ctx): string => {
if (typeof node.description === 'string') return node.description;
if ('type' in node && node.type === 'page') {
if (node.type === 'page') {
const page = loader.getNodePage(node, ctx.lang);
if (page) return page.data.description ?? '';
} else {
const meta = loader.getNodeMeta(node, ctx.lang);
if (meta) return meta?.data.description ?? '';
}
return String(node.description);
return typeof node.description === 'string' ? node.description : '';
},
} = config;
@@ -44,6 +53,8 @@ export function llms<C extends LoaderConfig>(loader: LoaderOutput<C>, config: LL
const out: string[] = [];
const ctx: Context = { lang };
out.push(`# ${renderName(pageTree, ctx)}`);
const description = renderDescription(pageTree, ctx);
if (description) out.push('', `> ${description}`);
out.push('');
function item(name: string, description: string, indent: number) {
+5 -11
View File
@@ -318,12 +318,7 @@ export class PageTreeBuilder {
collapsible: metadata.collapsible,
children: [],
$id: this.generateId(folderPath),
$ref:
!this.ctx.noRef && meta
? {
metaFile: metaPath,
}
: undefined,
$ref: !this.ctx.noRef && meta ? metaPath : undefined,
};
this.pathToNode.set(folderPath, node);
this.unfinished.add(node);
@@ -400,11 +395,7 @@ export class PageTreeBuilder {
description,
icon,
url: this.ctx.getUrl(page.slugs, this.ctx.locale),
$ref: !this.ctx.noRef
? {
file: path,
}
: undefined,
$ref: !this.ctx.noRef ? path : undefined,
};
for (const transformer of this.transformers) {
if (!transformer.file) continue;
@@ -418,8 +409,11 @@ export class PageTreeBuilder {
root(id = 'root', path = ''): PageTree.Root {
const folder = this.folder(path);
let root: PageTree.Root = {
type: 'root',
$ref: folder?.$ref,
$id: this.generateId(id),
name: folder?.name || 'Docs',
description: folder?.description,
children: folder ? folder.children : [],
};
@@ -21,4 +21,5 @@
},
],
"name": "Docs",
"type": "root",
}
@@ -9,4 +9,5 @@
},
],
"name": "Docs",
"type": "root",
}
@@ -29,4 +29,5 @@
},
],
"name": "Docs",
"type": "root",
}
@@ -23,6 +23,7 @@
},
],
"name": "Docs Chinese",
"type": "root",
},
"en": {
"$id": "en:root",
@@ -48,5 +49,6 @@
},
],
"name": "Docs English",
"type": "root",
},
}
@@ -23,6 +23,7 @@
},
],
"name": "Docs Chinese",
"type": "root",
},
"en": {
"$id": "en:root",
@@ -48,5 +49,6 @@
},
],
"name": "Docs English",
"type": "root",
},
}
+2
View File
@@ -23,6 +23,7 @@
},
],
"name": "Docs Chinese",
"type": "root",
},
"en": {
"$id": "en:root",
@@ -48,5 +49,6 @@
},
],
"name": "Docs English",
"type": "root",
},
}
@@ -77,6 +77,8 @@
},
],
"name": "Docs",
"type": "root",
},
"name": "Docs",
"type": "root",
}
@@ -47,4 +47,5 @@
},
],
"name": "Docs",
"type": "root",
}
+1
View File
@@ -15,4 +15,5 @@
},
],
"name": "Docs",
"type": "root",
}
+2
View File
@@ -346,6 +346,7 @@ test('Loader: No duplicate pages when referencing subfolder items and folder', (
},
],
"name": "Docs",
"type": "root",
}
`);
});
@@ -423,6 +424,7 @@ test('Loader: Serialize data', async () => {
},
],
"name": "Docs",
"type": "root",
},
}
`);