mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core: handle circular reference in page tree
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
"fumadocs-core": patch
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
[Page Tree] Extract index page from folder
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
handle circular reference in page tree
|
||||
@@ -152,19 +152,22 @@ function createFlattenPathResolver(storage: ContentStorage) {
|
||||
|
||||
function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
const resolveFlattenPath = createFlattenPathResolver(ctx.storage);
|
||||
/** virtual file path -> output page tree node (if built) */
|
||||
/** virtual file path -> output page tree node (if cached) */
|
||||
const pathToNode = new Map<string, PageTree.Node>();
|
||||
const nodeOwner = new Map<PageTree.Node, { owner: string; priority: number }>();
|
||||
/** unfinished nodes */
|
||||
const unfinished = new WeakSet<PageTree.Node>();
|
||||
const ownerMap = new Map<PageTree.Node, { owner: string; priority: number }>();
|
||||
|
||||
/**
|
||||
* when a node is referenced by multiple folders via `...`, this determines which owner they should belong to.
|
||||
*
|
||||
* @returns whether the owner owns the node.
|
||||
*/
|
||||
function registerOwner(ownerPath: string, node: PageTree.Node, priority: number): boolean {
|
||||
const existing = nodeOwner.get(node);
|
||||
function own(ownerPath: string, node: PageTree.Node, priority: number): boolean {
|
||||
if (unfinished.has(node)) return false;
|
||||
const existing = ownerMap.get(node);
|
||||
if (!existing) {
|
||||
nodeOwner.set(node, { owner: ownerPath, priority });
|
||||
ownerMap.set(node, { owner: ownerPath, priority });
|
||||
return true;
|
||||
}
|
||||
if (existing.owner === ownerPath) {
|
||||
@@ -189,10 +192,8 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
}
|
||||
|
||||
function transferOwner(ownerPath: string, node: PageTree.Node) {
|
||||
const existing = nodeOwner.get(node);
|
||||
if (existing) {
|
||||
existing.owner = ownerPath;
|
||||
}
|
||||
const existing = ownerMap.get(node);
|
||||
if (existing) existing.owner = ownerPath;
|
||||
}
|
||||
|
||||
let _id = 0;
|
||||
@@ -292,7 +293,7 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
if (!node) return;
|
||||
|
||||
const children = node.index ? [node.index, ...node.children] : node.children;
|
||||
if (registerOwner(folderPath, node, 2)) {
|
||||
if (own(folderPath, node, 2)) {
|
||||
for (const child of children) {
|
||||
transferOwner(folderPath, child);
|
||||
outputArray.push(child);
|
||||
@@ -300,7 +301,7 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
excludedPaths.add(path);
|
||||
} else {
|
||||
for (const child of children) {
|
||||
if (registerOwner(folderPath, child, 2)) outputArray.push(child);
|
||||
if (own(folderPath, child, 2)) outputArray.push(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -312,9 +313,8 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
path = resolveFlattenPath(path, 'page');
|
||||
node = this.file(path);
|
||||
}
|
||||
if (!node) return;
|
||||
|
||||
if (registerOwner(folderPath, node, 2)) outputArray.push(node);
|
||||
if (!node || !own(folderPath, node, 2)) return;
|
||||
outputArray.push(node);
|
||||
excludedPaths.add(path);
|
||||
},
|
||||
folder(folderPath: string): PageTree.Folder | undefined {
|
||||
@@ -348,10 +348,11 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
: undefined,
|
||||
};
|
||||
pathToNode.set(folderPath, node);
|
||||
unfinished.add(node);
|
||||
|
||||
if (!(metadata.root ?? isGlobalRoot)) {
|
||||
const file = this.file(indexPath);
|
||||
if (file && registerOwner(folderPath, file, 0)) node.index = file;
|
||||
if (file && own(folderPath, file, 0)) node.index = file;
|
||||
}
|
||||
|
||||
if (metadata.pages) {
|
||||
@@ -379,7 +380,7 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
item === restReversed,
|
||||
);
|
||||
for (const child of resolvedItem) {
|
||||
if (registerOwner(folderPath, child, 0)) node.children.push(child);
|
||||
if (own(folderPath, child, 0)) node.children.push(child);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -387,12 +388,13 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
files,
|
||||
node.index ? (file) => file !== indexPath : undefined,
|
||||
)) {
|
||||
if (registerOwner(folderPath, item, 0)) node.children.push(item);
|
||||
if (own(folderPath, item, 0)) node.children.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
node.icon = metadata.icon ?? node.index?.icon;
|
||||
node.name = metadata.title ?? node.index?.name;
|
||||
unfinished.delete(node);
|
||||
if (!node.name) {
|
||||
const folderName = basename(folderPath);
|
||||
node.name = pathToName(group.exec(folderName)?.[1] ?? folderName);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$id": "root",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:a",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:a/page.mdx",
|
||||
"name": "First Page",
|
||||
"type": "page",
|
||||
"url": "/a/page",
|
||||
},
|
||||
{
|
||||
"$id": "root:a/b",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:a/b/page.mdx",
|
||||
"name": "Second Page",
|
||||
"type": "page",
|
||||
"url": "/a/b/page",
|
||||
},
|
||||
],
|
||||
"name": "B",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "A",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs",
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Source } from '@/source';
|
||||
|
||||
export const source: Source = {
|
||||
files: [
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'meta.json',
|
||||
data: {
|
||||
pages: ['a'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'a/b/meta.json',
|
||||
data: {
|
||||
pages: ['../', '...'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'a/page.mdx',
|
||||
data: {
|
||||
title: 'First Page',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'a/b/page.mdx',
|
||||
data: {
|
||||
title: 'Second Page',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -98,6 +98,11 @@ const pageTreeTests: {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Circular Reference',
|
||||
source: (await import('./fixtures/page-trees/circular')).source,
|
||||
output: './fixtures/page-trees/circular.test.json',
|
||||
},
|
||||
];
|
||||
|
||||
for (const pageTreeTest of pageTreeTests) {
|
||||
|
||||
Reference in New Issue
Block a user