From 7a539dfb400414fe0dab46d3afcb740209c95b78 Mon Sep 17 00:00:00 2001 From: SoonIter Date: Wed, 25 Jun 2025 20:44:24 +0800 Subject: [PATCH 1/4] chore: update --- .../src/node/auto-nav-sidebar/locales.test.ts | 42 +++++------ .../src/node/auto-nav-sidebar/walk.test.ts | 4 +- packages/core/src/node/initRsbuild.ts | 6 ++ .../node/mdx/remarkPlugins/normalizeLink.ts | 5 +- packages/core/src/node/route/RouteService.ts | 5 -- .../src/node/route/normalizeRoutePath.test.ts | 75 +++---------------- .../core/src/node/route/normalizeRoutePath.ts | 10 +-- .../src/node/runtimeModule/runtimeConfig.ts | 20 +++++ .../src/node/runtimeModule/siteData/index.ts | 1 - .../siteData/normalizeThemeConfig.ts | 5 +- packages/core/src/node/runtimeModule/types.ts | 1 + packages/core/src/node/ssg/renderPages.ts | 4 +- packages/core/src/node/utils/normalizePath.ts | 4 +- packages/core/src/runtime/ClientApp.tsx | 2 + packages/core/src/runtime/env.d.ts | 6 ++ packages/core/src/runtime/initPageData.ts | 6 +- packages/core/src/runtime/ssrServerEntry.tsx | 3 +- packages/runtime/src/env.d.ts | 6 ++ packages/runtime/src/utils.ts | 9 ++- packages/shared/src/index.ts | 1 - packages/shared/src/runtime-utils/sidebar.ts | 20 ++--- .../shared/src/runtime-utils/utils.test.ts | 10 +-- packages/shared/src/runtime-utils/utils.ts | 16 +--- packages/shared/src/types/index.ts | 6 +- .../src/components/Link/index.tsx | 13 +++- .../src/components/Nav/NavMenuGroup.tsx | 10 +-- .../src/components/Nav/NavMenuSingleItem.tsx | 13 ++-- .../src/components/Nav/index.tsx | 2 +- .../src/components/Nav/menuDataHooks.tsx | 3 +- .../src/components/NavScreen/index.tsx | 2 +- .../src/components/Overview/index.tsx | 12 +-- .../src/components/Overview/utils.ts | 7 +- .../src/components/Sidebar/SidebarGroup.tsx | 3 +- packages/theme-default/src/env.d.ts | 6 ++ .../src/logic/getSidebarDataGroup.test.ts | 10 ++- .../src/logic/getSidebarDataGroup.ts | 6 +- .../src/logic/usePrevNextPage.ts | 4 +- 37 files changed, 150 insertions(+), 208 deletions(-) create mode 100644 packages/core/src/node/runtimeModule/runtimeConfig.ts diff --git a/packages/core/src/node/auto-nav-sidebar/locales.test.ts b/packages/core/src/node/auto-nav-sidebar/locales.test.ts index 02f808e84..819df1526 100644 --- a/packages/core/src/node/auto-nav-sidebar/locales.test.ts +++ b/packages/core/src/node/auto-nav-sidebar/locales.test.ts @@ -12,7 +12,6 @@ describe('walk', () => { const mockNormalizeRoutePath = (link: string) => { return normalizeRoutePath( link, - '/base', 'en', '', ['zh', 'en'], @@ -23,7 +22,6 @@ describe('walk', () => { const mockGetRoutePathParts = (link: string) => { return getRoutePathParts( link, - '/base', 'en', '', ['zh', 'en'], @@ -46,13 +44,13 @@ describe('walk', () => { "default": [ { "activeMatch": "^/guide/", - "link": "/base/zh/guide/", + "link": "/zh/guide/", "text": "Guide", }, ], }, "sidebar": { - "/base/zh/guide": [ + "/zh/guide": [ { "_fileKey": "zh/guide/test-dir/index", "collapsed": undefined, @@ -62,13 +60,13 @@ describe('walk', () => { { "_fileKey": "zh/guide/test-dir/getting-started", "context": undefined, - "link": "/base/zh/guide/test-dir/getting-started", + "link": "/zh/guide/test-dir/getting-started", "overviewHeaders": undefined, "tag": undefined, "text": "Getting started 页面", }, ], - "link": "/base/zh/guide/test-dir/", + "link": "/zh/guide/test-dir/", "overviewHeaders": undefined, "tag": undefined, "text": "Test dir 页面", @@ -82,13 +80,13 @@ describe('walk', () => { { "_fileKey": "zh/guide/test-same-name-dir/index", "context": undefined, - "link": "/base/zh/guide/test-same-name-dir/", + "link": "/zh/guide/test-same-name-dir/", "overviewHeaders": undefined, "tag": undefined, "text": "Test same name dir 页面", }, ], - "link": "/base/zh/guide/test-same-name-dir", + "link": "/zh/guide/test-same-name-dir", "overviewHeaders": undefined, "tag": undefined, "text": "Test same name dir in file 页面", @@ -96,7 +94,7 @@ describe('walk', () => { { "_fileKey": "zh/guide/a", "context": undefined, - "link": "/base/zh/guide/a", + "link": "/zh/guide/a", "overviewHeaders": undefined, "tag": undefined, "text": "Page a 页面", @@ -104,7 +102,7 @@ describe('walk', () => { { "_fileKey": "zh/guide/b", "context": undefined, - "link": "/base/zh/guide/b", + "link": "/zh/guide/b", "overviewHeaders": undefined, "tag": undefined, "text": "Page b 页面", @@ -112,14 +110,14 @@ describe('walk', () => { { "_fileKey": "zh/guide/c", "context": undefined, - "link": "/base/zh/guide/c", + "link": "/zh/guide/c", "overviewHeaders": undefined, "tag": undefined, "text": "c", }, { "context": undefined, - "link": "/base/zh/guide/test-dir", + "link": "/zh/guide/test-dir", "tag": undefined, "text": "My Link", }, @@ -131,13 +129,13 @@ describe('walk', () => { "default": [ { "activeMatch": "^/guide/", - "link": "/base/guide/", + "link": "/guide/", "text": "Guide", }, ], }, "sidebar": { - "/base/guide": [ + "/guide": [ { "_fileKey": "en/guide/test-dir/index", "collapsed": undefined, @@ -147,13 +145,13 @@ describe('walk', () => { { "_fileKey": "en/guide/test-dir/getting-started", "context": undefined, - "link": "/base/guide/test-dir/getting-started", + "link": "/guide/test-dir/getting-started", "overviewHeaders": undefined, "tag": undefined, "text": "Getting started", }, ], - "link": "/base/guide/test-dir/", + "link": "/guide/test-dir/", "overviewHeaders": undefined, "tag": undefined, "text": "Test dir", @@ -167,13 +165,13 @@ describe('walk', () => { { "_fileKey": "en/guide/test-same-name-dir/index", "context": undefined, - "link": "/base/guide/test-same-name-dir/", + "link": "/guide/test-same-name-dir/", "overviewHeaders": undefined, "tag": undefined, "text": "Test same name dir", }, ], - "link": "/base/guide/test-same-name-dir", + "link": "/guide/test-same-name-dir", "overviewHeaders": undefined, "tag": undefined, "text": "Test same name dir in file", @@ -181,7 +179,7 @@ describe('walk', () => { { "_fileKey": "en/guide/a", "context": undefined, - "link": "/base/guide/a", + "link": "/guide/a", "overviewHeaders": undefined, "tag": undefined, "text": "Page a", @@ -189,7 +187,7 @@ describe('walk', () => { { "_fileKey": "en/guide/b", "context": undefined, - "link": "/base/guide/b", + "link": "/guide/b", "overviewHeaders": undefined, "tag": undefined, "text": "Page b", @@ -197,14 +195,14 @@ describe('walk', () => { { "_fileKey": "en/guide/c", "context": undefined, - "link": "/base/guide/c", + "link": "/guide/c", "overviewHeaders": undefined, "tag": undefined, "text": "c", }, { "context": undefined, - "link": "/base/guide/test-dir", + "link": "/guide/test-dir", "tag": undefined, "text": "My Link", }, diff --git a/packages/core/src/node/auto-nav-sidebar/walk.test.ts b/packages/core/src/node/auto-nav-sidebar/walk.test.ts index 06903eaab..0074b51b5 100644 --- a/packages/core/src/node/auto-nav-sidebar/walk.test.ts +++ b/packages/core/src/node/auto-nav-sidebar/walk.test.ts @@ -9,11 +9,11 @@ import { import { walk } from './walk'; const mockNormalizeRoutePath = (link: string) => { - return normalizeRoutePath(link, '/', '', '', [], [], ['.md', '.mdx', '.tsx']); + return normalizeRoutePath(link, '', '', [], [], ['.md', '.mdx', '.tsx']); }; const mockGetRoutePathParts = (link: string) => { - return getRoutePathParts(link, '/', '', '', [], []); + return getRoutePathParts(link, '', '', [], []); }; RouteService.__instance__ = { diff --git a/packages/core/src/node/initRsbuild.ts b/packages/core/src/node/initRsbuild.ts index 13903af1c..f577c6951 100644 --- a/packages/core/src/node/initRsbuild.ts +++ b/packages/core/src/node/initRsbuild.ts @@ -40,6 +40,7 @@ import { globalStylesVMPlugin } from './runtimeModule/globalStyles'; import { globalUIComponentsVMPlugin } from './runtimeModule/globalUIComponents'; import { i18nVMPlugin } from './runtimeModule/i18n'; import { routeListVMPlugin } from './runtimeModule/routeList'; +import { runtimeConfigVMPlugin } from './runtimeModule/runtimeConfig'; import { searchHookVMPlugin } from './runtimeModule/searchHooks'; import type { FactoryContext } from './runtimeModule/types'; import { serveSearchIndexMiddleware } from './searchIndex'; @@ -149,6 +150,11 @@ async function createInternalBuildConfig( * Get virtual modules from plugins */ ...(await getVirtualModulesFromPlugins(pluginDriver)), + + /** + * Serialize rspress.config.ts to runtime + */ + ...runtimeConfigVMPlugin(context), }, }), ...(enableSSG diff --git a/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts b/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts index 3d64a9450..fa3f4ee53 100644 --- a/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts +++ b/packages/core/src/node/mdx/remarkPlugins/normalizeLink.ts @@ -47,11 +47,10 @@ function normalizeLink( if (url.startsWith('.')) { url = path.posix.join(slash(path.dirname(relativePath)), url); } else if (routeService) { - const [_, pathVersion, pathLang] = routeService.getRoutePathParts( + const [pathVersion, pathLang] = routeService.getRoutePathParts( slash(relativePath), ); - const [__, urlVersion, urlLang, urlPath] = - routeService.getRoutePathParts(url); + const [urlVersion, urlLang, urlPath] = routeService.getRoutePathParts(url); url = addLeadingSlash(urlPath); diff --git a/packages/core/src/node/route/RouteService.ts b/packages/core/src/node/route/RouteService.ts index d64210c6b..fa3b63317 100644 --- a/packages/core/src/node/route/RouteService.ts +++ b/packages/core/src/node/route/RouteService.ts @@ -50,8 +50,6 @@ export class RouteService { #exclude: string[] = []; - #base: string = ''; - #tempDir: string = ''; #pluginDriver: PluginDriver; @@ -94,7 +92,6 @@ export class RouteService { userConfig?.themeConfig?.locales ?? [] ).map(item => item.lang); - this.#base = userConfig?.base || ''; this.#tempDir = tempDir; this.#pluginDriver = pluginDriver; @@ -239,7 +236,6 @@ ${routeMeta getRoutePathParts(routePath: string) { return getRoutePathParts( routePath, - this.#base, this.#defaultLang, this.#defaultVersion, this.#langs, @@ -250,7 +246,6 @@ ${routeMeta normalizeRoutePath(routePath: string) { return normalizeRoutePath( routePath, - this.#base, this.#defaultLang, this.#defaultVersion, this.#langs, diff --git a/packages/core/src/node/route/normalizeRoutePath.test.ts b/packages/core/src/node/route/normalizeRoutePath.test.ts index ae3c693be..031ca99cb 100644 --- a/packages/core/src/node/route/normalizeRoutePath.test.ts +++ b/packages/core/src/node/route/normalizeRoutePath.test.ts @@ -2,85 +2,45 @@ import { getRoutePathParts, normalizeRoutePath } from './normalizeRoutePath'; test('getRoutePathParts', () => { expect( - getRoutePathParts( - '/v1/en/foo/bar', - '/', - 'en', - 'v1', - ['zh', 'en'], - ['v1', 'v2'], - ), + getRoutePathParts('/v1/en/foo/bar', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toMatchInlineSnapshot(` [ - "/", "", "", "foo/bar", ] `); expect( - getRoutePathParts( - '/v1/zh/foo/bar', - '/', - 'en', - 'v1', - ['zh', 'en'], - ['v1', 'v2'], - ), + getRoutePathParts('/v1/zh/foo/bar', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toMatchInlineSnapshot(` [ - "/", "", "zh", "foo/bar", ] `); expect( - getRoutePathParts( - '/v2/en/foo/bar', - '/', - 'en', - 'v1', - ['zh', 'en'], - ['v1', 'v2'], - ), + getRoutePathParts('/v2/en/foo/bar', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toMatchInlineSnapshot(` [ - "/", "v2", "", "foo/bar", ] `); expect( - getRoutePathParts( - '/v2/zh/foo/bar', - '/', - 'en', - 'v1', - ['zh', 'en'], - ['v1', 'v2'], - ), + getRoutePathParts('/v2/zh/foo/bar', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toMatchInlineSnapshot(` [ - "/", "v2", "zh", "foo/bar", ] `); expect( - getRoutePathParts( - '/v2/en/api/', - '/', - 'en', - 'v1', - ['zh', 'en'], - ['v1', 'v2'], - ), + getRoutePathParts('/v2/en/api/', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toMatchInlineSnapshot(` [ - "/", "v2", "", "api/", @@ -88,10 +48,9 @@ test('getRoutePathParts', () => { `); expect( - getRoutePathParts('/foo/bar', '/', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), + getRoutePathParts('/foo/bar', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toMatchInlineSnapshot(` [ - "/", "", "", "foo/bar", @@ -101,7 +60,7 @@ test('getRoutePathParts', () => { expect( getRoutePathParts( '/foo/bar/baz.xyz', - '/', + 'en', 'v1', ['zh', 'en'], @@ -109,7 +68,6 @@ test('getRoutePathParts', () => { ), ).toMatchInlineSnapshot(` [ - "/", "", "", "foo/bar/baz.xyz", @@ -119,7 +77,6 @@ test('getRoutePathParts', () => { expect( getRoutePathParts( '/foo/bar/baz.xyz', - '/', 'en', 'v1', ['zh', 'en'], @@ -127,7 +84,6 @@ test('getRoutePathParts', () => { ), ).toMatchInlineSnapshot(` [ - "/", "", "", "foo/bar/baz.xyz", @@ -139,7 +95,6 @@ test('normalizeRoutePath', () => { expect( normalizeRoutePath( '/v1/en/foo/bar', - '/', 'en', 'v1', ['zh', 'en'], @@ -153,7 +108,6 @@ test('normalizeRoutePath', () => { expect( normalizeRoutePath( '/v1/zh/foo/bar', - '/', 'en', 'v1', ['zh', 'en'], @@ -167,7 +121,6 @@ test('normalizeRoutePath', () => { expect( normalizeRoutePath( '/v2/en/foo/bar', - '/', 'en', 'v1', ['zh', 'en'], @@ -181,7 +134,6 @@ test('normalizeRoutePath', () => { expect( normalizeRoutePath( '/v2/zh/foo/bar', - '/', 'en', 'v1', ['zh', 'en'], @@ -193,14 +145,7 @@ test('normalizeRoutePath', () => { routePath: '/v2/zh/foo/bar', }); expect( - normalizeRoutePath( - '/v2/en/api/', - '/', - 'en', - 'v1', - ['zh', 'en'], - ['v1', 'v2'], - ), + normalizeRoutePath('/v2/en/api/', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toEqual({ lang: 'en', version: 'v2', @@ -208,7 +153,7 @@ test('normalizeRoutePath', () => { }); expect( - normalizeRoutePath('/foo/bar', '/', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), + normalizeRoutePath('/foo/bar', 'en', 'v1', ['zh', 'en'], ['v1', 'v2']), ).toEqual({ lang: 'en', version: 'v1', @@ -218,7 +163,6 @@ test('normalizeRoutePath', () => { expect( normalizeRoutePath( '/foo/bar/baz.xyz', - '/', 'en', 'v1', ['zh', 'en'], @@ -233,7 +177,6 @@ test('normalizeRoutePath', () => { expect( normalizeRoutePath( '/foo/bar/baz.xyz', - '/', 'en', 'v1', ['zh', 'en'], diff --git a/packages/core/src/node/route/normalizeRoutePath.ts b/packages/core/src/node/route/normalizeRoutePath.ts index 2d481960c..4e2320941 100644 --- a/packages/core/src/node/route/normalizeRoutePath.ts +++ b/packages/core/src/node/route/normalizeRoutePath.ts @@ -1,9 +1,8 @@ -import { addLeadingSlash, addTrailingSlash, withBase } from '@rspress/shared'; +import { addLeadingSlash, addTrailingSlash } from '@rspress/shared'; import { DEFAULT_PAGE_EXTENSIONS } from '@rspress/shared/constants'; export const getRoutePathParts = ( routePath: string, - base: string, lang: string, version: string, langs: string[], @@ -40,7 +39,6 @@ export const getRoutePathParts = ( purePathPart = parts.join('/'); return [ - base, versionPart, langPart, // restore the trail slash @@ -50,16 +48,14 @@ export const getRoutePathParts = ( export const normalizeRoutePath = ( routePath: string, - base: string, lang: string, version: string, langs: string[], versions: string[], extensions: string[] = DEFAULT_PAGE_EXTENSIONS, ) => { - const [_, versionPart, langPart, purePathPart] = getRoutePathParts( + const [versionPart, langPart, purePathPart] = getRoutePathParts( routePath, - base, lang, version, langs, @@ -81,7 +77,7 @@ export const normalizeRoutePath = ( .replace(/\/index$/, '/'); return { - routePath: withBase(normalizedRoutePath, base), + routePath: normalizedRoutePath, lang: langPart || lang, version: versionPart || version, }; diff --git a/packages/core/src/node/runtimeModule/runtimeConfig.ts b/packages/core/src/node/runtimeModule/runtimeConfig.ts new file mode 100644 index 000000000..7f9a89df4 --- /dev/null +++ b/packages/core/src/node/runtimeModule/runtimeConfig.ts @@ -0,0 +1,20 @@ +import { normalizeSlash } from '@rspress/shared'; +import { RuntimeModuleID, type VirtualModulePlugin } from './types'; + +/** + * some configuration in `rspress.config.ts` serialized from compile time side to runtime + */ +export const runtimeConfigVMPlugin: VirtualModulePlugin = context => { + return { + [RuntimeModuleID.RuntimeConfig]: () => { + const { config } = context; + + const { base } = config; + + // TODO: base can be normalized in compile time side in an earlier stage + const normalizedBase = normalizeSlash(base ?? '/'); + // Use named export for better tree-shaking support + return `export const base = ${JSON.stringify(normalizedBase)};`; + }, + }; +}; diff --git a/packages/core/src/node/runtimeModule/siteData/index.ts b/packages/core/src/node/runtimeModule/siteData/index.ts index e97707dc8..7a365bc5d 100644 --- a/packages/core/src/node/runtimeModule/siteData/index.ts +++ b/packages/core/src/node/runtimeModule/siteData/index.ts @@ -118,7 +118,6 @@ export async function siteDataVMPlugin(context: FactoryContext) { icon: getIconUrlPath(userConfig?.icon) || '', route: userConfig?.route || {}, themeConfig: normalizeThemeConfig(userConfig), - base: userConfig?.base || '/', lang: userConfig?.lang || '', locales: userConfig?.locales || userConfig.themeConfig?.locales || [], logo: userConfig?.logo || '', diff --git a/packages/core/src/node/runtimeModule/siteData/normalizeThemeConfig.ts b/packages/core/src/node/runtimeModule/siteData/normalizeThemeConfig.ts index b5a8d22c5..b43b08d68 100644 --- a/packages/core/src/node/runtimeModule/siteData/normalizeThemeConfig.ts +++ b/packages/core/src/node/runtimeModule/siteData/normalizeThemeConfig.ts @@ -13,7 +13,6 @@ import { addLeadingSlash, isExternalUrl, slash, - withoutBase, } from '@rspress/shared'; import { applyReplaceRules } from '../../utils/applyReplaceRules'; import { getI18nData } from '../i18n'; @@ -23,7 +22,6 @@ export function normalizeThemeConfig( ): NormalizedDefaultThemeConfig { const { locales: siteLocales, - base = '', lang, replaceRules = [], multiVersion, @@ -40,9 +38,8 @@ export function normalizeThemeConfig( if ( !currentLang || !link || - withoutBase(normalizedLink, base).startsWith(`/${currentLang}`) || + normalizedLink.startsWith(`/${currentLang}`) || isExternalUrl(normalizedLink) || - // In multi version case, we have got the complete link prefix in `auto-nav-sidebar` and does not need to add the lang prefix hasMultiVersion ) { return normalizedLink; diff --git a/packages/core/src/node/runtimeModule/types.ts b/packages/core/src/node/runtimeModule/types.ts index c8b3a315b..8e1af8e88 100644 --- a/packages/core/src/node/runtimeModule/types.ts +++ b/packages/core/src/node/runtimeModule/types.ts @@ -23,4 +23,5 @@ export enum RuntimeModuleID { SearchIndexHash = 'virtual-search-index-hash', I18nText = 'virtual-i18n-text', SearchHooks = 'virtual-search-hooks', + RuntimeConfig = 'virtual-runtime-config', } diff --git a/packages/core/src/node/ssg/renderPages.ts b/packages/core/src/node/ssg/renderPages.ts index 2bb7cfddf..b8f952965 100644 --- a/packages/core/src/node/ssg/renderPages.ts +++ b/packages/core/src/node/ssg/renderPages.ts @@ -12,7 +12,6 @@ import { type Route, type UserConfig, normalizeSlash, - withBase, } from '@rspress/shared'; import { logger } from '@rspress/shared/logger'; import pMap from 'p-map'; @@ -66,11 +65,10 @@ export async function renderPages( try { const routes = routeService!.getRoutes(); - const base = config?.base ?? ''; // Get the html generated by builder, as the default ssr template const additionalRoutes = (await pluginDriver.addSSGRoutes()).map(route => ({ - routePath: withBase(route.path, base), + routePath: route.path, })); const allRoutes = [...routes, ...additionalRoutes]; const is404RouteInRoutes = allRoutes.some( diff --git a/packages/core/src/node/utils/normalizePath.ts b/packages/core/src/node/utils/normalizePath.ts index 68108a98a..094759edb 100644 --- a/packages/core/src/node/utils/normalizePath.ts +++ b/packages/core/src/node/utils/normalizePath.ts @@ -41,9 +41,9 @@ function absolutePathToRoutePrefix( ), ); const routeService = RouteService.getInstance(); - const [basePrefix, versionPrefix, langPrefix] = + const [versionPrefix, langPrefix] = routeService.getRoutePathParts(relativePath); - return `${basePrefix}${versionPrefix ? `/${versionPrefix}` : ''}${langPrefix ? `/${langPrefix}` : ''}`; + return `${versionPrefix ? `/${versionPrefix}` : ''}${langPrefix ? `/${langPrefix}` : ''}`; } export function addRoutePrefix( diff --git a/packages/core/src/runtime/ClientApp.tsx b/packages/core/src/runtime/ClientApp.tsx index 96a9488f5..22c97b773 100644 --- a/packages/core/src/runtime/ClientApp.tsx +++ b/packages/core/src/runtime/ClientApp.tsx @@ -3,6 +3,7 @@ import type { PageData } from '@rspress/shared'; import { useThemeState } from '@theme'; import { UnheadProvider, createHead } from '@unhead/react/client'; import { useMemo, useState } from 'react'; +import { base } from 'virtual-runtime-config'; import { App } from './App'; const head = createHead(); @@ -27,6 +28,7 @@ export function ClientApp({ v7_relativeSplatPath: true, v7_startTransition: true, }} + basename={base} > diff --git a/packages/core/src/runtime/env.d.ts b/packages/core/src/runtime/env.d.ts index 30c13e0f2..c270ae584 100644 --- a/packages/core/src/runtime/env.d.ts +++ b/packages/core/src/runtime/env.d.ts @@ -44,3 +44,9 @@ declare module '*.svg' { declare module '@theme' { export * from '@rspress/theme-default'; } + +declare module 'virtual-runtime-config' { + import type { NormalizedRuntimeConfig } from '@rspress/shared'; + + export const base: NormalizedRuntimeConfig['base']; +} \ No newline at end of file diff --git a/packages/core/src/runtime/initPageData.ts b/packages/core/src/runtime/initPageData.ts index c761f48c3..3467247c5 100644 --- a/packages/core/src/runtime/initPageData.ts +++ b/packages/core/src/runtime/initPageData.ts @@ -7,6 +7,7 @@ import { type PageData, cleanUrl, } from '@rspress/shared'; +import { base } from 'virtual-runtime-config'; import siteData from 'virtual-site-data'; type PageMeta = { @@ -68,10 +69,7 @@ export async function initPageData(routePath: string): Promise { let version = siteData.multiVersion?.default || ''; if (siteData.lang && typeof window !== 'undefined') { - const path = location.pathname - .replace(siteData.base, '') - .split('/') - .slice(0, 2); + const path = location.pathname.replace(base, '').split('/').slice(0, 2); if (siteData.locales.length) { const result = siteData.locales.find(({ lang }) => path.includes(lang)); diff --git a/packages/core/src/runtime/ssrServerEntry.tsx b/packages/core/src/runtime/ssrServerEntry.tsx index dca765bbb..0d6c65995 100644 --- a/packages/core/src/runtime/ssrServerEntry.tsx +++ b/packages/core/src/runtime/ssrServerEntry.tsx @@ -11,6 +11,7 @@ import { renderToPipeableStream } from 'react-dom/server'; import { PassThrough } from 'node:stream'; import { text } from 'node:stream/consumers'; import type { ReactNode } from 'react'; +import { base } from 'virtual-runtime-config'; import { App } from './App'; import { initPageData } from './initPageData'; @@ -46,7 +47,7 @@ export async function render( const appHtml = await renderToHtml( - + diff --git a/packages/runtime/src/env.d.ts b/packages/runtime/src/env.d.ts index 8e7bde756..fbe70d6c0 100644 --- a/packages/runtime/src/env.d.ts +++ b/packages/runtime/src/env.d.ts @@ -10,3 +10,9 @@ declare module 'virtual-site-data' { } declare module 'virtual-i18n-text'; + +declare module 'virtual-runtime-config' { + import type { NormalizedRuntimeConfig } from '@rspress/shared'; + + export const base: NormalizedRuntimeConfig['base']; +} \ No newline at end of file diff --git a/packages/runtime/src/utils.ts b/packages/runtime/src/utils.ts index 2aa220609..90d994a22 100644 --- a/packages/runtime/src/utils.ts +++ b/packages/runtime/src/utils.ts @@ -10,20 +10,21 @@ import { removeHash, removeTrailingSlash, } from '@rspress/shared'; +import { base } from 'virtual-runtime-config'; import siteData from 'virtual-site-data'; export function withBase(url = '/'): string { - return rawWithBase(url, siteData.base); + return rawWithBase(url, base); } export function removeBase(url: string): string { - return rawRemoveBase(url, siteData.base); + return rawRemoveBase(url, base); } export function isEqualPath(a: string, b: string) { return ( - withBase(normalizeHrefInRuntime(removeHash(a))) === - withBase(normalizeHrefInRuntime(removeHash(b))) + removeBase(normalizeHrefInRuntime(removeHash(a))) === + removeBase(normalizeHrefInRuntime(removeHash(b))) ); } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 20c8be39e..f652f44b8 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -34,6 +34,5 @@ export { replaceVersion, slash, withBase, - withoutBase, withoutLang, } from './runtime-utils/utils'; diff --git a/packages/shared/src/runtime-utils/sidebar.ts b/packages/shared/src/runtime-utils/sidebar.ts index 7e488638b..4d9f5786e 100644 --- a/packages/shared/src/runtime-utils/sidebar.ts +++ b/packages/shared/src/runtime-utils/sidebar.ts @@ -1,6 +1,4 @@ import type { NavItemWithLink, NormalizedSidebar } from '../types/defaultTheme'; -import { withBase, withoutBase } from './utils'; -import { addTrailingSlash } from './utils'; /** * match the sidebar key in user config @@ -10,14 +8,12 @@ import { addTrailingSlash } from './utils'; export const matchSidebar = ( pattern: string, currentPathname: string, - base: string, ): boolean => { - const prefix = withBase(pattern, base); - if (prefix === currentPathname) { + if (pattern === currentPathname) { return true; } - const prefixWithTrailingSlash = addTrailingSlash(prefix); - if (currentPathname.startsWith(prefixWithTrailingSlash)) { + + if (currentPathname.startsWith(pattern)) { return true; } @@ -25,7 +21,7 @@ export const matchSidebar = ( // '/api/react': [ // { link: '/api/react.use' } // ] - const prefixWithDot = `${prefix}.`; + const prefixWithDot = `${pattern}.`; return currentPathname.startsWith(prefixWithDot); }; @@ -38,7 +34,6 @@ export const matchSidebar = ( export const getSidebarDataGroup = ( sidebar: NormalizedSidebar, currentPathname: string, - base: string, ): NormalizedSidebar[string] => { /** * why sort? @@ -54,7 +49,7 @@ export const getSidebarDataGroup = ( */ const navRoutes = Object.keys(sidebar).sort((a, b) => b.length - a.length); for (const name of navRoutes) { - if (matchSidebar(name, currentPathname, base)) { + if (matchSidebar(name, currentPathname)) { const sidebarGroup = sidebar[name]; return sidebarGroup; } @@ -65,9 +60,6 @@ export const getSidebarDataGroup = ( export const matchNavbar = ( item: NavItemWithLink, currentPathname: string, - base: string, ): boolean => { - return new RegExp(item.activeMatch || item.link).test( - withoutBase(currentPathname, base), - ); + return new RegExp(item.activeMatch || item.link).test(currentPathname); }; diff --git a/packages/shared/src/runtime-utils/utils.test.ts b/packages/shared/src/runtime-utils/utils.test.ts index 5e86deed8..cea0913a9 100644 --- a/packages/shared/src/runtime-utils/utils.test.ts +++ b/packages/shared/src/runtime-utils/utils.test.ts @@ -3,10 +3,10 @@ import { normalizeHref, normalizePosixPath, parseUrl, + removeBase, replaceLang, replaceVersion, withBase, - withoutBase, withoutLang, } from './utils'; @@ -29,10 +29,10 @@ describe('test shared utils', () => { expect(secondResult).toBe('/my-base/guide/'); }); - test('withoutBase', () => { - expect(withoutBase('/zh/guide/', '/zh/')).toBe('/guide/'); - expect(withoutBase('/guide/', '/')).toBe('/guide/'); - expect(withoutBase('/guide/', '')).toBe('/guide/'); + test('removeBase', () => { + expect(removeBase('/zh/guide/', '/zh/')).toBe('/guide/'); + expect(removeBase('/guide/', '/')).toBe('/guide/'); + expect(removeBase('/guide/', '')).toBe('/guide/'); }); test('normalizePosix', () => { diff --git a/packages/shared/src/runtime-utils/utils.ts b/packages/shared/src/runtime-utils/utils.ts index 50e0c3b80..4638d744b 100644 --- a/packages/shared/src/runtime-utils/utils.ts +++ b/packages/shared/src/runtime-utils/utils.ts @@ -161,11 +161,8 @@ export function replaceLang( purePathPart = cleanUrls ? 'index' : 'index.html'; } - return withBase( - addLeadingSlash( - [versionPart, langPart, purePathPart].filter(Boolean).join('/'), - ), - base, + return addLeadingSlash( + [versionPart, langPart, purePathPart].filter(Boolean).join('/'), ); } @@ -204,10 +201,7 @@ export function replaceVersion( restPart = cleanUrls ? 'index' : 'index.html'; } - return withBase( - addLeadingSlash([versionPart, restPart].filter(Boolean).join('/')), - base, - ); + return addLeadingSlash([versionPart, restPart].filter(Boolean).join('/')); } export const parseUrl = ( @@ -264,10 +258,6 @@ export function withoutLang(path: string, langs: string[]) { return addLeadingSlash(path.replace(langRegexp, '')); } -export function withoutBase(path: string, base: string) { - return addLeadingSlash(path).replace(normalizeSlash(base), ''); -} - export function withBase(url: string, base: string): string { const normalizedUrl = addLeadingSlash(url); const normalizedBase = normalizeSlash(base); diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index 794e9cf5c..f7b308f6d 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -214,7 +214,6 @@ type PluginShikiOptions = RehypeShikiOptions; export interface SiteData { root: string; - base: string; lang: string; route: RouteOptions; locales: { lang: string; label: string }[]; @@ -238,6 +237,11 @@ export interface SiteData { }; } +// TODO: migrate more SiteData to NormalizedRuntimeConfig, and rename "SiteData" to "PageData" or "Pages" +export interface NormalizedRuntimeConfig { + base: string; +} + /** * @description search-index.json file * "_foo" is the private field that won't be written to search-index.json file diff --git a/packages/theme-default/src/components/Link/index.tsx b/packages/theme-default/src/components/Link/index.tsx index 990ca4ae6..962c34d4c 100644 --- a/packages/theme-default/src/components/Link/index.tsx +++ b/packages/theme-default/src/components/Link/index.tsx @@ -1,6 +1,7 @@ import { normalizeHrefInRuntime as normalizeHref, pathnameToRouteService, + removeBase, useLocation, useNavigate, withBase, @@ -57,6 +58,10 @@ export function Link(props: LinkProps) { return withBase(normalizeHref(href)); }, [href]); + const withoutBaseUrl = useMemo(() => { + return removeBase(normalizeHref(href)); + }, [href]); + const isExternal = isExternalUrl(href); const isHashOnlyLink = href.startsWith('#'); @@ -101,9 +106,9 @@ export function Link(props: LinkProps) { e.preventDefault(); // handle normal link - const inCurrentPage = isActive(withBaseUrl, pathname); + const inCurrentPage = isActive(withoutBaseUrl, pathname); if (!process.env.__SSR__ && !inCurrentPage) { - const matchedRoute = pathnameToRouteService(withBaseUrl); + const matchedRoute = pathnameToRouteService(withoutBaseUrl); if (matchedRoute) { const timer = setTimeout(() => { nprogress.start(); @@ -114,7 +119,7 @@ export function Link(props: LinkProps) { } } onNavigate?.(); - navigate(withBaseUrl, { replace: false }); + navigate(withoutBaseUrl, { replace: false }); setTimeout(() => { scrollToAnchor(false, scrollPaddingTop); }, 100); @@ -125,7 +130,7 @@ export function Link(props: LinkProps) { {...props} href={withBaseUrl} className={`${styles.link} ${className}`} - onMouseEnter={() => preloadLink(withBaseUrl)} + onMouseEnter={() => preloadLink(withoutBaseUrl)} onClick={event => { onClick?.(event); handleNavigate(event); diff --git a/packages/theme-default/src/components/Nav/NavMenuGroup.tsx b/packages/theme-default/src/components/Nav/NavMenuGroup.tsx index b208c1054..2a963911c 100644 --- a/packages/theme-default/src/components/Nav/NavMenuGroup.tsx +++ b/packages/theme-default/src/components/Nav/NavMenuGroup.tsx @@ -62,13 +62,7 @@ function NormalGroupItem({ item }: { item: NavItemWithLink }) { } export function NavMenuGroup(item: NavMenuGroupItem) { - const { - activeValue, - items: groupItems, - base = '', - link = '', - pathname = '', - } = item; + const { activeValue, items: groupItems, link = '', pathname = '' } = item; const [isOpen, setIsOpen] = useState(false); const closeTimerRef = useRef(null); @@ -96,7 +90,7 @@ export function NavMenuGroup(item: NavMenuGroupItem) { }; const renderLinkItem = (item: NavItemWithLink) => { - const isLinkActive = matchNavbar(item, pathname, base); + const isLinkActive = matchNavbar(item, pathname); if (activeValue === item.text || (!activeValue && isLinkActive)) { return ; } diff --git a/packages/theme-default/src/components/Nav/NavMenuSingleItem.tsx b/packages/theme-default/src/components/Nav/NavMenuSingleItem.tsx index 6e43214a3..89ade67eb 100644 --- a/packages/theme-default/src/components/Nav/NavMenuSingleItem.tsx +++ b/packages/theme-default/src/components/Nav/NavMenuSingleItem.tsx @@ -1,8 +1,7 @@ import { normalizeHrefInRuntime as normalizeHref } from '@rspress/runtime'; -import { - type NavItemWithLink, - type NavItemWithLinkAndChildren, - withoutBase, +import type { + NavItemWithLink, + NavItemWithLinkAndChildren, } from '@rspress/shared'; import { Link, Tag } from '@theme'; import * as styles from './index.module.scss'; @@ -18,10 +17,8 @@ interface Props { export function NavMenuSingleItem( item: (NavItemWithLink | NavItemWithLinkAndChildren) & Props, ) { - const { pathname, base } = item; - const isActive = new RegExp(item.activeMatch || item.link).test( - withoutBase(pathname, base), - ); + const { pathname } = item; + const isActive = new RegExp(item.activeMatch || item.link).test(pathname); return ( diff --git a/packages/theme-default/src/components/Nav/index.tsx b/packages/theme-default/src/components/Nav/index.tsx index e87e07371..2b510f943 100644 --- a/packages/theme-default/src/components/Nav/index.tsx +++ b/packages/theme-default/src/components/Nav/index.tsx @@ -1,6 +1,7 @@ import { useLocation, usePageData, useWindowSize } from '@rspress/runtime'; import type { NavItem } from '@rspress/shared'; import { Search } from '@theme'; +import { base } from 'virtual-runtime-config'; import { useHiddenNav } from '../../logic/useHiddenNav'; import { useNavData } from '../../logic/useNav'; import { NavHamburger } from '../NavHamburger'; @@ -27,7 +28,6 @@ export function Nav(props: NavProps) { const { beforeNavTitle, afterNavTitle, beforeNav, afterNavMenu, navTitle } = props; const { siteData, page } = usePageData(); - const { base } = siteData; const { pathname } = useLocation(); const { width } = useWindowSize(); const hiddenNav = useHiddenNav(); diff --git a/packages/theme-default/src/components/Nav/menuDataHooks.tsx b/packages/theme-default/src/components/Nav/menuDataHooks.tsx index e5d205dd9..190f8dbf0 100644 --- a/packages/theme-default/src/components/Nav/menuDataHooks.tsx +++ b/packages/theme-default/src/components/Nav/menuDataHooks.tsx @@ -1,6 +1,7 @@ import { useLocation, usePageData, useVersion } from '@rspress/runtime'; import { replaceLang, replaceVersion } from '@rspress/shared'; import Translator from '@theme-assets/translator'; +import { base } from 'virtual-runtime-config'; import { SvgWrapper } from '../SvgWrapper'; import type { NavMenuGroupItem } from './NavMenuGroup'; @@ -16,7 +17,6 @@ export function useTranslationMenuData(): NavMenuGroupItem { const cleanUrls = siteData.route?.cleanUrls || false; const hasMultiLanguage = localeLanguages.length > 1; const { lang: currentLang, pageType } = page; - const { base } = siteData; const translationMenuData = hasMultiLanguage ? { @@ -61,7 +61,6 @@ export function useVersionMenuData() { const cleanUrls = siteData.route?.cleanUrls || false; const defaultVersion = siteData.multiVersion.default || ''; const versions = siteData.multiVersion.versions || []; - const { base } = siteData; const versionsMenuData = { items: versions.map(version => ({ text: version, diff --git a/packages/theme-default/src/components/NavScreen/index.tsx b/packages/theme-default/src/components/NavScreen/index.tsx index 801c1a53a..78d178d68 100644 --- a/packages/theme-default/src/components/NavScreen/index.tsx +++ b/packages/theme-default/src/components/NavScreen/index.tsx @@ -3,6 +3,7 @@ import type { DefaultThemeConfig, NavItem } from '@rspress/shared'; import type { SiteData } from '@rspress/shared'; import { clearAllBodyScrollLocks, disableBodyScroll } from 'body-scroll-lock'; import { useEffect, useRef } from 'react'; +import { base } from 'virtual-runtime-config'; import { useNavData } from '../../logic/useNav'; import { NavMenuSingleItem } from '../Nav/NavMenuSingleItem'; import { @@ -108,7 +109,6 @@ export function NavScreen(props: Props) { const socialLinks = siteData?.themeConfig?.socialLinks || []; const hasSocialLinks = socialLinks.length > 0; const langs = localesData.map(item => item.lang || 'zh') || []; - const { base } = siteData; useEffect(() => { screen.current && diff --git a/packages/theme-default/src/components/Overview/index.tsx b/packages/theme-default/src/components/Overview/index.tsx index e7e7dce11..4a23f6e7c 100644 --- a/packages/theme-default/src/components/Overview/index.tsx +++ b/packages/theme-default/src/components/Overview/index.tsx @@ -2,7 +2,6 @@ import { isEqualPath, normalizeHrefInRuntime as normalizeHref, usePageData, - withBase, } from '@rspress/runtime'; import type { Header, @@ -137,8 +136,8 @@ export function Overview(props: { const subFilter = (link: string) => // sidebar items link without base path // pages route path with base path - withBase(link).startsWith(routePath.replace(/overview$/, '')) && - !isEqualPath(withBase(link), routePath); + link.startsWith(routePath.replace(/overview$/, '')) && + !isEqualPath(link, routePath); const getChildLink = ( traverseItem: SidebarDivider | SidebarItem | NormalizedSidebarGroup, ): string => { @@ -182,10 +181,7 @@ export function Overview(props: { return false; } // do not display overview title in sub pages overview - if ( - withBase(item.link) === `${routePath}index` && - frontmatter?.overview === true - ) { + if (item.link === `${routePath}index` && frontmatter?.overview === true) { return false; } // props > frontmatter in single file > _meta.json config in a file > frontmatter in overview page > _meta.json config in sidebar @@ -195,7 +191,7 @@ export function Overview(props: { sidebarGroup?.overviewHeaders ?? [2]; // sidebar items link without base path const pageModule = overviewModules.find(m => - isEqualPath(m.routePath, withBase(item.link || '')), + isEqualPath(m.routePath, item.link || ''), ); const link = getChildLink(item); return { diff --git a/packages/theme-default/src/components/Overview/utils.ts b/packages/theme-default/src/components/Overview/utils.ts index fe3c162af..1d43caff5 100644 --- a/packages/theme-default/src/components/Overview/utils.ts +++ b/packages/theme-default/src/components/Overview/utils.ts @@ -1,4 +1,3 @@ -import { withBase } from '@rspress/runtime'; import type { NormalizedSidebarGroup, SidebarDivider, @@ -27,14 +26,10 @@ export function findItemByRoutePath( if (isSidebarDivider(item)) { return false; } - const withBaseUrl = withBase(item.link); - const removeIndexUrl = removeIndex(withBaseUrl); + const removeIndexUrl = removeIndex(item.link || '/'); const removeBackSlashedRoutePath = routePath.replace(/\/$/, ''); return ( - // FIXME: 😅 we should refactor all the path logic, /index.html / or no /, l - withBaseUrl === routePath || removeIndexUrl === routePath || - withBaseUrl === removeBackSlashedRoutePath || removeIndexUrl === removeBackSlashedRoutePath ); } diff --git a/packages/theme-default/src/components/Sidebar/SidebarGroup.tsx b/packages/theme-default/src/components/Sidebar/SidebarGroup.tsx index c61de3062..5be3924cb 100644 --- a/packages/theme-default/src/components/Sidebar/SidebarGroup.tsx +++ b/packages/theme-default/src/components/Sidebar/SidebarGroup.tsx @@ -1,7 +1,6 @@ import { normalizeHrefInRuntime as normalizeHref, useNavigate, - withBase, } from '@rspress/runtime'; import type { NormalizedSidebarGroup } from '@rspress/shared'; import { Tag } from '@theme'; @@ -119,7 +118,7 @@ export function SidebarGroup(props: SidebarItemProps) { onMouseEnter={() => item.link && preloadLink(item.link)} onClick={e => { if (item.link) { - navigate(withBase(normalizeHref(item.link))); + navigate(normalizeHref(item.link)); } collapsible && toggleCollapse(e); }} diff --git a/packages/theme-default/src/env.d.ts b/packages/theme-default/src/env.d.ts index 8381d4572..7c27b7839 100644 --- a/packages/theme-default/src/env.d.ts +++ b/packages/theme-default/src/env.d.ts @@ -38,3 +38,9 @@ declare module 'virtual-search-index-hash' { } declare const __WEBPACK_PUBLIC_PATH__: string; + +declare module 'virtual-runtime-config' { + import type { NormalizedRuntimeConfig } from '@rspress/shared'; + + export const base: NormalizedRuntimeConfig['base']; +} diff --git a/packages/theme-default/src/logic/getSidebarDataGroup.test.ts b/packages/theme-default/src/logic/getSidebarDataGroup.test.ts index 4d498c527..b1c447229 100644 --- a/packages/theme-default/src/logic/getSidebarDataGroup.test.ts +++ b/packages/theme-default/src/logic/getSidebarDataGroup.test.ts @@ -5,14 +5,16 @@ vi.mock('virtual-i18n-text', () => { return { default: {} }; }); -vi.mock('virtual-site-data', () => { +vi.mock('virtual-runtime-config', () => { return { - default: { - base: '/', - }, + base: '/', }; }); +vi.mock('virtual-site-data', () => { + return {}; +}); + vi.mock('virtual-routes', () => { return { routes: [] }; }); diff --git a/packages/theme-default/src/logic/getSidebarDataGroup.ts b/packages/theme-default/src/logic/getSidebarDataGroup.ts index e422ea20f..5bb358719 100644 --- a/packages/theme-default/src/logic/getSidebarDataGroup.ts +++ b/packages/theme-default/src/logic/getSidebarDataGroup.ts @@ -1,7 +1,6 @@ import { normalizeRoutePath, matchPath as reactRouterDomMatchPath, - withBase, } from '@rspress/runtime'; import type { NormalizedSidebar, @@ -10,7 +9,6 @@ import type { SidebarItem, } from '@rspress/shared'; import { matchSidebar } from '@rspress/shared'; -import siteData from 'virtual-site-data'; import type { SidebarData } from '../components/Sidebar'; /** @@ -26,7 +24,7 @@ import type { SidebarData } from '../components/Sidebar'; * @returns */ export function isActive(itemLink: string, currentPathname: string): boolean { - const normalizedItemLink = normalizeRoutePath(withBase(itemLink)); + const normalizedItemLink = normalizeRoutePath(itemLink); const normalizedCurrentPathname = normalizeRoutePath(currentPathname); const linkMatched = reactRouterDomMatchPath( normalizedItemLink, @@ -101,7 +99,7 @@ export const getSidebarDataGroup = ( */ const navRoutes = Object.keys(sidebar).sort((a, b) => b.length - a.length); for (const name of navRoutes) { - if (matchSidebar(name, currentPathname, siteData.base)) { + if (matchSidebar(name, currentPathname)) { const sidebarGroup = sidebar[name]; return sidebarGroup; } diff --git a/packages/theme-default/src/logic/usePrevNextPage.ts b/packages/theme-default/src/logic/usePrevNextPage.ts index 875d1db7c..f4e4e8d69 100644 --- a/packages/theme-default/src/logic/usePrevNextPage.ts +++ b/packages/theme-default/src/logic/usePrevNextPage.ts @@ -1,4 +1,4 @@ -import { isEqualPath, useLocation, withBase } from '@rspress/runtime'; +import { isEqualPath, useLocation } from '@rspress/runtime'; import type { NormalizedSidebarGroup, SidebarItem } from '@rspress/shared'; import { useSidebarData } from './useSidebarData'; @@ -29,7 +29,7 @@ export function usePrevNextPage(): { items.forEach(item => !('dividerType' in item) && walk(item)); const pageIndex = flattenTitles.findIndex(item => - isEqualPath(withBase(item.link), pathname), + isEqualPath(item.link, pathname), ); const prevPage = flattenTitles[pageIndex - 1] || null; From 9af3e5aa1931ff4f0c1089a5046a26e62bf3da1c Mon Sep 17 00:00:00 2001 From: SoonIter Date: Thu, 26 Jun 2025 11:59:36 +0800 Subject: [PATCH 2/4] chore: remove base --- packages/plugin-llms/src/plugin.ts | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/packages/plugin-llms/src/plugin.ts b/packages/plugin-llms/src/plugin.ts index bef456f8e..172be9716 100644 --- a/packages/plugin-llms/src/plugin.ts +++ b/packages/plugin-llms/src/plugin.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import type { RsbuildPlugin } from '@rsbuild/core'; import type { RouteService } from '@rspress/core'; import { matchPath } from '@rspress/runtime/server'; -import { getSidebarDataGroup, removeBase } from '@rspress/shared'; +import { getSidebarDataGroup } from '@rspress/shared'; import type { Nav, PageIndexInfo, @@ -28,7 +28,6 @@ const rsbuildPluginLlms = ({ descriptionRef, langRef, sidebar, - baseRef, docDirectoryRef, routeServiceRef, nav, @@ -45,7 +44,6 @@ const rsbuildPluginLlms = ({ } = rspressPluginOptions; api.onBeforeBuild(async () => { - const base = baseRef.current; const docDirectory = docDirectoryRef.current; const disableSSG = disableSSGRef.current; @@ -89,9 +87,7 @@ const rsbuildPluginLlms = ({ const navItem = navList[i]; if ( lang === navItem.lang && - new RegExp(navItem.activeMatch ?? navItem.link).test( - removeBase(routePath, base), - ) + new RegExp(navItem.activeMatch ?? navItem.link).test(routePath) ) { pageArrayItem.push(pageData); return; @@ -101,7 +97,7 @@ const rsbuildPluginLlms = ({ }); for (const array of pageArray) { - organizeBySidebar(sidebar, array, base); + organizeBySidebar(sidebar, array); } if (llmsTxt) { @@ -256,20 +252,12 @@ function flatSidebar( .filter(Boolean) as string[]; } -function organizeBySidebar( - sidebar: Sidebar, - pages: PageIndexInfo[], - base: string, -) { +function organizeBySidebar(sidebar: Sidebar, pages: PageIndexInfo[]) { if (pages.length === 0) { return; } const pageItem = pages[0]; - const currSidebar = getSidebarDataGroup( - sidebar as any, - pageItem.routePath, - base, - ); + const currSidebar = getSidebarDataGroup(sidebar as any, pageItem.routePath); if (currSidebar.length === 0) { return; From dfa47700a08756a658d27f7caea0bca4bb47ba96 Mon Sep 17 00:00:00 2001 From: SoonIter Date: Thu, 26 Jun 2025 12:49:42 +0800 Subject: [PATCH 3/4] fix: plugin-rss should work --- packages/plugin-rss/src/createFeed.ts | 5 ++++- packages/plugin-rss/src/options.ts | 11 ++++------- packages/plugin-rss/src/plugin-rss.ts | 19 +++---------------- packages/plugin-rss/src/type.ts | 4 +--- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/packages/plugin-rss/src/createFeed.ts b/packages/plugin-rss/src/createFeed.ts index 7b21e2dd2..e995a9f98 100644 --- a/packages/plugin-rss/src/createFeed.ts +++ b/packages/plugin-rss/src/createFeed.ts @@ -22,7 +22,10 @@ export function generateFeedItem(page: PageIndexInfo, siteUrl: string) { author: toAuthors(fm.author), link: resolveUrl( siteUrl, - selectNonNullishProperty(fm.permalink, page.routePath) || '', + selectNonNullishProperty(fm.permalink, page.routePath)?.replace( + /^\//, + '', + ) || '', ), description: selectNonNullishProperty(fm.description) || '', content: selectNonNullishProperty(fm.summary, page._html) || '', diff --git a/packages/plugin-rss/src/options.ts b/packages/plugin-rss/src/options.ts index bd61d0ed4..9b05e2c29 100644 --- a/packages/plugin-rss/src/options.ts +++ b/packages/plugin-rss/src/options.ts @@ -7,18 +7,15 @@ import type { FeedChannel, FeedOutputType, PluginRssOptions } from './type'; export function testPage( test: FeedChannel['test'], page: PageIndexInfo, - base = '/', ): boolean { if (Array.isArray(test)) { - return test.some(item => testPage(item, page, base)); + return test.some(item => testPage(item, page)); } if (typeof test === 'function') { - return test(page, base); + return test(page); } const routePath = page.routePath; - const pureRoutePath = `/${ - routePath.startsWith(base) ? routePath.slice(base.length) : routePath - }`.replace(/^\/+/, '/'); + const pureRoutePath = `/${routePath}`.replace(/^\/+/, '/'); if (typeof test === 'string') { return [routePath, pureRoutePath].some(path => path.startsWith(test)); } @@ -27,7 +24,7 @@ export function testPage( } throw new Error( - 'test must be of `RegExp` or `string` or `(page: PageIndexInfo, base: string) => boolean`', + 'test must be of `RegExp` or `string` or `(page: PageIndexInfo) => boolean`', ); } diff --git a/packages/plugin-rss/src/plugin-rss.ts b/packages/plugin-rss/src/plugin-rss.ts index b7d9b91c1..b93479ac9 100644 --- a/packages/plugin-rss/src/plugin-rss.ts +++ b/packages/plugin-rss/src/plugin-rss.ts @@ -50,19 +50,14 @@ class FeedsSet { function getRssItems( feeds: TransformedFeedChannel[], page: PageIndexInfo, - config: UserConfig, siteUrl: string, ): Promise { return Promise.all( feeds - .filter(options => testPage(options.test, page, config.base)) + .filter(options => testPage(options.test, page)) .map(async options => { const after = options.item || ((feed: FeedItem) => feed); - const item = await after( - generateFeedItem(page, siteUrl), - page, - siteUrl, - ); + const item = await after(generateFeedItem(page, siteUrl), page); return { ...item, channel: options.id }; }), ); @@ -79,7 +74,6 @@ export function pluginRss(pluginRssOptions: PluginRssOptions): RspressPlugin { string, PromiseLike > = null; - let _config: null | UserConfig; return { name: PluginName, @@ -90,7 +84,6 @@ export function pluginRss(pluginRssOptions: PluginRssOptions): RspressPlugin { return; } _rssWorkaround = {}; - _config = config; feedsSet.set(pluginRssOptions, config); }, async extendPageData(pageData) { @@ -100,12 +93,7 @@ export function pluginRss(pluginRssOptions: PluginRssOptions): RspressPlugin { // - let's cache rss items within a complete rspress build _rssWorkaround[pageData.routePath] = _rssWorkaround[pageData.routePath] || - getRssItems( - feedsSet.get(), - pageData, - _config!, - pluginRssOptions.siteUrl, - ); + getRssItems(feedsSet.get(), pageData, pluginRssOptions.siteUrl); const feeds = await _rssWorkaround[pageData.routePath]; const showRssList = new Set( @@ -150,7 +138,6 @@ export function pluginRss(pluginRssOptions: PluginRssOptions): RspressPlugin { await writeFile(path, output.getContent(feed)); } _rssWorkaround = null; - _config = null; }, }; } diff --git a/packages/plugin-rss/src/type.ts b/packages/plugin-rss/src/type.ts index 136c15daf..706a7fcc1 100644 --- a/packages/plugin-rss/src/type.ts +++ b/packages/plugin-rss/src/type.ts @@ -62,18 +62,16 @@ export interface FeedChannel | RegExp | string | (RegExp | string)[] - | ((item: PageIndexInfo, base: string) => boolean); + | ((item: PageIndexInfo) => boolean); /** * a function to modify feed item * @param item pre-generated feed item * @param page page data - * @param base base path of the rspress site * @returns modified feed item */ item?: ( item: FeedItem, page: PageIndexInfo, - base: string, ) => FeedItem | PromiseLike; /** * feed level output config From 4b03b822a8ce52ab4c8426f04d77180e90507a3f Mon Sep 17 00:00:00 2001 From: SoonIter Date: Thu, 26 Jun 2025 13:05:56 +0800 Subject: [PATCH 4/4] chore: update --- packages/core/src/runtime/ssrServerEntry.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/core/src/runtime/ssrServerEntry.tsx b/packages/core/src/runtime/ssrServerEntry.tsx index 0d6c65995..e9b5a9c67 100644 --- a/packages/core/src/runtime/ssrServerEntry.tsx +++ b/packages/core/src/runtime/ssrServerEntry.tsx @@ -2,6 +2,7 @@ import { DataContext, ThemeContext, pathnameToRouteService, + withBase, } from '@rspress/runtime'; import { StaticRouter } from '@rspress/runtime/server'; import type { PageData } from '@rspress/shared'; @@ -38,16 +39,16 @@ function renderToHtml(app: ReactNode): Promise { } export async function render( - pagePath: string, + routePath: string, head: Unhead, ): Promise<{ appHtml: string; pageData: PageData }> { - const initialPageData = await initPageData(pagePath); - await preloadRoute(pagePath); + const initialPageData = await initPageData(routePath); + await preloadRoute(routePath); const appHtml = await renderToHtml( - +