1
1
import { UNINITIALIZED } from '../../../constants.js' ;
2
2
import { snapshot } from '../../shared/clone.js' ;
3
- import { inspect_effect , validate_effect } from '../reactivity/effects.js' ;
3
+ import { inspect_effect , render_effect , validate_effect } from '../reactivity/effects.js' ;
4
4
import { untrack } from '../runtime.js' ;
5
5
6
6
/**
@@ -12,29 +12,44 @@ export function inspect(get_value, inspector = console.log) {
12
12
validate_effect ( '$inspect' ) ;
13
13
14
14
let initial = true ;
15
+ let error = /** @type {any } */ ( UNINITIALIZED ) ;
15
16
17
+ // Inspect effects runs synchronously so that we can capture useful
18
+ // stack traces. As a consequence, reading the value might result
19
+ // in an error (an `$inspect(object.property)` will run before the
20
+ // `{#if object}...{/if}` that contains it)
16
21
inspect_effect ( ( ) => {
17
- /** @type {any } */
18
- var value = UNINITIALIZED ;
19
-
20
- // Capturing the value might result in an exception due to the inspect effect being
21
- // sync and thus operating on stale data. In the case we encounter an exception we
22
- // can bail-out of reporting the value. Instead we simply console.error the error
23
- // so at least it's known that an error occured, but we don't stop execution
24
22
try {
25
- value = get_value ( ) ;
26
- } catch ( error ) {
27
- // eslint-disable-next-line no-console
28
- console . error ( error ) ;
23
+ var value = get_value ( ) ;
24
+ } catch ( e ) {
25
+ error = e ;
26
+ return ;
29
27
}
30
28
31
- if ( value !== UNINITIALIZED ) {
32
- var snap = snapshot ( value , true ) ;
33
- untrack ( ( ) => {
34
- inspector ( initial ? 'init' : 'update' , ...snap ) ;
35
- } ) ;
36
- }
29
+ var snap = snapshot ( value , true ) ;
30
+ untrack ( ( ) => {
31
+ inspector ( initial ? 'init' : 'update' , ...snap ) ;
32
+ } ) ;
37
33
38
34
initial = false ;
39
35
} ) ;
36
+
37
+ // If an error occurs, we store it (along with its stack trace).
38
+ // If the render effect subsequently runs, we log the error,
39
+ // but if it doesn't run it's because the `$inspect` was
40
+ // destroyed, meaning we don't need to bother
41
+ render_effect ( ( ) => {
42
+ try {
43
+ // call `get_value` so that this runs alongside the inspect effect
44
+ get_value ( ) ;
45
+ } catch {
46
+ // ignore
47
+ }
48
+
49
+ if ( error !== UNINITIALIZED ) {
50
+ // eslint-disable-next-line no-console
51
+ console . error ( error ) ;
52
+ error = UNINITIALIZED ;
53
+ }
54
+ } ) ;
40
55
}
0 commit comments