Skip to content

Commit 8cda791

Browse files
authored
fix: prevent binding to imports (#13035)
fixes #13027 — prevents binding to imports, just like we already prevent binding to other constants (including derived values)
1 parent 8f3f07a commit 8cda791

File tree

10 files changed

+68
-15
lines changed

10 files changed

+68
-15
lines changed

.changeset/six-beans-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: prevent binding to imports

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ export function BindDirective(node, context) {
4444
e.bind_invalid_value(node.expression);
4545
}
4646

47-
if (binding?.kind === 'derived') {
48-
e.constant_binding(node.expression, 'derived state');
49-
}
50-
5147
if (context.state.analysis.runes && binding?.kind === 'each') {
5248
e.each_item_invalid_assignment(node);
5349
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ export function validate_no_const_assignment(node, argument, scope, is_binding)
7171
}
7272
} else if (argument.type === 'Identifier') {
7373
const binding = scope.get(argument.name);
74-
if (binding?.declaration_kind === 'const' && binding.kind !== 'each') {
74+
if (
75+
binding?.kind === 'derived' ||
76+
binding?.declaration_kind === 'import' ||
77+
(binding?.declaration_kind === 'const' && binding.kind !== 'each')
78+
) {
7579
// e.invalid_const_assignment(
7680
// node,
7781
// is_binding,
@@ -83,7 +87,12 @@ export function validate_no_const_assignment(node, argument, scope, is_binding)
8387
// );
8488

8589
// TODO have a more specific error message for assignments to things like `{:then foo}`
86-
const thing = 'constant';
90+
const thing =
91+
binding.declaration_kind === 'import'
92+
? 'import'
93+
: binding.kind === 'derived'
94+
? 'derived state'
95+
: 'constant';
8796

8897
if (is_binding) {
8998
e.constant_binding(node, thing);

packages/svelte/src/compiler/phases/scope.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
390390

391391
if (node.expression) {
392392
for (const id of extract_identifiers_from_destructuring(node.expression)) {
393-
const binding = scope.declare(id, 'derived', 'const');
393+
const binding = scope.declare(id, 'template', 'const');
394394
bindings.push(binding);
395395
}
396396
} else {
@@ -401,7 +401,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
401401
start: node.start,
402402
end: node.end
403403
};
404-
const binding = scope.declare(id, 'derived', 'const');
404+
const binding = scope.declare(id, 'template', 'const');
405405
bindings.push(binding);
406406
}
407407
},
@@ -492,7 +492,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
492492
for (const id of extract_identifiers(declarator.id)) {
493493
const binding = state.scope.declare(
494494
id,
495-
is_parent_const_tag ? 'derived' : 'normal',
495+
is_parent_const_tag ? 'template' : 'normal',
496496
node.kind,
497497
declarator.init
498498
);
@@ -548,7 +548,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
548548
binding.metadata = { inside_rest: is_rest_id };
549549
}
550550
if (node.context.type !== 'Identifier') {
551-
scope.declare(b.id('$$item'), 'derived', 'synthetic');
551+
scope.declare(b.id('$$item'), 'template', 'synthetic');
552552
}
553553
// Visit to pick up references from default initializers
554554
visit(node.context, { scope });
@@ -557,7 +557,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
557557
const is_keyed =
558558
node.key &&
559559
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index);
560-
scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const', node);
560+
scope.declare(b.id(node.index), is_keyed ? 'template' : 'normal', 'const', node);
561561
}
562562
if (node.key) visit(node.key, { scope });
563563

@@ -604,7 +604,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
604604
scopes.set(node.value, value_scope);
605605
context.visit(node.value, { scope: value_scope });
606606
for (const id of extract_identifiers(node.value)) {
607-
then_scope.declare(id, 'derived', 'const');
607+
then_scope.declare(id, 'template', 'const');
608608
value_scope.declare(id, 'normal', 'const');
609609
}
610610
}
@@ -618,7 +618,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
618618
scopes.set(node.error, error_scope);
619619
context.visit(node.error, { scope: error_scope });
620620
for (const id of extract_identifiers(node.error)) {
621-
catch_scope.declare(id, 'derived', 'const');
621+
catch_scope.declare(id, 'template', 'const');
622622
error_scope.declare(id, 'normal', 'const');
623623
}
624624
}

packages/svelte/src/compiler/types/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export interface Binding {
267267
* - `snippet`: A snippet parameter
268268
* - `store_sub`: A $store value
269269
* - `legacy_reactive`: A `$:` declaration
270+
* - `template`: A binding declared in the template, e.g. in an `await` block or `const` tag
270271
*/
271272
kind:
272273
| 'normal'
@@ -279,7 +280,8 @@ export interface Binding {
279280
| 'each'
280281
| 'snippet'
281282
| 'store_sub'
282-
| 'legacy_reactive';
283+
| 'legacy_reactive'
284+
| 'template';
283285
declaration_kind: DeclarationKind;
284286
/**
285287
* What the value was initialized with.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"code": "constant_binding",
4+
"message": "Cannot bind to import",
5+
"start": {
6+
"line": 6,
7+
"column": 7
8+
},
9+
"end": {
10+
"line": 6,
11+
"column": 25
12+
}
13+
}
14+
]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
import Input from './Input.svelte';
3+
import { dummy } from './dummy.js';
4+
</script>
5+
6+
<Input bind:value={dummy} />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"code": "constant_binding",
4+
"message": "Cannot bind to import",
5+
"start": {
6+
"line": 5,
7+
"column": 7
8+
},
9+
"end": {
10+
"line": 5,
11+
"column": 25
12+
}
13+
}
14+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
import { dummy } from './dummy.js';
3+
</script>
4+
5+
<input bind:value={dummy}>

packages/svelte/types/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,7 @@ declare module 'svelte/compiler' {
922922
* - `snippet`: A snippet parameter
923923
* - `store_sub`: A $store value
924924
* - `legacy_reactive`: A `$:` declaration
925+
* - `template`: A binding declared in the template, e.g. in an `await` block or `const` tag
925926
*/
926927
kind:
927928
| 'normal'
@@ -934,7 +935,8 @@ declare module 'svelte/compiler' {
934935
| 'each'
935936
| 'snippet'
936937
| 'store_sub'
937-
| 'legacy_reactive';
938+
| 'legacy_reactive'
939+
| 'template';
938940
declaration_kind: DeclarationKind;
939941
/**
940942
* What the value was initialized with.

0 commit comments

Comments
 (0)