From 73c955c7a1753e893f44f08c1984c629c7ae27ba Mon Sep 17 00:00:00 2001 From: Cosmin Popovici Date: Fri, 3 Jul 2026 14:35:52 +0300 Subject: [PATCH] fix(render): strip SSR markers before teleport DOM round-trip --- src/render/createRenderer.ts | 36 ++++++++++++++----------- src/tests/render/createRenderer.test.ts | 20 ++++++++++++++ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/render/createRenderer.ts b/src/render/createRenderer.ts index 9d89e0c6..8c38b7b2 100644 --- a/src/render/createRenderer.ts +++ b/src/render/createRenderer.ts @@ -511,6 +511,25 @@ export async function createRenderer( html = html.replace('', `${bodyTags}\n`) } + /** + * Strip Vue SSR fragment markers + teleport anchor comments. These + * are rendering hygiene, not transformer concerns — must run + * regardless of `useTransformers` state. Fragment markers contain + * `-->`, which would prematurely terminate MSO conditional + * comments downstream — including in the DOM round-trip below, + * whose parser would otherwise close `` and mangle the conditional on re-serialize. + */ + const stripSsrMarkers = (str: string) => str + .replaceAll('', '') + .replaceAll('', '') + .replaceAll('', '') + .replaceAll('', '') + .replaceAll('', '') + .replaceAll('', '') + + html = stripSsrMarkers(html) + // Inject SSR teleport content into their target elements const hasTeleports = ssrContext.teleports && Object.keys(ssrContext.teleports).length > 0 const hasFonts = (renderContext.fonts?.length ?? 0) > 0 @@ -525,7 +544,7 @@ export async function createRenderer( const prepend = rawTarget.endsWith(':start') const target = prepend ? rawTarget.slice(0, -6) : rawTarget - const targetChildren = parseDom(content) + const targetChildren = parseDom(stripSsrMarkers(content)) walk(dom, (node) => { const el = node as import('domhandler').Element @@ -566,21 +585,6 @@ export async function createRenderer( html = html.replace(/]*)>/, `${previewHtml}`) } - /** - * Strip Vue SSR fragment markers + teleport anchor comments. These - * are rendering hygiene, not transformer concerns — must run - * regardless of `useTransformers` state. Fragment markers contain - * `-->`, which would prematurely terminate MSO conditional - * comments downstream. - */ - html = html - .replaceAll('', '') - .replaceAll('', '') - .replaceAll('', '') - .replaceAll('', '') - .replaceAll('', '') - .replaceAll('', '') - return { html, doctype: renderContext.doctype, diff --git a/src/tests/render/createRenderer.test.ts b/src/tests/render/createRenderer.test.ts index 61a54e84..9de88ba0 100644 --- a/src/tests/render/createRenderer.test.ts +++ b/src/tests/render/createRenderer.test.ts @@ -49,6 +49,26 @@ describe('createRenderer', () => { expect(result.html).toContain('fonts.googleapis.com') }) + it('keeps MSO conditionals intact when Preheader triggers the teleport DOM round-trip', async () => { + const config = await resolveConfig({ root: tempDir }) + const result = await render( + ``, + config, + ) + expect(result.html).toContain('') + expect(result.html).toContain('') + }) + it('wraps a markdown code fence in a table with shiki highlighting', async () => { writeFileSync(join(tempDir, 'page.md'), '# Title\n\n```js\nconst x = 1\n```\n') const result = await render(join(tempDir, 'page.md'), { useTransformers: false })