pull/16962/merge
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,
next,
skip_nodes,
set_hydrate_node
set_hydrate_node,
set_hydrating
} from '../hydration.js';
import { queue_micro_task } from '../task.js';
import * as e from '../../errors.js';
@ -208,7 +209,14 @@ export class Boundary {
this.#main_effect = this.#run(() => {
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) {

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

Loading…
Cancel
Save