@@ -17,102 +17,6 @@ import { is_content_editable_binding, is_svg } from '../../../../utils.js';
17
17
* @param {Context } context
18
18
*/
19
19
export function BindDirective ( node , context ) {
20
- validate_no_const_assignment ( node , node . expression , context . state . scope , true ) ;
21
-
22
- const assignee = node . expression ;
23
- const left = object ( assignee ) ;
24
-
25
- if ( left === null ) {
26
- e . bind_invalid_expression ( node ) ;
27
- }
28
-
29
- const binding = context . state . scope . get ( left . name ) ;
30
-
31
- if ( assignee . type === 'Identifier' ) {
32
- // reassignment
33
- if (
34
- node . name !== 'this' && // bind:this also works for regular variables
35
- ( ! binding ||
36
- ( binding . kind !== 'state' &&
37
- binding . kind !== 'raw_state' &&
38
- binding . kind !== 'prop' &&
39
- binding . kind !== 'bindable_prop' &&
40
- binding . kind !== 'each' &&
41
- binding . kind !== 'store_sub' &&
42
- ! binding . updated ) ) // TODO wut?
43
- ) {
44
- e . bind_invalid_value ( node . expression ) ;
45
- }
46
-
47
- if ( context . state . analysis . runes && binding ?. kind === 'each' ) {
48
- e . each_item_invalid_assignment ( node ) ;
49
- }
50
-
51
- if ( binding ?. kind === 'snippet' ) {
52
- e . snippet_parameter_assignment ( node ) ;
53
- }
54
- }
55
-
56
- if ( node . name === 'group' ) {
57
- if ( ! binding ) {
58
- throw new Error ( 'Cannot find declaration for bind:group' ) ;
59
- }
60
-
61
- // Traverse the path upwards and find all EachBlocks who are (indirectly) contributing to bind:group,
62
- // i.e. one of their declarations is referenced in the binding. This allows group bindings to work
63
- // correctly when referencing a variable declared in an EachBlock by using the index of the each block
64
- // entries as keys.
65
- const each_blocks = [ ] ;
66
- const [ keypath , expression_ids ] = extract_all_identifiers_from_expression ( node . expression ) ;
67
- let ids = expression_ids ;
68
-
69
- let i = context . path . length ;
70
- while ( i -- ) {
71
- const parent = context . path [ i ] ;
72
-
73
- if ( parent . type === 'EachBlock' ) {
74
- const references = ids . filter ( ( id ) => parent . metadata . declarations . has ( id . name ) ) ;
75
-
76
- if ( references . length > 0 ) {
77
- parent . metadata . contains_group_binding = true ;
78
-
79
- each_blocks . push ( parent ) ;
80
- ids = ids . filter ( ( id ) => ! references . includes ( id ) ) ;
81
- ids . push ( ...extract_all_identifiers_from_expression ( parent . expression ) [ 1 ] ) ;
82
- }
83
- }
84
- }
85
-
86
- // The identifiers that make up the binding expression form they key for the binding group.
87
- // If the same identifiers in the same order are used in another bind:group, they will be in the same group.
88
- // (there's an edge case where `bind:group={a[i]}` will be in a different group than `bind:group={a[j]}` even when i == j,
89
- // but this is a limitation of the current static analysis we do; it also never worked in Svelte 4)
90
- const bindings = expression_ids . map ( ( id ) => context . state . scope . get ( id . name ) ) ;
91
- let group_name ;
92
-
93
- outer: for ( const [ [ key , b ] , group ] of context . state . analysis . binding_groups ) {
94
- if ( b . length !== bindings . length || key !== keypath ) continue ;
95
- for ( let i = 0 ; i < bindings . length ; i ++ ) {
96
- if ( bindings [ i ] !== b [ i ] ) continue outer;
97
- }
98
- group_name = group ;
99
- }
100
-
101
- if ( ! group_name ) {
102
- group_name = context . state . scope . root . unique ( 'binding_group' ) ;
103
- context . state . analysis . binding_groups . set ( [ keypath , bindings ] , group_name ) ;
104
- }
105
-
106
- node . metadata = {
107
- binding_group_name : group_name ,
108
- parent_each_blocks : each_blocks
109
- } ;
110
- }
111
-
112
- if ( binding ?. kind === 'each' && binding . metadata ?. inside_rest ) {
113
- w . bind_invalid_each_rest ( binding . node , binding . node . name ) ;
114
- }
115
-
116
20
const parent = context . path . at ( - 1 ) ;
117
21
118
22
if (
@@ -218,5 +122,123 @@ export function BindDirective(node, context) {
218
122
}
219
123
}
220
124
125
+ // When dealing with bind getters/setters skip the specific binding validation
126
+ // Group bindings aren't supported for getter/setters so we don't need to handle
127
+ // the metadata
128
+ if ( node . expression . type === 'SequenceExpression' ) {
129
+ if ( node . name === 'group' ) {
130
+ e . bind_group_invalid_expression ( node ) ;
131
+ }
132
+
133
+ let i = /** @type {number } */ ( node . expression . start ) ;
134
+ while ( context . state . analysis . source [ -- i ] !== '{' ) {
135
+ if ( context . state . analysis . source [ i ] === '(' ) {
136
+ e . bind_invalid_parens ( node , node . name ) ;
137
+ }
138
+ }
139
+
140
+ if ( node . expression . expressions . length !== 2 ) {
141
+ e . bind_invalid_expression ( node ) ;
142
+ }
143
+
144
+ return ;
145
+ }
146
+
147
+ validate_no_const_assignment ( node , node . expression , context . state . scope , true ) ;
148
+
149
+ const assignee = node . expression ;
150
+ const left = object ( assignee ) ;
151
+
152
+ if ( left === null ) {
153
+ e . bind_invalid_expression ( node ) ;
154
+ }
155
+
156
+ const binding = context . state . scope . get ( left . name ) ;
157
+
158
+ if ( assignee . type === 'Identifier' ) {
159
+ // reassignment
160
+ if (
161
+ node . name !== 'this' && // bind:this also works for regular variables
162
+ ( ! binding ||
163
+ ( binding . kind !== 'state' &&
164
+ binding . kind !== 'raw_state' &&
165
+ binding . kind !== 'prop' &&
166
+ binding . kind !== 'bindable_prop' &&
167
+ binding . kind !== 'each' &&
168
+ binding . kind !== 'store_sub' &&
169
+ ! binding . updated ) ) // TODO wut?
170
+ ) {
171
+ e . bind_invalid_value ( node . expression ) ;
172
+ }
173
+
174
+ if ( context . state . analysis . runes && binding ?. kind === 'each' ) {
175
+ e . each_item_invalid_assignment ( node ) ;
176
+ }
177
+
178
+ if ( binding ?. kind === 'snippet' ) {
179
+ e . snippet_parameter_assignment ( node ) ;
180
+ }
181
+ }
182
+
183
+ if ( node . name === 'group' ) {
184
+ if ( ! binding ) {
185
+ throw new Error ( 'Cannot find declaration for bind:group' ) ;
186
+ }
187
+
188
+ // Traverse the path upwards and find all EachBlocks who are (indirectly) contributing to bind:group,
189
+ // i.e. one of their declarations is referenced in the binding. This allows group bindings to work
190
+ // correctly when referencing a variable declared in an EachBlock by using the index of the each block
191
+ // entries as keys.
192
+ const each_blocks = [ ] ;
193
+ const [ keypath , expression_ids ] = extract_all_identifiers_from_expression ( node . expression ) ;
194
+ let ids = expression_ids ;
195
+
196
+ let i = context . path . length ;
197
+ while ( i -- ) {
198
+ const parent = context . path [ i ] ;
199
+
200
+ if ( parent . type === 'EachBlock' ) {
201
+ const references = ids . filter ( ( id ) => parent . metadata . declarations . has ( id . name ) ) ;
202
+
203
+ if ( references . length > 0 ) {
204
+ parent . metadata . contains_group_binding = true ;
205
+
206
+ each_blocks . push ( parent ) ;
207
+ ids = ids . filter ( ( id ) => ! references . includes ( id ) ) ;
208
+ ids . push ( ...extract_all_identifiers_from_expression ( parent . expression ) [ 1 ] ) ;
209
+ }
210
+ }
211
+ }
212
+
213
+ // The identifiers that make up the binding expression form they key for the binding group.
214
+ // If the same identifiers in the same order are used in another bind:group, they will be in the same group.
215
+ // (there's an edge case where `bind:group={a[i]}` will be in a different group than `bind:group={a[j]}` even when i == j,
216
+ // but this is a limitation of the current static analysis we do; it also never worked in Svelte 4)
217
+ const bindings = expression_ids . map ( ( id ) => context . state . scope . get ( id . name ) ) ;
218
+ let group_name ;
219
+
220
+ outer: for ( const [ [ key , b ] , group ] of context . state . analysis . binding_groups ) {
221
+ if ( b . length !== bindings . length || key !== keypath ) continue ;
222
+ for ( let i = 0 ; i < bindings . length ; i ++ ) {
223
+ if ( bindings [ i ] !== b [ i ] ) continue outer;
224
+ }
225
+ group_name = group ;
226
+ }
227
+
228
+ if ( ! group_name ) {
229
+ group_name = context . state . scope . root . unique ( 'binding_group' ) ;
230
+ context . state . analysis . binding_groups . set ( [ keypath , bindings ] , group_name ) ;
231
+ }
232
+
233
+ node . metadata = {
234
+ binding_group_name : group_name ,
235
+ parent_each_blocks : each_blocks
236
+ } ;
237
+ }
238
+
239
+ if ( binding ?. kind === 'each' && binding . metadata ?. inside_rest ) {
240
+ w . bind_invalid_each_rest ( binding . node , binding . node . name ) ;
241
+ }
242
+
221
243
context . next ( ) ;
222
244
}
0 commit comments