Skip to content

Commit 989ff2f

Browse files
committed
move validation
1 parent d563884 commit 989ff2f

File tree

1 file changed

+100
-100
lines changed

1 file changed

+100
-100
lines changed

packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js

Lines changed: 100 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -17,106 +17,6 @@ import { is_content_editable_binding, is_svg } from '../../../../utils.js';
1717
* @param {Context} context
1818
*/
1919
export 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

Comments
 (0)