diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts
index 2f8874de7a..85623c6da7 100644
--- a/src/compiler/compile/Component.ts
+++ b/src/compiler/compile/Component.ts
@@ -788,6 +788,33 @@ export default class Component {
scope = map.get(node);
}
+ let deep = false;
+ let names: string[] | null;
+
+ if (node.type === 'AssignmentExpression') {
+ deep = node.left.type === 'MemberExpression';
+ names = deep
+ ? [get_object(node.left).name]
+ : extract_names(node.left);
+ } else if (node.type === 'UpdateExpression') {
+ deep = node.argument.type === 'MemberExpression';
+ const { name } = get_object(node.argument);
+ names = [name];
+ }
+
+
+ if (names) {
+ names.forEach(name => {
+ const variable = component.var_lookup.get(name);
+ if (variable && variable.writable === false && !deep) {
+ component.warn(node as any, {
+ code: 'assignment-to-const',
+ message: 'You are assigning to a const'
+ });
+ }
+ });
+ }
+
if (node.type === 'ImportDeclaration') {
component.extract_imports(node);
// TODO: to use actual remove
diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts
index 0603c15589..c3817dc3da 100644
--- a/src/compiler/compile/nodes/shared/Expression.ts
+++ b/src/compiler/compile/nodes/shared/Expression.ts
@@ -62,7 +62,7 @@ export default class Expression {
// discover dependencies, but don't change the code yet
walk(info, {
- enter(node: any, parent: any, key: string) {
+ enter(node: Node, parent: any, key: string) {
// don't manipulate shorthand props twice
if (key === 'key' && parent.shorthand) return;
// don't manipulate `import.meta`, `new.target`
@@ -126,6 +126,7 @@ export default class Expression {
deep = node.left.type === 'MemberExpression';
names = extract_names(deep ? get_object(node.left) : node.left);
} else if (node.type === 'UpdateExpression') {
+ deep = node.argument.type === 'MemberExpression';
names = extract_names(get_object(node.argument));
}
}
@@ -146,8 +147,18 @@ export default class Expression {
} else {
component.add_reference(node, name);
- const variable = component.var_lookup.get(name);
- if (variable) variable[deep ? 'mutated' : 'reassigned'] = true;
+ const variable = component.var_lookup.get(name);
+
+ if (variable) {
+ variable[deep ? 'mutated' : 'reassigned'] = true;
+
+ if (!deep && variable.writable === false) {
+ component.warn(node as any, {
+ code: 'assignment-to-const',
+ message: 'You are assigning to a const'
+ });
+ }
+ }
}
});
}
diff --git a/test/validator/samples/assignment-to-const/input.svelte b/test/validator/samples/assignment-to-const/input.svelte
new file mode 100644
index 0000000000..b685507b56
--- /dev/null
+++ b/test/validator/samples/assignment-to-const/input.svelte
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/validator/samples/assignment-to-const/warnings.json b/test/validator/samples/assignment-to-const/warnings.json
new file mode 100644
index 0000000000..fac54a6058
--- /dev/null
+++ b/test/validator/samples/assignment-to-const/warnings.json
@@ -0,0 +1,62 @@
+[
+ {
+ "code": "assignment-to-const",
+ "message": "You are assigning to a const",
+ "start": {
+ "line": 31,
+ "column": 24,
+ "character": 512
+ },
+ "end": {
+ "line": 31,
+ "column": 41,
+ "character": 529
+ },
+ "pos": 512
+ },
+ {
+ "code": "assignment-to-const",
+ "message": "You are assigning to a const",
+ "start": {
+ "line": 32,
+ "column": 24,
+ "character": 570
+ },
+ "end": {
+ "line": 32,
+ "column": 36,
+ "character": 582
+ },
+ "pos": 570
+ },
+ {
+ "code": "assignment-to-const",
+ "message": "You are assigning to a const",
+ "start": {
+ "line": 10,
+ "column": 2,
+ "character": 171
+ },
+ "end": {
+ "line": 10,
+ "column": 19,
+ "character": 188
+ },
+ "pos": 171
+ },
+ {
+ "code": "assignment-to-const",
+ "message": "You are assigning to a const",
+ "start": {
+ "line": 14,
+ "column": 2,
+ "character": 215
+ },
+ "end": {
+ "line": 14,
+ "column": 14,
+ "character": 227
+ },
+ "pos": 215
+ }
+]
\ No newline at end of file