fix: try finally pop render

svelte-custom-renderer
paoloricciuti 6 days ago
parent 17e37a51bc
commit 9b97b7fbf7

@ -277,45 +277,47 @@ export class Boundary {
queue_micro_task(() => {
var pop_renderer = push_renderer(this.#effect.r);
var fragment = (this.#offscreen_fragment = create_fragment());
var anchor = create_text();
var handled = false;
try {
var fragment = (this.#offscreen_fragment = create_fragment());
var anchor = create_text();
var handled = false;
append_child(fragment, anchor);
append_child(fragment, anchor);
this.#main_effect = this.#run(() => {
try {
return branch(() => this.#children(anchor));
} catch (error) {
this.#main_effect = this.#run(() => {
try {
this.error(error);
handled = true;
return branch(() => this.#children(anchor));
} catch (error) {
invoke_error_boundary(error, this.#effect.parent);
try {
this.error(error);
handled = true;
} catch (error) {
invoke_error_boundary(error, this.#effect.parent);
}
return null;
}
});
return null;
if (this.#main_effect === null) {
this.#offscreen_fragment = null;
if (handled) this.#resolve(/** @type {Batch} */ (current_batch));
return;
}
});
if (this.#main_effect === null) {
this.#offscreen_fragment = null;
if (handled) this.#resolve(/** @type {Batch} */ (current_batch));
return;
}
if (this.#pending_count === 0) {
insert_before(this.#anchor, fragment);
this.#offscreen_fragment = null;
if (this.#pending_count === 0) {
insert_before(this.#anchor, fragment);
this.#offscreen_fragment = null;
pause_effect(/** @type {Effect} */ (this.#pending_effect), () => {
this.#pending_effect = null;
});
pause_effect(/** @type {Effect} */ (this.#pending_effect), () => {
this.#pending_effect = null;
});
this.#resolve(/** @type {Batch} */ (current_batch));
this.#resolve(/** @type {Batch} */ (current_batch));
}
} finally {
pop_renderer?.();
}
pop_renderer?.();
});
}

@ -98,91 +98,93 @@ export class BranchManager {
var pop_renderer = push_renderer(this.#renderer);
var key = /** @type {Key} */ (this.#batches.get(batch));
try {
var key = /** @type {Key} */ (this.#batches.get(batch));
var onscreen = this.#onscreen.get(key);
var onscreen = this.#onscreen.get(key);
if (onscreen) {
// effect is already in the DOM — abort any current outro
resume_effect(onscreen);
this.#outroing.delete(key);
} else {
// effect is currently offscreen. put it in the DOM
var offscreen = this.#offscreen.get(key);
if (offscreen) {
// effect could have been outro'ed before through a prior batch — resume if necessary
resume_effect(offscreen.effect);
this.#onscreen.set(key, offscreen.effect);
this.#offscreen.delete(key);
if (DEV) {
// Tell hmr.js about the anchor it should use for updates,
// since the initial one will be removed
/** @type {any} */ (get_last_child(offscreen.fragment))[HMR_ANCHOR] = this.anchor;
if (onscreen) {
// effect is already in the DOM — abort any current outro
resume_effect(onscreen);
this.#outroing.delete(key);
} else {
// effect is currently offscreen. put it in the DOM
var offscreen = this.#offscreen.get(key);
if (offscreen) {
// effect could have been outro'ed before through a prior batch — resume if necessary
resume_effect(offscreen.effect);
this.#onscreen.set(key, offscreen.effect);
this.#offscreen.delete(key);
if (DEV) {
// Tell hmr.js about the anchor it should use for updates,
// since the initial one will be removed
/** @type {any} */ (get_last_child(offscreen.fragment))[HMR_ANCHOR] = this.anchor;
}
// remove the anchor...
remove_node(/** @type {ChildNode} */ (get_last_child(offscreen.fragment)));
// ...and append the fragment
insert_before(this.anchor, offscreen.fragment);
onscreen = offscreen.effect;
}
}
// remove the anchor...
remove_node(/** @type {ChildNode} */ (get_last_child(offscreen.fragment)));
for (const [b, k] of this.#batches) {
this.#batches.delete(b);
// ...and append the fragment
insert_before(this.anchor, offscreen.fragment);
onscreen = offscreen.effect;
}
}
if (b === batch) {
// keep values for newer batches
break;
}
for (const [b, k] of this.#batches) {
this.#batches.delete(b);
const offscreen = this.#offscreen.get(k);
if (b === batch) {
// keep values for newer batches
break;
if (offscreen) {
// for older batches, destroy offscreen effects
// as they will never be committed
destroy_effect(offscreen.effect);
this.#offscreen.delete(k);
}
}
const offscreen = this.#offscreen.get(k);
// outro/destroy all onscreen effects...
for (const [k, effect] of this.#onscreen) {
// ...except the one that was just committed
// or those that are already outroing (else the transition is aborted and the effect destroyed right away)
if (k === key || this.#outroing.has(k)) continue;
if (offscreen) {
// for older batches, destroy offscreen effects
// as they will never be committed
destroy_effect(offscreen.effect);
this.#offscreen.delete(k);
}
}
const on_destroy = () => {
const keys = Array.from(this.#batches.values());
// outro/destroy all onscreen effects...
for (const [k, effect] of this.#onscreen) {
// ...except the one that was just committed
// or those that are already outroing (else the transition is aborted and the effect destroyed right away)
if (k === key || this.#outroing.has(k)) continue;
if (keys.includes(k)) {
// keep the effect offscreen, as another batch will need it
var fragment = create_fragment();
move_effect(effect, fragment);
const on_destroy = () => {
const keys = Array.from(this.#batches.values());
append_child(fragment, create_text()); // TODO can we avoid this?
if (keys.includes(k)) {
// keep the effect offscreen, as another batch will need it
var fragment = create_fragment();
move_effect(effect, fragment);
this.#offscreen.set(k, { effect, fragment });
} else {
destroy_effect(effect);
}
append_child(fragment, create_text()); // TODO can we avoid this?
this.#outroing.delete(k);
this.#onscreen.delete(k);
};
this.#offscreen.set(k, { effect, fragment });
if (this.#transition || !onscreen) {
this.#outroing.add(k);
pause_effect(effect, on_destroy, false);
} else {
destroy_effect(effect);
on_destroy();
}
this.#outroing.delete(k);
this.#onscreen.delete(k);
};
if (this.#transition || !onscreen) {
this.#outroing.add(k);
pause_effect(effect, on_destroy, false);
} else {
on_destroy();
}
} finally {
pop_renderer?.();
}
pop_renderer?.();
};
/**

@ -273,30 +273,32 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
var pop_renderer = push_renderer(renderer);
state.pending.delete(batch);
state.fallback = fallback;
reconcile(state, array, anchor, flags, get_key);
if (fallback !== null) {
if (array.length === 0) {
if ((fallback.f & EFFECT_OFFSCREEN) === 0) {
resume_effect(fallback);
try {
state.pending.delete(batch);
state.fallback = fallback;
reconcile(state, array, anchor, flags, get_key);
if (fallback !== null) {
if (array.length === 0) {
if ((fallback.f & EFFECT_OFFSCREEN) === 0) {
resume_effect(fallback);
} else {
fallback.f ^= EFFECT_OFFSCREEN;
move(fallback, null, anchor);
}
} else {
fallback.f ^= EFFECT_OFFSCREEN;
move(fallback, null, anchor);
pause_effect(fallback, () => {
// TODO only null out if no pending batch needs it,
// otherwise re-add `fallback.fragment` and move the
// effect into it
fallback = null;
});
}
} else {
pause_effect(fallback, () => {
// TODO only null out if no pending batch needs it,
// otherwise re-add `fallback.fragment` and move the
// effect into it
fallback = null;
});
}
} finally {
pop_renderer?.();
}
pop_renderer?.();
}
/**

@ -753,17 +753,19 @@ export function move_effect(effect, fragment) {
var pop_renderer = push_renderer(effect.r);
/** @type {TemplateNode | null} */
var node = effect.nodes.start;
var end = effect.nodes.end;
while (node !== null) {
try {
/** @type {TemplateNode | null} */
var next = node === end ? null : get_next_sibling(node);
var node = effect.nodes.start;
var end = effect.nodes.end;
append_child(fragment, node);
node = next;
}
while (node !== null) {
/** @type {TemplateNode | null} */
var next = node === end ? null : get_next_sibling(node);
pop_renderer?.();
append_child(fragment, node);
node = next;
}
} finally {
pop_renderer?.();
}
}

Loading…
Cancel
Save