diff --git a/e2e/solid-start/basic-test-suite/src/streaming.spec.ts b/e2e/solid-start/basic-test-suite/src/streaming.spec.ts index 15f60b7deb..0457d94123 100644 --- a/e2e/solid-start/basic-test-suite/src/streaming.spec.ts +++ b/e2e/solid-start/basic-test-suite/src/streaming.spec.ts @@ -27,6 +27,22 @@ test('Directly visiting the deferred route', async ({ page }) => { ) }) +test('deferred route streams boundaries independently', async ({ page }) => { + await page.goto('/deferred', { waitUntil: 'commit' }) + + await expect(page.getByTestId('regular-person')).toContainText('John Doe') + await expect(page.getByText('Loading person...')).toBeVisible() + await expect(page.getByText('Loading stuff...')).toBeVisible() + + await expect(page.getByTestId('deferred-person')).toContainText( + 'Tanner Linsley', + ) + await expect(page.getByText('Loading stuff...')).toBeVisible() + await expect(page.getByTestId('deferred-stuff')).toContainText( + 'Hello deferred!', + ) +}) + test('streaming loader data', async ({ page }) => { await page.goto('/stream') diff --git a/packages/router-core/src/ssr/transformStreamWithRouter.ts b/packages/router-core/src/ssr/transformStreamWithRouter.ts index a96f2fa63d..a596077899 100644 --- a/packages/router-core/src/ssr/transformStreamWithRouter.ts +++ b/packages/router-core/src/ssr/transformStreamWithRouter.ts @@ -227,7 +227,9 @@ export function transformStreamWithRouter( // between-chunk text buffer; keep bounded to avoid unbounded memory let leftover = '' - // captured closing tags from onward + // Captured closing tags that must stay after router-injected scripts. + // Some renderers, like Solid, continue streaming boundary chunks after + // ; those chunks should still pass through before these tags. let pendingClosingTags = '' // conservative cap: enough to hold any partial closing tag + a bit @@ -404,9 +406,11 @@ export function transformStreamWithRouter( } } - // If we already saw , everything else is part of tail; buffer it. + // If we already saw , keep streaming app chunks before the + // captured closing tags instead of buffering them until render end. if (pendingClosingTags) { - pendingClosingTags += chunkString + flushPendingRouterHtml() + safeEnqueue(chunkString) leftover = '' continue } @@ -419,9 +423,11 @@ export function transformStreamWithRouter( htmlEndIndex !== -1 && bodyEndIndex < htmlEndIndex ) { - pendingClosingTags = chunkString.slice(bodyEndIndex) + const htmlEndTagEnd = htmlEndIndex + HTML_END_TAG.length + pendingClosingTags = chunkString.slice(bodyEndIndex, htmlEndTagEnd) safeEnqueue(chunkString.slice(0, bodyEndIndex)) flushPendingRouterHtml() + safeEnqueue(chunkString.slice(htmlEndTagEnd)) leftover = '' continue }