diff --git a/e2e/fixtures/check-dead-link/doc/en/guide/basic/quick-start.mdx b/e2e/fixtures/check-dead-link/doc/en/guide/basic/quick-start.mdx
index acd71afc0..7bb390448 100644
--- a/e2e/fixtures/check-dead-link/doc/en/guide/basic/quick-start.mdx
+++ b/e2e/fixtures/check-dead-link/doc/en/guide/basic/quick-start.mdx
@@ -25,3 +25,11 @@
[../../api/](../../api/)
[../../api](../../api)
+
+## Corner cases
+
+[/en](/en)
+
+[/en/](/en/)
+
+[/](/)
diff --git a/e2e/fixtures/check-dead-link/doc/zh/guide/basic/quick-start.mdx b/e2e/fixtures/check-dead-link/doc/zh/guide/basic/quick-start.mdx
index 78164ba7c..b566105f7 100644
--- a/e2e/fixtures/check-dead-link/doc/zh/guide/basic/quick-start.mdx
+++ b/e2e/fixtures/check-dead-link/doc/zh/guide/basic/quick-start.mdx
@@ -8,7 +8,7 @@
[/guide/basic/install.mdx](/guide/basic/install.mdx)
-[/en/guide/basic/install.mdx](/en/guide/basic/install.mdx)
+[/zh/guide/basic/install.mdx](/zh/guide/basic/install.mdx)
## 相对路径
@@ -25,3 +25,11 @@
[../../api/](../../api/)
[../../api](../../api)
+
+## Corner cases
+
+[/zh](/zh)
+
+[/zh/](/zh/)
+
+[/](/)
diff --git a/e2e/fixtures/check-dead-link/index.test.ts b/e2e/fixtures/check-dead-link/index.test.ts
index dad15956b..ad481b1fb 100644
--- a/e2e/fixtures/check-dead-link/index.test.ts
+++ b/e2e/fixtures/check-dead-link/index.test.ts
@@ -21,30 +21,64 @@ test.describe('check dead links', async () => {
await runBuildCommand(appDir);
app = await runPreviewCommand(appDir, appPort);
- await page.goto(
- `http://localhost:${appPort}/base/guide/basic/quick-start`,
- {
- waitUntil: 'networkidle',
- },
- );
- // Get all the element
- const linkDoms = await page.$$('.rspress-doc a');
+ {
+ await page.goto(
+ `http://localhost:${appPort}/base/guide/basic/quick-start`,
+ {
+ waitUntil: 'networkidle',
+ },
+ );
+ // Get all the element
+ const linkDoms = await page.$$('.rspress-doc a');
- const links = (
- await Promise.all(linkDoms.map(linkDom => linkDom.getAttribute('href')))
- ).filter(i => !i?.startsWith('#'));
- expect(links).toEqual([
- '/base/guide/basic/install.html',
- '/base/guide/basic/install.html',
- '/base/guide/basic/install.html',
- '/base/guide/basic/install.html',
- '/base/guide/basic/install.html',
- '/base/guide/basic/install.html',
- '/base/guide/basic/install.html',
- '/base/api/index.html',
- '/base/api.html',
- '/base/api.html',
- ]);
+ const links = (
+ await Promise.all(linkDoms.map(linkDom => linkDom.getAttribute('href')))
+ ).filter(i => !i?.startsWith('#'));
+ expect(links).toEqual([
+ '/base/guide/basic/install.html',
+ '/base/guide/basic/install.html',
+ '/base/guide/basic/install.html',
+ '/base/guide/basic/install.html',
+ '/base/guide/basic/install.html',
+ '/base/guide/basic/install.html',
+ '/base/guide/basic/install.html',
+ '/base/api/index.html',
+ '/base/api.html', // FIXME: should be /base/api/index.html
+ '/base/api.html',
+ '/base/index.html',
+ '/base/index.html',
+ '/base/index.html',
+ ]);
+ }
+ {
+ await page.goto(
+ `http://localhost:${appPort}/base/en/guide/basic/quick-start`,
+ {
+ waitUntil: 'networkidle',
+ },
+ );
+ // Get all the element
+ const linkDoms = await page.$$('.rspress-doc a');
+
+ const links = (
+ await Promise.all(linkDoms.map(linkDom => linkDom.getAttribute('href')))
+ ).filter(i => !i?.startsWith('#'));
+ expect(links).toEqual([
+ '/base/en/guide/basic/install.html',
+ '/base/en/guide/basic/install.html',
+ '/base/en/guide/basic/install.html',
+ '/base/en/guide/basic/install.html',
+ '/base/en/guide/basic/install.html',
+ '/base/en/guide/basic/install.html',
+ '/base/en/guide/basic/install.html',
+ '/base/en/api/index.html',
+ '/base/en/api.html',
+ '/base/en/api.html',
+ '/base/en/index.html',
+ '/base/en/index.html',
+ '/base/en/index.html',
+ ]);
+ }
});
test('should link the correct page - cleanUrl', async ({ page }) => {
@@ -53,29 +87,64 @@ test.describe('check dead links', async () => {
await runBuildCommand(appDir, 'rspress-clean.config.ts');
app = await runPreviewCommand(appDir, appPort);
- await page.goto(
- `http://localhost:${appPort}/base/guide/basic/quick-start`,
- {
- waitUntil: 'networkidle',
- },
- );
- // Get all the element
- const linkDoms = await page.$$('.rspress-doc a');
+ {
+ await page.goto(
+ `http://localhost:${appPort}/base/guide/basic/quick-start`,
+ {
+ waitUntil: 'networkidle',
+ },
+ );
+ // Get all the element
+ const linkDoms = await page.$$('.rspress-doc a');
- const links = (
- await Promise.all(linkDoms.map(linkDom => linkDom.getAttribute('href')))
- ).filter(i => !i?.startsWith('#'));
- expect(links).toEqual([
- '/base/guide/basic/install',
- '/base/guide/basic/install',
- '/base/guide/basic/install',
- '/base/guide/basic/install',
- '/base/guide/basic/install',
- '/base/guide/basic/install',
- '/base/guide/basic/install',
- '/base/api',
- '/base/api',
- '/base/api',
- ]);
+ const links = (
+ await Promise.all(linkDoms.map(linkDom => linkDom.getAttribute('href')))
+ ).filter(i => !i?.startsWith('#'));
+ expect(links).toEqual([
+ '/base/guide/basic/install',
+ '/base/guide/basic/install',
+ '/base/guide/basic/install',
+ '/base/guide/basic/install',
+ '/base/guide/basic/install',
+ '/base/guide/basic/install',
+ '/base/guide/basic/install',
+ '/base/api',
+ '/base/api',
+ '/base/api',
+ '/base/',
+ '/base/',
+ '/base/',
+ ]);
+ }
+
+ {
+ await page.goto(
+ `http://localhost:${appPort}/base/en/guide/basic/quick-start`,
+ {
+ waitUntil: 'networkidle',
+ },
+ );
+ // Get all the element
+ const linkDoms = await page.$$('.rspress-doc a');
+
+ const links = (
+ await Promise.all(linkDoms.map(linkDom => linkDom.getAttribute('href')))
+ ).filter(i => !i?.startsWith('#'));
+ expect(links).toEqual([
+ '/base/en/guide/basic/install',
+ '/base/en/guide/basic/install',
+ '/base/en/guide/basic/install',
+ '/base/en/guide/basic/install',
+ '/base/en/guide/basic/install',
+ '/base/en/guide/basic/install',
+ '/base/en/guide/basic/install',
+ '/base/en/api',
+ '/base/en/api',
+ '/base/en/api',
+ '/base/en',
+ '/base/en',
+ '/base/en',
+ ]);
+ }
});
});
diff --git a/e2e/fixtures/markdown-link/doc/guide/index.mdx b/e2e/fixtures/markdown-link/doc/guide/index.mdx
index 743539c5d..0c81a0f12 100644
--- a/e2e/fixtures/markdown-link/doc/guide/index.mdx
+++ b/e2e/fixtures/markdown-link/doc/guide/index.mdx
@@ -14,56 +14,56 @@
## RelativeLinks
- [./installation](./installation)
-- [installation(TODO)](installation)
+- [installation](installation)
- [./installation.mdx](./installation.mdx)
-- [installation.mdx(TODO)](installation.mdx).
+- [installation.mdx](installation.mdx).
- [Definition: ./installation]
-- [Definition: installation(TODO)]
+- [Definition: installation]
- [Definition: ./installation.mdx]
-- [Definition: installation.mdx(TODO)]
+- [Definition: installation.mdx]
[Definition: ./installation]: ./installation
-[Definition: installation(TODO)]: installation
+[Definition: installation]: installation
[Definition: ./installation.mdx]: ./installation.mdx
-[Definition: installation.mdx(TODO)]: installation.mdx
+[Definition: installation.mdx]: installation.mdx
## RelativeLinks with subfolders
- [./subfolder/foo](./subfolder/foo)
-- [subfolder/foo(TODO)](subfolder/foo)
+- [subfolder/foo](subfolder/foo)
- [./subfolder/foo.mdx](./subfolder/foo.mdx)
-- [subfolder/foo.mdx(TODO)](subfolder/foo.mdx).
+- [subfolder/foo.mdx](subfolder/foo.mdx).
- [Definition2: ./subfolder/foo]
-- [Definition2: subfolder/foo(TODO)]
+- [Definition2: subfolder/foo]
- [Definition2: ./subfolder/foo.mdx]
-- [Definition2: subfolder/foo.mdx(TODO)]
+- [Definition2: subfolder/foo.mdx]
[Definition2: ./subfolder/foo]: ./subfolder/foo
-[Definition2: subfolder/foo(TODO)]: subfolder/foo
+[Definition2: subfolder/foo]: subfolder/foo
[Definition2: ./subfolder/foo.mdx]: ./subfolder/foo.mdx
-[Definition2: subfolder/foo.mdx(TODO)]: subfolder/foo.mdx
+[Definition2: subfolder/foo.mdx]: subfolder/foo.mdx
## RelativeLinks to subfolders index
- [./subfolder](./subfolder)
-- [subfolder(TODO)](subfolder)
+- [subfolder](subfolder)
- [./subfolder/index.mdx](./subfolder/index.mdx)
-- [subfolder/index.mdx(TODO)](subfolder/index.mdx).
+- [subfolder/index.mdx](subfolder/index.mdx).
- [Definition3: ./subfolder]
-- [Definition3: subfolder(TODO)]
+- [Definition3: subfolder]
- [Definition3: ./subfolder/index.mdx]
-- [Definition3: subfolder/index.mdx(TODO)]
+- [Definition3: subfolder/index.mdx]
[Definition3: ./subfolder]: ./subfolder
-[Definition3: subfolder(TODO)]: subfolder
+[Definition3: subfolder]: subfolder
[Definition3: ./subfolder/index.mdx]: ./subfolder/index.mdx
-[Definition3: subfolder/index.mdx(TODO)]: subfolder/index.mdx
+[Definition3: subfolder/index.mdx]: subfolder/index.mdx
diff --git a/e2e/fixtures/markdown-link/index.test.ts b/e2e/fixtures/markdown-link/index.test.ts
index 49570a935..d0d5dc8b2 100644
--- a/e2e/fixtures/markdown-link/index.test.ts
+++ b/e2e/fixtures/markdown-link/index.test.ts
@@ -18,7 +18,7 @@ test.describe('basic test', async () => {
});
test('all links should be normalized', async ({ page }) => {
- await page.goto(`http://localhost:${appPort}/guide`);
+ await page.goto(`http://localhost:${appPort}/base/guide`);
const links = await page.$$('.rspress-doc ul li a');
const urls = await Promise.all(
links.map(async link => {
@@ -27,35 +27,38 @@ test.describe('basic test', async () => {
}),
);
- const snapshot = urls.join('\n');
-
- expect(snapshot).toEqual(`/guide/installation.html
-/guide/installation.html
-/guide/installation.html
-/guide/installation.html
-/guide/installation.html
-/installation.html
-/guide/installation.html
-/installation.html
-/guide/installation.html
-/installation.html
-/guide/installation.html
-/installation.html
-/guide/subfolder/foo.html
-/subfolder/foo.html
-/guide/subfolder/foo.html
-/subfolder/foo.html
-/guide/subfolder/foo.html
-/subfolder/foo.html
-/guide/subfolder/foo.html
-/subfolder/foo.html
-/guide/subfolder.html
-/subfolder.html
-/guide/subfolder/index.html
-/subfolder/index.html
-/guide/subfolder.html
-/subfolder.html
-/guide/subfolder/index.html
-/subfolder/index.html`);
+ expect(urls).toEqual([
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ //
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ '/base/guide/installation.html',
+ //
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ '/base/guide/subfolder/foo.html',
+ //
+ '/base/guide/subfolder.html',
+ '/base/guide/subfolder.html',
+ '/base/guide/subfolder/index.html',
+ '/base/guide/subfolder/index.html',
+ '/base/guide/subfolder.html',
+ '/base/guide/subfolder.html',
+ '/base/guide/subfolder/index.html',
+ '/base/guide/subfolder/index.html',
+ ]);
});
});
diff --git a/e2e/fixtures/markdown-link/rspress.config.ts b/e2e/fixtures/markdown-link/rspress.config.ts
index 0731c0907..39540b9bc 100644
--- a/e2e/fixtures/markdown-link/rspress.config.ts
+++ b/e2e/fixtures/markdown-link/rspress.config.ts
@@ -2,5 +2,6 @@ import * as path from 'node:path';
import { defineConfig } from 'rspress/config';
export default defineConfig({
+ base: '/base/',
root: path.join(__dirname, 'doc'),
});
diff --git a/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts b/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts
index 482cb2f10..7799688e6 100644
--- a/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts
+++ b/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts
@@ -5,11 +5,9 @@ import {
isProduction,
normalizeHref,
parseUrl,
- removeLeadingSlash,
removeTrailingSlash,
withBase,
} from '@rspress/shared';
-import { DEFAULT_PAGE_EXTENSIONS } from '@rspress/shared/constants';
import { getNodeAttribute } from '@rspress/shared/node-utils';
import type { Root } from 'mdast';
import type { MdxjsEsm } from 'mdast-util-mdxjs-esm';
@@ -20,8 +18,6 @@ import { logger } from '@rspress/shared/logger';
import type { RouteService } from '../../route/RouteService';
import { getASTNodeImport } from '../../utils';
-// TODO: support relative path [subfolder](subfolder) equal to [subfolder](./subfolder)
-
function checkDeadLinks(
internalLinks: Map,
filePath: string,
@@ -35,6 +31,7 @@ function checkDeadLinks(
}
// allow fuzzy matching, e.g: /guide/ and /guide is equal
+ // This is a simple judgment, the performance will be better than "matchPath" in react-router-dom
if (
!routeService.isExistRoute(removeTrailingSlash(cleanLinkPath)) &&
!routeService.isExistRoute(addTrailingSlash(cleanLinkPath))
@@ -85,26 +82,14 @@ function normalizeLink(
return nodeUrl;
}
- const extname = path.extname(url);
-
- if ((routeService?.extensions ?? DEFAULT_PAGE_EXTENSIONS).includes(extname)) {
- url = url.replace(new RegExp(`\\${extname}$`), '');
- }
-
- if (url.startsWith('.')) {
+ // 1. [](/api/getting-started)) or [](/en/api/getting-started))
+ if (url.startsWith('/')) {
+ const { routePath } = routeService.normalizeRoutePath(url);
+ url = routePath;
+ } else {
+ // 2. [getting-started](getting-started) or [getting-started](./getting-started) or [getting-started](../getting-started)
const anotherFileAbsolutePath = path.join(path.dirname(filePath), url);
url = routeService.absolutePathToRoutePath(anotherFileAbsolutePath);
- } else {
- url = url.replace(/\/index\.html$/, '/');
- url = url.replace(/\/index$/, '/');
- const [pathVersion, pathLang] = routeService.getRoutePathParts(
- routeService.absolutePathToRelativePath(filePath),
- );
-
- const [_, __, urlPath] = routeService.getRoutePathParts(url);
-
- url = removeLeadingSlash(urlPath);
- url = [pathVersion, pathLang, url].filter(Boolean).join('/');
}
if (typeof cleanUrls === 'boolean') {
diff --git a/packages/core/src/node/route/RouteService.test.ts b/packages/core/src/node/route/RouteService.test.ts
index 2a36a26e8..d129073bc 100644
--- a/packages/core/src/node/route/RouteService.test.ts
+++ b/packages/core/src/node/route/RouteService.test.ts
@@ -2,16 +2,19 @@ import path from 'node:path';
import type { UserConfig } from '@rspress/shared';
import { describe, expect, it } from 'vitest';
import { PluginDriver } from '../PluginDriver';
-import { normalizePath } from '../utils';
import { RouteService } from './RouteService';
-async function initRouteService(config: UserConfig) {
- const testDir = normalizePath(path.join(__dirname, 'fixtures', 'basic'));
+const BASIC_DIR = path.join(__dirname, 'fixtures', 'basic');
+
+async function initRouteService(
+ config: UserConfig,
+ fixtureDir: string = BASIC_DIR,
+) {
const routeService = await RouteService.create({
config,
pluginDriver: new PluginDriver(config, false),
runtimeTempDir: '.rsbuild',
- scanDir: testDir,
+ scanDir: fixtureDir,
});
const { routeData } = routeService;
@@ -277,3 +280,112 @@ describe('RouteService', async () => {
`);
});
});
+
+describe('RouteService with i18n', async () => {
+ it('basic', async () => {
+ const BASIC_DIR = path.join(__dirname, 'fixtures', 'locales');
+
+ const { routeData } = await initRouteService(
+ {
+ lang: 'en',
+ themeConfig: {
+ locales: [
+ {
+ lang: 'en',
+ label: 'English',
+ },
+ {
+ lang: 'zh',
+ label: '中文',
+ },
+ ],
+ },
+ },
+ BASIC_DIR,
+ );
+ expect(routeData).toMatchInlineSnapshot(`
+ Map {
+ "/guide/basic/install" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/en/guide/basic/install.mdx",
+ "lang": "en",
+ "pageName": "en_guide_basic_install",
+ "relativePath": "en/guide/basic/install.mdx",
+ "routePath": "/guide/basic/install",
+ "version": "",
+ },
+ },
+ "/guide/basic/quick-start" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/en/guide/basic/quick-start.mdx",
+ "lang": "en",
+ "pageName": "en_guide_basic_quick-start",
+ "relativePath": "en/guide/basic/quick-start.mdx",
+ "routePath": "/guide/basic/quick-start",
+ "version": "",
+ },
+ },
+ "/guide/" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/en/guide/index.mdx",
+ "lang": "en",
+ "pageName": "en_guide_index",
+ "relativePath": "en/guide/index.mdx",
+ "routePath": "/guide/",
+ "version": "",
+ },
+ },
+ "/" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/en/index.mdx",
+ "lang": "en",
+ "pageName": "en_index",
+ "relativePath": "en/index.mdx",
+ "routePath": "/",
+ "version": "",
+ },
+ },
+ "/zh/guide/basic/install" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/zh/guide/basic/install.mdx",
+ "lang": "zh",
+ "pageName": "zh_guide_basic_install",
+ "relativePath": "zh/guide/basic/install.mdx",
+ "routePath": "/zh/guide/basic/install",
+ "version": "",
+ },
+ },
+ "/zh/guide/basic/quick-start" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/zh/guide/basic/quick-start.mdx",
+ "lang": "zh",
+ "pageName": "zh_guide_basic_quick-start",
+ "relativePath": "zh/guide/basic/quick-start.mdx",
+ "routePath": "/zh/guide/basic/quick-start",
+ "version": "",
+ },
+ },
+ "/zh/guide/" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/zh/guide/index.mdx",
+ "lang": "zh",
+ "pageName": "zh_guide_index",
+ "relativePath": "zh/guide/index.mdx",
+ "routePath": "/zh/guide/",
+ "version": "",
+ },
+ },
+ "/zh/" => RoutePage {
+ "routeMeta": {
+ "absolutePath": "/packages/core/src/node/route/fixtures/locales/zh/index.mdx",
+ "lang": "zh",
+ "pageName": "zh_index",
+ "relativePath": "zh/index.mdx",
+ "routePath": "/zh/",
+ "version": "",
+ },
+ },
+ }
+ `);
+ });
+});
diff --git a/packages/core/src/node/route/fixtures/locales/en/guide/basic/install.mdx b/packages/core/src/node/route/fixtures/locales/en/guide/basic/install.mdx
new file mode 100644
index 000000000..55af1c56e
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/en/guide/basic/install.mdx
@@ -0,0 +1 @@
+# Install
diff --git a/packages/core/src/node/route/fixtures/locales/en/guide/basic/quick-start.mdx b/packages/core/src/node/route/fixtures/locales/en/guide/basic/quick-start.mdx
new file mode 100644
index 000000000..754bef8a6
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/en/guide/basic/quick-start.mdx
@@ -0,0 +1 @@
+# Quick start
diff --git a/packages/core/src/node/route/fixtures/locales/en/guide/index.mdx b/packages/core/src/node/route/fixtures/locales/en/guide/index.mdx
new file mode 100644
index 000000000..8c0d02fad
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/en/guide/index.mdx
@@ -0,0 +1 @@
+# Guide
diff --git a/packages/core/src/node/route/fixtures/locales/en/index.mdx b/packages/core/src/node/route/fixtures/locales/en/index.mdx
new file mode 100644
index 000000000..f13970f5c
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/en/index.mdx
@@ -0,0 +1 @@
+# homePage
diff --git a/packages/core/src/node/route/fixtures/locales/zh/guide/basic/install.mdx b/packages/core/src/node/route/fixtures/locales/zh/guide/basic/install.mdx
new file mode 100644
index 000000000..7144b3c74
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/zh/guide/basic/install.mdx
@@ -0,0 +1 @@
+# 安装
diff --git a/packages/core/src/node/route/fixtures/locales/zh/guide/basic/quick-start.mdx b/packages/core/src/node/route/fixtures/locales/zh/guide/basic/quick-start.mdx
new file mode 100644
index 000000000..5d58acf6f
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/zh/guide/basic/quick-start.mdx
@@ -0,0 +1 @@
+# 快速开始
diff --git a/packages/core/src/node/route/fixtures/locales/zh/guide/index.mdx b/packages/core/src/node/route/fixtures/locales/zh/guide/index.mdx
new file mode 100644
index 000000000..80357a5be
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/zh/guide/index.mdx
@@ -0,0 +1 @@
+# Guide 页面
diff --git a/packages/core/src/node/route/fixtures/locales/zh/index.mdx b/packages/core/src/node/route/fixtures/locales/zh/index.mdx
new file mode 100644
index 000000000..911f777b8
--- /dev/null
+++ b/packages/core/src/node/route/fixtures/locales/zh/index.mdx
@@ -0,0 +1 @@
+# 首页
diff --git a/packages/core/src/node/route/normalizeRoutePath.ts b/packages/core/src/node/route/normalizeRoutePath.ts
index 07015b410..5521a3f95 100644
--- a/packages/core/src/node/route/normalizeRoutePath.ts
+++ b/packages/core/src/node/route/normalizeRoutePath.ts
@@ -1,8 +1,4 @@
-import {
- addLeadingSlash,
- addTrailingSlash,
- removeLeadingSlash,
-} from '@rspress/shared';
+import { addLeadingSlash, addTrailingSlash } from '@rspress/shared';
import { DEFAULT_PAGE_EXTENSIONS } from '@rspress/shared/constants';
export const getRoutePathParts = (
@@ -50,6 +46,11 @@ export const getRoutePathParts = (
] as const;
};
+/**
+ *
+ * @param relativePath "/v3/en/guide/getting-started.mdx" or "/v3/guide/getting-started.mdx" or "/en/guide/getting-started.mdx" or "/guide/getting-started.mdx"
+ * @returns
+ */
export const normalizeRoutePath = (
relativePath: string,
lang: string,
@@ -58,23 +59,18 @@ export const normalizeRoutePath = (
versions: string[],
extensions: string[] = DEFAULT_PAGE_EXTENSIONS,
) => {
- // /v3/en/guide/getting-started.html
- let routePath = relativePath;
-
// 1. remove extension
const extensionsWithoutDot = extensions.map(i => i.slice(1));
const cleanExtensionPattern = new RegExp(
`\\.(${extensionsWithoutDot.join('|')})$`,
'i',
);
- routePath = routePath
+
+ let routePath = relativePath
.replace(cleanExtensionPattern, '')
.replace(/\.html$/, '');
- // 2. remove /index
- routePath = routePath.replace(/\/index$/, '/');
-
- // 3. remove /v3/en
+ // 2. remove /v3/en
const [versionPart, langPart, purePathPart] = getRoutePathParts(
routePath,
lang,
@@ -83,14 +79,14 @@ export const normalizeRoutePath = (
versions,
);
- routePath = removeLeadingSlash(purePathPart);
-
+ // 3. remove index
+ routePath = purePathPart.replace(/\/index$/, '/');
if (routePath === 'index') {
routePath = '';
}
const normalizedRoutePath = addLeadingSlash(
- [versionPart, langPart, routePath].filter(Boolean).join('/'),
+ [...[versionPart, langPart].filter(Boolean), routePath].join('/'),
);
return {
diff --git a/packages/document/docs/en/api/config/config-basic.mdx b/packages/document/docs/en/api/config/config-basic.mdx
index 2991b17cf..68c75c539 100644
--- a/packages/document/docs/en/api/config/config-basic.mdx
+++ b/packages/document/docs/en/api/config/config-basic.mdx
@@ -409,7 +409,7 @@ export default defineConfig({
:::note
-We recommend using [addPages hook](plugin/system/plugin-api.html#addpages) in a custom Rspress plugin to add some additional files to the route, so that the page route and file path/content can be specified more flexibly and reasonably.
+We recommend using [addPages hook](/plugin/system/plugin-api.html#addpages) in a custom Rspress plugin to add some additional files to the route, so that the page route and file path/content can be specified more flexibly and reasonably.
:::
diff --git a/packages/document/docs/zh/api/config/config-basic.mdx b/packages/document/docs/zh/api/config/config-basic.mdx
index ba8d7be03..6f14a83d2 100644
--- a/packages/document/docs/zh/api/config/config-basic.mdx
+++ b/packages/document/docs/zh/api/config/config-basic.mdx
@@ -408,7 +408,7 @@ export default defineConfig({
:::note
-我们更加推荐在自定义 Rspress 插件中使用 [addPages hook](plugin/system/plugin-api.html#addpages) 来在路由中添加一些额外的文件,这样可以更灵活且更合理地指定页面路由和文件路径/内容。
+我们更加推荐在自定义 Rspress 插件中使用 [addPages hook](/plugin/system/plugin-api.html#addpages) 来在路由中添加一些额外的文件,这样可以更灵活且更合理地指定页面路由和文件路径/内容。
:::