Skip to content

Commit 2e1dd48

Browse files
fix: coordinate mount of snippets with await expressions (#17021)
* fix: coordinate mount of snippets with await expressions * try this * deduplicate --------- Co-authored-by: Simon H <[email protected]>
1 parent 523c85b commit 2e1dd48

File tree

5 files changed

+86
-2
lines changed

5 files changed

+86
-2
lines changed

.changeset/huge-poets-tickle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: coordinate mount of snippets with await expressions

packages/svelte/src/internal/client/dom/blocks/boundary.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { Batch, effect_pending_updates } from '../../reactivity/batch.js';
3838
import { internal_set, source } from '../../reactivity/sources.js';
3939
import { tag } from '../../dev/tracing.js';
4040
import { createSubscriber } from '../../../../reactivity/create-subscriber.js';
41+
import { create_text } from '../operations.js';
4142

4243
/**
4344
* @typedef {{
@@ -92,6 +93,9 @@ export class Boundary {
9293
/** @type {DocumentFragment | null} */
9394
#offscreen_fragment = null;
9495

96+
/** @type {TemplateNode | null} */
97+
#pending_anchor = null;
98+
9599
#local_pending_count = 0;
96100
#pending_count = 0;
97101

@@ -155,8 +159,10 @@ export class Boundary {
155159
this.#hydrate_resolved_content();
156160
}
157161
} else {
162+
var anchor = this.#get_anchor();
163+
158164
try {
159-
this.#main_effect = branch(() => children(this.#anchor));
165+
this.#main_effect = branch(() => children(anchor));
160166
} catch (error) {
161167
this.error(error);
162168
}
@@ -167,6 +173,10 @@ export class Boundary {
167173
this.#pending = false;
168174
}
169175
}
176+
177+
return () => {
178+
this.#pending_anchor?.remove();
179+
};
170180
}, flags);
171181

172182
if (hydrating) {
@@ -194,9 +204,11 @@ export class Boundary {
194204
this.#pending_effect = branch(() => pending(this.#anchor));
195205

196206
Batch.enqueue(() => {
207+
var anchor = this.#get_anchor();
208+
197209
this.#main_effect = this.#run(() => {
198210
Batch.ensure();
199-
return branch(() => this.#children(this.#anchor));
211+
return branch(() => this.#children(anchor));
200212
});
201213

202214
if (this.#pending_count > 0) {
@@ -211,6 +223,19 @@ export class Boundary {
211223
});
212224
}
213225

226+
#get_anchor() {
227+
var anchor = this.#anchor;
228+
229+
if (this.#pending) {
230+
this.#pending_anchor = create_text();
231+
this.#anchor.before(this.#pending_anchor);
232+
233+
anchor = this.#pending_anchor;
234+
}
235+
236+
return anchor;
237+
}
238+
214239
/**
215240
* Returns `true` if the effect exists inside a boundary whose pending snippet is shown
216241
* @returns {boolean}
@@ -252,6 +277,7 @@ export class Boundary {
252277

253278
if (this.#main_effect !== null) {
254279
this.#offscreen_fragment = document.createDocumentFragment();
280+
this.#offscreen_fragment.append(/** @type {TemplateNode} */ (this.#pending_anchor));
255281
move_effect(this.#main_effect, this.#offscreen_fragment);
256282
}
257283

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
let { children, push } = $props();
3+
let message = await push('hello from child');
4+
</script>
5+
6+
<p>message: {message}</p>
7+
{@render children()}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
async test({ assert, target }) {
6+
const [shift] = target.querySelectorAll('button');
7+
8+
shift.click();
9+
await tick();
10+
11+
assert.htmlEqual(target.innerHTML, `<button>shift</button><p>loading...</p>`);
12+
13+
shift.click();
14+
await tick();
15+
16+
assert.htmlEqual(
17+
target.innerHTML,
18+
`
19+
<button>shift</button>
20+
<p>message: hello from child</p>
21+
<p>hello from parent</p>
22+
`
23+
);
24+
}
25+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
const resolvers = [];
4+
function push(value) {
5+
const { promise, resolve } = Promise.withResolvers();
6+
resolvers.push(() => resolve(value));
7+
return promise;
8+
}
9+
</script>
10+
11+
<button onclick={() => resolvers.shift()?.()}>shift</button>
12+
13+
<svelte:boundary>
14+
<Child {push}>
15+
<p>{await push('hello from parent')}</p>
16+
</Child>
17+
18+
{#snippet pending()}
19+
<p>loading...</p>
20+
{/snippet}
21+
</svelte:boundary>

0 commit comments

Comments
 (0)