UNPKG

svelte

Version:

Cybernetically enhanced web apps

134 lines (114 loc) 3.22 kB
/** @import { Effect, TemplateNode, } from '#client' */ import { BOUNDARY_EFFECT, EFFECT_TRANSPARENT } from '../../constants.js'; import { component_context, set_component_context } from '../../context.js'; import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js'; import { active_effect, active_reaction, handle_error, set_active_effect, set_active_reaction, reset_is_throwing_error } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating, next, remove_nodes, set_hydrate_node } from '../hydration.js'; import { queue_micro_task } from '../task.js'; /** * @param {Effect} boundary * @param {() => void} fn */ function with_boundary(boundary, fn) { var previous_effect = active_effect; var previous_reaction = active_reaction; var previous_ctx = component_context; set_active_effect(boundary); set_active_reaction(boundary); set_component_context(boundary.ctx); try { fn(); } finally { set_active_effect(previous_effect); set_active_reaction(previous_reaction); set_component_context(previous_ctx); } } /** * @param {TemplateNode} node * @param {{ * onerror?: (error: unknown, reset: () => void) => void, * failed?: (anchor: Node, error: () => unknown, reset: () => () => void) => void * }} props * @param {((anchor: Node) => void)} boundary_fn * @returns {void} */ export function boundary(node, props, boundary_fn) { var anchor = node; /** @type {Effect} */ var boundary_effect; block(() => { var boundary = /** @type {Effect} */ (active_effect); var hydrate_open = hydrate_node; var is_creating_fallback = false; // We re-use the effect's fn property to avoid allocation of an additional field boundary.fn = (/** @type {unknown}} */ error) => { var onerror = props.onerror; let failed = props.failed; // If we have nothing to capture the error, or if we hit an error while // rendering the fallback, re-throw for another boundary to handle if ((!onerror && !failed) || is_creating_fallback) { throw error; } var reset = () => { pause_effect(boundary_effect); with_boundary(boundary, () => { is_creating_fallback = false; boundary_effect = branch(() => boundary_fn(anchor)); reset_is_throwing_error(); }); }; onerror?.(error, reset); if (boundary_effect) { destroy_effect(boundary_effect); } else if (hydrating) { set_hydrate_node(hydrate_open); next(); set_hydrate_node(remove_nodes()); } if (failed) { // Render the `failed` snippet in a microtask queue_micro_task(() => { with_boundary(boundary, () => { is_creating_fallback = true; try { boundary_effect = branch(() => { failed( anchor, () => error, () => reset ); }); } catch (error) { handle_error(error, boundary, null, boundary.ctx); } reset_is_throwing_error(); is_creating_fallback = false; }); }); } }; if (hydrating) { hydrate_next(); } boundary_effect = branch(() => boundary_fn(anchor)); reset_is_throwing_error(); }, EFFECT_TRANSPARENT | BOUNDARY_EFFECT); if (hydrating) { anchor = hydrate_node; } }