mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
Core: Support directory-based i18n routing
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-core': patch
|
||||
---
|
||||
|
||||
Support directory-based i18n routing
|
||||
@@ -79,10 +79,7 @@ export class Storage {
|
||||
this.makeDir(node.file.dirname);
|
||||
this.readDir(node.file.dirname)?.children.push(node);
|
||||
this.files.set(
|
||||
joinPath(
|
||||
node.file.dirname,
|
||||
`${node.file.name}${node.file.locale}.${node.format}`,
|
||||
),
|
||||
joinPath(node.file.dirname, `${node.file.name}.${node.format}`),
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,10 +4,17 @@ import { Storage } from './file-system';
|
||||
|
||||
export interface LoadOptions {
|
||||
transformers?: Transformer[];
|
||||
rootDir?: string;
|
||||
getSlugs: (info: FileInfo) => string[];
|
||||
}
|
||||
|
||||
export interface I18nLoadOptions extends LoadOptions {
|
||||
i18n: {
|
||||
parser: 'dot' | 'dir';
|
||||
languages: string[];
|
||||
defaultLanguage: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface VirtualFile {
|
||||
/**
|
||||
* Relative path
|
||||
@@ -30,28 +37,27 @@ export type Transformer = (context: {
|
||||
}) => void;
|
||||
|
||||
// Virtual files -> Virtual Storage -> Transformers -> Result
|
||||
export function loadFiles(files: VirtualFile[], options: LoadOptions): Storage {
|
||||
export function loadFiles<O extends LoadOptions>(
|
||||
files: VirtualFile[],
|
||||
options: O,
|
||||
): Storage {
|
||||
const { transformers = [] } = options;
|
||||
const storage = new Storage();
|
||||
const rootDir = normalizePath(options.rootDir ?? '');
|
||||
|
||||
for (const file of files) {
|
||||
const normalizedPath = normalizePath(file.path);
|
||||
if (!normalizedPath.startsWith(rootDir)) continue;
|
||||
|
||||
const relativePath = normalizedPath.slice(rootDir.length);
|
||||
const parsedPath = normalizePath(file.path);
|
||||
|
||||
if (file.type === 'page') {
|
||||
const slugs = file.slugs ?? options.getSlugs(parseFilePath(relativePath));
|
||||
const slugs = file.slugs ?? options.getSlugs(parseFilePath(parsedPath));
|
||||
|
||||
storage.write(relativePath, file.type, {
|
||||
storage.write(parsedPath, file.type, {
|
||||
slugs,
|
||||
data: file.data as PageData,
|
||||
});
|
||||
}
|
||||
|
||||
if (file.type === 'meta') {
|
||||
storage.write(relativePath, file.type, file.data as MetaData);
|
||||
storage.write(parsedPath, file.type, file.data as MetaData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,3 +70,56 @@ export function loadFiles(files: VirtualFile[], options: LoadOptions): Storage {
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
export function loadFilesI18n(
|
||||
files: VirtualFile[],
|
||||
options: I18nLoadOptions,
|
||||
): Record<string, Storage> {
|
||||
const parser = options.i18n.parser === 'dir' ? dirParser : dotParser;
|
||||
const storages: Record<string, Storage> = {};
|
||||
|
||||
for (const lang of options.i18n.languages) {
|
||||
storages[lang] = loadFiles(
|
||||
files.flatMap((file) => {
|
||||
const [path, locale] = parser(normalizePath(file.path));
|
||||
|
||||
if ((locale ?? options.i18n.defaultLanguage) === lang) {
|
||||
return {
|
||||
...file,
|
||||
path,
|
||||
};
|
||||
}
|
||||
|
||||
return [];
|
||||
}),
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
return storages;
|
||||
}
|
||||
|
||||
function dirParser(path: string): [string, string?] {
|
||||
const parsed = path.split('/');
|
||||
if (parsed.length >= 2) return [parsed.slice(1).join('/'), parsed[0]];
|
||||
|
||||
return [path];
|
||||
}
|
||||
|
||||
function dotParser(path: string): [string, string?] {
|
||||
const segs = path.split('/');
|
||||
if (segs.length === 0) return [path];
|
||||
|
||||
const name = segs[segs.length - 1].split('.');
|
||||
if (name.length >= 3) {
|
||||
const locale = name.splice(name.length - 2, 1)[0];
|
||||
|
||||
if (locale.length > 0 && !/\d+/.test(locale)) {
|
||||
segs[segs.length - 1] = name.join('.');
|
||||
|
||||
return [segs.join('/'), locale];
|
||||
}
|
||||
}
|
||||
|
||||
return [path];
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
type LoadOptions,
|
||||
type VirtualFile,
|
||||
type Transformer,
|
||||
loadFilesI18n,
|
||||
type I18nLoadOptions,
|
||||
} from './load-files';
|
||||
import type { MetaData, PageData, UrlFn } from './types';
|
||||
import type { BuildPageTreeOptions } from './page-tree-builder';
|
||||
@@ -24,12 +26,6 @@ export interface SourceConfig {
|
||||
}
|
||||
|
||||
export interface LoaderOptions {
|
||||
/**
|
||||
* @deprecated It is now recommended to filter files on `source` level
|
||||
* @defaultValue `''`
|
||||
*/
|
||||
rootDir?: string;
|
||||
|
||||
baseUrl: string;
|
||||
|
||||
icon?: NonNullable<BuildPageTreeOptions['resolveIcon']>;
|
||||
@@ -48,7 +44,9 @@ export interface LoaderOptions {
|
||||
/**
|
||||
* Configure i18n
|
||||
*/
|
||||
i18n?: I18nConfig;
|
||||
i18n?: I18nConfig & {
|
||||
parser?: I18nLoadOptions['i18n']['parser'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface Source<Config extends SourceConfig> {
|
||||
@@ -56,7 +54,7 @@ export interface Source<Config extends SourceConfig> {
|
||||
* @internal
|
||||
*/
|
||||
_config?: Config;
|
||||
files: VirtualFile[] | ((rootDir: string) => VirtualFile[]);
|
||||
files: VirtualFile[] | (() => VirtualFile[]);
|
||||
}
|
||||
|
||||
export interface Page<Data = PageData> {
|
||||
@@ -139,7 +137,7 @@ export interface LoaderOutput<Config extends LoaderConfig> {
|
||||
}
|
||||
|
||||
function indexPages(
|
||||
storage: Storage,
|
||||
storages: Record<string, Storage>,
|
||||
getUrl: UrlFn,
|
||||
i18n?: I18nConfig,
|
||||
): {
|
||||
@@ -152,29 +150,23 @@ function indexPages(
|
||||
const map = new Map<string, Page>();
|
||||
const pathToFile = new Map<string, Page | Meta>();
|
||||
|
||||
for (const item of storage.list()) {
|
||||
for (const item of storages[defaultLanguage].list()) {
|
||||
if (item.format === 'meta')
|
||||
pathToFile.set(item.file.path, fileToMeta(item));
|
||||
|
||||
if (item.format === 'page') {
|
||||
if (
|
||||
item.file.locale.length > 0 &&
|
||||
item.file.locale.slice(1) !== defaultLanguage
|
||||
)
|
||||
continue;
|
||||
|
||||
const page = fileToPage(item, getUrl, defaultLanguage);
|
||||
pathToFile.set(item.file.path, page);
|
||||
|
||||
map.set(`${defaultLanguage}.${page.slugs.join('/')}`, page);
|
||||
if (!i18n) continue;
|
||||
const basePath = joinPath(item.file.dirname, item.file.name);
|
||||
const path = joinPath(item.file.dirname, item.file.name);
|
||||
|
||||
for (const lang of i18n.languages) {
|
||||
if (lang === defaultLanguage) continue;
|
||||
|
||||
const localizedPage = fileToPage(
|
||||
storage.read(`${basePath}.${lang}`, 'page') ?? item,
|
||||
storages[lang].read(path, 'page') ?? item,
|
||||
getUrl,
|
||||
lang,
|
||||
);
|
||||
@@ -239,19 +231,29 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
console.warn('`loader()` now requires a `baseUrl` option to be defined.');
|
||||
}
|
||||
|
||||
const { source, rootDir = '', slugs: slugsFn = getSlugs } = options;
|
||||
const { source, slugs: slugsFn = getSlugs } = options;
|
||||
const getUrl =
|
||||
options.url ?? createGetUrl(options.baseUrl ?? '/', options.i18n);
|
||||
|
||||
const storage = loadFiles(
|
||||
typeof source.files === 'function' ? source.files(rootDir) : source.files,
|
||||
{
|
||||
transformers: options.transformers,
|
||||
rootDir,
|
||||
getSlugs: slugsFn,
|
||||
},
|
||||
);
|
||||
const walker = indexPages(storage, getUrl, options.i18n);
|
||||
const files =
|
||||
typeof source.files === 'function' ? source.files() : source.files;
|
||||
const storages = options.i18n
|
||||
? loadFilesI18n(files, {
|
||||
i18n: {
|
||||
...options.i18n,
|
||||
parser: options.i18n.parser ?? 'dot',
|
||||
},
|
||||
transformers: options.transformers,
|
||||
getSlugs: slugsFn,
|
||||
})
|
||||
: {
|
||||
'': loadFiles(files, {
|
||||
transformers: options.transformers,
|
||||
getSlugs: slugsFn,
|
||||
}),
|
||||
};
|
||||
|
||||
const walker = indexPages(storages, getUrl, options.i18n);
|
||||
const builder = createPageTreeBuilder();
|
||||
let pageTree: LoaderOutput<LoaderConfig>['pageTree'] | undefined;
|
||||
|
||||
@@ -260,7 +262,7 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
get pageTree() {
|
||||
if (options.i18n) {
|
||||
pageTree ??= builder.buildI18n({
|
||||
storage,
|
||||
storages,
|
||||
resolveIcon: options.icon,
|
||||
getUrl,
|
||||
i18n: options.i18n,
|
||||
@@ -268,7 +270,7 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
|
||||
}) as unknown as LoaderOutput<LoaderConfig>['pageTree'];
|
||||
} else {
|
||||
pageTree ??= builder.build({
|
||||
storage,
|
||||
storage: storages[''],
|
||||
resolveIcon: options.icon,
|
||||
getUrl,
|
||||
...options.pageTree,
|
||||
|
||||
@@ -6,15 +6,16 @@ import { joinPath } from '@/utils/path';
|
||||
import { type UrlFn } from './types';
|
||||
|
||||
interface PageTreeBuilderContext {
|
||||
lang?: string;
|
||||
defaultLanguage?: string;
|
||||
|
||||
storage: Storage;
|
||||
|
||||
locale?: string;
|
||||
localeStorage?: Storage;
|
||||
|
||||
builder: PageTreeBuilder;
|
||||
options: BuildPageTreeOptions;
|
||||
options: Options;
|
||||
}
|
||||
|
||||
export interface BuildPageTreeOptions {
|
||||
interface Options {
|
||||
/**
|
||||
* Remove references to the file path of original nodes (`$ref`)
|
||||
*
|
||||
@@ -30,12 +31,16 @@ export interface BuildPageTreeOptions {
|
||||
) => PageTree.Folder;
|
||||
attachSeparator?: (node: PageTree.Separator) => PageTree.Separator;
|
||||
|
||||
storage: Storage;
|
||||
getUrl: UrlFn;
|
||||
resolveIcon?: (icon: string | undefined) => ReactElement | undefined;
|
||||
}
|
||||
|
||||
export interface BuildPageTreeOptionsWithI18n extends BuildPageTreeOptions {
|
||||
export interface BuildPageTreeOptions extends Options {
|
||||
storage: Storage;
|
||||
}
|
||||
|
||||
export interface BuildPageTreeOptionsWithI18n extends Options {
|
||||
storages: Record<string, Storage>;
|
||||
i18n: I18nConfig;
|
||||
}
|
||||
|
||||
@@ -63,7 +68,7 @@ function isPageFile(node: Folder | File): node is PageFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodes - All nodes to be built
|
||||
* @param nodes - All nodes to be built (in default locale)
|
||||
* @param ctx - Context
|
||||
* @param skipIndex - Skip index
|
||||
* @returns Nodes with specified locale in context (sorted)
|
||||
@@ -80,14 +85,8 @@ function buildAll(
|
||||
a.file.name.localeCompare(b.file.name),
|
||||
)) {
|
||||
if (isPageFile(node)) {
|
||||
if (
|
||||
node.file.locale.length > 0 &&
|
||||
node.file.locale.slice(1) !== ctx.defaultLanguage
|
||||
)
|
||||
continue;
|
||||
|
||||
const localized = ctx.storage.read(
|
||||
joinPath(node.file.dirname, `${node.file.name}.${ctx.lang}`),
|
||||
const localized = ctx.localeStorage?.read(
|
||||
joinPath(node.file.dirname, node.file.name),
|
||||
'page',
|
||||
);
|
||||
|
||||
@@ -166,15 +165,10 @@ function resolveFolderItem(
|
||||
|
||||
const path = joinPath(folder.file.path, filename);
|
||||
|
||||
let itemNode: Folder | PageFile | undefined = ctx.storage.readDir(path);
|
||||
|
||||
if (!itemNode) {
|
||||
itemNode =
|
||||
(ctx.lang ? ctx.storage.read(`${path}.${ctx.lang}`, 'page') : null) ??
|
||||
ctx.storage.read(`${path}.${ctx.defaultLanguage}`, 'page') ??
|
||||
ctx.storage.read(path, 'page');
|
||||
}
|
||||
|
||||
const itemNode =
|
||||
ctx.storage.readDir(path) ??
|
||||
ctx.localeStorage?.read(path, 'page') ??
|
||||
ctx.storage.read(path, 'page');
|
||||
if (!itemNode) return [];
|
||||
|
||||
addedNodePaths.add(itemNode.file.path);
|
||||
@@ -196,8 +190,7 @@ function buildFolderNode(
|
||||
): PageTree.Folder {
|
||||
const metaPath = joinPath(folder.file.path, 'meta');
|
||||
const meta =
|
||||
ctx.storage.read(`${metaPath}.${ctx.lang}`, 'meta') ??
|
||||
ctx.storage.read(`${metaPath}.${ctx.defaultLanguage}`, 'meta') ??
|
||||
ctx.localeStorage?.read(metaPath, 'meta') ??
|
||||
ctx.storage.read(metaPath, 'meta');
|
||||
|
||||
const indexFile = ctx.storage.read(
|
||||
@@ -275,7 +268,7 @@ function buildFileNode(
|
||||
name: file.data.data.title ?? pathToName(file.file.name),
|
||||
description: file.data.data.description,
|
||||
icon: ctx.options.resolveIcon?.(file.data.data.icon),
|
||||
url: ctx.options.getUrl(file.data.slugs, ctx.lang),
|
||||
url: ctx.options.getUrl(file.data.slugs, ctx.locale),
|
||||
$ref: !ctx.options.noRef
|
||||
? {
|
||||
file: file.file.path,
|
||||
@@ -308,11 +301,11 @@ export function createPageTreeBuilder(): PageTreeBuilder {
|
||||
buildI18n({ i18n, ...options }) {
|
||||
const entries = i18n.languages.map<[string, PageTree.Root]>((lang) => {
|
||||
const tree = build({
|
||||
lang,
|
||||
options,
|
||||
builder: this,
|
||||
storage: options.storage,
|
||||
defaultLanguage: i18n.defaultLanguage,
|
||||
locale: lang,
|
||||
storage: options.storages[i18n.defaultLanguage],
|
||||
localeStorage: options.storages[lang],
|
||||
});
|
||||
|
||||
return [lang, tree];
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import { slash, splitPath } from '@/utils/path';
|
||||
|
||||
export interface FileInfo {
|
||||
/**
|
||||
* locale extension from the last second `.`, like `.en`
|
||||
*
|
||||
* empty string if no locale
|
||||
*/
|
||||
locale: string;
|
||||
|
||||
/**
|
||||
* File path without extension
|
||||
*
|
||||
@@ -55,36 +48,22 @@ export function parseFilePath(path: string): FileInfo {
|
||||
const dirname = segments.slice(0, -1).join('/');
|
||||
let name = segments.at(-1) ?? '';
|
||||
let ext = '';
|
||||
let locale = '';
|
||||
|
||||
let dotIdx = name.lastIndexOf('.');
|
||||
const dotIdx = name.lastIndexOf('.');
|
||||
if (dotIdx !== -1) {
|
||||
ext = name.substring(dotIdx);
|
||||
name = name.substring(0, dotIdx);
|
||||
}
|
||||
|
||||
dotIdx = name.lastIndexOf('.');
|
||||
if (dotIdx !== -1 && isLocale(name.substring(dotIdx))) {
|
||||
locale = name.substring(dotIdx);
|
||||
name = name.substring(0, dotIdx);
|
||||
}
|
||||
|
||||
return {
|
||||
dirname,
|
||||
name,
|
||||
locale,
|
||||
path: segments.join('/'),
|
||||
ext,
|
||||
flattenedPath: [dirname, `${name}${locale}`]
|
||||
.filter((p) => p.length > 0)
|
||||
.join('/'),
|
||||
flattenedPath: [dirname, name].filter((p) => p.length > 0).join('/'),
|
||||
};
|
||||
}
|
||||
|
||||
function isLocale(code: string): boolean {
|
||||
return code.length > 0 && !/\d+/.test(code);
|
||||
}
|
||||
|
||||
export function parseFolderPath(path: string): FolderInfo {
|
||||
const segments = splitPath(slash(path));
|
||||
const base = segments.at(-1) ?? '';
|
||||
|
||||
@@ -7,8 +7,7 @@ export function splitPath(path: string): string[] {
|
||||
|
||||
/**
|
||||
* Resolve paths, slashes within the path will be ignored
|
||||
* @param from - Path to resolve from
|
||||
* @param join - Paths to resolve
|
||||
* @param paths - Paths to join
|
||||
* @example
|
||||
* ```
|
||||
* ['a','b'] // 'a/b'
|
||||
@@ -17,25 +16,25 @@ export function splitPath(path: string): string[] {
|
||||
* ['a', '../b/c'] // 'b/c'
|
||||
* ```
|
||||
*/
|
||||
export function joinPath(from: string, join: string): string {
|
||||
const v1 = splitPath(from),
|
||||
v2 = splitPath(join);
|
||||
export function joinPath(...paths: string[]): string {
|
||||
const out = [];
|
||||
const parsed = paths.flatMap(splitPath);
|
||||
|
||||
while (v2.length > 0) {
|
||||
switch (v2[0]) {
|
||||
while (parsed.length > 0) {
|
||||
switch (parsed[0]) {
|
||||
case '..':
|
||||
v1.pop();
|
||||
out.pop();
|
||||
break;
|
||||
case '.':
|
||||
break;
|
||||
default:
|
||||
v1.push(v2[0]);
|
||||
out.push(parsed[0]);
|
||||
}
|
||||
|
||||
v2.shift();
|
||||
parsed.shift();
|
||||
}
|
||||
|
||||
return v1.join('/');
|
||||
return out.join('/');
|
||||
}
|
||||
|
||||
export function slash(path: string): string {
|
||||
|
||||
@@ -43,7 +43,6 @@ describe('Path utilities', () => {
|
||||
"dirname": "",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "test.mdx",
|
||||
}
|
||||
@@ -54,7 +53,6 @@ describe('Path utilities', () => {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "nested/test.mdx",
|
||||
}
|
||||
@@ -65,8 +63,7 @@ describe('Path utilities', () => {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test.cn",
|
||||
"locale": ".cn",
|
||||
"name": "test",
|
||||
"name": "test.cn",
|
||||
"path": "nested/test.cn.mdx",
|
||||
}
|
||||
`);
|
||||
@@ -76,7 +73,6 @@ describe('Path utilities', () => {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test.01",
|
||||
"locale": "",
|
||||
"name": "test.01",
|
||||
"path": "nested/test.01.mdx",
|
||||
}
|
||||
@@ -87,8 +83,7 @@ describe('Path utilities', () => {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test.cn",
|
||||
"locale": ".cn",
|
||||
"name": "test",
|
||||
"name": "test.cn",
|
||||
"path": "nested/test.cn.mdx",
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -302,9 +302,9 @@ test('Internationalized Routing', () => {
|
||||
"$ref": {},
|
||||
"children": [
|
||||
{
|
||||
"$id": "nested/test.en.mdx",
|
||||
"$id": "nested/test.mdx",
|
||||
"$ref": {
|
||||
"file": "nested/test.en.mdx",
|
||||
"file": "nested/test.mdx",
|
||||
},
|
||||
"name": "Nested Page",
|
||||
"type": "page",
|
||||
@@ -324,9 +324,9 @@ test('Internationalized Routing', () => {
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"$id": "test.cn.mdx",
|
||||
"$id": "test.mdx",
|
||||
"$ref": {
|
||||
"file": "test.cn.mdx",
|
||||
"file": "test.mdx",
|
||||
},
|
||||
"name": "Hello Chinese",
|
||||
"type": "page",
|
||||
@@ -335,13 +335,13 @@ test('Internationalized Routing', () => {
|
||||
{
|
||||
"$id": "nested",
|
||||
"$ref": {
|
||||
"metaFile": "nested/meta.cn.json",
|
||||
"metaFile": "nested/meta.json",
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"$id": "nested/test.cn.mdx",
|
||||
"$id": "nested/test.mdx",
|
||||
"$ref": {
|
||||
"file": "nested/test.cn.mdx",
|
||||
"file": "nested/test.mdx",
|
||||
},
|
||||
"name": "Nested Page Chinese",
|
||||
"type": "page",
|
||||
@@ -368,10 +368,9 @@ test('Internationalized Routing', () => {
|
||||
"file": {
|
||||
"dirname": "",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "test.cn",
|
||||
"locale": ".cn",
|
||||
"flattenedPath": "test",
|
||||
"name": "test",
|
||||
"path": "test.cn.mdx",
|
||||
"path": "test.mdx",
|
||||
},
|
||||
"locale": "cn",
|
||||
"slugs": [
|
||||
@@ -386,10 +385,9 @@ test('Internationalized Routing', () => {
|
||||
"file": {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test.cn",
|
||||
"locale": ".cn",
|
||||
"flattenedPath": "nested/test",
|
||||
"name": "test",
|
||||
"path": "nested/test.cn.mdx",
|
||||
"path": "nested/test.mdx",
|
||||
},
|
||||
"locale": "cn",
|
||||
"slugs": [
|
||||
@@ -411,7 +409,6 @@ test('Internationalized Routing', () => {
|
||||
"dirname": "",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "test.mdx",
|
||||
},
|
||||
@@ -428,10 +425,9 @@ test('Internationalized Routing', () => {
|
||||
"file": {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test.en",
|
||||
"locale": ".en",
|
||||
"flattenedPath": "nested/test",
|
||||
"name": "test",
|
||||
"path": "nested/test.en.mdx",
|
||||
"path": "nested/test.mdx",
|
||||
},
|
||||
"locale": "en",
|
||||
"slugs": [
|
||||
@@ -476,7 +472,7 @@ test('Internationalized Routing: Hide Prefix', () => {
|
||||
"$id": "nested",
|
||||
"children": [
|
||||
{
|
||||
"$id": "nested/test.en.mdx",
|
||||
"$id": "nested/test.mdx",
|
||||
"name": "Nested Page",
|
||||
"type": "page",
|
||||
"url": "/nested/test",
|
||||
@@ -495,7 +491,7 @@ test('Internationalized Routing: Hide Prefix', () => {
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"$id": "test.cn.mdx",
|
||||
"$id": "test.mdx",
|
||||
"name": "Hello Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/test",
|
||||
@@ -504,7 +500,7 @@ test('Internationalized Routing: Hide Prefix', () => {
|
||||
"$id": "nested",
|
||||
"children": [
|
||||
{
|
||||
"$id": "nested/test.cn.mdx",
|
||||
"$id": "nested/test.mdx",
|
||||
"name": "Nested Page Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/nested/test",
|
||||
@@ -625,3 +621,142 @@ test('Loader: Rest operator', () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
const i18nSource2: Source<SourceConfig> = {
|
||||
files: [
|
||||
{
|
||||
type: 'page',
|
||||
path: 'en/test.mdx',
|
||||
data: {
|
||||
title: 'Hello',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'cn/test.mdx',
|
||||
data: {
|
||||
title: 'Hello Chinese',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'en/meta.json',
|
||||
data: {
|
||||
title: 'Docs English',
|
||||
pages: ['test', 'nested'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'cn/meta.json',
|
||||
data: {
|
||||
title: 'Docs Chinese',
|
||||
pages: ['test', 'nested'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'en/nested/test.mdx',
|
||||
data: {
|
||||
title: 'Nested Page',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'page',
|
||||
path: 'cn/nested/test.mdx',
|
||||
data: {
|
||||
title: 'Nested Page Chinese',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'meta',
|
||||
path: 'cn/nested/meta.json',
|
||||
data: {
|
||||
title: 'Nested Chinese',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
test('Internationalized Routing: dir', () => {
|
||||
const result = loader({
|
||||
baseUrl: '/',
|
||||
i18n: {
|
||||
parser: 'dir',
|
||||
languages: ['cn', 'en'],
|
||||
defaultLanguage: 'en',
|
||||
},
|
||||
source: i18nSource2,
|
||||
});
|
||||
|
||||
expect(removeUndefined(result.pageTree['en'], true), 'Page Tree')
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"$id": "test.mdx",
|
||||
"$ref": {
|
||||
"file": "test.mdx",
|
||||
},
|
||||
"name": "Hello",
|
||||
"type": "page",
|
||||
"url": "/en/test",
|
||||
},
|
||||
{
|
||||
"$id": "nested",
|
||||
"$ref": {},
|
||||
"children": [
|
||||
{
|
||||
"$id": "nested/test.mdx",
|
||||
"$ref": {
|
||||
"file": "nested/test.mdx",
|
||||
},
|
||||
"name": "Nested Page",
|
||||
"type": "page",
|
||||
"url": "/en/nested/test",
|
||||
},
|
||||
],
|
||||
"name": "Nested",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs English",
|
||||
}
|
||||
`);
|
||||
|
||||
expect(removeUndefined(result.pageTree['cn'], true), 'Page Tree')
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"$id": "test.mdx",
|
||||
"$ref": {
|
||||
"file": "test.mdx",
|
||||
},
|
||||
"name": "Hello Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/test",
|
||||
},
|
||||
{
|
||||
"$id": "nested",
|
||||
"$ref": {
|
||||
"metaFile": "nested/meta.json",
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"$id": "nested/test.mdx",
|
||||
"$ref": {
|
||||
"file": "nested/test.mdx",
|
||||
},
|
||||
"name": "Nested Page Chinese",
|
||||
"type": "page",
|
||||
"url": "/cn/nested/test",
|
||||
},
|
||||
],
|
||||
"name": "Nested Chinese",
|
||||
"type": "folder",
|
||||
},
|
||||
],
|
||||
"name": "Docs Chinese",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -77,7 +77,6 @@ test('Building File Graph', () => {
|
||||
"dirname": "",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "test.mdx",
|
||||
},
|
||||
@@ -99,7 +98,6 @@ test('Building File Graph', () => {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "nested/test.mdx",
|
||||
},
|
||||
@@ -122,7 +120,6 @@ test('Building File Graph', () => {
|
||||
"dirname": "nested/nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/nested/test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "nested/nested/test.mdx",
|
||||
},
|
||||
@@ -145,63 +142,3 @@ test('Building File Graph', () => {
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('Building File Graph - with root directory', () => {
|
||||
const storage = loadFiles(demoFiles, {
|
||||
rootDir: 'nested',
|
||||
getSlugs,
|
||||
});
|
||||
|
||||
expect(storage.root().children).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"data": {
|
||||
"title": "Nested Page",
|
||||
},
|
||||
"slugs": [
|
||||
"test",
|
||||
],
|
||||
},
|
||||
"file": {
|
||||
"dirname": "",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "test.mdx",
|
||||
},
|
||||
"format": "page",
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"data": {
|
||||
"data": {
|
||||
"title": "Nested Nested Page",
|
||||
},
|
||||
"slugs": [
|
||||
"nested",
|
||||
"test",
|
||||
],
|
||||
},
|
||||
"file": {
|
||||
"dirname": "nested",
|
||||
"ext": ".mdx",
|
||||
"flattenedPath": "nested/test",
|
||||
"locale": "",
|
||||
"name": "test",
|
||||
"path": "nested/test.mdx",
|
||||
},
|
||||
"format": "page",
|
||||
},
|
||||
],
|
||||
"file": {
|
||||
"dirname": "",
|
||||
"name": "nested",
|
||||
"path": "nested",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user