@@ -28,6 +28,9 @@ import { lifecycle_outside_component } from '../shared/errors.js';
28
28
const FLUSH_MICROTASK = 0 ;
29
29
const FLUSH_SYNC = 1 ;
30
30
31
+ // Used for DEV time error handling
32
+ /** @param {WeakSet<Error> } value */
33
+ const handled_errors = new WeakSet ( ) ;
31
34
// Used for controlling the flush of effects.
32
35
let current_scheduler_mode = FLUSH_MICROTASK ;
33
36
// Used for handling scheduling
@@ -239,6 +242,62 @@ export function check_dirtiness(reaction) {
239
242
return is_dirty ;
240
243
}
241
244
245
+ /**
246
+ * @param {Error } error
247
+ * @param {import("#client").Effect } effect
248
+ * @param {import("#client").ComponentContext | null } component_context
249
+ */
250
+ function handle_error ( error , effect , component_context ) {
251
+ // Given we don't yet have error boundaries, we will just always throw.
252
+ if ( ! DEV || handled_errors . has ( error ) || component_context === null ) {
253
+ throw error ;
254
+ }
255
+
256
+ const component_stack = [ ] ;
257
+
258
+ const effect_name = effect . fn . name ;
259
+
260
+ if ( effect_name ) {
261
+ component_stack . push ( effect_name ) ;
262
+ }
263
+
264
+ /** @type {import("#client").ComponentContext | null } */
265
+ let current_context = component_context ;
266
+
267
+ while ( current_context !== null ) {
268
+ var filename = current_context . function ?. filename ;
269
+
270
+ if ( filename ) {
271
+ const file = filename . split ( '/' ) . at ( - 1 ) ;
272
+ component_stack . push ( file ) ;
273
+ }
274
+
275
+ current_context = current_context . p ;
276
+ }
277
+
278
+ const indent = / F i r e f o x / . test ( navigator . userAgent ) ? ' ' : '\t' ;
279
+ error . message += `\n${ component_stack . map ( ( name ) => `\n${ indent } in ${ name } ` ) . join ( '' ) } \n` ;
280
+
281
+ const stack = error . stack ;
282
+
283
+ // Filter out internal files from callstack
284
+ if ( stack ) {
285
+ const lines = stack . split ( '\n' ) ;
286
+ const new_lines = [ ] ;
287
+ for ( let i = 0 ; i < lines . length ; i ++ ) {
288
+ const line = lines [ i ] ;
289
+ if ( line . includes ( 'svelte/src/internal' ) ) {
290
+ continue ;
291
+ }
292
+ new_lines . push ( line ) ;
293
+ }
294
+ error . stack = new_lines . join ( '\n' ) ;
295
+ }
296
+
297
+ handled_errors . add ( error ) ;
298
+ throw error ;
299
+ }
300
+
242
301
/**
243
302
* @template V
244
303
* @param {import('#client').Reaction } signal
@@ -260,7 +319,7 @@ export function execute_reaction_fn(signal) {
260
319
current_untracking = false ;
261
320
262
321
try {
263
- let res = signal . fn ( ) ;
322
+ let res = ( 0 , signal . fn ) ( ) ;
264
323
let dependencies = /** @type {import('#client').Value<unknown>[] } **/ ( signal . deps ) ;
265
324
if ( current_dependencies !== null ) {
266
325
let i ;
@@ -431,6 +490,8 @@ export function execute_effect(effect) {
431
490
execute_effect_teardown ( effect ) ;
432
491
var teardown = execute_reaction_fn ( effect ) ;
433
492
effect . teardown = typeof teardown === 'function' ? teardown : null ;
493
+ } catch ( error ) {
494
+ handle_error ( /** @type {Error } */ ( error ) , effect , current_component_context ) ;
434
495
} finally {
435
496
current_effect = previous_effect ;
436
497
current_component_context = previous_component_context ;
0 commit comments