fix: do not add the same page twice in the sidebar tree (#2244)

This commit is contained in:
Tommy D. Rossi
2025-08-23 09:49:38 +08:00
committed by GitHub
parent 8d110108ba
commit 88b5a4e4c2
3 changed files with 184 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@fumadocs/core": patch
---
Fix duplicate pages in page tree when referencing subpage in meta.json and using `...` or adding the subfolder again
@@ -27,6 +27,7 @@ export interface PageTreeBuilderContext<
storages?: Record<string, ContentStorage<Page, Meta>>;
locale?: string;
visitedPages?: Set<string>;
}
export interface PageTreeTransformer<
@@ -120,9 +121,13 @@ function buildAll(
);
for (const path of sortedPaths) {
if (ctx.visitedPages?.has(path)) continue;
const fileNode = buildFileNode(path, ctx);
if (!fileNode) continue;
ctx.visitedPages?.add(path);
if (basename(path, extname(path)) === 'index') output.unshift(fileNode);
else output.push(fileNode);
}
@@ -205,6 +210,9 @@ function resolveFolderItem(
}
const fileNode = buildFileNode(path, ctx);
if (fileNode) {
ctx.visitedPages?.add(path);
}
return fileNode ? [fileNode] : [];
}
@@ -302,6 +310,7 @@ function buildFileNode(
ctx: PageTreeBuilderContext,
): PageTree.Item | undefined {
const { options, getUrl, storage, locale, transformers } = ctx;
const page = storage.read(path);
if (page?.format !== 'page') return;
@@ -400,6 +409,7 @@ export function createPageTreeBuilder(getUrl: UrlFn): PageTreeBuilder {
locale,
storage,
storages,
visitedPages: new Set<string>(),
resolveName(name, format) {
return resolve(name, format) ?? name;
},
+169
View File
@@ -252,3 +252,172 @@ test('Loader: Rest operator', () => {
}
`);
});
test('Loader: Allow duplicate pages when explicitly referenced twice', () => {
const result = loader({
baseUrl: '/',
pageTree: {
noRef: true,
},
source: {
files: [
{
type: 'meta',
path: 'meta.json',
data: {
pages: ['page1', 'page1', 'page2'],
},
},
{
type: 'page',
path: 'page1.mdx',
data: {
title: 'Page 1',
},
},
{
type: 'page',
path: 'page2.mdx',
data: {
title: 'Page 2',
},
},
],
},
});
const treeChildren = result.pageTree.children;
expect(treeChildren.length).toBe(3);
expect(treeChildren[0].$id).toBe('page1.mdx');
expect(treeChildren[1].$id).toBe('page1.mdx');
expect(treeChildren[2].$id).toBe('page2.mdx');
});
test('Loader: No duplicate pages when referencing subfolder items and folder', () => {
const result = loader({
baseUrl: '/',
pageTree: {
noRef: true,
},
source: {
files: [
{
type: 'meta',
path: 'meta.json',
data: {
pages: [
'index',
'subfolder/page1', // Reference specific page in subfolder
'subfolder/page2', // Reference another specific page
'other-page',
'subfolder', // Reference the entire folder
],
},
},
{
type: 'page',
path: 'index.mdx',
data: {
title: 'Home',
},
},
{
type: 'page',
path: 'other-page.mdx',
data: {
title: 'Other Page',
},
},
{
type: 'page',
path: 'subfolder/page1.mdx',
data: {
title: 'Subfolder Page 1',
},
},
{
type: 'page',
path: 'subfolder/page2.mdx',
data: {
title: 'Subfolder Page 2',
},
},
{
type: 'page',
path: 'subfolder/page3.mdx',
data: {
title: 'Subfolder Page 3',
},
},
],
},
});
// Check that pages are not duplicated
const pages = result.getPages();
const pagePaths = pages.map((page) => page.slugs.join('/'));
// Should have exactly 5 pages total
expect(pages.length).toBe(5);
// Check that each page appears only once
const uniquePaths = new Set(pagePaths);
expect(uniquePaths.size).toBe(pagePaths.length);
// Verify all pages are present
expect(pagePaths.sort()).toEqual([
'', // index
'other-page',
'subfolder/page1',
'subfolder/page2',
'subfolder/page3',
]);
// Check the page tree structure
expect(removeUndefined(result.pageTree, true), 'Page Tree')
.toMatchInlineSnapshot(`
{
"$id": "root",
"children": [
{
"$id": "index.mdx",
"name": "Home",
"type": "page",
"url": "/",
},
{
"$id": "subfolder/page1.mdx",
"name": "Subfolder Page 1",
"type": "page",
"url": "/subfolder/page1",
},
{
"$id": "subfolder/page2.mdx",
"name": "Subfolder Page 2",
"type": "page",
"url": "/subfolder/page2",
},
{
"$id": "other-page.mdx",
"name": "Other Page",
"type": "page",
"url": "/other-page",
},
{
"$id": "subfolder",
"children": [
{
"$id": "subfolder/page3.mdx",
"name": "Subfolder Page 3",
"type": "page",
"url": "/subfolder/page3",
},
],
"name": "Subfolder",
"type": "folder",
},
],
"name": "",
}
`);
});