Core: Support folder group on Page Tree Builder

This commit is contained in:
fuma-nama
2024-07-17 19:01:58 +08:00
parent c22371a7b2
commit 2ffd5ea287
21 changed files with 84 additions and 29 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Support folder group on Page Tree Builder
+1 -7
View File
@@ -37,10 +37,4 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
apps/docs/content/docs/ui/museum/events.mdx
apps/docs/content/docs/ui/museum/operations.mdx
apps/docs/content/docs/ui/museum/tickets.mdx
apps/docs/content/docs/ui/typescript.mdx
+5 -1
View File
@@ -1 +1,5 @@
out out
content/docs/ui/museum/events.mdx
content/docs/ui/museum/operations.mdx
content/docs/ui/museum/tickets.mdx
content/docs/ui/(integrations)/typescript.mdx
+1 -3
View File
@@ -14,8 +14,6 @@ interface Param {
slug: string[]; slug: string[];
} }
export const dynamicParams = false;
export default function Page({ export default function Page({
params, params,
}: { }: {
@@ -64,7 +62,6 @@ export default function Page({
</p> </p>
<DocsBody> <DocsBody>
{preview && preview in Preview ? Preview[preview] : null} {preview && preview in Preview ? Preview[preview] : null}
{page.data.index ? <Category page={page} /> : null}
<page.data.exports.default <page.data.exports.default
components={{ components={{
HeadlessOnly: HeadlessOnly:
@@ -77,6 +74,7 @@ export default function Page({
: () => undefined, : () => undefined,
}} }}
/> />
{page.data.index ? <Category page={page} /> : null}
</DocsBody> </DocsBody>
</DocsPage> </DocsPage>
); );
@@ -0,0 +1,8 @@
---
title: Blocks
description: Introduction to blocks
index: true
---
Blocks are essential elements of docs, including layouts and navigation elements.
They provide basic functionalities to browse the docs, you can see their usage and customisable options.
@@ -0,0 +1,5 @@
---
title: Components
description: Additional components to improve your docs
index: true
---
+1 -1
View File
@@ -100,7 +100,7 @@ Some common questions you may encounter.
they are customisable with exposed props like `style` and `className`. they are customisable with exposed props like `style` and `className`.
Blocks are essential elements of docs, providing navigation and basic functionality. Blocks are essential elements of docs, providing navigation and basic functionality.
We provided many options to customise them, see the **Blocks** section of the docs. We provided many options to customise them, see [Blocks](/docs/ui/blocks).
Although the docs didn't explicitly mention, you can always style the elements with CSS selectors. Although the docs didn't explicitly mention, you can always style the elements with CSS selectors.
+1 -1
View File
@@ -72,7 +72,7 @@ see [MDX Syntax](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax) to learn more.
### Cards ### Cards
A [`Card`](/docs/ui/components/mdx/card) component is included by default. Similar to links, but you can display more information (e.g. icons). A [`Card`](/docs/ui/mdx/card) component is included by default. Similar to links, but you can display more information (e.g. icons).
```mdx ```mdx
<Cards> <Cards>
@@ -1,11 +1,12 @@
--- ---
title: MDX title: MDX
description: Default MDX Components description: Default MDX Components
index: true
--- ---
## Usage ## Usage
The default components include Card, Code Blocks and Headings. The default MDX components include Card, Code Blocks and Headings.
```ts ```ts
import defaultComponents from 'fumadocs-ui/mdx'; import defaultComponents from 'fumadocs-ui/mdx';
+6 -10
View File
@@ -10,17 +10,13 @@
"markdown", "markdown",
"page-conventions", "page-conventions",
"navigation", "navigation",
"---Components---", "---References---",
"...components", "components",
"---Blocks---", "mdx",
"...blocks", "blocks",
"---Integrations---", "(integrations)",
"math",
"typescript",
"twoslash",
"openapi",
"museum",
"---Others---", "---Others---",
"museum",
"[Vercel](https://vercel.com)" "[Vercel](https://vercel.com)"
] ]
} }
+1
View File
@@ -25,6 +25,7 @@ const config = {
ignoreDuringBuilds: true, ignoreDuringBuilds: true,
}, },
images: { images: {
unoptimized: true,
remotePatterns: [ remotePatterns: [
{ {
protocol: 'https', protocol: 'https',
+2 -1
View File
@@ -141,7 +141,8 @@ export function createGetUrl(baseUrl: string): UrlFn {
export function getSlugs(info: FileInfo): string[] { export function getSlugs(info: FileInfo): string[] {
const result = [...info.dirname.split('/'), info.name].filter( const result = [...info.dirname.split('/'), info.name].filter(
(v) => v.length > 0, // filter empty folder names and file groups like (group_name)
(v) => v.length > 0 && !/^\(.+\)$/.test(v),
); );
return result[result.length - 1] === 'index' ? result.slice(0, -1) : result; return result[result.length - 1] === 'index' ? result.slice(0, -1) : result;
+19 -3
View File
@@ -56,6 +56,7 @@ export interface PageTreeBuilder {
) => Record<string, PageTree.Root>; ) => Record<string, PageTree.Root>;
} }
const group = /^\((?<name>.+)\)$/;
const link = /^\[(?<text>.+)]\((?<url>.+)\)$/; const link = /^\[(?<text>.+)]\((?<url>.+)\)$/;
const separator = /^---(?<name>.*?)---$/; const separator = /^---(?<name>.*?)---$/;
const rest = '...'; const rest = '...';
@@ -204,7 +205,7 @@ function buildFolderNode(
const node: PageTree.Folder = { const node: PageTree.Folder = {
type: 'folder', type: 'folder',
name: metadata?.title ?? index?.name ?? pathToName(folder.file.name), name: metadata?.title ?? index?.name ?? pathToName(folder.file.name, true),
icon: ctx.options.resolveIcon?.(metadata?.icon), icon: ctx.options.resolveIcon?.(metadata?.icon),
root: metadata?.root, root: metadata?.root,
defaultOpen: metadata?.defaultOpen, defaultOpen: metadata?.defaultOpen,
@@ -282,8 +283,23 @@ function findLocalizedFile(
return ctx.storage.read(`${path}.${ctx.lang}`, format); return ctx.storage.read(`${path}.${ctx.lang}`, format);
} }
function pathToName(path: string): string { /**
return path.slice(0, 1).toUpperCase() + path.slice(1); * Get item name from file name
*
* @param name - file name
* @param resolveGroup - resolve folder groups like (group_name)
*/
function pathToName(name: string, resolveGroup = false): string {
const resolved = resolveGroup ? (group.exec(name)?.[1] ?? name) : name;
const result = [];
for (const c of resolved) {
if (result.length === 0) result.push(c.toLocaleUpperCase());
else if (c === '-') result.push(' ');
else result.push(c);
}
return result.join('');
} }
function removeUndefined<T extends object>(value: T): T { function removeUndefined<T extends object>(value: T): T {
+27 -1
View File
@@ -8,6 +8,10 @@ test('Get Slugs', () => {
'to', 'to',
'file', 'file',
]); ]);
expect(getSlugs(parseFilePath('path/(group)/file'))).toStrictEqual([
'path',
'file',
]);
expect(getSlugs(parseFilePath(''))).toStrictEqual([]); expect(getSlugs(parseFilePath(''))).toStrictEqual([]);
}); });
@@ -96,6 +100,13 @@ test('Page Tree: Nested Directories', async () => {
title: 'Nested Page', title: 'Nested Page',
}, },
}, },
{
type: 'page',
path: '/(nested)/hello.mdx',
data: {
title: 'Route Group Page',
},
},
], ],
}, },
}); });
@@ -103,6 +114,17 @@ test('Page Tree: Nested Directories', async () => {
expect(result.pageTree, 'Page Tree').toMatchInlineSnapshot(` expect(result.pageTree, 'Page Tree').toMatchInlineSnapshot(`
{ {
"children": [ "children": [
{
"children": [
{
"name": "Route Group Page",
"type": "page",
"url": "/hello",
},
],
"name": "Nested",
"type": "folder",
},
{ {
"name": "Hello", "name": "Hello",
"type": "page", "type": "page",
@@ -129,8 +151,12 @@ test('Page Tree: Nested Directories', async () => {
"name": "", "name": "",
} }
`); `);
expect(result.getPages().length).toBe(3); expect(result.getPages().length).toBe(4);
// page in folder
expect(result.getPage(['nested', 'test'])).toBeDefined(); expect(result.getPage(['nested', 'test'])).toBeDefined();
// page in folder group
expect(result.getPage(['hello'])).toBeDefined();
}); });
test('Page Tree: Internationalized Routing', () => { test('Page Tree: Internationalized Routing', () => {