mirror of https://github.com/sveltejs/svelte
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>teemingc-hydration-regression-test
parent
943cb84c53
commit
834d798208
@ -0,0 +1,13 @@
|
||||
<script>
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div>Resolving a promise here: {await Promise.resolve('ok')}</div>
|
||||
|
||||
<svelte:boundary>
|
||||
{@render children?.()}
|
||||
|
||||
{#snippet pending()}
|
||||
loading...
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
||||
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
let { data, children } = $props();
|
||||
|
||||
let { root, child } = $derived(data);
|
||||
|
||||
$effect(() => {
|
||||
console.log(root, child);
|
||||
});
|
||||
</script>
|
||||
|
||||
{@render children?.()}
|
||||
@ -0,0 +1 @@
|
||||
<div>this is the child</div>
|
||||
@ -0,0 +1,5 @@
|
||||
<script>
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
{@render children?.()}
|
||||
@ -0,0 +1,30 @@
|
||||
import { settled } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
// Regression test for https://github.com/sveltejs/kit/issues/17383
|
||||
export default test({
|
||||
mode: ['hydrate'],
|
||||
skip_initial_flushSync: true,
|
||||
|
||||
async test({ assert, target, component, mod, hydrate, logs }) {
|
||||
const tree = mod.create_tree();
|
||||
const { data, child } = tree;
|
||||
tree.data = {};
|
||||
tree.child = undefined;
|
||||
component.tree = tree;
|
||||
await Promise.resolve();
|
||||
|
||||
// Kit applies the loaded data to its existing root immediately before hydration.
|
||||
// Do not flush these writes: the async boundary must encounter the pending batch.
|
||||
tree.data = data;
|
||||
tree.child = child;
|
||||
hydrate();
|
||||
await settled();
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
'<div>Resolving a promise here: ok</div><div>this is the child</div>'
|
||||
);
|
||||
assert.deepEqual(logs, ['root', 'child']);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,94 @@
|
||||
<script module>
|
||||
import RootLayout from './RootLayout.svelte';
|
||||
import AsyncLayout from './AsyncLayout.svelte';
|
||||
import DataLayout from './DataLayout.svelte';
|
||||
import Page from './Page.svelte';
|
||||
import { RenderNode } from './state.svelte.js';
|
||||
|
||||
function onerror(error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const root_data = { root: 'root' };
|
||||
const async_data = { root: 'root' };
|
||||
const data_layout_data = { root: 'root', child: 'child' };
|
||||
const page_data = { root: 'root', child: 'child' };
|
||||
|
||||
/**
|
||||
* Builds a plain, non-reactive tree with the same shape as the one produced
|
||||
* by `create_tree`. Used for SSR, where no class writes should happen.
|
||||
*/
|
||||
function create_server_tree() {
|
||||
return {
|
||||
component: RootLayout,
|
||||
error: undefined,
|
||||
data: root_data,
|
||||
child: {
|
||||
component: AsyncLayout,
|
||||
error: undefined,
|
||||
data: async_data,
|
||||
child: {
|
||||
component: DataLayout,
|
||||
error: undefined,
|
||||
data: data_layout_data,
|
||||
child: {
|
||||
component: Page,
|
||||
error: undefined,
|
||||
data: page_data,
|
||||
child: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the reactive tree. The `data` and `child` writes happen here, i.e.
|
||||
* before hydration starts, rather than during component initialization.
|
||||
*/
|
||||
export function create_tree() {
|
||||
const root = new RenderNode(RootLayout);
|
||||
const async_layout = new RenderNode(AsyncLayout);
|
||||
const data_layout = new RenderNode(DataLayout);
|
||||
const page = new RenderNode(Page);
|
||||
|
||||
root.data = root_data;
|
||||
root.child = async_layout;
|
||||
|
||||
async_layout.data = async_data;
|
||||
async_layout.child = data_layout;
|
||||
|
||||
data_layout.data = data_layout_data;
|
||||
data_layout.child = page;
|
||||
|
||||
page.data = page_data;
|
||||
|
||||
return root;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
let { tree = create_server_tree() } = $props();
|
||||
</script>
|
||||
|
||||
{#snippet node(n, depth)}
|
||||
{const Component = $derived(n.component)}
|
||||
{const Error = $derived(n.error)}
|
||||
{const data = $derived(n.data)}
|
||||
|
||||
{#snippet failed(error)}
|
||||
<Error {error} />
|
||||
{/snippet}
|
||||
|
||||
<svelte:boundary onerror={Error ? onerror : undefined} failed={Error ? failed : undefined}>
|
||||
{#if n.child}
|
||||
<Component {data}>
|
||||
{@render node(n.child, depth + 1)}
|
||||
</Component>
|
||||
{:else}
|
||||
<Component {data} />
|
||||
{/if}
|
||||
</svelte:boundary>
|
||||
{/snippet}
|
||||
|
||||
{@render node(tree, 0)}
|
||||
@ -0,0 +1,21 @@
|
||||
export class RenderNode {
|
||||
/** @type {any} */
|
||||
component;
|
||||
|
||||
/** @type {any} */
|
||||
error;
|
||||
|
||||
data = $state.raw({});
|
||||
|
||||
/** @type {RenderNode | undefined} */
|
||||
child = $state.raw();
|
||||
|
||||
/**
|
||||
* @param {any} component
|
||||
* @param {any} [error]
|
||||
*/
|
||||
constructor(component, error) {
|
||||
this.component = component;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue