@@ -4,6 +4,19 @@ import { current_hydration_fragment } from './hydration.js';
4
4
import { child_frag } from './operations.js' ;
5
5
import { proxy } from './proxy/proxy.js' ;
6
6
7
+ /**
8
+ * @typedef {Record<string | symbol, any> | undefined } ComponentReturn
9
+ *
10
+ * @typedef {any[] } ComponentArgs
11
+ *
12
+ * @typedef {(...args: ComponentArgs) => ComponentReturn } Component
13
+ *
14
+ * @typedef {{
15
+ * set_component: (new_component: Component) => void
16
+ * proxy_component?: (...args: ComponentArgs) => ComponentReturn
17
+ * }} HotData<Component>
18
+ */
19
+
7
20
function find_surrounding_ssr_commments ( ) {
8
21
if ( ! current_hydration_fragment ?. [ 0 ] ) return null ;
9
22
@@ -42,76 +55,103 @@ function find_surrounding_ssr_commments() {
42
55
}
43
56
44
57
/**
45
- * @template {any[]} ComponentArgs
46
- * @template {Record<string | symbol, any> | undefined} ComponentReturn
47
- * @template {(...args: ComponentArgs) => ComponentReturn} Component
48
- *
49
- * @param {{
50
- * component_signal: ReturnType<typeof source<Component>>,
51
- * proxy?: (...args: ComponentArgs) => ComponentReturn
52
- * }} hot_data
53
58
* @param {Component } new_component
54
59
*/
55
- export function hmr ( hot_data , new_component ) {
56
- if ( hot_data . proxy ) {
57
- set ( hot_data . component_signal , new_component ) ;
58
- } else {
59
- hot_data . component_signal = source ( new_component ) ;
60
+ function create_proxy_component ( new_component ) {
61
+ const component_signal = source ( new_component ) ;
60
62
61
- // @ts -ignore
62
- hot_data . proxy = function ( $$anchor , ...args ) {
63
- const accessors_proxy = proxy ( /** @type {import('./proxy/proxy.js').StateObject } */ ( { } ) ) ;
64
- /** @type {Set<string> } */
65
- const accessors_keys = new Set ( ) ;
63
+ let component_name = '' ;
66
64
67
- // During hydration the root component will receive a null $$anchor. The
68
- // following is a hack to get our `key` a node to render to, all while
69
- // avoiding it to "consume" the SSR marker.
70
- //
71
- // TODO better get the eyes of someone with understanding of hydration on this
72
- //
73
- // If this failes, we get an ugly hydration failure message, but HMR should
74
- // still work after that... Maybe we can show a more specific error message than
75
- // the generic hydration failure one (that could be misleading in this case).
76
- //
77
- if ( ! $$anchor && current_hydration_fragment ?. [ 0 ] ) {
78
- const ssr0 = find_surrounding_ssr_commments ( ) ;
79
- if ( ssr0 ) {
80
- const [ before , after ] = ssr0 ;
81
- current_hydration_fragment . unshift ( before ) ;
82
- current_hydration_fragment . push ( after ) ;
83
- $$anchor = child_frag ( current_hydration_fragment ) ;
84
- }
65
+ /**
66
+ * @type {HotData["set_component"] }
67
+ */
68
+ function set_component ( new_component ) {
69
+ component_name = new_component . name ;
70
+ set ( component_signal , new_component ) ;
71
+ }
72
+
73
+ // @ts -ignore
74
+ function proxy_component ( $$anchor , ...args ) {
75
+ const accessors_proxy = proxy ( /** @type {import('./proxy/proxy.js').StateObject } */ ( { } ) ) ;
76
+ /** @type {Set<string> } */
77
+ const accessors_keys = new Set ( ) ;
78
+
79
+ // During hydration the root component will receive a null $$anchor. The
80
+ // following is a hack to get our `key` a node to render to, all while
81
+ // avoiding it to "consume" the SSR marker.
82
+ //
83
+ // TODO better get the eyes of someone with understanding of hydration on this
84
+ //
85
+ // If this fails, we get an ugly hydration failure message, but HMR should
86
+ // still work after that... Maybe we can show a more specific error message than
87
+ // the generic hydration failure one (that could be misleading in this case).
88
+ //
89
+ if ( ! $$anchor && current_hydration_fragment ?. [ 0 ] ) {
90
+ const ssr0 = find_surrounding_ssr_commments ( ) ;
91
+ if ( ssr0 ) {
92
+ const [ before , after ] = ssr0 ;
93
+ current_hydration_fragment . unshift ( before ) ;
94
+ current_hydration_fragment . push ( after ) ;
95
+ $$anchor = child_frag ( current_hydration_fragment ) ;
85
96
}
97
+ }
86
98
87
- key (
88
- $$anchor ,
89
- ( ) => get ( hot_data . component_signal ) ,
90
- ( $$anchor ) => {
91
- const component = get ( hot_data . component_signal ) ;
92
- // @ts -ignore
93
- const new_accessors = component ( $$anchor , ...args ) ;
94
-
95
- const removed_keys = new Set ( accessors_keys ) ;
96
-
97
- if ( new_accessors ) {
98
- for ( const [ key , value ] of Object . entries ( new_accessors ) ) {
99
- accessors_proxy [ key ] = value ;
100
- accessors_keys . add ( key ) ;
101
- removed_keys . delete ( key ) ;
102
- }
103
- }
99
+ key (
100
+ $$anchor ,
101
+ ( ) => get ( component_signal ) ,
102
+ ( $$anchor ) => {
103
+ const component = get ( component_signal ) ;
104
104
105
- for ( const key of removed_keys ) {
106
- accessors_keys . delete ( key ) ;
107
- accessors_proxy [ key ] = undefined ;
105
+ // @ts -ignore
106
+ const new_accessors = component ( $$anchor , ...args ) ;
107
+
108
+ const removed_keys = new Set ( accessors_keys ) ;
109
+
110
+ if ( new_accessors ) {
111
+ for ( const [ key , value ] of Object . entries ( new_accessors ) ) {
112
+ accessors_proxy [ key ] = value ;
113
+ accessors_keys . add ( key ) ;
114
+ removed_keys . delete ( key ) ;
108
115
}
109
116
}
110
- ) ;
111
117
112
- return accessors_proxy ;
113
- } ;
118
+ for ( const key of removed_keys ) {
119
+ accessors_keys . delete ( key ) ;
120
+ accessors_proxy [ key ] = undefined ;
121
+ }
122
+ }
123
+ ) ;
124
+
125
+ try {
126
+ Object . defineProperty ( proxy_component , 'name' , {
127
+ get ( ) {
128
+ return component_name ;
129
+ }
130
+ } ) ;
131
+ } catch ( err ) {
132
+ console . warn ( "[Svelte HMR] Failed to proxy component function's name" , err ) ;
133
+ }
134
+
135
+ return accessors_proxy ;
136
+ }
137
+
138
+ return { proxy_component, set_component } ;
139
+ }
140
+
141
+ /**
142
+ * @param {HotData } hot_data
143
+ * @param {Component } new_component
144
+ */
145
+ export function hmr ( hot_data , new_component ) {
146
+ if ( hot_data . set_component ) {
147
+ hot_data . set_component ( new_component ) ;
148
+ } else {
149
+ ( {
150
+ //
151
+ proxy_component : hot_data . proxy_component ,
152
+ set_component : hot_data . set_component
153
+ } = create_proxy_component ( new_component ) ) ;
114
154
}
115
155
116
- return hot_data . proxy ;
156
+ return hot_data . proxy_component ;
117
157
}
0 commit comments