@@ -17,102 +17,6 @@ import { is_content_editable_binding, is_svg } from '../../../../utils.js';
1717 * @param {Context } context
1818 */
1919export 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-
11620 const parent = context . path . at ( - 1 ) ;
11721
11822 if (
@@ -218,5 +122,123 @@ export function BindDirective(node, context) {
218122 }
219123 }
220124
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+
221243 context . next ( ) ;
222244}
0 commit comments