fix(search): respect per-locale markdown options (#5351)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
pull/5278/merge
Divyansh Singh 1 month ago committed by GitHub
parent 0adb886745
commit 27c4a1a2a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,7 +1,9 @@
import MiniSearch from 'minisearch'
import { resolveConfig } from 'node/config'
import { disposeMdItInstance } from 'node/markdown/markdown'
import { createMarkdownToVueRenderFn } from 'node/markdownToVue'
import { localSearchPlugin } from 'node/plugins/localSearchPlugin'
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
@ -12,6 +14,9 @@ describe('node/plugins/localSearchPlugin', () => {
beforeEach(() => {
nodeEnv = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
// createMarkdownRenderer is a module-level singleton keyed on nothing —
// reset it so each test's config actually reaches the renderer
disposeMdItInstance()
})
afterEach(async () => {
@ -102,6 +107,83 @@ describe('node/plugins/localSearchPlugin', () => {
expect(zhIndex.search('rootonlytoken')).toEqual([])
})
test('applies per-locale markdown options (#5350)', async () => {
root = await mkdtemp(path.join(tmpdir(), 'vitepress-local-search-'))
const configDir = path.join(root, '.vitepress')
await mkdir(configDir)
await mkdir(path.join(root, 'zh'))
const content = '::: tip\ntipcontent\n:::\n'
await writeFile(path.join(root, 'index.md'), `# Home\n\n${content}`)
await writeFile(
path.join(root, 'zh', 'index.md'),
`# Chinese home\n\n${content}`
)
await writeFile(
path.join(configDir, 'config.ts'),
[
'export default {',
' locales: {',
" root: { label: 'English', lang: 'en' },",
' zh: {',
" label: 'Chinese',",
" lang: 'zh',",
" markdown: { container: { tipLabel: 'zhtiplabel' } }",
' }',
' },',
' themeConfig: {',
" search: { provider: 'local' }",
' }',
'}'
].join('\n')
)
const siteConfig = await resolveConfig(root, 'build', 'production')
const plugin = await localSearchPlugin(siteConfig)
// configResolved hooks run concurrently, so the search plugin may be the
// one that creates the shared markdown renderer — page renders must still
// pick up per-locale options from it
await (plugin.configResolved as any)?.call(
{},
{ publicDir: siteConfig.publicDir }
)
const render = await createMarkdownToVueRenderFn(
siteConfig.srcDir,
siteConfig.markdown ?? {},
siteConfig.site.base,
false,
false,
siteConfig
)
const rootFile = path.join(root, 'index.md')
const zhFile = path.join(root, 'zh', 'index.md')
const rootPage = await render(await readFile(rootFile, 'utf-8'), rootFile)
const zhPage = await render(await readFile(zhFile, 'utf-8'), zhFile)
expect(rootPage.vueSrc).toContain('TIP')
expect(zhPage.vueSrc).toContain('zhtiplabel')
// the indexed text must use the localized labels too
await (plugin.load as any)?.handler.call({}, '/@localSearchIndex')
const rootIndex = loadIndex(
(await (plugin.load as any)?.handler.call(
{},
'/@localSearchIndexroot'
)) as string
)
const zhIndex = loadIndex(
(await (plugin.load as any)?.handler.call(
{},
'/@localSearchIndexzh'
)) as string
)
expect(zhIndex.search('zhtiplabel')).toHaveLength(1)
expect(rootIndex.search('zhtiplabel')).toEqual([])
expect(rootIndex.search('tip')).toHaveLength(1)
})
test('warns and skips pages that fail to render', async () => {
root = await mkdtemp(path.join(tmpdir(), 'vitepress-local-search-'))
const configDir = path.join(root, '.vitepress')

@ -6,7 +6,10 @@ import c from 'picocolors'
import type { Plugin, ViteDevServer } from 'vite'
import type { SiteConfig } from '../config'
import type { DefaultTheme } from '../defaultTheme'
import { createMarkdownRenderer } from '../markdown/markdown'
import {
createMarkdownRenderer,
mergeMarkdownLocales
} from '../markdown/markdown'
import { getLocaleForPath, slash, type MarkdownEnv } from '../shared'
import { readTextFile } from '../utils/fs'
@ -51,10 +54,15 @@ export async function localSearchPlugin(
const options = siteConfig.site.themeConfig.search.options || {}
async function render(file: string) {
async function render(file: string, localeIndex: string) {
const { srcDir, cleanUrls = false } = siteConfig
const relativePath = slash(path.relative(srcDir, file))
const env: MarkdownEnv = { path: file, relativePath, cleanUrls }
const env: MarkdownEnv = {
path: file,
relativePath,
cleanUrls,
localeIndex
}
const raw = await readTextFile(file).catch((e) => {
if (e.code === 'ENOENT') {
debug(`File not found: ${file}`)
@ -135,7 +143,7 @@ export async function localSearchPlugin(
// retrieve file and split into "sections"
let html: string
try {
html = await render(file)
html = await render(file, locale)
} catch (e) {
// in dev this runs unawaited, so a page that fails to render must not
// reject and take the server down with it — the page reports the error
@ -178,9 +186,12 @@ export async function localSearchPlugin(
name: 'vitepress:local-search',
async configResolved(config) {
// fold in `locales.*.markdown` like markdownToVue does — the renderer
// is a singleton, so whichever configResolved hook creates it first
// must pass the complete options (#5350)
md = await createMarkdownRenderer(
siteConfig.srcDir,
siteConfig.markdown,
mergeMarkdownLocales(siteConfig.markdown, siteConfig.site.locales),
siteConfig.site.base,
siteConfig.logger,
config.publicDir

Loading…
Cancel
Save