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