diff --git a/__tests__/unit/node/markdownToVue-cache.test.ts b/__tests__/unit/node/markdownToVue-cache.test.ts new file mode 100644 index 000000000..fc9920531 --- /dev/null +++ b/__tests__/unit/node/markdownToVue-cache.test.ts @@ -0,0 +1,70 @@ +import { mkdtemp, rm, writeFile } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import path from 'node:path' + +import { resolveConfig } from 'node/config' +import { disposeMdItInstance } from 'node/markdown/markdown' +import { clearCache, createMarkdownToVueRenderFn } from 'node/markdownToVue' + +describe('node/markdownToVue cache invalidation', () => { + let root: string + + beforeEach(async () => { + clearCache() + disposeMdItInstance() + root = await mkdtemp(path.join(tmpdir(), 'vitepress-md-cache-')) + }) + + afterEach(async () => { + clearCache() + disposeMdItInstance() + await rm(root, { recursive: true, force: true }) + }) + + test.each([ + ['include', ''], + ['snippet', '<<< ./shared.md{txt}'] + ])( + 'invalidates every source version when its %s changes', + async (_, importSrc) => { + const file = path.join(root, 'page.md') + const otherFile = path.join(root, 'other-page.md') + const sharedFile = path.join(root, 'shared.md') + const sources = ['First', 'Second', 'Third'].map( + (title) => `# ${title}\n\n${importSrc}\n` + ) + await writeFile(file, sources[0]) + await writeFile(otherFile, '# Other page') + await writeFile(sharedFile, 'old shared content') + + const config = await resolveConfig(root, 'serve', 'development') + const render = await createMarkdownToVueRenderFn( + config.srcDir, + { highlight: (code) => code }, + '/', + false, + false, + config + ) + + // Editing the page creates several cached versions of the same path. + const first = await render(sources[0], file) + expect(await render(sources[0], file)).toBe(first) + await render(sources[1], file) + const other = await render('# Other page', otherFile) + await render(sources[2], file) + + await writeFile(sharedFile, 'updated shared content') + // The include/snippet HMR handler invalidates each importing page by path. + clearCache('page.md') + + // Undoing a page edit must not revive a stale version of the dependency. + for (const src of [...sources].reverse()) { + const result = await render(src, file) + expect(result.vueSrc).toContain('updated shared content') + expect(result.vueSrc).not.toContain('old shared content') + } + expect(await render('# Other page', otherFile)).toBe(other) + } + ) +}) diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index e21e27a32..06ce309bd 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -59,7 +59,9 @@ export function clearCache(relativePath?: string) { return } - cache.find((_, key) => key.endsWith(`:${relativePath}`) && cache.delete(key)) + for (const key of cache.keys()) { + if (key.endsWith(`:${relativePath}`)) cache.delete(key) + } } function normalizeDriveLetter(file: string) {