ottomated 2 days ago committed by GitHub
commit b64bc7ceb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: hydrate boundaries inside async components correctly

@ -28,7 +28,8 @@ import {
hydrating, hydrating,
next, next,
skip_nodes, skip_nodes,
set_hydrate_node set_hydrate_node,
set_hydrating
} from '../hydration.js'; } from '../hydration.js';
import { queue_micro_task } from '../task.js'; import { queue_micro_task } from '../task.js';
import * as e from '../../errors.js'; import * as e from '../../errors.js';
@ -208,7 +209,14 @@ export class Boundary {
this.#main_effect = this.#run(() => { this.#main_effect = this.#run(() => {
Batch.ensure(); Batch.ensure();
return branch(() => this.#children(anchor)); return branch(() => {
// We've already hydrated the pending content. We stop hydrating
// here so the resolved content is rendered on top of it.
if (hydrating) {
set_hydrating(false);
}
return this.#children(anchor);
});
}); });
if (this.#pending_count > 0) { if (this.#pending_count > 0) {

@ -0,0 +1,11 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async: true,
async test(assert, target) {
assert.htmlEqual(target.innerHTML, 'component: loaded, boundary: loading');
await tick();
assert.htmlEqual(target.innerHTML, 'component: loaded, boundary: loaded');
}
});

@ -0,0 +1,12 @@
<script>
await Promise.resolve();
</script>
component: loaded, boundary:
<svelte:boundary>
loaded
{#snippet pending()}
loading
{/snippet}
</svelte:boundary>

@ -10,8 +10,10 @@ import { createClassComponent } from 'svelte/legacy';
import { render } from 'svelte/server'; import { render } from 'svelte/server';
import type { CompileOptions } from '#compiler'; import type { CompileOptions } from '#compiler';
import { flushSync } from 'svelte'; import { flushSync } from 'svelte';
import type { RenderOutput, SyncRenderOutput } from '#server';
interface HydrationTest extends BaseTest { interface HydrationTest extends BaseTest {
async?: boolean;
load_compiled?: boolean; load_compiled?: boolean;
server_props?: Record<string, any>; server_props?: Record<string, any>;
id_prefix?: string; id_prefix?: string;
@ -43,6 +45,11 @@ function read(path: string): string | void {
} }
const { test, run } = suite<HydrationTest>(async (config, cwd) => { const { test, run } = suite<HydrationTest>(async (config, cwd) => {
if (config.async) {
config.compileOptions ??= {};
config.compileOptions.experimental ??= {};
config.compileOptions.experimental.async = true;
}
if (!config.load_compiled) { if (!config.load_compiled) {
await compile_directory(cwd, 'client', { await compile_directory(cwd, 'client', {
accessors: true, accessors: true,
@ -56,16 +63,20 @@ const { test, run } = suite<HydrationTest>(async (config, cwd) => {
const target = window.document.body; const target = window.document.body;
const head = window.document.head; const head = window.document.head;
const rendered = render((await import(`${cwd}/_output/server/main.svelte.js`)).default, { let rendered: RenderOutput | SyncRenderOutput = render(
props: config.server_props ?? config.props ?? {}, (await import(`${cwd}/_output/server/main.svelte.js`)).default,
idPrefix: config?.id_prefix {
}); props: config.server_props ?? config.props ?? {},
idPrefix: config?.id_prefix
}
);
if (config.async) rendered = await rendered;
const override = read(`${cwd}/_override.html`); const override = read(`${cwd}/_override.html`);
const override_head = read(`${cwd}/_override_head.html`); const override_head = read(`${cwd}/_override_head.html`);
fs.writeFileSync(`${cwd}/_output/body.html`, rendered.html + '\n'); fs.writeFileSync(`${cwd}/_output/body.html`, rendered.body + '\n');
target.innerHTML = override ?? rendered.html; target.innerHTML = override ?? rendered.body;
if (rendered.head) { if (rendered.head) {
fs.writeFileSync(`${cwd}/_output/head.html`, rendered.head + '\n'); fs.writeFileSync(`${cwd}/_output/head.html`, rendered.head + '\n');
@ -134,7 +145,7 @@ const { test, run } = suite<HydrationTest>(async (config, cwd) => {
const normalize = (string: string) => const normalize = (string: string) =>
string.trim().replaceAll('\r\n', '\n').replaceAll('/>', '>'); string.trim().replaceAll('\r\n', '\n').replaceAll('/>', '>');
const expected = read(`${cwd}/_expected.html`) ?? rendered.html; const expected = read(`${cwd}/_expected.html`) ?? rendered.body;
assert.equal(normalize(target.innerHTML), normalize(expected)); assert.equal(normalize(target.innerHTML), normalize(expected));
if (rendered.head) { if (rendered.head) {

Loading…
Cancel
Save