diff --git a/__tests__/e2e/.vitepress/config.ts b/__tests__/e2e/.vitepress/config.ts index 9a40532f..f44226fe 100644 --- a/__tests__/e2e/.vitepress/config.ts +++ b/__tests__/e2e/.vitepress/config.ts @@ -87,6 +87,17 @@ export default defineConfig({ title: 'Example', description: 'An example app using VitePress.', themeConfig: { - sidebar + sidebar, + search: { + provider: 'local', + options: { + exclude: (pageData) => { + return ( + pageData.relativePath.startsWith('local-search/excluded') || + pageData.frontmatter.search === false + ) + } + } + } } }) diff --git a/__tests__/e2e/local-search/excluded.md b/__tests__/e2e/local-search/excluded.md new file mode 100644 index 00000000..eb9e7c01 --- /dev/null +++ b/__tests__/e2e/local-search/excluded.md @@ -0,0 +1 @@ +# Local search config excluded \ No newline at end of file diff --git a/__tests__/e2e/local-search/frontmatter-excluded.md b/__tests__/e2e/local-search/frontmatter-excluded.md new file mode 100644 index 00000000..afc32512 --- /dev/null +++ b/__tests__/e2e/local-search/frontmatter-excluded.md @@ -0,0 +1,5 @@ +--- +search: false +--- + +# Local search frontmatter excluded \ No newline at end of file diff --git a/__tests__/e2e/local-search/index.md b/__tests__/e2e/local-search/index.md new file mode 100644 index 00000000..08d8b31a --- /dev/null +++ b/__tests__/e2e/local-search/index.md @@ -0,0 +1 @@ +# Local search included \ No newline at end of file diff --git a/__tests__/e2e/local-search/local-search.test.ts b/__tests__/e2e/local-search/local-search.test.ts new file mode 100644 index 00000000..9f36f7c0 --- /dev/null +++ b/__tests__/e2e/local-search/local-search.test.ts @@ -0,0 +1,30 @@ +beforeEach(async () => { + await goto('/') +}) + +describe('Local search', () => { + test('exclude content from search results', async () => { + await page.locator('#local-search button').click() + + const input = await page.locator('input#localsearch-input') + await input.waitFor({ state: 'visible' }) + await input.type('local') + + await page.waitForSelector('ul#localsearch-list', { state: 'visible' }) + + const searchResults = await page.locator('#localsearch-list') + expect(await searchResults.locator('li[role=option]').count()).toBe(1) + + await searchResults.filter({ hasText: 'Local search included' }).isVisible() + + expect( + await searchResults.filter({ hasText: 'Local search excluded' }).count() + ).toBe(0) + + expect( + await searchResults + .filter({ hasText: 'Local search frontmatter excluded' }) + .count() + ).toBe(0) + }) +}) diff --git a/src/node/plugins/localSearchPlugin.ts b/src/node/plugins/localSearchPlugin.ts index 6c54d5bc..f0edaa91 100644 --- a/src/node/plugins/localSearchPlugin.ts +++ b/src/node/plugins/localSearchPlugin.ts @@ -7,6 +7,7 @@ import type { SiteConfig } from '../config' import type { MarkdownEnv } from '../markdown' import { createMarkdownRenderer } from '../markdown' import { resolveSiteDataByRoute, slash, type DefaultTheme } from '../shared' +import matter from 'gray-matter' const debug = _debug('vitepress:local-search') @@ -120,7 +121,20 @@ export async function localSearchPlugin( const documentsByLocale = new Map() await Promise.all( files - .filter((file) => fs.existsSync(file)) + .filter((file) => { + if (!fs.existsSync(file)) { + return false + } + const src = fs.readFileSync(file, 'utf-8') + const { data: frontmatter } = matter(src, { + excerpt: true + }) + const { relativePath } = createMarkdownEnv(file) + return !siteConfig.userConfig.themeConfig?.search?.options?.exclude({ + relativePath, + frontmatter + }) + }) .map(async (file) => { const fileId = getDocId(file) const sections = splitPageIntoSections( diff --git a/types/default-theme.d.ts b/types/default-theme.d.ts index b0ea5dbb..6b6e84ff 100644 --- a/types/default-theme.d.ts +++ b/types/default-theme.d.ts @@ -350,6 +350,12 @@ export namespace DefaultTheme { */ searchOptions?: MiniSearchOptions['searchOptions'] } + /** + * exclude content from search results + */ + exclude?: ( + pageData: Pick + ) => boolean } // algolia -------------------------------------------------------------------