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); 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') { if (context.state.analysis.runes && binding?.kind === 'each') {
e.each_item_invalid_assignment(node); 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') { } else if (argument.type === 'Identifier') {
const binding = scope.get(argument.name); 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( // e.invalid_const_assignment(
// node, // node,
// is_binding, // 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}` // 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) { if (is_binding) {
e.constant_binding(node, thing); e.constant_binding(node, thing);

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

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

Loading…
Cancel
Save