UNPKG

svelte

Version:

Cybernetically enhanced web apps

115 lines (95 loc) 2.94 kB
/** @import { Derived, Effect } from '#client' */ /** @import { Boundary } from './dom/blocks/boundary.js' */ import { DEV } from 'esm-env'; import { FILENAME } from '../../constants.js'; import { is_firefox } from './dom/operations.js'; import { ERROR_VALUE, BOUNDARY_EFFECT, EFFECT_RAN } from './constants.js'; import { define_property, get_descriptor } from '../shared/utils.js'; import { active_effect, active_reaction } from './runtime.js'; const adjustments = new WeakMap(); /** * @param {unknown} error */ export function handle_error(error) { var effect = active_effect; // for unowned deriveds, don't throw until we read the value if (effect === null) { /** @type {Derived} */ (active_reaction).f |= ERROR_VALUE; return error; } if (DEV && error instanceof Error && !adjustments.has(error)) { adjustments.set(error, get_adjustments(error, effect)); } if ((effect.f & EFFECT_RAN) === 0) { // if the error occurred while creating this subtree, we let it // bubble up until it hits a boundary that can handle it if ((effect.f & BOUNDARY_EFFECT) === 0) { if (!effect.parent && error instanceof Error) { apply_adjustments(error); } throw error; } /** @type {Boundary} */ (effect.b).error(error); } else { // otherwise we bubble up the effect tree ourselves invoke_error_boundary(error, effect); } } /** * @param {unknown} error * @param {Effect | null} effect */ export function invoke_error_boundary(error, effect) { while (effect !== null) { if ((effect.f & BOUNDARY_EFFECT) !== 0) { try { /** @type {Boundary} */ (effect.b).error(error); return; } catch {} } effect = effect.parent; } if (error instanceof Error) { apply_adjustments(error); } throw error; } /** * Add useful information to the error message/stack in development * @param {Error} error * @param {Effect} effect */ function get_adjustments(error, effect) { const message_descriptor = get_descriptor(error, 'message'); // if the message was already changed and it's not configurable we can't change it // or it will throw a different error swallowing the original error if (message_descriptor && !message_descriptor.configurable) return; var indent = is_firefox ? ' ' : '\t'; var component_stack = `\n${indent}in ${effect.fn?.name || '<unknown>'}`; var context = effect.ctx; while (context !== null) { component_stack += `\n${indent}in ${context.function?.[FILENAME].split('/').pop()}`; context = context.p; } return { message: error.message + `\n${component_stack}\n`, stack: error.stack ?.split('\n') .filter((line) => !line.includes('svelte/src/internal')) .join('\n') }; } /** * @param {Error} error */ function apply_adjustments(error) { const adjusted = adjustments.get(error); if (adjusted) { define_property(error, 'message', { value: adjusted.message }); define_property(error, 'stack', { value: adjusted.stack }); } }