Skip to content

Commit a8fc627

Browse files
Merge remote-tracking branch 'origin' into adjust-boundary-error-message
2 parents f98aa72 + 18dd456 commit a8fc627

File tree

3 files changed

+52
-78
lines changed

3 files changed

+52
-78
lines changed

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

Lines changed: 46 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,7 @@ export class Boundary {
5858
/** @type {Boundary | null} */
5959
parent;
6060

61-
/**
62-
* Whether this boundary is inside a boundary (including this one) that's showing a pending snippet.
63-
* @type {boolean}
64-
*/
65-
get pending() {
66-
if (this.has_pending_snippet()) {
67-
return this.#pending;
68-
}
69-
70-
// intentionally not throwing here, as the answer to "am I in a pending snippet" is false when
71-
// there's no pending snippet at all
72-
return this.parent?.pending ?? false;
73-
}
74-
75-
set pending(value) {
76-
if (this.has_pending_snippet()) {
77-
this.#pending = value;
78-
} else if (this.parent) {
79-
this.parent.pending = value;
80-
} else if (value) {
81-
e.await_outside_boundary();
82-
}
83-
// if we're trying to set it to `false` and yeeting that into the void, it's fine
84-
}
61+
#pending = false;
8562

8663
/** @type {TemplateNode} */
8764
#anchor;
@@ -110,28 +87,9 @@ export class Boundary {
11087
/** @type {DocumentFragment | null} */
11188
#offscreen_fragment = null;
11289

113-
/**
114-
* Whether this boundary is inside a boundary (including this one) that's showing a pending snippet.
115-
* Derived from {@link props.pending} and {@link cascading_pending_count}.
116-
*/
117-
#pending = false;
118-
119-
/**
120-
* The number of pending async deriveds/expressions within this boundary, not counting any parent or child boundaries.
121-
* This controls `$effect.pending` for this boundary.
122-
*
123-
* Don't ever set this directly; use {@link update_pending_count} instead.
124-
*/
90+
#local_pending_count = 0;
12591
#pending_count = 0;
12692

127-
/**
128-
* Like {@link #pending_count}, but treats boundaries with no `pending` snippet as porous.
129-
* This controls the pending snippet for this boundary.
130-
*
131-
* Don't ever set this directly; use {@link update_pending_count} instead.
132-
*/
133-
#cascading_pending_count = 0;
134-
13593
#is_creating_fallback = false;
13694
/** @type {boolean} */
13795
#server_rendered_pending = false;
@@ -147,12 +105,12 @@ export class Boundary {
147105

148106
#effect_pending_update = () => {
149107
if (this.#effect_pending) {
150-
internal_set(this.#effect_pending, this.#pending_count);
108+
internal_set(this.#effect_pending, this.#local_pending_count);
151109
}
152110
};
153111

154112
#effect_pending_subscriber = createSubscriber(() => {
155-
this.#effect_pending = source(this.#pending_count);
113+
this.#effect_pending = source(this.#local_pending_count);
156114

157115
if (DEV) {
158116
tag(this.#effect_pending, '$effect.pending()');
@@ -179,7 +137,7 @@ export class Boundary {
179137

180138
this.parent = /** @type {Effect} */ (active_effect).b;
181139

182-
this.pending = !!this.#props.pending;
140+
this.#pending = !!this.#props.pending;
183141

184142
this.#effect = block(() => {
185143
/** @type {Effect} */ (active_effect).b = this;
@@ -201,10 +159,10 @@ export class Boundary {
201159
this.error(error);
202160
}
203161

204-
if (this.#cascading_pending_count > 0) {
162+
if (this.#pending_count > 0) {
205163
this.#show_pending_snippet();
206164
} else {
207-
this.pending = false;
165+
this.#pending = false;
208166
}
209167
}
210168
}, flags);
@@ -235,7 +193,7 @@ export class Boundary {
235193

236194
// Since server rendered resolved content, we never show pending state
237195
// Even if client-side async operations are still running, the content is already displayed
238-
this.pending = false;
196+
this.#pending = false;
239197
}
240198

241199
#hydrate_pending_content() {
@@ -255,7 +213,7 @@ export class Boundary {
255213
return branch(() => this.#children(this.#anchor));
256214
});
257215

258-
if (this.#cascading_pending_count > 0) {
216+
if (this.#pending_count > 0) {
259217
this.#show_pending_snippet();
260218
} else {
261219
pause_effect(/** @type {Effect} */ (this.#pending_effect), () => {
@@ -267,6 +225,14 @@ export class Boundary {
267225
});
268226
}
269227

228+
/**
229+
* Returns `true` if the effect exists inside a boundary whose pending snippet is shown
230+
* @returns {boolean}
231+
*/
232+
is_pending() {
233+
return this.#pending || (!!this.parent && this.parent.is_pending());
234+
}
235+
270236
has_pending_snippet() {
271237
return !!this.#props.pending;
272238
}
@@ -308,12 +274,25 @@ export class Boundary {
308274
}
309275
}
310276

311-
/** @param {number} d */
312-
#update_cascading_pending_count(d) {
313-
this.#cascading_pending_count = Math.max(this.#cascading_pending_count + d, 0);
277+
/**
278+
* Updates the pending count associated with the currently visible pending snippet,
279+
* if any, such that we can replace the snippet with content once work is done
280+
* @param {1 | -1} d
281+
*/
282+
#update_pending_count(d) {
283+
if (!this.has_pending_snippet()) {
284+
if (this.parent) {
285+
this.parent.#update_pending_count(d);
286+
return;
287+
}
314288

315-
if (this.#cascading_pending_count === 0) {
316-
this.pending = false;
289+
e.await_outside_boundary();
290+
}
291+
292+
this.#pending_count += d;
293+
294+
if (this.#pending_count === 0) {
295+
this.#pending = false;
317296

318297
if (this.#pending_effect) {
319298
pause_effect(this.#pending_effect, () => {
@@ -329,21 +308,15 @@ export class Boundary {
329308
}
330309

331310
/**
332-
* @param {number} d
333-
* @param {boolean} safe
334-
* @param {boolean} first
311+
* Update the source that powers `$effect.pending()` inside this boundary,
312+
* and controls when the current `pending` snippet (if any) is removed.
313+
* Do not call from inside the class
314+
* @param {1 | -1} d
335315
*/
336-
update_pending_count(d, safe = false, first = true) {
337-
if (first) {
338-
this.#pending_count = Math.max(this.#pending_count + d, 0);
339-
}
340-
341-
if (this.has_pending_snippet()) {
342-
this.#update_cascading_pending_count(d);
343-
} else if (this.parent) {
344-
this.parent.update_pending_count(d, safe, false);
345-
}
316+
update_pending_count(d) {
317+
this.#update_pending_count(d);
346318

319+
this.#local_pending_count += d;
347320
effect_pending_updates.add(this.#effect_pending_update);
348321
}
349322

@@ -396,9 +369,7 @@ export class Boundary {
396369
// If the failure happened while flushing effects, current_batch can be null
397370
Batch.ensure();
398371

399-
// this ensures we modify the cascading_pending_count of the correct parent
400-
// by the number we're decreasing this boundary by
401-
this.update_pending_count(-this.#pending_count, true);
372+
this.#local_pending_count = 0;
402373

403374
if (this.#failed_effect !== null) {
404375
pause_effect(this.#failed_effect, () => {
@@ -408,17 +379,17 @@ export class Boundary {
408379

409380
// we intentionally do not try to find the nearest pending boundary. If this boundary has one, we'll render it on reset
410381
// but it would be really weird to show the parent's boundary on a child reset.
411-
this.pending = this.has_pending_snippet();
382+
this.#pending = this.has_pending_snippet();
412383

413384
this.#main_effect = this.#run(() => {
414385
this.#is_creating_fallback = false;
415386
return branch(() => this.#children(this.#anchor));
416387
});
417388

418-
if (this.#cascading_pending_count > 0) {
389+
if (this.#pending_count > 0) {
419390
this.#show_pending_snippet();
420391
} else {
421-
this.pending = false;
392+
this.#pending = false;
422393
}
423394
};
424395

packages/svelte/src/internal/client/reactivity/batch.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,10 @@ export class Batch {
297297
this.#render_effects.push(effect);
298298
} else if ((flags & CLEAN) === 0) {
299299
if ((flags & ASYNC) !== 0) {
300-
var effects = effect.b?.pending ? this.#boundary_async_effects : this.#async_effects;
300+
var effects = effect.b?.is_pending()
301+
? this.#boundary_async_effects
302+
: this.#async_effects;
303+
301304
effects.push(effect);
302305
} else if (is_dirty(effect)) {
303306
if ((effect.f & BLOCK_EFFECT) !== 0) this.#block_effects.push(effect);
@@ -669,7 +672,7 @@ export function schedule_effect(signal) {
669672
export function suspend() {
670673
var boundary = get_boundary();
671674
var batch = /** @type {Batch} */ (current_batch);
672-
var pending = boundary.pending;
675+
var pending = boundary.is_pending();
673676

674677
boundary.update_pending_count(1);
675678
if (!pending) batch.increment();

packages/svelte/src/internal/client/reactivity/deriveds.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export function async_derived(fn, location) {
135135
prev = promise;
136136

137137
var batch = /** @type {Batch} */ (current_batch);
138-
var pending = boundary.pending;
138+
var pending = boundary.is_pending();
139139

140140
if (should_suspend) {
141141
boundary.update_pending_count(1);

0 commit comments

Comments
 (0)