Core: Always encode generated slugs for non-ASCII characters

This commit is contained in:
Fuma Nama
2025-06-03 19:10:32 +08:00
parent 89adec9e86
commit 8738b9c468
5 changed files with 53 additions and 45 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
Always encode generated slugs for non-ASCII characters in `loader()`
+18 -15
View File
@@ -223,15 +223,22 @@ export function createGetUrl(baseUrl: string, i18n?: I18nConfig): UrlFn {
};
}
/**
* Convert file path into slugs, also encode non-ASCII characters, so they can work in pathname
*/
export function getSlugs(info: FileInfo): string[] {
return [...info.dirname.split('/'), info.name].filter(
// filter empty folder names and file groups like (group_name)
(v, i, arr) => {
if (v.length === 0) return false;
const slugs: string[] = [];
return i === arr.length - 1 ? v !== 'index' : !/^\(.+\)$/.test(v);
},
);
for (const seg of info.dirname.split('/')) {
// filter empty names and file groups like (group_name)
if (seg.length > 0 && !/^\(.+\)$/.test(seg)) slugs.push(encodeURI(seg));
}
if (info.name !== 'index') {
slugs.push(encodeURI(info.name));
}
return slugs;
}
export function loader<
@@ -326,8 +333,8 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
getPages(language = defaultLanguage) {
const pages: Page[] = [];
for (const key of walker.pages.keys()) {
if (key.startsWith(`${language}.`)) pages.push(walker.pages.get(key)!);
for (const [key, value] of walker.pages.entries()) {
if (key.startsWith(`${language}.`)) pages.push(value);
}
return pages;
@@ -352,18 +359,14 @@ function createOutput(options: LoaderOptions): LoaderOutput<LoaderConfig> {
const ref = node.$ref?.metaFile;
if (!ref) return;
const file = storages[language]
.list()
.find((v) => v.format === 'meta' && v.file.path === ref);
const file = storages[language].read(ref, 'meta');
if (file) return walker.getResultFromFile(file) as Meta;
},
getNodePage(node, language = defaultLanguage) {
const ref = node.$ref?.file;
if (!ref) return;
const file = storages[language]
.list()
.find((v) => v.format === 'page' && v.file.path === ref);
const file = storages[language].read(ref, 'page');
if (file) return walker.getResultFromFile(file) as Page;
},
getPageTree(locale) {
+17 -13
View File
@@ -257,35 +257,39 @@ function buildFolderNode(
index,
children,
$id: folder.file.path,
$ref: !options.noRef
? {
metaFile: meta?.file.path,
}
: undefined,
$ref:
!options.noRef && meta
? {
metaFile: metaPath,
}
: undefined,
};
return options.attachFolder?.(node, folder, meta) ?? node;
}
function buildFileNode(
file: PageFile,
page: PageFile,
{ options, getUrl, locale }: PageTreeBuilderContext,
): PageTree.Item {
const file = page.file;
const { slugs, data } = page.data;
const item: PageTree.Item = {
$id: file.file.path,
$id: file.path,
type: 'page',
name: file.data.data.title ?? pathToName(file.file.name),
description: file.data.data.description,
icon: options.resolveIcon?.(file.data.data.icon),
url: getUrl(file.data.slugs, locale),
name: data.title ?? pathToName(file.name),
description: data.description,
icon: options.resolveIcon?.(data.icon),
url: getUrl(slugs, locale),
$ref: !options.noRef
? {
file: file.file.path,
file: joinPath(file.dirname, file.name),
}
: undefined,
};
return options.attachFile?.(item, file) ?? item;
return options.attachFile?.(item, page) ?? item;
}
function build(ctx: PageTreeBuilderContext): PageTree.Root {
+3 -5
View File
@@ -20,18 +20,16 @@ export function joinPath(...paths: string[]): string {
const out = [];
const parsed = paths.flatMap(splitPath);
while (parsed.length > 0) {
switch (parsed[0]) {
for (const seg of parsed) {
switch (seg) {
case '..':
out.pop();
break;
case '.':
break;
default:
out.push(parsed[0]);
out.push(seg);
}
parsed.shift();
}
return out.join('/');
+10 -12
View File
@@ -306,7 +306,7 @@ test('Internationalized Routing', () => {
{
"$id": "test.mdx",
"$ref": {
"file": "test.mdx",
"file": "test",
},
"name": "Hello",
"type": "page",
@@ -314,12 +314,11 @@ test('Internationalized Routing', () => {
},
{
"$id": "nested",
"$ref": {},
"children": [
{
"$id": "nested/test.mdx",
"$ref": {
"file": "nested/test.mdx",
"file": "nested/test",
},
"name": "Nested Page",
"type": "page",
@@ -342,7 +341,7 @@ test('Internationalized Routing', () => {
{
"$id": "test.mdx",
"$ref": {
"file": "test.mdx",
"file": "test",
},
"name": "Hello Chinese",
"type": "page",
@@ -351,13 +350,13 @@ test('Internationalized Routing', () => {
{
"$id": "nested",
"$ref": {
"metaFile": "nested/meta.json",
"metaFile": "nested/meta",
},
"children": [
{
"$id": "nested/test.mdx",
"$ref": {
"file": "nested/test.mdx",
"file": "nested/test",
},
"name": "Nested Page Chinese",
"type": "page",
@@ -720,7 +719,7 @@ test('Internationalized Routing: dir', () => {
{
"$id": "test.mdx",
"$ref": {
"file": "test.mdx",
"file": "test",
},
"name": "Hello",
"type": "page",
@@ -728,12 +727,11 @@ test('Internationalized Routing: dir', () => {
},
{
"$id": "nested",
"$ref": {},
"children": [
{
"$id": "nested/test.mdx",
"$ref": {
"file": "nested/test.mdx",
"file": "nested/test",
},
"name": "Nested Page",
"type": "page",
@@ -756,7 +754,7 @@ test('Internationalized Routing: dir', () => {
{
"$id": "test.mdx",
"$ref": {
"file": "test.mdx",
"file": "test",
},
"name": "Hello Chinese",
"type": "page",
@@ -765,13 +763,13 @@ test('Internationalized Routing: dir', () => {
{
"$id": "nested",
"$ref": {
"metaFile": "nested/meta.json",
"metaFile": "nested/meta",
},
"children": [
{
"$id": "nested/test.mdx",
"$ref": {
"file": "nested/test.mdx",
"file": "nested/test",
},
"name": "Nested Page Chinese",
"type": "page",