fix: prevent binding to imports (#13035)

fixes #13027 — prevents binding to imports, just like we already prevent binding to other constants (including derived values)
pull/13038/head
Rich Harris 1 year ago committed by GitHub
parent 8f3f07a70b
commit 8cda791d5a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: prevent binding to imports

@ -44,10 +44,6 @@ export function BindDirective(node, context) {
e.bind_invalid_value(node.expression);
}
if (binding?.kind === 'derived') {
e.constant_binding(node.expression, 'derived state');
}
if (context.state.analysis.runes && binding?.kind === 'each') {
e.each_item_invalid_assignment(node);
}

@ -71,7 +71,11 @@ export function validate_no_const_assignment(node, argument, scope, is_binding)
}
} else if (argument.type === 'Identifier') {
const binding = scope.get(argument.name);
if (binding?.declaration_kind === 'const' && binding.kind !== 'each') {
if (
binding?.kind === 'derived' ||
binding?.declaration_kind === 'import' ||
(binding?.declaration_kind === 'const' && binding.kind !== 'each')
) {
// e.invalid_const_assignment(
// node,
// is_binding,
@ -83,7 +87,12 @@ export function validate_no_const_assignment(node, argument, scope, is_binding)
// );
// TODO have a more specific error message for assignments to things like `{:then foo}`
const thing = 'constant';
const thing =
binding.declaration_kind === 'import'
? 'import'
: binding.kind === 'derived'
? 'derived state'
: 'constant';
if (is_binding) {
e.constant_binding(node, thing);

@ -390,7 +390,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
if (node.expression) {
for (const id of extract_identifiers_from_destructuring(node.expression)) {
const binding = scope.declare(id, 'derived', 'const');
const binding = scope.declare(id, 'template', 'const');
bindings.push(binding);
}
} else {
@ -401,7 +401,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
start: node.start,
end: node.end
};
const binding = scope.declare(id, 'derived', 'const');
const binding = scope.declare(id, 'template', 'const');
bindings.push(binding);
}
},
@ -492,7 +492,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
for (const id of extract_identifiers(declarator.id)) {
const binding = state.scope.declare(
id,
is_parent_const_tag ? 'derived' : 'normal',
is_parent_const_tag ? 'template' : 'normal',
node.kind,
declarator.init
);
@ -548,7 +548,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
binding.metadata = { inside_rest: is_rest_id };
}
if (node.context.type !== 'Identifier') {
scope.declare(b.id('$$item'), 'derived', 'synthetic');
scope.declare(b.id('$$item'), 'template', 'synthetic');
}
// Visit to pick up references from default initializers
visit(node.context, { scope });
@ -557,7 +557,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
const is_keyed =
node.key &&
(node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index);
scope.declare(b.id(node.index), is_keyed ? 'derived' : 'normal', 'const', node);
scope.declare(b.id(node.index), is_keyed ? 'template' : 'normal', 'const', node);
}
if (node.key) visit(node.key, { scope });
@ -604,7 +604,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
scopes.set(node.value, value_scope);
context.visit(node.value, { scope: value_scope });
for (const id of extract_identifiers(node.value)) {
then_scope.declare(id, 'derived', 'const');
then_scope.declare(id, 'template', 'const');
value_scope.declare(id, 'normal', 'const');
}
}
@ -618,7 +618,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
scopes.set(node.error, error_scope);
context.visit(node.error, { scope: error_scope });
for (const id of extract_identifiers(node.error)) {
catch_scope.declare(id, 'derived', 'const');
catch_scope.declare(id, 'template', 'const');
error_scope.declare(id, 'normal', 'const');
}
}

@ -267,6 +267,7 @@ export interface Binding {
* - `snippet`: A snippet parameter
* - `store_sub`: A $store value
* - `legacy_reactive`: A `$:` declaration
* - `template`: A binding declared in the template, e.g. in an `await` block or `const` tag
*/
kind:
| 'normal'
@ -279,7 +280,8 @@ export interface Binding {
| 'each'
| 'snippet'
| 'store_sub'
| 'legacy_reactive';
| 'legacy_reactive'
| 'template';
declaration_kind: DeclarationKind;
/**
* What the value was initialized with.

@ -0,0 +1,14 @@
[
{
"code": "constant_binding",
"message": "Cannot bind to import",
"start": {
"line": 6,
"column": 7
},
"end": {
"line": 6,
"column": 25
}
}
]

@ -0,0 +1,6 @@
<script>
import Input from './Input.svelte';
import { dummy } from './dummy.js';
</script>
<Input bind:value={dummy} />

@ -0,0 +1,14 @@
[
{
"code": "constant_binding",
"message": "Cannot bind to import",
"start": {
"line": 5,
"column": 7
},
"end": {
"line": 5,
"column": 25
}
}
]

@ -0,0 +1,5 @@
<script>
import { dummy } from './dummy.js';
</script>
<input bind:value={dummy}>

@ -922,6 +922,7 @@ declare module 'svelte/compiler' {
* - `snippet`: A snippet parameter
* - `store_sub`: A $store value
* - `legacy_reactive`: A `$:` declaration
* - `template`: A binding declared in the template, e.g. in an `await` block or `const` tag
*/
kind:
| 'normal'
@ -934,7 +935,8 @@ declare module 'svelte/compiler' {
| 'each'
| 'snippet'
| 'store_sub'
| 'legacy_reactive';
| 'legacy_reactive'
| 'template';
declaration_kind: DeclarationKind;
/**
* What the value was initialized with.

Loading…
Cancel
Save