fix(build): respect cleanUrls in content loader render

createContentLoader called md.renderAsync without a MarkdownEnv, so
the internal link plugin fell back to defaults and always emitted
.html links in rendered content and excerpts even with cleanUrls
enabled, and plugins reading env.path/relativePath broke. Build the
same env as a normal page render and forward it to both calls.

fixes #4331
closes #5299

Co-authored-by: TowyTowy <towy@airreps.link>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/5014/head
Divyansh Singh 7 days ago
parent 1534a67d86
commit 027f0461e0

@ -0,0 +1,51 @@
import { resolveConfig } from 'node/config'
import { createContentLoader } from 'node/contentLoader'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
describe('node/contentLoader', () => {
let root: string | undefined
afterEach(async () => {
if (root) {
await rm(root, { recursive: true, force: true })
root = undefined
}
delete (global as any).VITEPRESS_CONFIG
})
async function setup(cleanUrls: boolean) {
root = await mkdtemp(path.join(tmpdir(), 'vitepress-content-loader-'))
await writeFile(
path.join(root, 'index.md'),
'# Home\n\n[link](./other.md)\n'
)
await writeFile(path.join(root, 'other.md'), '# Other\n')
const siteConfig = await resolveConfig(root, 'build', 'production')
siteConfig.cleanUrls = cleanUrls
;(global as any).VITEPRESS_CONFIG = siteConfig
}
test('rendered internal links get .html when cleanUrls is false', async () => {
await setup(false)
const data = await createContentLoader('index.md', {
render: true
}).load()
expect(data[0].html).toContain('href="./other.html"')
})
test('rendered internal links are clean when cleanUrls is true', async () => {
await setup(true)
const data = await createContentLoader('index.md', {
render: true
}).load()
expect(data[0].html).toContain('href="./other"')
expect(data[0].html).not.toContain('./other.html')
})
})

@ -8,7 +8,7 @@ import {
createMarkdownRenderer,
mergeMarkdownLocales
} from './markdown/markdown'
import type { Awaitable } from './shared'
import type { Awaitable, MarkdownEnv } from './shared'
import { glob, normalizeGlob, type GlobOptions } from './utils/glob'
export interface ContentOptions<T = ContentData[]> {
@ -132,15 +132,29 @@ export function createContentLoader<T = ContentData[]>(
)
const relFile = normalizePath(path.relative(config.srcDir, file))
const relativePath = config.rewrites.map[relFile] || relFile
const url =
'/' +
(config.rewrites.map[relFile] || relFile)
relativePath
.replace(/(^|\/)index\.md$/, '$1')
.replace(/\.md$/, config.cleanUrls ? '' : '.html')
const html = options.render ? await md.renderAsync(src) : undefined
// pass a markdown env so plugins (e.g. the internal link plugin)
// resolve links, `cleanUrls` and paths the same way as during a
// normal page render instead of falling back to defaults
const env: MarkdownEnv = {
path: file,
relativePath,
cleanUrls: !!config.cleanUrls,
realPath: file
}
const html = options.render
? await md.renderAsync(src, env)
: undefined
const renderedExcerpt = renderExcerpt
? excerpt && (await md.renderAsync(excerpt))
? excerpt && (await md.renderAsync(excerpt, env))
: undefined
const data: ContentData = {

Loading…
Cancel
Save