Merge remote-tracking branch 'origin' into adjust-boundary-error-message

pull/16762/head
S. Elliott Johnson 1 month ago
commit a8fc627279

@ -58,30 +58,7 @@ export class Boundary {
/** @type {Boundary | null} */ /** @type {Boundary | null} */
parent; parent;
/** #pending = false;
* Whether this boundary is inside a boundary (including this one) that's showing a pending snippet.
* @type {boolean}
*/
get pending() {
if (this.has_pending_snippet()) {
return this.#pending;
}
// intentionally not throwing here, as the answer to "am I in a pending snippet" is false when
// there's no pending snippet at all
return this.parent?.pending ?? false;
}
set pending(value) {
if (this.has_pending_snippet()) {
this.#pending = value;
} else if (this.parent) {
this.parent.pending = value;
} else if (value) {
e.await_outside_boundary();
}
// if we're trying to set it to `false` and yeeting that into the void, it's fine
}
/** @type {TemplateNode} */ /** @type {TemplateNode} */
#anchor; #anchor;
@ -110,28 +87,9 @@ export class Boundary {
/** @type {DocumentFragment | null} */ /** @type {DocumentFragment | null} */
#offscreen_fragment = null; #offscreen_fragment = null;
/** #local_pending_count = 0;
* Whether this boundary is inside a boundary (including this one) that's showing a pending snippet.
* Derived from {@link props.pending} and {@link cascading_pending_count}.
*/
#pending = false;
/**
* The number of pending async deriveds/expressions within this boundary, not counting any parent or child boundaries.
* This controls `$effect.pending` for this boundary.
*
* Don't ever set this directly; use {@link update_pending_count} instead.
*/
#pending_count = 0; #pending_count = 0;
/**
* Like {@link #pending_count}, but treats boundaries with no `pending` snippet as porous.
* This controls the pending snippet for this boundary.
*
* Don't ever set this directly; use {@link update_pending_count} instead.
*/
#cascading_pending_count = 0;
#is_creating_fallback = false; #is_creating_fallback = false;
/** @type {boolean} */ /** @type {boolean} */
#server_rendered_pending = false; #server_rendered_pending = false;
@ -147,12 +105,12 @@ export class Boundary {
#effect_pending_update = () => { #effect_pending_update = () => {
if (this.#effect_pending) { if (this.#effect_pending) {
internal_set(this.#effect_pending, this.#pending_count); internal_set(this.#effect_pending, this.#local_pending_count);
} }
}; };
#effect_pending_subscriber = createSubscriber(() => { #effect_pending_subscriber = createSubscriber(() => {
this.#effect_pending = source(this.#pending_count); this.#effect_pending = source(this.#local_pending_count);
if (DEV) { if (DEV) {
tag(this.#effect_pending, '$effect.pending()'); tag(this.#effect_pending, '$effect.pending()');
@ -179,7 +137,7 @@ export class Boundary {
this.parent = /** @type {Effect} */ (active_effect).b; this.parent = /** @type {Effect} */ (active_effect).b;
this.pending = !!this.#props.pending; this.#pending = !!this.#props.pending;
this.#effect = block(() => { this.#effect = block(() => {
/** @type {Effect} */ (active_effect).b = this; /** @type {Effect} */ (active_effect).b = this;
@ -201,10 +159,10 @@ export class Boundary {
this.error(error); this.error(error);
} }
if (this.#cascading_pending_count > 0) { if (this.#pending_count > 0) {
this.#show_pending_snippet(); this.#show_pending_snippet();
} else { } else {
this.pending = false; this.#pending = false;
} }
} }
}, flags); }, flags);
@ -235,7 +193,7 @@ export class Boundary {
// Since server rendered resolved content, we never show pending state // Since server rendered resolved content, we never show pending state
// Even if client-side async operations are still running, the content is already displayed // Even if client-side async operations are still running, the content is already displayed
this.pending = false; this.#pending = false;
} }
#hydrate_pending_content() { #hydrate_pending_content() {
@ -255,7 +213,7 @@ export class Boundary {
return branch(() => this.#children(this.#anchor)); return branch(() => this.#children(this.#anchor));
}); });
if (this.#cascading_pending_count > 0) { if (this.#pending_count > 0) {
this.#show_pending_snippet(); this.#show_pending_snippet();
} else { } else {
pause_effect(/** @type {Effect} */ (this.#pending_effect), () => { pause_effect(/** @type {Effect} */ (this.#pending_effect), () => {
@ -267,6 +225,14 @@ export class Boundary {
}); });
} }
/**
* Returns `true` if the effect exists inside a boundary whose pending snippet is shown
* @returns {boolean}
*/
is_pending() {
return this.#pending || (!!this.parent && this.parent.is_pending());
}
has_pending_snippet() { has_pending_snippet() {
return !!this.#props.pending; return !!this.#props.pending;
} }
@ -308,12 +274,25 @@ export class Boundary {
} }
} }
/** @param {number} d */ /**
#update_cascading_pending_count(d) { * Updates the pending count associated with the currently visible pending snippet,
this.#cascading_pending_count = Math.max(this.#cascading_pending_count + d, 0); * if any, such that we can replace the snippet with content once work is done
* @param {1 | -1} d
*/
#update_pending_count(d) {
if (!this.has_pending_snippet()) {
if (this.parent) {
this.parent.#update_pending_count(d);
return;
}
if (this.#cascading_pending_count === 0) { e.await_outside_boundary();
this.pending = false; }
this.#pending_count += d;
if (this.#pending_count === 0) {
this.#pending = false;
if (this.#pending_effect) { if (this.#pending_effect) {
pause_effect(this.#pending_effect, () => { pause_effect(this.#pending_effect, () => {
@ -329,21 +308,15 @@ export class Boundary {
} }
/** /**
* @param {number} d * Update the source that powers `$effect.pending()` inside this boundary,
* @param {boolean} safe * and controls when the current `pending` snippet (if any) is removed.
* @param {boolean} first * Do not call from inside the class
* @param {1 | -1} d
*/ */
update_pending_count(d, safe = false, first = true) { update_pending_count(d) {
if (first) { this.#update_pending_count(d);
this.#pending_count = Math.max(this.#pending_count + d, 0);
}
if (this.has_pending_snippet()) {
this.#update_cascading_pending_count(d);
} else if (this.parent) {
this.parent.update_pending_count(d, safe, false);
}
this.#local_pending_count += d;
effect_pending_updates.add(this.#effect_pending_update); effect_pending_updates.add(this.#effect_pending_update);
} }
@ -396,9 +369,7 @@ export class Boundary {
// If the failure happened while flushing effects, current_batch can be null // If the failure happened while flushing effects, current_batch can be null
Batch.ensure(); Batch.ensure();
// this ensures we modify the cascading_pending_count of the correct parent this.#local_pending_count = 0;
// by the number we're decreasing this boundary by
this.update_pending_count(-this.#pending_count, true);
if (this.#failed_effect !== null) { if (this.#failed_effect !== null) {
pause_effect(this.#failed_effect, () => { pause_effect(this.#failed_effect, () => {
@ -408,17 +379,17 @@ export class Boundary {
// we intentionally do not try to find the nearest pending boundary. If this boundary has one, we'll render it on reset // we intentionally do not try to find the nearest pending boundary. If this boundary has one, we'll render it on reset
// but it would be really weird to show the parent's boundary on a child reset. // but it would be really weird to show the parent's boundary on a child reset.
this.pending = this.has_pending_snippet(); this.#pending = this.has_pending_snippet();
this.#main_effect = this.#run(() => { this.#main_effect = this.#run(() => {
this.#is_creating_fallback = false; this.#is_creating_fallback = false;
return branch(() => this.#children(this.#anchor)); return branch(() => this.#children(this.#anchor));
}); });
if (this.#cascading_pending_count > 0) { if (this.#pending_count > 0) {
this.#show_pending_snippet(); this.#show_pending_snippet();
} else { } else {
this.pending = false; this.#pending = false;
} }
}; };

@ -297,7 +297,10 @@ export class Batch {
this.#render_effects.push(effect); this.#render_effects.push(effect);
} else if ((flags & CLEAN) === 0) { } else if ((flags & CLEAN) === 0) {
if ((flags & ASYNC) !== 0) { if ((flags & ASYNC) !== 0) {
var effects = effect.b?.pending ? this.#boundary_async_effects : this.#async_effects; var effects = effect.b?.is_pending()
? this.#boundary_async_effects
: this.#async_effects;
effects.push(effect); effects.push(effect);
} else if (is_dirty(effect)) { } else if (is_dirty(effect)) {
if ((effect.f & BLOCK_EFFECT) !== 0) this.#block_effects.push(effect); if ((effect.f & BLOCK_EFFECT) !== 0) this.#block_effects.push(effect);
@ -669,7 +672,7 @@ export function schedule_effect(signal) {
export function suspend() { export function suspend() {
var boundary = get_boundary(); var boundary = get_boundary();
var batch = /** @type {Batch} */ (current_batch); var batch = /** @type {Batch} */ (current_batch);
var pending = boundary.pending; var pending = boundary.is_pending();
boundary.update_pending_count(1); boundary.update_pending_count(1);
if (!pending) batch.increment(); if (!pending) batch.increment();

@ -135,7 +135,7 @@ export function async_derived(fn, location) {
prev = promise; prev = promise;
var batch = /** @type {Batch} */ (current_batch); var batch = /** @type {Batch} */ (current_batch);
var pending = boundary.pending; var pending = boundary.is_pending();
if (should_suspend) { if (should_suspend) {
boundary.update_pending_count(1); boundary.update_pending_count(1);

Loading…
Cancel
Save