diff --git a/e2e/fixtures/search-code-blocks/doc/index.mdx b/e2e/fixtures/search-code-blocks/doc/index.mdx new file mode 100644 index 000000000..98f810897 --- /dev/null +++ b/e2e/fixtures/search-code-blocks/doc/index.mdx @@ -0,0 +1,5 @@ +# Hello world + +```js +console.log('hello from code block'); +``` diff --git a/e2e/fixtures/search-code-blocks/package.json b/e2e/fixtures/search-code-blocks/package.json new file mode 100644 index 000000000..b122944e3 --- /dev/null +++ b/e2e/fixtures/search-code-blocks/package.json @@ -0,0 +1,16 @@ +{ + "name": "@rspress-fixture/rspress-search-code-blocks", + "version": "1.0.0", + "private": true, + "scripts": { + "build": "rspress build", + "dev": "rspress dev", + "preview": "rspress preview" + }, + "dependencies": { + "rspress": "workspace:*" + }, + "devDependencies": { + "@types/node": "^18.11.17" + } +} diff --git a/e2e/fixtures/search-code-blocks/rspress.config.ts b/e2e/fixtures/search-code-blocks/rspress.config.ts new file mode 100644 index 000000000..399522b20 --- /dev/null +++ b/e2e/fixtures/search-code-blocks/rspress.config.ts @@ -0,0 +1,9 @@ +import * as path from 'node:path'; +import { defineConfig } from 'rspress/config'; + +export default defineConfig({ + root: path.join(__dirname, 'doc'), + search: { + codeBlocks: true, + }, +}); diff --git a/e2e/fixtures/search-code-blocks/tsconfig.json b/e2e/fixtures/search-code-blocks/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/e2e/fixtures/search-code-blocks/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/e2e/tests/search-code-blocks.test.ts b/e2e/tests/search-code-blocks.test.ts new file mode 100644 index 000000000..df845b0d1 --- /dev/null +++ b/e2e/tests/search-code-blocks.test.ts @@ -0,0 +1,31 @@ +import path from 'node:path'; +import { expect, test } from '@playwright/test'; +import { getPort, killProcess, runDevCommand } from '../utils/runCommands'; +import { searchInPage } from '../utils/search'; + +const fixtureDir = path.resolve(__dirname, '../fixtures'); + +test.describe('search code blocks test', async () => { + let appPort; + let app; + + test.beforeAll(async () => { + const appDir = path.join(fixtureDir, 'search-code-blocks'); + appPort = await getPort(); + app = await runDevCommand(appDir, appPort); + }); + + test.afterAll(async () => { + if (app) { + await killProcess(app); + } + }); + + test('search index should include content of code blocks', async ({ + page, + }) => { + await page.goto(`http://localhost:${appPort}`); + const suggestItems = await searchInPage(page, 'hello from code block'); + expect(suggestItems.length).toBe(1); + }); +}); diff --git a/packages/core/src/node/runtimeModule/siteData/extractPageData.ts b/packages/core/src/node/runtimeModule/siteData/extractPageData.ts index 13fe2a2b4..ffd53c0b7 100644 --- a/packages/core/src/node/runtimeModule/siteData/extractPageData.ts +++ b/packages/core/src/node/runtimeModule/siteData/extractPageData.ts @@ -36,6 +36,7 @@ export async function extractPageData( root: string, routeService: RouteService, highlighterLangs: Set, + searchCodeBlocks: boolean, ): Promise { const pageData = await Promise.all( routeService @@ -108,7 +109,7 @@ export async function extractPageData( { // Skip code blocks selector: 'pre > code', - format: 'skip', + format: searchCodeBlocks ? 'block' : 'skip', }, ...['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].map(tag => ({ selector: tag, diff --git a/packages/core/src/node/runtimeModule/siteData/index.ts b/packages/core/src/node/runtimeModule/siteData/index.ts index ad2308093..661e83d44 100644 --- a/packages/core/src/node/runtimeModule/siteData/index.ts +++ b/packages/core/src/node/runtimeModule/siteData/index.ts @@ -41,11 +41,16 @@ export async function siteDataVMPlugin(context: FactoryContext) { const highlightLanguages: Set = new Set(); const replaceRules = userConfig?.replaceRules || []; + + const searchConfig = userConfig?.search || {}; + // If the dev server restart when config file, we will reuse the siteData instead of extracting the siteData from source files again. const domain = - userConfig?.search && userConfig?.search?.mode === 'remote' - ? (userConfig?.search.domain ?? '') - : ''; + searchConfig?.mode === 'remote' ? (searchConfig.domain ?? '') : ''; + + const searchCodeBlocks = + 'codeBlocks' in searchConfig ? searchConfig.codeBlocks : false; + const pages = await extractPageData( replaceRules, alias, @@ -53,6 +58,7 @@ export async function siteDataVMPlugin(context: FactoryContext) { userDocRoot, routeService, highlightLanguages, + searchCodeBlocks, ); // modify page index by plugins await pluginDriver.modifySearchIndexData(pages); diff --git a/packages/document/docs/en/api/config/config-basic.mdx b/packages/document/docs/en/api/config/config-basic.mdx index 9f5a47a59..06f26f799 100644 --- a/packages/document/docs/en/api/config/config-basic.mdx +++ b/packages/document/docs/en/api/config/config-basic.mdx @@ -261,10 +261,21 @@ export default defineConfig({ ## search -- Type: `{ searchHooks: string; versioned: boolean; }` +- Type: + +```ts +type SearchOptions = { + searchHooks?: string; + versioned?: boolean; + codeBlocks?: boolean; +}; +``` ### searchHooks +- Type: `string` +- Default: `undefined` + You can add search runtime hooks logic through the `searchHooks` parameter, for example: ```ts title="rspress.config.ts" @@ -282,6 +293,9 @@ For specific hook logic, you can read [Customize Search Functions](/guide/advanc ### versioned +- Type: `boolean` +- Default: `false` + If you are using `multiVersion`, the `versioned` parameter allows you to create a separate search index for each version of your documentation. When enabled, the search will only query the index corresponding to the currently selected version. @@ -295,6 +309,23 @@ export default defineConfig({ }); ``` +### codeBlocks + +- Type: `boolean` +- Default: `false` + +If enabled, the search index will include code block content, which allows users to search code blocks. + +```ts title="rspress.config.ts" +import { defineConfig } from 'rspress/config'; + +export default defineConfig({ + search: { + codeBlocks: true, + }, +}); +``` + ## globalUIComponents - Type: `(string | [string, object])[]` diff --git a/packages/document/docs/zh/api/config/config-basic.mdx b/packages/document/docs/zh/api/config/config-basic.mdx index b1514a1a6..f42546b2a 100644 --- a/packages/document/docs/zh/api/config/config-basic.mdx +++ b/packages/document/docs/zh/api/config/config-basic.mdx @@ -261,10 +261,21 @@ export default defineConfig({ ## search -- Type: `{ searchHooks: string; versioned: boolean; }` +- Type: + +```ts +type SearchOptions = { + searchHooks?: string; + versioned?: boolean; + codeBlocks?: boolean; +}; +``` ### searchHooks +- Type: `string` +- Default: `undefined` + 你可以通过 `searchHooks` 参数来增加搜索运行时钩子逻辑,比如: ```ts title="rspress.config.ts" @@ -282,6 +293,9 @@ export default defineConfig({ ### versioned +- Type: `boolean` +- Default: `false` + 如果你配置了 `multiVersion`,可以使用 `versioned` 参数为文档的每个版本创建单独的搜索索引。开启该选项后,搜索将仅会查询当前所选版本对应的索引。 ```ts title="rspress.config.ts" @@ -294,6 +308,23 @@ export default defineConfig({ }); ``` +### codeBlocks + +- Type: `boolean` +- Default: `false` + +开启后,搜索的索引将包含代码块的内容,从而允许用户搜索代码块。 + +```ts title="rspress.config.ts" +import { defineConfig } from 'rspress/config'; + +export default defineConfig({ + search: { + codeBlocks: true, + }, +}); +``` + ## globalUIComponents - Type: `(string | object)[]` diff --git a/packages/document/rspress.config.ts b/packages/document/rspress.config.ts index 8c66e8356..84426cb6b 100644 --- a/packages/document/rspress.config.ts +++ b/packages/document/rspress.config.ts @@ -45,6 +45,9 @@ export default defineConfig({ }), ], }, + search: { + codeBlocks: true, + }, route: { cleanUrls: true, exclude: ['**/fragments/**'], diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index e754d745d..a3b94edaa 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -365,6 +365,11 @@ export type LocalSearchOptions = SearchHooks & { * Whether to generate separate search index for each version */ versioned?: boolean; + /** + * If enabled, the search index will include code block content, which allows users to search code blocks. + * @default false + */ + codeBlocks?: boolean; }; export type RemoteSearchIndexInfo = diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a5fd54f5..9dc2c00e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -581,6 +581,16 @@ importers: specifier: ^18.11.17 version: 18.11.17 + e2e/fixtures/search-code-blocks: + dependencies: + rspress: + specifier: workspace:* + version: link:../../../packages/cli + devDependencies: + '@types/node': + specifier: ^18.11.17 + version: 18.11.17 + e2e/fixtures/ssg-fail-strict: dependencies: rspress: