mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-26 23:49:51 +00:00
Core: define the priority to resolve node owner
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
[Page Tree Builder] define the priority to resolve node owner
|
||||
@@ -8,7 +8,7 @@ import type { SourceConfig } from '../source';
|
||||
export interface PageTreeBuilderContext<Config extends SourceConfig = SourceConfig> {
|
||||
rootId: string;
|
||||
generateNodeId: () => string;
|
||||
options: PageTreeOptions;
|
||||
noRef: boolean;
|
||||
transformers: PageTreeTransformer<Config>[];
|
||||
|
||||
builder: PageTreeBuilder;
|
||||
@@ -109,7 +109,7 @@ export function createPageTreeBuilder(loaderConfig: ResolvedLoaderConfig): PageT
|
||||
rootId,
|
||||
transformers,
|
||||
builder: this,
|
||||
options,
|
||||
noRef: options.noRef ?? false,
|
||||
getUrl: url,
|
||||
locale,
|
||||
storage,
|
||||
@@ -142,7 +142,35 @@ function createFlattenPathResolver(storage: ContentStorage) {
|
||||
|
||||
function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
const resolveFlattenPath = createFlattenPathResolver(ctx.storage);
|
||||
const visitedPaths = new Set<string>();
|
||||
/** virtual file path -> output page tree node (if built) */
|
||||
const pathToNode = new Map<string, PageTree.Node>();
|
||||
const nodeOwner = 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);
|
||||
if (existing) {
|
||||
if (existing.owner === ownerPath) {
|
||||
existing.priority = Math.max(existing.priority, priority);
|
||||
return true;
|
||||
}
|
||||
if (existing.priority >= priority) return false;
|
||||
|
||||
// return ownership
|
||||
const folder = pathToNode.get(existing.owner);
|
||||
if (folder && folder.type === 'folder') {
|
||||
if (folder.index === node) delete folder.index;
|
||||
else folder.children = folder.children.filter((child) => child !== node);
|
||||
}
|
||||
}
|
||||
|
||||
nodeOwner.set(node, { owner: ownerPath, priority });
|
||||
return true;
|
||||
}
|
||||
|
||||
function nextNodeId(localId = ctx.generateNodeId()) {
|
||||
return `${ctx.rootId}:${localId}`;
|
||||
@@ -170,8 +198,16 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
items.push(...folders);
|
||||
return items;
|
||||
},
|
||||
resolveFolderItem(folderPath: string, item: string): PageTree.Node[] | '...' | 'z...a' {
|
||||
if (item === rest || item === restReversed) return item;
|
||||
resolveFolderItem(
|
||||
folderPath: string,
|
||||
item: string,
|
||||
outputArray: (PageTree.Node | '...' | 'z...a')[],
|
||||
excludedPaths: Set<string>,
|
||||
) {
|
||||
if (item === rest || item === restReversed) {
|
||||
outputArray.push(item);
|
||||
return;
|
||||
}
|
||||
|
||||
let match = separator.exec(item);
|
||||
if (match?.groups) {
|
||||
@@ -186,8 +222,8 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
if (!transformer.separator) continue;
|
||||
node = transformer.separator.call(ctx, node);
|
||||
}
|
||||
|
||||
return [node];
|
||||
outputArray.push(node);
|
||||
return;
|
||||
}
|
||||
|
||||
match = link.exec(item);
|
||||
@@ -207,80 +243,83 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
if (!transformer.file) continue;
|
||||
node = transformer.file.call(ctx, node);
|
||||
}
|
||||
|
||||
return [node];
|
||||
outputArray.push(node);
|
||||
return;
|
||||
}
|
||||
|
||||
const isExcept = item.startsWith(excludePrefix);
|
||||
const isExtract = !isExcept && item.startsWith(extractPrefix);
|
||||
|
||||
let filename = item;
|
||||
if (isExcept) {
|
||||
filename = item.slice(excludePrefix.length);
|
||||
} else if (isExtract) {
|
||||
filename = item.slice(extractPrefix.length);
|
||||
if (item.startsWith(excludePrefix)) {
|
||||
excludedPaths.add(
|
||||
resolveFlattenPath(joinPath(folderPath, item.slice(excludePrefix.length)), 'page'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const path = resolveFlattenPath(joinPath(folderPath, filename), 'page');
|
||||
if (item.startsWith(extractPrefix)) {
|
||||
const path = joinPath(folderPath, item.slice(extractPrefix.length));
|
||||
const dirNode = this.folder(path, false);
|
||||
if (!dirNode) return;
|
||||
|
||||
if (isExcept) {
|
||||
visitedPaths.add(path);
|
||||
return [];
|
||||
for (const child of dirNode.children) {
|
||||
if (registerOwner(folderPath, child, 1)) outputArray.push(child);
|
||||
}
|
||||
excludedPaths.add(path);
|
||||
return;
|
||||
}
|
||||
|
||||
const dirNode = this.folder(path, false);
|
||||
if (dirNode) {
|
||||
return isExtract ? dirNode.children : [dirNode];
|
||||
const path = resolveFlattenPath(joinPath(folderPath, item), 'page');
|
||||
const node = this.folder(path, false) ?? this.file(path);
|
||||
if (node) {
|
||||
outputArray.push(node);
|
||||
excludedPaths.add(path);
|
||||
}
|
||||
|
||||
const fileNode = this.file(path);
|
||||
return fileNode ? [fileNode] : [];
|
||||
},
|
||||
folder(folderPath: string, isGlobalRoot: boolean): PageTree.Folder | undefined {
|
||||
const { storage, options, transformers } = ctx;
|
||||
const files = storage.readDir(folderPath);
|
||||
const cached = pathToNode.get(folderPath);
|
||||
if (cached) return cached as PageTree.Folder;
|
||||
|
||||
const files = ctx.storage.readDir(folderPath);
|
||||
if (!files) return;
|
||||
|
||||
const metaPath = resolveFlattenPath(joinPath(folderPath, 'meta'), 'meta');
|
||||
const indexPath = resolveFlattenPath(joinPath(folderPath, 'index'), 'page');
|
||||
|
||||
let meta = storage.read(metaPath);
|
||||
let meta = ctx.storage.read(metaPath);
|
||||
if (meta && meta.format !== 'meta') meta = undefined;
|
||||
|
||||
const metadata = meta?.data ?? {};
|
||||
const { root = isGlobalRoot, pages } = metadata;
|
||||
let index: PageTree.Item | undefined;
|
||||
let children: PageTree.Node[];
|
||||
const children: PageTree.Node[] = [];
|
||||
|
||||
if (pages) {
|
||||
const resolved = pages.flatMap<PageTree.Node | typeof rest | typeof restReversed>((item) =>
|
||||
this.resolveFolderItem(folderPath, item),
|
||||
);
|
||||
if (!(metadata.root ?? isGlobalRoot)) {
|
||||
const file = this.file(indexPath);
|
||||
if (file && registerOwner(folderPath, file, 0)) index = file;
|
||||
}
|
||||
|
||||
if (!root && !visitedPaths.has(indexPath)) {
|
||||
index = this.file(indexPath);
|
||||
if (metadata.pages) {
|
||||
const outputArray: (PageTree.Node | typeof rest | typeof restReversed)[] = [];
|
||||
const excludedPaths = new Set<string>();
|
||||
for (const item of metadata.pages) {
|
||||
this.resolveFolderItem(folderPath, item, outputArray, excludedPaths);
|
||||
}
|
||||
|
||||
for (let i = 0; i < resolved.length; i++) {
|
||||
const item = resolved[i];
|
||||
if (item !== rest && item !== restReversed) continue;
|
||||
for (const item of outputArray) {
|
||||
if (item !== rest && item !== restReversed) {
|
||||
if (item === index) index = undefined;
|
||||
if (registerOwner(folderPath, item, 3)) children.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
const items = this.buildPaths(
|
||||
files.filter((file) => !visitedPaths.has(file)),
|
||||
const resolvedItem = this.buildPaths(
|
||||
files.filter((file) => !excludedPaths.has(file)),
|
||||
item === restReversed,
|
||||
);
|
||||
|
||||
resolved.splice(i, 1, ...items);
|
||||
break;
|
||||
for (const child of resolvedItem) {
|
||||
if (registerOwner(folderPath, child, 0)) children.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
children = resolved as PageTree.Node[];
|
||||
} else {
|
||||
if (!root && !visitedPaths.has(indexPath)) {
|
||||
index = this.file(indexPath);
|
||||
for (const item of this.buildPaths(files)) {
|
||||
if (item !== index && registerOwner(folderPath, item, 0)) children.push(item);
|
||||
}
|
||||
|
||||
children = this.buildPaths(files.filter((file) => !visitedPaths.has(file)));
|
||||
}
|
||||
|
||||
let node: PageTree.Folder = {
|
||||
@@ -301,26 +340,26 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
children,
|
||||
$id: nextNodeId(folderPath),
|
||||
$ref:
|
||||
!options.noRef && meta
|
||||
!ctx.noRef && meta
|
||||
? {
|
||||
metaFile: metaPath,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
visitedPaths.add(folderPath);
|
||||
for (const transformer of transformers) {
|
||||
for (const transformer of ctx.transformers) {
|
||||
if (!transformer.folder) continue;
|
||||
node = transformer.folder.call(ctx, node, folderPath, metaPath);
|
||||
}
|
||||
|
||||
pathToNode.set(folderPath, node);
|
||||
return node;
|
||||
},
|
||||
file(path: string): PageTree.Item | undefined {
|
||||
const { options, getUrl, storage, locale, transformers } = ctx;
|
||||
const cached = pathToNode.get(path);
|
||||
if (cached) return cached as PageTree.Item;
|
||||
|
||||
const page = storage.read(path);
|
||||
if (page?.format !== 'page') return;
|
||||
const page = ctx.storage.read(path);
|
||||
if (!page || page.format !== 'page') return;
|
||||
|
||||
const { title, description, icon } = page.data;
|
||||
let item: PageTree.Item = {
|
||||
@@ -329,20 +368,19 @@ function createPageTreeBuilderUtils(ctx: PageTreeBuilderContext) {
|
||||
name: title ?? pathToName(basename(path, extname(path))),
|
||||
description,
|
||||
icon,
|
||||
url: getUrl(page.slugs, locale),
|
||||
$ref: !options.noRef
|
||||
url: ctx.getUrl(page.slugs, ctx.locale),
|
||||
$ref: !ctx.noRef
|
||||
? {
|
||||
file: path,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
visitedPaths.add(path);
|
||||
for (const transformer of transformers) {
|
||||
for (const transformer of ctx.transformers) {
|
||||
if (!transformer.file) continue;
|
||||
item = transformer.file.call(ctx, item, path);
|
||||
}
|
||||
|
||||
pathToNode.set(path, item);
|
||||
return item;
|
||||
},
|
||||
root(): PageTree.Root {
|
||||
|
||||
@@ -18,7 +18,8 @@ export function transformerFallback(): PageTreeTransformer {
|
||||
if (isolatedStorage.getFiles().length === 0) return root;
|
||||
|
||||
root.fallback = this.builder.build(isolatedStorage, {
|
||||
...this.options,
|
||||
noRef: this.noRef,
|
||||
transformers: this.transformers,
|
||||
id: `fallback-${root.$id ?? ''}`,
|
||||
generateFallback: false,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$id": "root",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:test.mdx",
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/test",
|
||||
},
|
||||
{
|
||||
"$id": "root:hello",
|
||||
"children": [],
|
||||
"index": {
|
||||
"$id": "root:hello/index.mdx",
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/hello",
|
||||
},
|
||||
"name": "Hello",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs",
|
||||
}
|
||||
+19
@@ -18,3 +18,22 @@ export const source: Source = {
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const noMeta: Source = {
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: 'test.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'hello/index.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"cn": {
|
||||
"$id": "cn",
|
||||
"children": [
|
||||
{
|
||||
"$id": "cn:test.mdx",
|
||||
"name": "Hello Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/test",
|
||||
},
|
||||
{
|
||||
"$id": "cn:nested",
|
||||
"children": [
|
||||
{
|
||||
"$id": "cn:nested/test.mdx",
|
||||
"name": "Nested Page Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/nested/test",
|
||||
},
|
||||
],
|
||||
"name": "Nested Chinese",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs Chinese",
|
||||
},
|
||||
"en": {
|
||||
"$id": "en",
|
||||
"children": [
|
||||
{
|
||||
"$id": "en:test.mdx",
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/en/test",
|
||||
},
|
||||
{
|
||||
"$id": "en:nested",
|
||||
"children": [
|
||||
{
|
||||
"$id": "en:nested/test.mdx",
|
||||
"name": "Nested Page",
|
||||
"type": "page",
|
||||
"url": "/en/nested/test",
|
||||
},
|
||||
],
|
||||
"name": "Nested",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs English",
|
||||
},
|
||||
}
|
||||
@@ -4,24 +4,15 @@
|
||||
"children": [
|
||||
{
|
||||
"$id": "cn:test.mdx",
|
||||
"$ref": {
|
||||
"file": "test.mdx",
|
||||
},
|
||||
"name": "Hello Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/test",
|
||||
},
|
||||
{
|
||||
"$id": "cn:nested",
|
||||
"$ref": {
|
||||
"metaFile": "nested/meta.json",
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"$id": "cn:nested/test.mdx",
|
||||
"$ref": {
|
||||
"file": "nested/test.mdx",
|
||||
},
|
||||
"name": "Nested Page Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/nested/test",
|
||||
@@ -38,9 +29,6 @@
|
||||
"children": [
|
||||
{
|
||||
"$id": "en:test.mdx",
|
||||
"$ref": {
|
||||
"file": "test.mdx",
|
||||
},
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/en/test",
|
||||
@@ -50,9 +38,6 @@
|
||||
"children": [
|
||||
{
|
||||
"$id": "en:nested/test.mdx",
|
||||
"$ref": {
|
||||
"file": "nested/test.mdx",
|
||||
},
|
||||
"name": "Nested Page",
|
||||
"type": "page",
|
||||
"url": "/en/nested/test",
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"$id": "root",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:a",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:a/hello.mdx",
|
||||
"name": "Page A",
|
||||
"type": "page",
|
||||
"url": "/a/hello",
|
||||
},
|
||||
],
|
||||
"name": "A",
|
||||
"type": "folder",
|
||||
},
|
||||
{
|
||||
"$id": "root:b",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:b/test.mdx",
|
||||
"name": "Page B (Test)",
|
||||
"type": "page",
|
||||
"url": "/b/test",
|
||||
},
|
||||
],
|
||||
"name": "B",
|
||||
"type": "folder",
|
||||
},
|
||||
{
|
||||
"$id": "root:c",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:c/test.mdx",
|
||||
"name": "Page C",
|
||||
"type": "page",
|
||||
"url": "/c/test",
|
||||
},
|
||||
{
|
||||
"$id": "root:b/hello.mdx",
|
||||
"name": "Page B (Hello)",
|
||||
"type": "page",
|
||||
"url": "/b/hello",
|
||||
},
|
||||
],
|
||||
"name": "C",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs",
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$id": "root",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:2-2.mdx",
|
||||
"name": "2.2",
|
||||
"type": "page",
|
||||
"url": "/2-2",
|
||||
},
|
||||
{
|
||||
"$id": "root:1-2.mdx",
|
||||
"name": "1.2",
|
||||
"type": "page",
|
||||
"url": "/1-2",
|
||||
},
|
||||
],
|
||||
"name": "Docs",
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { Source } from '@/source';
|
||||
|
||||
export const source: Source = {
|
||||
files: [
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'meta.json',
|
||||
data: {
|
||||
pages: ['z...a'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '1-2.mdx',
|
||||
data: {
|
||||
title: '1.2',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '2-2.mdx',
|
||||
data: {
|
||||
title: '2.2',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// priority to determine the owner of a node (from top to low):
|
||||
// 1. directly specified (e.g. path in `pages`).
|
||||
// 2. extracted (e.g. `...dir`).
|
||||
// 3. rest `...`.
|
||||
export const withPriority: Source = {
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: '/a/hello.mdx',
|
||||
data: {
|
||||
title: 'Page A',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'b/meta.json',
|
||||
data: {
|
||||
pages: ['test', '...'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/b/hello.mdx',
|
||||
data: {
|
||||
title: 'Page B (Hello)',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/b/test.mdx',
|
||||
data: {
|
||||
title: 'Page B (Test)',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'c/meta.json',
|
||||
data: {
|
||||
pages: ['test', '...../b'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '/c/test.mdx',
|
||||
data: {
|
||||
title: 'Page C',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -14,8 +14,12 @@
|
||||
|
||||
<Files>
|
||||
<Folder name="page-trees">
|
||||
<File name="basic-no-meta.tree.json" />
|
||||
|
||||
<File name="basic.tree.json" />
|
||||
|
||||
<File name="i18n-dir.test.json" />
|
||||
|
||||
<File name="i18n-dir.tree.json" />
|
||||
|
||||
<File name="i18n-no-prefix.tree.json" />
|
||||
@@ -25,5 +29,9 @@
|
||||
<File name="i18n.tree.json" />
|
||||
|
||||
<File name="nested.tree.json" />
|
||||
|
||||
<File name="rest-priority.tree.json" />
|
||||
|
||||
<File name="rest.tree.json" />
|
||||
</Folder>
|
||||
</Files>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { createGetUrl, getSlugs, loader } from '@/source';
|
||||
import { createGetUrl, getSlugs, loader, LoaderOptions, Source } from '@/source';
|
||||
import type { ReactElement } from 'react';
|
||||
import { removeUndefined } from '@/utils/remove-undefined';
|
||||
import { lucideIconsPlugin } from '@/source/plugins/lucide-icons';
|
||||
@@ -32,19 +32,94 @@ test('Get URL: Base', () => {
|
||||
expect(getUrl([''])).toBe('/docs');
|
||||
});
|
||||
|
||||
const pageTreeTests: {
|
||||
title: string;
|
||||
output: string;
|
||||
source: Source;
|
||||
loader?: Partial<LoaderOptions>;
|
||||
}[] = [
|
||||
{
|
||||
title: 'Basic',
|
||||
source: (await import('./fixtures/page-trees/basic')).source,
|
||||
output: './fixtures/page-trees/basic.tree.json',
|
||||
},
|
||||
{
|
||||
title: 'Basic: no meta.json',
|
||||
source: (await import('./fixtures/page-trees/basic')).noMeta,
|
||||
output: './fixtures/page-trees/basic-no-meta.tree.json',
|
||||
},
|
||||
{
|
||||
title: 'Rest',
|
||||
source: (await import('./fixtures/page-trees/rest')).source,
|
||||
output: './fixtures/page-trees/rest.tree.json',
|
||||
},
|
||||
{
|
||||
title: 'Rest: priority',
|
||||
source: (await import('./fixtures/page-trees/rest')).withPriority,
|
||||
output: './fixtures/page-trees/rest-priority.tree.json',
|
||||
},
|
||||
{
|
||||
title: 'Nested Directory',
|
||||
source: (await import('./fixtures/page-trees/nested')).source,
|
||||
output: './fixtures/page-trees/nested.tree.json',
|
||||
},
|
||||
{
|
||||
title: 'Internationalized Routing',
|
||||
source: (await import('./fixtures/page-trees/i18n')).source,
|
||||
output: './fixtures/page-trees/i18n.tree.json',
|
||||
loader: {
|
||||
i18n: {
|
||||
languages: ['cn', 'en'],
|
||||
defaultLanguage: 'en',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Internationalized Routing: No Prefix',
|
||||
source: (await import('./fixtures/page-trees/i18n')).source,
|
||||
output: './fixtures/page-trees/i18n-no-prefix.tree.json',
|
||||
loader: {
|
||||
i18n: {
|
||||
languages: ['cn', 'en'],
|
||||
defaultLanguage: 'en',
|
||||
hideLocale: 'default-locale',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Internationalized Routing: dir',
|
||||
source: (await import('./fixtures/page-trees/i18n-dir')).source,
|
||||
output: './fixtures/page-trees/i18n-dir.test.json',
|
||||
loader: {
|
||||
i18n: {
|
||||
parser: 'dir',
|
||||
languages: ['cn', 'en'],
|
||||
defaultLanguage: 'en',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const pageTreeTest of pageTreeTests) {
|
||||
test(`Page Tree: ${pageTreeTest.title}`, async () => {
|
||||
const source = loader(pageTreeTest.source, {
|
||||
baseUrl: '/',
|
||||
pageTree: {
|
||||
noRef: true,
|
||||
},
|
||||
...pageTreeTest.loader,
|
||||
});
|
||||
|
||||
await expect(removeUndefined(source.pageTree, true)).toMatchFileSnapshot(pageTreeTest.output);
|
||||
});
|
||||
}
|
||||
|
||||
test('Loader: Simple', async () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
pageTree: {
|
||||
noRef: true,
|
||||
},
|
||||
source: (await import('./fixtures/page-trees/basic')).source,
|
||||
});
|
||||
|
||||
await expect(removeUndefined(result.pageTree, true)).toMatchFileSnapshot(
|
||||
'./fixtures/page-trees/basic.tree.json',
|
||||
);
|
||||
|
||||
expect(result.getPages().length).toBe(1);
|
||||
expect(result.getPage(['test'])).toBeDefined();
|
||||
});
|
||||
@@ -53,16 +128,9 @@ test('Nested Directories', async () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
icon: (v) => v as unknown as ReactElement,
|
||||
pageTree: {
|
||||
noRef: true,
|
||||
},
|
||||
source: (await import('./fixtures/page-trees/nested')).source,
|
||||
});
|
||||
|
||||
await expect(removeUndefined(result.pageTree, true)).toMatchFileSnapshot(
|
||||
'./fixtures/page-trees/nested.tree.json',
|
||||
);
|
||||
|
||||
expect(result.getPages().map((page) => page.slugs.join('/'))).toMatchInlineSnapshot(`
|
||||
[
|
||||
"test",
|
||||
@@ -89,9 +157,6 @@ test('Internationalized Routing', async () => {
|
||||
source: (await import('./fixtures/page-trees/i18n')).source,
|
||||
});
|
||||
|
||||
await expect(removeUndefined(result.pageTree, true)).toMatchFileSnapshot(
|
||||
'./fixtures/page-trees/i18n.tree.json',
|
||||
);
|
||||
await expect(removeUndefined(result.getLanguages(), true)).toMatchFileSnapshot(
|
||||
'./fixtures/page-trees/i18n.entries.json',
|
||||
);
|
||||
@@ -105,145 +170,13 @@ test('Internationalized Routing: Hide Prefix', async () => {
|
||||
defaultLanguage: 'en',
|
||||
hideLocale: 'default-locale',
|
||||
},
|
||||
pageTree: {
|
||||
noRef: true,
|
||||
},
|
||||
source: (await import('./fixtures/page-trees/i18n')).source,
|
||||
});
|
||||
|
||||
await expect(removeUndefined(result.pageTree, true)).toMatchFileSnapshot(
|
||||
'./fixtures/page-trees/i18n-no-prefix.tree.json',
|
||||
);
|
||||
expect(result.getPages().length).toBe(4);
|
||||
expect(result.getPage(['test'])?.url).toBe('/test');
|
||||
expect(result.getPage(['test'], 'cn')?.url).toBe('/cn/test');
|
||||
});
|
||||
|
||||
test('Internationalized Routing: dir', async () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
i18n: {
|
||||
parser: 'dir',
|
||||
languages: ['cn', 'en'],
|
||||
defaultLanguage: 'en',
|
||||
},
|
||||
source: (await import('./fixtures/page-trees/i18n-dir')).source,
|
||||
});
|
||||
|
||||
await expect(removeUndefined(result.pageTree, true)).toMatchFileSnapshot(
|
||||
'./fixtures/page-trees/i18n-dir.tree.json',
|
||||
);
|
||||
});
|
||||
|
||||
test('Loader: Without meta.json', () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
pageTree: {
|
||||
noRef: true,
|
||||
},
|
||||
source: {
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: 'test.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'hello/index.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(removeUndefined(result.pageTree, true), 'Page Tree').toMatchInlineSnapshot(`
|
||||
{
|
||||
"$id": "root",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:test.mdx",
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/test",
|
||||
},
|
||||
{
|
||||
"$id": "root:hello",
|
||||
"children": [],
|
||||
"index": {
|
||||
"$id": "root:hello/index.mdx",
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/hello",
|
||||
},
|
||||
"name": "Hello",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('Loader: Rest operator', () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
pageTree: {
|
||||
noRef: true,
|
||||
},
|
||||
source: {
|
||||
files: [
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'meta.json',
|
||||
data: {
|
||||
pages: ['z...a'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '1-2.mdx',
|
||||
data: {
|
||||
title: '1.2',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: '2-2.mdx',
|
||||
data: {
|
||||
title: '2.2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(removeUndefined(result.pageTree, true), 'Page Tree').toMatchInlineSnapshot(`
|
||||
{
|
||||
"$id": "root",
|
||||
"children": [
|
||||
{
|
||||
"$id": "root:2-2.mdx",
|
||||
"name": "2.2",
|
||||
"type": "page",
|
||||
"url": "/2-2",
|
||||
},
|
||||
{
|
||||
"$id": "root:1-2.mdx",
|
||||
"name": "1.2",
|
||||
"type": "page",
|
||||
"url": "/1-2",
|
||||
},
|
||||
],
|
||||
"name": "Docs",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('Loader: Allow duplicate pages when explicitly referenced twice', () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
|
||||
Reference in New Issue
Block a user